Showing preview only (945K chars total). Download the full file or copy to clipboard to get everything.
Repository: GoogleCloudPlatform/kubectl-ai
Branch: main
Commit: 54037aecc023
Files: 152
Total size: 899.0 KB
Directory structure:
gitextract_bjyvth3y/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ ├── actions/
│ │ └── kind-cluster-setup/
│ │ └── action.yaml
│ ├── kubectl-ai.cast
│ └── workflows/
│ ├── ci-periodic.yaml
│ ├── ci-presubmit.yaml
│ ├── k8s-bench-evals.yaml
│ └── release.yaml
├── .gitignore
├── .goreleaser.yaml
├── .krew.yaml
├── CONTAINER.md
├── LICENSE
├── README.md
├── cmd/
│ ├── main.go
│ ├── mcp.go
│ └── mcp_test.go
├── contributing.md
├── dev/
│ ├── ci/
│ │ ├── periodics/
│ │ │ ├── analyze-evals.sh
│ │ │ └── run-evals.sh
│ │ └── presubmits/
│ │ ├── go-build.sh
│ │ ├── go-vet.sh
│ │ ├── verify-autogen.sh
│ │ ├── verify-format.sh
│ │ ├── verify-gomod.sh
│ │ └── verify-mocks.sh
│ └── tasks/
│ ├── build-images
│ ├── demo.md
│ ├── deploy-to-gke
│ ├── deploy-to-kind
│ ├── format.sh
│ ├── generate-github-actions.sh
│ └── gomod.sh
├── docs/
│ ├── bedrock.md
│ ├── gke-deployment.md
│ ├── mcp-client.md
│ ├── mcp-server.md
│ ├── mocking.md
│ ├── tool-samples/
│ │ ├── argocd.yaml
│ │ ├── gcloud.yaml
│ │ ├── gh.yaml
│ │ └── kustomize.yaml
│ └── tools.md
├── go.mod
├── go.sum
├── go.work
├── go.work.sum
├── gollm/
│ ├── README.md
│ ├── anthropic.go
│ ├── anthropic_test.go
│ ├── azopenai.go
│ ├── bedrock.go
│ ├── factory.go
│ ├── factory_test.go
│ ├── gemini.go
│ ├── go.mod
│ ├── go.sum
│ ├── grok.go
│ ├── http_journal.go
│ ├── interfaces.go
│ ├── llamacpp.go
│ ├── ollama.go
│ ├── openai.go
│ ├── openai_response.go
│ ├── openai_test.go
│ ├── persist.go
│ ├── schema.go
│ └── shims.go
├── images/
│ └── kubectl-ai/
│ └── Dockerfile
├── install.sh
├── internal/
│ └── mocks/
│ ├── agent_mock.go
│ ├── generate.go
│ ├── gollm_mock.go
│ └── tools_mock.go
├── k8s/
│ ├── all_in_one.yaml
│ ├── kubectl-ai-gke.yaml
│ ├── kubectl-ai.yaml
│ └── sandbox/
│ ├── all-in-one.yaml
│ ├── cluster_role.yaml
│ ├── cluster_role_binding.yaml
│ ├── namespace.yaml
│ ├── role.yaml
│ ├── role_binding.yaml
│ └── service_account.yaml
├── kubectl-utils/
│ ├── README.md
│ ├── cmd/
│ │ └── kubectl-expect/
│ │ └── main.go
│ ├── go.mod
│ ├── go.sum
│ └── pkg/
│ ├── kel/
│ │ ├── expression.go
│ │ └── info.go
│ └── kube/
│ ├── client.go
│ └── discovery.go
├── makefile
├── modelserving/
│ ├── .gitignore
│ ├── README.md
│ ├── dev/
│ │ └── tasks/
│ │ ├── build-images
│ │ ├── deploy-to-gke
│ │ ├── deploy-to-kind
│ │ ├── download-model
│ │ └── run-local
│ ├── images/
│ │ ├── llamacpp-gemma3-12b-it/
│ │ │ └── Dockerfile
│ │ └── llamacpp-server/
│ │ └── Dockerfile
│ └── k8s/
│ ├── llm-server-cpu.yaml
│ ├── llm-server-rpc.yaml
│ ├── llm-server.yaml
│ ├── rpc-server-cpu.yaml
│ └── rpc-server-cuda.yaml
└── pkg/
├── agent/
│ ├── agent_e2e_test.go
│ ├── conversation.go
│ ├── conversation_test.go
│ ├── manager.go
│ ├── mcp_client.go
│ └── systemprompt_template_default.txt
├── api/
│ └── models.go
├── journal/
│ ├── context.go
│ ├── loader.go
│ ├── log.go
│ └── recorder.go
├── mcp/
│ ├── README.md
│ ├── client.go
│ ├── config.go
│ ├── constants.go
│ ├── default_config.yaml
│ ├── http_client.go
│ ├── interfaces.go
│ ├── manager.go
│ ├── stdio_client.go
│ └── utils.go
├── sandbox/
│ ├── executor.go
│ ├── kubernetes.go
│ ├── local.go
│ ├── seatbelt_executor.go
│ └── seatbelt_executor_others.go
├── sessions/
│ ├── filesystem.go
│ ├── manager.go
│ ├── memory.go
│ └── store.go
├── tools/
│ ├── bash_tool.go
│ ├── custom_tool.go
│ ├── custom_tool_test.go
│ ├── interfaces.go
│ ├── kubectl_filter.go
│ ├── kubectl_filter_test.go
│ ├── kubectl_tool.go
│ ├── mcp_tool.go
│ ├── streaming.go
│ └── tools.go
└── ui/
├── html/
│ ├── htmlui.go
│ └── index.html
├── interfaces.go
├── terminal.go
└── tui.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: 🐛 Bug Report
about: Report a reproducible bug or issue
title: "[Bug]: <describe bug>"
labels: bug
---
**Environment (please complete the following):**
- OS: [e.g. Ubuntu 22.04]
- kubectl-ai version (run `kubectl-ai version`): [e.g. 0.3.0]
- LLM provider: [e.g. gemini, openai, grok...]
- LLM model: [e.g. gemini-2.5-pro]
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Run command '...'
3. See error
**Expected behavior**
What you expected to happen.
**Additional context**
Add any other context or logs here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: 🚀 Feature Request
about: Suggest an idea for a new feature or improvement
title: "[Feature]: <describe your idea>"
labels: enhancement
---
**Is your feature request related to a problem? Please describe.**
A clear description of the problem you're trying to solve.
**Describe the solution you'd like**
A clear description of what you want to happen.
**Describe alternatives you've considered**
Any alternative solutions or features you’ve thought of.
**Additional context**
Add any other context, links, or screenshots here.
================================================
FILE: .github/actions/kind-cluster-setup/action.yaml
================================================
name: Kind Cluster Setup
description: "Sets up a Kind Kubernetes cluster and authenticates with GCP"
inputs:
cluster_name:
description: "The name of the Kind cluster"
required: false
default: "periodic-eval-cluster"
runs:
using: "composite"
steps:
- uses: actions/checkout@v4
- name: Create k8s Kind Cluster
uses: helm/kind-action@v1.12.0
with:
cluster_name: ${{ inputs.cluster_name }}
wait: 300s
- uses: "google-github-actions/auth@v2"
with:
project_id: "sunilarora-fp"
workload_identity_provider: "projects/512195022720/locations/global/workloadIdentityPools/github/providers/kubectl-ai"
================================================
FILE: .github/kubectl-ai.cast
================================================
{"version": 2, "width": 190, "height": 49, "timestamp": 1744912218, "env": {"SHELL": "/bin/bash", "TERM": "screen-256color"}}
[0.017789, "o", "bash-3.2$ "]
[8.320293, "o", "."]
[8.513894, "o", "/"]
[8.769729, "o", "k"]
[8.993115, "o", "u"]
[9.054592, "o", "b"]
[9.209666, "o", "e"]
[9.861315, "o", "c"]
[10.073644, "o", "t"]
[10.307934, "o", "\u0007"]
[10.308033, "o", "l-"]
[10.853505, "o", "a"]
[10.969856, "o", "i"]
[11.170596, "o", " "]
[14.210904, "o", "\r\n"]
[14.351719, "o", "\r\n\u001b[38;5;252m\u001b[0m\u001b[38;5;252m\u001b[0m \u001b[38;5;252mHey there, what can I help you with\u001b[0m\u001b[38;5;252m today?\u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\u001b[38;5;252m \u001b[0m\r\n\r\n\r\n\r\n>>> "]
[18.72908, "o", "h"]
[18.977149, "o", "o"]
[19.130497, "o", "w"]
[19.379084, "o", "'"]
[19.442264, "o", "s"]
[19.535301, "o", " "]
[19.720667, "o", "n"]
[19.945368, "o", "g"]
[20.007533, "o", "i"]
[20.078888, "o", "n"]
[20.191491, "o", "x"]
[20.318561, "o", " "]
[20.414191, "o", "a"]
[20.509116, "o", "p"]
[20.640961, "o", "p"]
[21.018349, "o", " "]
[21.121216, "o", "d"]
[21.224431, "o", "o"]
[21.42611, "o", "i"]
[21.484012, "o", "n"]
[21.554566, "o", "g"]
[21.595759, "o", " "]
[21.692772, "o", "i"]
[21.743452, "o", "n"]
[21.831695, "o", " "]
[21.953349, "o", "m"]
[22.153589, "o", "y"]
[22.188041, "o", " "]
[22.286241, "o", "c"]
[22.390762, "o", "l"]
[22.571458, "o", "u"]
[22.625392, "o", "s"]
[22.834233, "o", "t"]
[23.01689, "o", "e"]
[23.15806, "o", "r"]
[23.247809, "o", " "]
[23.442082, "o", "?"]
[23.657884, "o", "\r\n"]
[32.871756, "o", "\u001b[31mError: reading streaming LLM response: iterateResponseStream: invalid stream chunk: {\r\n \"error\": {\r\n \"code\": 500,\r\n \"message\": \"An internal error has occurred. Please retry or report in https://developers.generativeai.google/guide/troubleshooting\",\r\n \"status\": \"INTERNAL\",\r\n \"details\": [\r\n {\r\n \"@type\": \"type.googleapis.com/google.rpc.DebugInfo\",\r\n \"detail\": \"syntax error in the model generated tool call.\"\r\n }\r\n ]\r\n }\r\n}\r\n\r\n\u001b[0m\r\n\r\n>>> "]
[52.693406, "o", "^C"]
[52.69373, "o", "Received signal, shutting down... interrupt\r\n"]
[52.702021, "o", "bash-3.2$ "]
[53.929981, "o", "w"]
[54.073289, "o", "h"]
[54.105594, "o", "i"]
[54.199234, "o", "c"]
[54.278376, "o", "h"]
[54.391889, "o", " "]
[55.888282, "o", "k"]
[56.095038, "o", "u"]
[56.172749, "o", "e"]
[56.18066, "o", "b"]
[56.361241, "o", "c"]
[56.569981, "o", "t"]
[56.6488, "o", "l"]
[56.847693, "o", "-"]
[57.289273, "o", "\b\u001b[K"]
[57.429839, "o", "\b\u001b[K"]
[57.585486, "o", "\b\u001b[K"]
[57.717702, "o", "\b\u001b[K"]
[57.874424, "o", "\b\u001b[K"]
[58.002212, "o", "\b\u001b[K"]
[60.177642, "o", "\b\u001b[K"]
[60.429335, "o", "\b\u001b[K"]
[60.461237, "o", "\b\u001b[K"]
[60.494139, "o", "\b\u001b[K"]
[60.527807, "o", "\b\u001b[K"]
[60.561504, "o", "\b\u001b[K"]
[60.599378, "o", "\b\u001b[K"]
[60.63316, "o", "\b\u001b[K"]
[60.661988, "o", "\u0007"]
[60.694941, "o", "\u0007"]
[60.72875, "o", "\u0007"]
[60.766251, "o", "\u0007"]
[61.063912, "o", "g"]
[61.136886, "o", "o"]
[61.25609, "o", " "]
[61.420433, "o", "b"]
[61.458215, "o", "u"]
[61.664819, "o", "i"]
[61.858026, "o", "l"]
[61.951196, "o", "d"]
[62.030982, "o", "\r\n"]
[64.101433, "o", "bash-3.2$ "]
[65.730822, "o", "exit\r\n"]
================================================
FILE: .github/workflows/ci-periodic.yaml
================================================
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Generated by dev/tasks/generate-github-actions
name: Run evals periodically
on:
schedule:
# Run every 15 minutes
- cron: "*/15 * * * *"
workflow_dispatch:
# This allows you to manually trigger the workflow from the GitHub UI
inputs:
reason:
description: "Reason for manual trigger"
required: false
default: "Manual run via UI"
jobs:
run-eval:
if: github.repository == 'GoogleCloudPlatform/kubectl-ai'
runs-on: ubuntu-latest
timeout-minutes: 12
# Add "id-token" with the intended permissions.
permissions:
contents: "read"
id-token: "write"
steps:
- uses: actions/checkout@v4
- name: Kind Cluster Setup
uses: ./.github/actions/kind-cluster-setup
with:
cluster_name: periodic-eval-cluster
continue-on-error: false
timeout-minutes: 3
- name: Run an easy eval
run: |
for attempt in 1 2; do
echo "=== Evaluation attempt $attempt/2 ==="
if timeout 4m bash -c 'TEST_ARGS="--llm-provider vertexai --models gemini-2.5-pro --concurrency=1 --task-pattern=scale-" ./dev/ci/periodics/run-evals.sh'; then
echo "Evaluation completed successfully on attempt $attempt"
break
else
echo "Attempt $attempt failed or timed out"
# Cleanup any hanging processes
pkill -f k8s-ai-bench || true
pkill -f kubectl-ai || true
if [ $attempt -eq 2 ]; then
echo "❌ Both attempts failed"
exit 1
else
echo "Waiting 10 seconds before retry..."
sleep 10
fi
fi
done
- name: Analyse results
run: |
./dev/ci/periodics/analyze-evals.sh
cat ${{ github.workspace }}/.build/k8s-ai-bench.md >> ${GITHUB_STEP_SUMMARY}
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: false
================================================
FILE: .github/workflows/ci-presubmit.yaml
================================================
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Generated by dev/tasks/generate-github-actions
name: ci-presubmit
on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches: ["main"]
jobs:
go-build:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
- name: "Run dev/ci/presubmits/go-build.sh"
run: |
./dev/ci/presubmits/go-build.sh
go-vet:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
- name: "Run dev/ci/presubmits/go-vet.sh"
run: |
./dev/ci/presubmits/go-vet.sh
verify-autogen:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
- name: "Run dev/ci/presubmits/verify-autogen.sh"
run: |
./dev/ci/presubmits/verify-autogen.sh
verify-format:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
- name: "Run dev/ci/presubmits/verify-format.sh"
run: |
./dev/ci/presubmits/verify-format.sh
verify-gomod:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
- name: "Run dev/ci/presubmits/verify-gomod.sh"
run: |
./dev/ci/presubmits/verify-gomod.sh
verify-mocks:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
- name: "Run dev/ci/presubmits/verify-mocks.sh"
run: |
./dev/ci/presubmits/verify-mocks.sh
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
================================================
FILE: .github/workflows/k8s-bench-evals.yaml
================================================
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This workflow allows PR owners who add new eval tasks to manually run tests on their changes using the GitHub Actions UI.
#It is intended for self-service validation of new or modified evals before merging.
name: On-Demand k8s-ai-bench Eval Test
on:
workflow_dispatch:
inputs:
task_pattern:
description: "Task name or glob pattern to test (must not be '*' or empty; e.g. scale-my-task, scale-foo)"
required: true
jobs:
run-eval:
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: "read"
id-token: "write"
steps:
- name: Validate task_pattern input
run: |
if [[ -z "${{ github.event.inputs.task_pattern }}" || "${{ github.event.inputs.task_pattern }}" == "*" || "${{ github.event.inputs.task_pattern }}" == "all" ]]; then
echo "Error: You must provide a specific task name or pattern. Wildcards or empty values are not allowed."
exit 1
fi
- uses: actions/checkout@v4
- name: Kind Cluster Setup
uses: ./.github/actions/kind-cluster-setup
with:
cluster_name: ${{ github.head_ref || github.ref_name }}
- name: Run evals
# In the future, more options (provider/model/tool-use-shim) may be user-selectable.
# For now, these are fixed for CI safety and consistency.
env:
TEST_ARGS: >-
--llm-provider ${{ github.event.inputs.llm_provider || 'vertexai' }} \
--models ${{ github.event.inputs.model || 'gemini-2.5-pro' }} \
--enable-tool-use-shim=${{ github.event.inputs.enable_tool_use_shim || 'false' }} \
--task-pattern=${{ github.event.inputs.task_pattern || 'scale-' }}
run: |
./dev/ci/periodics/run-evals.sh
- name: Analyse results
run: |
./dev/ci/periodics/analyze-evals.sh
cat ${{ github.workspace }}/.build/k8s-ai-bench.md >> ${GITHUB_STEP_SUMMARY}
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: false
================================================
FILE: .github/workflows/release.yaml
================================================
name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: write
packages: write
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v5
with:
check-latest: true
- name: Install mockgen
run: go install go.uber.org/mock/mockgen@latest
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
version: latest
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Update new version in krew-index
uses: rajatjindal/krew-release-bot@v0.0.46
================================================
FILE: .gitignore
================================================
# Binary
./kubectl-ai
bin/
.build/
# Log files
*.log
trace.log
prompt.log
app.log
# OS specific files
.DS_Store
.env
# IDE specific files
.idea/
.vscode/
*.swp
*.swo
# Go specific
*.exe
*.test
*.prof
*.out
.aider*
# Added by goreleaser init:
dist/
# Ignore generated credentials from google-github-actions/auth
gha-creds-*.json
# air config
.air.toml
================================================
FILE: .goreleaser.yaml
================================================
# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com
# The lines below are called `modelines`. See `:help modeline`
# Feel free to remove those if you don't want/need to use them.
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
version: 2
before:
hooks:
# You may remove this if you don't use go modules.
- go mod tidy
# you may remove this if you don't need go generate
- go generate ./...
builds:
- env:
- CGO_ENABLED=0
goos:
- linux
- windows
- darwin
main: ./cmd
ldflags:
- -s -w
- -X main.version={{.Version}}
- -X main.commit={{.Commit}}
- -X main.date={{.Date}}
archives:
- formats: [tar.gz]
# this name template makes the OS and Arch compatible with the results of `uname`.
name_template: >-
{{ .ProjectName }}_
{{- title .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i386
{{- else }}{{ .Arch }}{{ end }}
{{- if .Arm }}v{{ .Arm }}{{ end }}
# use zip for windows archives
format_overrides:
- goos: windows
formats: [zip]
changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
release:
footer: >-
---
Released by [GoReleaser](https://github.com/goreleaser/goreleaser).
================================================
FILE: .krew.yaml
================================================
apiVersion: krew.googlecontainertools.github.com/v1alpha2
kind: Plugin
metadata:
name: ai
spec:
version: {{ .TagName }}
homepage: https://github.com/GoogleCloudPlatform/kubectl-ai
shortDescription: AI-powered Kubernetes assistant
description: |
This plugin provides a natural language interface to carry out kubernetes
related tasks. The plugin can plan and execute multiple steps given a high
level description of a task.
It's important to note that this plugin does not replace kubectl. Instead,
it makes kubectl accessible to non-kubernetes users and makes kubectl users
more productive because now they don't have to remember all the syntax and
commands to perform common tasks.
caveats: |
This plugin uses AI models (LLM) to plan and execute tasks. It supports
multiple LLM providers such as Gemini, Azure-OpenAI, Ollama, llamacpp.
You can get the API key for the default provider (Gemini) from
https://aistudio.google.com/app/apikey.
platforms:
- selector:
matchLabels:
os: linux
arch: amd64
{{addURIAndSha "https://github.com/GoogleCloudPlatform/kubectl-ai/releases/download/{{ .TagName }}/kubectl-ai_Linux_x86_64.tar.gz" .TagName }}
bin: kubectl-ai
- selector:
matchLabels:
os: linux
arch: arm64
{{addURIAndSha "https://github.com/GoogleCloudPlatform/kubectl-ai/releases/download/{{ .TagName }}/kubectl-ai_Linux_arm64.tar.gz" .TagName }}
bin: kubectl-ai
- selector:
matchLabels:
os: darwin
arch: amd64
{{addURIAndSha "https://github.com/GoogleCloudPlatform/kubectl-ai/releases/download/{{ .TagName }}/kubectl-ai_Darwin_x86_64.tar.gz" .TagName }}
bin: kubectl-ai
- selector:
matchLabels:
os: darwin
arch: arm64
{{addURIAndSha "https://github.com/GoogleCloudPlatform/kubectl-ai/releases/download/{{ .TagName }}/kubectl-ai_Darwin_arm64.tar.gz" .TagName }}
bin: kubectl-ai
- selector:
matchLabels:
os: windows
arch: amd64
{{addURIAndSha "https://github.com/GoogleCloudPlatform/kubectl-ai/releases/download/{{ .TagName }}/kubectl-ai_Windows_x86_64.zip" .TagName }}
bin: kubectl-ai.exe
- selector:
matchLabels:
os: windows
arch: arm64
{{addURIAndSha "https://github.com/GoogleCloudPlatform/kubectl-ai/releases/download/{{ .TagName }}/kubectl-ai_Windows_arm64.zip" .TagName }}
bin: kubectl-ai.exe
================================================
FILE: CONTAINER.md
================================================
# Running kubectl-ai in a Docker Container
## 1. Build the Docker Image
First, clone the `kubectl-ai` repository and build the Docker image from the
source code.
```bash
git clone https://github.com/GoogleCloudPlatform/kubectl-ai.git
cd kubectl-ai
docker build -t kubectl-ai:latest -f images/kubectl-ai/Dockerfile .
```
## 2. Running against a GKE cluster
To access a GKE cluster, `kubectl-ai` needs two configurations from your local
machine: **Google Cloud credentials** and a **Kubernetes config file**.
### Create Google Cloud Credentials
First, create Application Default Credentials
[(ADC)](https://cloud.google.com/docs/authentication/application-default-credentials).
`kubectl` uses these credentials to authenticate with your GKE cluster.
```bash
gcloud auth application-default login
```
This command saves your credentials into the `~/.config/gcloud` directory.
### Configure `kubectl`
Next, generate the `kubeconfig` file. This file tells `kubectl` which cluster
to connect to and to use your ADC credentials for authentication.
```bash
gcloud container clusters get-credentials <cluster-name> --location <location>
```
This updates the configuration file at `~/.kube/config`.
## 3. Running the Container
Finally, mount both configuration directories into the `kubectl-ai` container
when you run it. This example shows how to run `kubectl-ai` with a web
interface, mounting all necessary credentials and providing a Gemini API key.
```bash
export GEMINI_API_KEY="your_api_key_here"
docker run --rm -it -p 8080:8080 \
-v ~/.kube:/root/.kube \
-v ~/.config/gcloud:/root/.config/gcloud \
-e GEMINI_API_KEY \
kubectl-ai:latest \
--ui-listen-address 0.0.0.0:8080 \
--ui-type web
```
Alternatively with the default terminal ui:
```bash
export GEMINI_API_KEY="your_api_key_here"
docker run --rm -it \
-v ~/.kube:/root/.kube \
-v ~/.config/gcloud:/root/.config/gcloud \
-e GEMINI_API_KEY \
kubectl-ai:latest
```
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
# kubectl-ai
[](https://goreportcard.com/report/github.com/GoogleCloudPlatform/kubectl-ai)

[](https://deepwiki.com/GoogleCloudPlatform/kubectl-ai)
[](https://github.com/GoogleCloudPlatform/kubectl-ai/stargazers)
`kubectl-ai` acts as an intelligent interface, translating user intent into
precise Kubernetes operations, making Kubernetes management more accessible and
efficient.

## Table of Contents
- [Quick Start](#quick-start)
- [Installation](#installation)
- [Usage](#usage)
- [Configuration](#configuration)
- [Tools](#tools)
- [Docker Quick Start](#docker-quick-start)
- [MCP Client Mode](#mcp-client-mode)
- [Extras](#extras)
- [MCP Server Mode](#mcp-server-mode)
- [Start Contributing](#start-contributing)
- [Learning Resources](#learning-resources)
## Quick Start
First, ensure that kubectl is installed and configured.
### Installation
#### Quick Install (Linux & MacOS only)
```shell
curl -sSL https://raw.githubusercontent.com/GoogleCloudPlatform/kubectl-ai/main/install.sh | bash
```
<details>
<summary>Other Installation Methods</summary>
#### Manual Installation (Linux, MacOS and Windows)
1. Download the latest release from the [releases page](https://github.com/GoogleCloudPlatform/kubectl-ai/releases/latest) for your target machine.
2. Untar the release, make the binary executable and move it to a directory in your $PATH (as shown below).
```shell
tar -zxvf kubectl-ai_Darwin_arm64.tar.gz
chmod a+x kubectl-ai
sudo mv kubectl-ai /usr/local/bin/
```
#### Install with Krew (Linux/macOS/Windows)
First of all, you need to have krew installed, refer to [krew document](https://krew.sigs.k8s.io/docs/user-guide/setup/install/) for more details
Then you can install with krew
```shell
kubectl krew install ai
```
Now you can invoke `kubectl-ai` as a kubectl plugin like this: `kubectl ai`.
#### Install on NixOS
There are multiple ways to install `kubectl-ai` on NixOS. For a permanent installation add the following to your NixOS-Configuration:
```nix
environment.systemPackages = with pkgs; [
kubectl-ai
];
```
For a temporary installation, you can use the following command:
```shell
nix-shell -p kubectl-ai
```
</details>
### Usage
`kubectl-ai` supports AI models from `gemini`, `vertexai`, `azopenai`, `openai`, `grok`, `bedrock` and local LLM providers such as `ollama` and `llama.cpp`.
#### Using Gemini (Default)
Set your Gemini API key as an environment variable. If you don't have a key, get one from [Google AI Studio](https://aistudio.google.com).
```bash
export GEMINI_API_KEY=your_api_key_here
kubectl-ai
# Use different gemini model
kubectl-ai --model gemini-2.5-pro-exp-03-25
# Use 2.5 flash (faster) model
kubectl-ai --quiet --model gemini-2.5-flash-preview-04-17 "check logs for nginx app in hello namespace"
```
<details>
<summary>Use other AI models</summary>
#### Using AI models running locally (ollama or llama.cpp)
You can use `kubectl-ai` with AI models running locally. `kubectl-ai` supports [ollama](https://ollama.com/) and [llama.cpp](https://github.com/ggml-org/llama.cpp) to use the AI models running locally.
Additionally, the [`modelserving`](modelserving) directory provides tools and instructions for deploying your own `llama.cpp`-based LLM serving endpoints locally or on a Kubernetes cluster. This allows you to host models like Gemma directly in your environment.
An example of using Google's `gemma3` model with `ollama`:
```shell
# assuming ollama is already running and you have pulled one of the gemma models
# ollama pull gemma3:12b-it-qat
# if your ollama server is at remote, use OLLAMA_HOST variable to specify the host
# export OLLAMA_HOST=http://192.168.1.3:11434/
# enable-tool-use-shim because models require special prompting to enable tool calling
kubectl-ai --llm-provider ollama --model gemma3:12b-it-qat --enable-tool-use-shim
# you can use `models` command to discover the locally available models
>> models
```
#### Using Grok
You can use X.AI's Grok model by setting your X.AI API key:
```bash
export GROK_API_KEY=your_xai_api_key_here
kubectl-ai --llm-provider=grok --model=grok-3-beta
```
#### Using AWS Bedrock
You can use AWS Bedrock Claude models with your AWS credentials:
```bash
# Configure AWS credentials using AWS SSO
aws sso login --profile your-profile-name
# Or use other AWS credential methods (IAM roles, environment variables, etc.)
# Use Claude 4 Sonnet (default)
kubectl-ai --llm-provider=bedrock --model=us.anthropic.claude-sonnet-4-20250514-v1:0
# Use Claude 3.7 Sonnet
kubectl-ai --llm-provider=bedrock --model=us.anthropic.claude-3-7-sonnet-20250219-v1:0
# Override model via environment variable
export BEDROCK_MODEL=us.anthropic.claude-sonnet-4-20250514-v1:0
kubectl-ai --llm-provider=bedrock
```
AWS Bedrock uses the standard AWS SDK credential chain, supporting:
- AWS SSO profiles
- IAM roles (for EC2/ECS/Lambda)
- Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
- AWS CLI configuration files
#### Using Azure OpenAI
You can also use Azure OpenAI deployment by setting your OpenAI API key and specifying the provider:
```bash
export AZURE_OPENAI_API_KEY=your_azure_openai_api_key_here
export AZURE_OPENAI_ENDPOINT=https://your_azure_openai_endpoint_here
kubectl-ai --llm-provider=azopenai --model=your_azure_openai_deployment_name_here
# or
az login
kubectl-ai --llm-provider=openai://your_azure_openai_endpoint_here --model=your_azure_openai_deployment_name_here
```
#### Using OpenAI
You can also use OpenAI models by setting your OpenAI API key and specifying the provider:
```bash
export OPENAI_API_KEY=your_openai_api_key_here
kubectl-ai --llm-provider=openai --model=gpt-4.1
```
#### Using OpenAI Compatible API
For example, you can use aliyun qwen-xxx models as follows.
```bash
export OPENAI_API_KEY=your_openai_api_key_here
export OPENAI_ENDPOINT=https://dashscope.aliyuncs.com/compatible-mode/v1
kubectl-ai --llm-provider=openai --model=qwen-plus
```
</details>
Run interactively:
```shell
kubectl-ai
```
The interactive mode allows you to have a chat with `kubectl-ai`, asking multiple questions in sequence while maintaining context from previous interactions. Simply type your queries and press Enter to receive responses. To exit the interactive shell, type `exit` or press Ctrl+C.
Or, run with a task as input:
```shell
kubectl-ai --quiet "fetch logs for nginx app in hello namespace"
```
Combine it with other unix commands:
```shell
kubectl-ai < query.txt
# OR
echo "list pods in the default namespace" | kubectl-ai
```
You can even combine a positional argument with stdin input. The positional argument will be used as a prefix to the stdin content:
```shell
cat error.log | kubectl-ai "explain the error"
```
We also support persistence between runs with an opt-in. This lets you save a session to the local filesystem, and resume it to maintain previous context. It even works between different interfaces!
```shell
kubectl-ai --new-session # start a new session
kubectl-ai --list-sessions # list all saved sessions
kubectl-ai --resume-session 20250807-510872 # resume session 20250807-510872
kubectl-ai --delete-session 20250807-510872 # delete session 20250807-510872
```
## Configuration
You can also configure `kubectl-ai` using a YAML configuration file at `~/.config/kubectl-ai/config.yaml`:
```shell
mkdir -p ~/.config/kubectl-ai/
cat <<EOF > ~/.config/kubectl-ai/config.yaml
model: gemini-2.5-flash-preview-04-17
llmProvider: gemini
toolConfigPaths: ~/.config/kubectl-ai/tools.yaml
EOF
```
Verify your configuration:
```shell
kubectl-ai --quiet model
```
<details>
<summary>More configuration Options</summary>
Here's a complete configuration file with all available options and their default values:
```yaml
# LLM provider configuration
llmProvider: "gemini" # Default LLM provider
model: "gemini-2.5-pro-preview-06-05" # Default model
skipVerifySSL: false # Skip SSL verification for LLM API calls
# Tool and permission settings
toolConfigPaths: ["~/.config/kubectl-ai/tools.yaml"] # Custom tools configuration paths
skipPermissions: false # Skip confirmation for resource-modifying commands
enableToolUseShim: false # Enable tool use shim for certain models
# MCP configuration
mcpServer: false # Run in MCP server mode
mcpClient: false # Enable MCP client mode
externalTools: false # Discover external MCP tools (requires mcp-server)
# Runtime settings
maxIterations: 20 # Maximum iterations for the agent
quiet: false # Run in non-interactive mode
removeWorkdir: false # Remove temporary working directory after execution
# Kubernetes configuration
kubeconfig: "~/.kube/config" # Path to kubeconfig file
# UI configuration
uiType: "terminal" # UI mode: "terminal" or "web"
uiListenAddress: "localhost:8888" # Address for HTML UI server
# Prompt configuration
promptTemplateFilePath: "" # Custom prompt template file
extraPromptPaths: [] # Additional prompt template paths
# Debug and trace settings
tracePath: "/tmp/kubectl-ai-trace.txt" # Path to trace file
```
</details>
All these settings can be configured through either:
1. Command line flags (e.g., `--model=gemini-2.5-pro`)
2. Configuration file (`~/.config/kubectl-ai/config.yaml`)
3. Environment variables (e.g., `GEMINI_API_KEY`)
Command line flags take precedence over configuration file settings.
## Tools
`kubectl-ai` leverages LLMs to suggest and execute Kubernetes operations using a set of powerful tools. It comes with built-in tools like `kubectl` and `bash`.
You can also extend its capabilities by defining your own custom tools. By default, `kubectl-ai` looks for your tool configurations in `~/.config/kubectl-ai/tools.yaml`.
To specify tools configuration files or directories containing tools configuration files, use:
```sh
./kubectl-ai --custom-tools-config=<path-to-tools-directory> "your prompt here"
```
For further details on how to configure your own tools, [go here](docs/tools.md).
## Docker Quick Start
This project provides a Docker image that gives you a standalone environment for running kubectl-ai, including against a GKE cluster.
### Running the container against GKE
#### Step 1: Build the Image
Clone the repository and build the image with the following command
```bash
git clone https://github.com/GoogleCloudPlatform/kubectl-ai.git
cd kubectl-ai
docker build -t kubectl-ai:latest -f images/kubectl-ai/Dockerfile .
```
#### Step 2: Connect to Your GKE Cluster
Set up application default credentials and connect to your GKE cluster.
```bash
gcloud auth application-default login # If in a gcloud shell this is not necessary
gcloud container clusters get-credentials <cluster-name> --zone <zone>
```
#### Step 3: Run the kubectl-ai container
Below is a sample command that can be used to launch the container with a locally hosted web-ui. Be sure to replace the placeholder values with your specific Google Cloud project ID and location. Note you do not need to mount the gcloud config directory if you're on a cloudshell machine.
```bash
docker run --rm -it -p 8080:8080 -v ~/.kube:/root/.kube -v ~/.config/gcloud:/root/.config/gcloud -e GOOGLE_CLOUD_LOCATION=us-central1 -e GOOGLE_CLOUD_PROJECT=my-gcp-project kubectl-ai:latest --llm-provider vertexai --ui-listen-address 0.0.0.0:8080 --ui-type web
```
For more info about running from the container image see [CONTAINER.md](CONTAINER.md)
## MCP Client Mode
> **Note:** MCP Client Mode is available in `kubectl-ai` version v0.0.12 and onwards.
`kubectl-ai` can connect to external [MCP](https://modelcontextprotocol.io/examples) Servers to access additional tools in addition to built-in tools.
### Quick Start with MCP Client
Enable MCP client mode:
```bash
kubectl-ai --mcp-client
```
### MCP Client Configuration
Create or edit `~/.config/kubectl-ai/mcp.yaml` to customize MCP servers:
```yaml
servers:
# Local MCP server (stdio-based)
# sequential-thinking: Advanced reasoning and step-by-step analysis
- name: sequential-thinking
command: npx
args:
- -y
- "@modelcontextprotocol/server-sequential-thinking"
# Remote MCP server (HTTP-based)
- name: cloudflare-documentation
url: https://docs.mcp.cloudflare.com/mcp
# Optional: Remote MCP server with authentication
- name: custom-api
url: https://api.example.com/mcp
auth:
type: "bearer"
token: "${MCP_TOKEN}"
```
The system automatically:
- Converts parameter names (snake_case → camelCase)
- Handles type conversion (strings → numbers/booleans when appropriate)
- Provides fallback behavior for unknown servers
No additional setup required - just use the `--mcp-client` flag and the AI will have access to all configured MCP tools.
📖 **For detailed configuration options, troubleshooting, and advanced features for MCP Client mode, see the [MCP Client Documentation](docs/mcp-client.md).**
📖 **For multi-server orchestration and security automation examples, see the [MCP Client Integration Guide](docs/mcp-client.md).**
## Extras
You can use the following special keywords for specific actions:
- `model`: Display the currently selected model.
- `models`: List all available models.
- `tools`: List all available tools.
- `version`: Display the `kubectl-ai` version.
- `reset`: Clear the conversational context.
- `clear`: Clear the terminal screen.
- `exit` or `quit`: Terminate the interactive shell (Ctrl+C also works).
### Invoking as kubectl plugin
You can also run `kubectl ai`. `kubectl` finds any executable file in your `PATH` whose name begins with `kubectl-` as a [plugin](https://kubernetes.io/docs/tasks/extend-kubectl/kubectl-plugins/).
## MCP Server Mode
`kubectl-ai` can act as an MCP server that exposes kubectl tools to other MCP clients (like Claude, Cursor, or VS Code). The server can run in two modes:
### Basic MCP Server (Built-in tools only)
Expose only kubectl-ai's native Kubernetes tools:
```bash
kubectl-ai --mcp-server
```
### Enhanced MCP Server (With external tool discovery)
Additionally discover and expose tools from other MCP servers as a unified interface:
```bash
kubectl-ai --mcp-server --external-tools
```
This creates a powerful **tool aggregation hub** where kubectl-ai acts as both:
- **MCP Server**: Exposing kubectl tools to clients
- **MCP Client**: Consuming tools from other MCP servers
To serve clients over HTTP using the streamable transport, run:
```bash
kubectl-ai --mcp-server --mcp-server-mode streamable-http --http-port 9080
```
This starts an MCP endpoint at `http://localhost:9080/mcp`.
The enhanced mode provides AI clients with access to both Kubernetes operations and general-purpose tools (filesystem, web search, databases, etc.) through a single MCP endpoint.
📖 **For detailed configuration, examples, and troubleshooting, see the [MCP Server Documentation](docs/mcp-server.md).**
## Start Contributing
We welcome contributions to `kubectl-ai` from the community. Take a look at our
[contribution guide](contributing.md) to get started.
## Learning Resources
### Talks and Presentations
- [From Natural Language to K8s Operations: The MCP Architecture and Practice of kubectl-ai](https://blog.wu-boy.com/2025/10/from-natural-language-to-k8s-operations-the-mcp-architecture-and-practice-of-kubectl-ai-en) - A comprehensive presentation covering the architecture and practical usage of kubectl-ai with MCP (Model Context Protocol).
---
*Note: This is not an officially supported Google product. This project is not
eligible for the [Google Open Source Software Vulnerability Rewards
Program](https://bughunters.google.com/open-source-security).*
================================================
FILE: cmd/main.go
================================================
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"bufio"
"bytes"
"context"
"errors"
"flag"
"fmt"
"io"
"log"
"os"
"os/signal"
"path/filepath"
"slices"
"strings"
"syscall"
"github.com/GoogleCloudPlatform/kubectl-ai/gollm"
"github.com/GoogleCloudPlatform/kubectl-ai/pkg/agent"
"github.com/GoogleCloudPlatform/kubectl-ai/pkg/api"
"github.com/GoogleCloudPlatform/kubectl-ai/pkg/journal"
"github.com/GoogleCloudPlatform/kubectl-ai/pkg/sessions"
"github.com/GoogleCloudPlatform/kubectl-ai/pkg/tools"
"github.com/GoogleCloudPlatform/kubectl-ai/pkg/ui"
"github.com/GoogleCloudPlatform/kubectl-ai/pkg/ui/html"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"k8s.io/klog/v2"
"sigs.k8s.io/yaml"
)
// Using the defaults from goreleaser as per https://goreleaser.com/cookbooks/using-main.version/
var (
version = "dev"
commit = "none"
date = "unknown"
)
func BuildRootCommand(opt *Options) (*cobra.Command, error) {
rootCmd := &cobra.Command{
Use: "kubectl-ai",
Short: "A CLI tool to interact with Kubernetes using natural language",
Long: "kubectl-ai is a command-line tool that allows you to interact with your Kubernetes cluster using natural language queries. It leverages large language models to understand your intent and translate it into kubectl",
Args: cobra.MaximumNArgs(1), // Only one positional arg is allowed.
RunE: func(cmd *cobra.Command, args []string) error {
return RunRootCommand(cmd.Context(), *opt, args)
},
}
rootCmd.AddCommand(&cobra.Command{
Use: "version",
Short: "Print the version number of kubectl-ai",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("version: %s\ncommit: %s\ndate: %s\n", version, commit, date)
os.Exit(0)
},
})
if err := opt.bindCLIFlags(rootCmd.Flags()); err != nil {
return nil, err
}
return rootCmd, nil
}
type Options struct {
ProviderID string `json:"llmProvider,omitempty"`
ModelID string `json:"model,omitempty"`
// SkipPermissions is a flag to skip asking for confirmation before executing kubectl commands
// that modifies resources in the cluster.
SkipPermissions bool `json:"skipPermissions,omitempty"`
// EnableToolUseShim is a flag to enable tool use shim.
// TODO(droot): figure out a better way to discover if the model supports tool use
// and set this automatically.
EnableToolUseShim bool `json:"enableToolUseShim,omitempty"`
// Quiet flag indicates if the agent should run in non-interactive mode.
// It requires a query to be provided as a positional argument.
Quiet bool `json:"quiet,omitempty"`
MCPServer bool `json:"mcpServer,omitempty"`
MCPClient bool `json:"mcpClient,omitempty"`
// ExternalTools enables discovery and exposure of external MCP tools (only works with --mcp-server)
ExternalTools bool `json:"externalTools,omitempty"`
MaxIterations int `json:"maxIterations,omitempty"`
// MCPServerMode is the mode of the MCP server. only works with --mcp-server.
MCPServerMode string `json:"mcpServerMode,omitempty"`
// Set the HTTP endpoint port for the MCP server when using HTTP transports like streamable-http.
HTTPPort int `json:"httpPort,omitempty"`
// KubeConfigPath is the path to the kubeconfig file.
// If not provided, the default kubeconfig path will be used.
KubeConfigPath string `json:"kubeConfigPath,omitempty"`
PromptTemplateFilePath string `json:"promptTemplateFilePath,omitempty"`
ExtraPromptPaths []string `json:"extraPromptPaths,omitempty"`
TracePath string `json:"tracePath,omitempty"`
RemoveWorkDir bool `json:"removeWorkDir,omitempty"`
ToolConfigPaths []string `json:"toolConfigPaths,omitempty"`
// UIType is the type of user interface to use.
UIType ui.Type `json:"uiType,omitempty"`
// UIListenAddress is the address to listen for the web UI.
UIListenAddress string `json:"uiListenAddress,omitempty"`
// SkipVerifySSL is a flag to skip verifying the SSL certificate of the LLM provider.
SkipVerifySSL bool `json:"skipVerifySSL,omitempty"`
// Session management options
ResumeSession string `json:"resumeSession,omitempty"`
NewSession bool `json:"newSession,omitempty"`
ListSessions bool `json:"listSessions,omitempty"`
DeleteSession string `json:"deleteSession,omitempty"`
SessionBackend string `json:"sessionBackend,omitempty"`
// ShowToolOutput is a flag to disable truncation of tool output in the terminal UI.
ShowToolOutput bool `json:"showToolOutput,omitempty"`
// Sandbox enables execution of tools in a sandbox environment.
// Supported values: "k8s", "seatbelt".
// If empty, tools are executed locally.
Sandbox string `json:"sandbox,omitempty"`
// SandboxImage is the container image to use for the sandbox
SandboxImage string `json:"sandboxImage,omitempty"`
}
var defaultToolConfigPaths = []string{
filepath.Join("{CONFIG}", "kubectl-ai", "tools.yaml"),
filepath.Join("{HOME}", ".config", "kubectl-ai", "tools.yaml"),
}
var defaultConfigPaths = []string{
filepath.Join("{CONFIG}", "kubectl-ai", "config.yaml"),
filepath.Join("{HOME}", ".config", "kubectl-ai", "config.yaml"),
}
func (o *Options) InitDefaults() {
o.ProviderID = "gemini"
o.ModelID = "gemini-2.5-pro"
// by default, confirm before executing kubectl commands that modify resources in the cluster.
o.SkipPermissions = false
o.MCPServer = false
o.MCPClient = false
// by default, external tools are disabled (only works with --mcp-server)
o.ExternalTools = false
// We now default to our strongest model (gemini-2.5-pro-exp-03-25) which supports tool use natively.
// so we don't need shim.
o.EnableToolUseShim = false
o.Quiet = false
o.MCPServer = false
o.MaxIterations = 20
o.KubeConfigPath = ""
o.PromptTemplateFilePath = ""
o.ExtraPromptPaths = []string{}
o.TracePath = filepath.Join(os.TempDir(), "kubectl-ai-trace.txt")
o.RemoveWorkDir = false
o.ToolConfigPaths = defaultToolConfigPaths
// Default to terminal UI
o.UIType = ui.UITypeTerminal
// Default UI listen address for HTML UI
o.UIListenAddress = "localhost:8888"
// Default to not skipping SSL verification
o.SkipVerifySSL = false
// Default MCP server mode is stdio
o.MCPServerMode = "stdio"
// Default port for HTTP endpoint when using streamable-http mode
o.HTTPPort = 9080
// Session management options
o.ResumeSession = ""
o.ListSessions = false
o.DeleteSession = ""
o.SessionBackend = "memory"
// By default, hide tool outputs
o.ShowToolOutput = false
o.Sandbox = ""
o.SandboxImage = "bitnami/kubectl:latest"
}
func (o *Options) LoadConfiguration(b []byte) error {
if err := yaml.Unmarshal(b, &o); err != nil {
return fmt.Errorf("parsing configuration: %w", err)
}
return nil
}
func (o *Options) LoadConfigurationFile() error {
configPaths := defaultConfigPaths
for _, configPath := range configPaths {
pathWithPlaceholdersExpanded := configPath
if strings.Contains(pathWithPlaceholdersExpanded, "{CONFIG}") {
configDir, err := os.UserConfigDir()
if err != nil {
return fmt.Errorf("getting user config directory (for config file path %q): %w", configPath, err)
}
pathWithPlaceholdersExpanded = strings.ReplaceAll(pathWithPlaceholdersExpanded, "{CONFIG}", configDir)
}
if strings.Contains(pathWithPlaceholdersExpanded, "{HOME}") {
homeDir, err := os.UserHomeDir()
if err != nil {
return fmt.Errorf("getting user home directory (for config file path %q): %w", configPath, err)
}
pathWithPlaceholdersExpanded = strings.ReplaceAll(pathWithPlaceholdersExpanded, "{HOME}", homeDir)
}
configPath = filepath.Clean(pathWithPlaceholdersExpanded)
configBytes, err := os.ReadFile(configPath)
if err != nil {
if os.IsNotExist(err) {
// ignore missing config files, they are optional
} else {
fmt.Fprintf(os.Stderr, "warning: could not load defaults from %q: %v\n", configPath, err)
}
} else if len(configBytes) > 0 {
if err := o.LoadConfiguration(configBytes); err != nil {
fmt.Fprintf(os.Stderr, "warning: error loading configuration from %q: %v\n", configPath, err)
}
}
}
return nil
}
func main() {
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer cancel()
go func() {
<-ctx.Done()
// restore default behavior for a second signal
signal.Stop(make(chan os.Signal))
cancel()
klog.Flush()
fmt.Fprintf(os.Stderr, "\nReceived signal, shutting down gracefully... (press Ctrl+C again to force)\n")
}()
if err := run(ctx); err != nil {
// Don't print error if it's a context cancellation
if !errors.Is(err, context.Canceled) {
fmt.Fprintln(os.Stderr, err)
}
// Exit with non-zero status code on error, unless it's a graceful shutdown.
if errors.Is(err, context.Canceled) {
os.Exit(0)
}
os.Exit(1)
}
}
func run(ctx context.Context) error {
// klog setup must happen before Cobra parses any flags
// add commandline flags for logging
klogFlags := flag.NewFlagSet("klog", flag.ExitOnError)
klog.InitFlags(klogFlags)
klogFlags.Set("logtostderr", "false")
klogFlags.Set("log_file", filepath.Join(os.TempDir(), "kubectl-ai.log"))
defer klog.Flush()
var opt Options
opt.InitDefaults()
// load YAML config values
if err := opt.LoadConfigurationFile(); err != nil {
return fmt.Errorf("failed to load config file: %w", err)
}
rootCmd, err := BuildRootCommand(&opt)
if err != nil {
return err
}
// cobra has to know that we pass pass flags with flag lib, otherwise it creates conflict with flags.parse() method
// We add just the klog flags we want, not all the klog flags (there are a lot, most of them are very niche)
rootCmd.PersistentFlags().AddGoFlag(klogFlags.Lookup("v"))
rootCmd.PersistentFlags().AddGoFlag(klogFlags.Lookup("alsologtostderr"))
// do this early, before the third-party code logs anything.
redirectStdLogToKlog()
if err := rootCmd.ExecuteContext(ctx); err != nil {
return err
}
return nil
}
func (opt *Options) bindCLIFlags(f *pflag.FlagSet) error {
f.IntVar(&opt.MaxIterations, "max-iterations", opt.MaxIterations, "maximum number of iterations agent will try before giving up")
f.StringVar(&opt.KubeConfigPath, "kubeconfig", opt.KubeConfigPath, "path to kubeconfig file")
f.StringVar(&opt.PromptTemplateFilePath, "prompt-template-file-path", opt.PromptTemplateFilePath, "path to custom prompt template file")
f.StringArrayVar(&opt.ExtraPromptPaths, "extra-prompt-paths", opt.ExtraPromptPaths, "extra prompt template paths")
f.StringVar(&opt.TracePath, "trace-path", opt.TracePath, "path to the trace file")
f.BoolVar(&opt.RemoveWorkDir, "remove-workdir", opt.RemoveWorkDir, "remove the temporary working directory after execution")
f.StringVar(&opt.ProviderID, "llm-provider", opt.ProviderID, "language model provider")
f.StringVar(&opt.ModelID, "model", opt.ModelID, "language model e.g. gemini-2.0-flash-thinking-exp-01-21, gemini-2.0-flash")
f.BoolVar(&opt.SkipPermissions, "skip-permissions", opt.SkipPermissions, "(dangerous) skip asking for confirmation before executing kubectl commands that modify resources")
f.BoolVar(&opt.MCPServer, "mcp-server", opt.MCPServer, "run in MCP server mode")
f.BoolVar(&opt.ExternalTools, "external-tools", opt.ExternalTools, "in MCP server mode, discover and expose external MCP tools")
f.StringArrayVar(&opt.ToolConfigPaths, "custom-tools-config", opt.ToolConfigPaths, "path to custom tools config file or directory")
f.BoolVar(&opt.MCPClient, "mcp-client", opt.MCPClient, "enable MCP client mode to connect to external MCP servers")
f.StringVar(&opt.MCPServerMode, "mcp-server-mode", opt.MCPServerMode, "mode of the MCP server. Supported values: stdio, streamable-http")
f.IntVar(&opt.HTTPPort, "http-port", opt.HTTPPort, "port for the HTTP endpoint in MCP server mode (used with --mcp-server when --mcp-server-mode is streamable-http)")
f.BoolVar(&opt.EnableToolUseShim, "enable-tool-use-shim", opt.EnableToolUseShim, "enable tool use shim")
f.BoolVar(&opt.Quiet, "quiet", opt.Quiet, "run in non-interactive mode, requires a query to be provided as a positional argument")
f.Var(&opt.UIType, "ui-type", "user interface type to use. Supported values: terminal, web, tui.")
f.StringVar(&opt.UIListenAddress, "ui-listen-address", opt.UIListenAddress, "address to listen for the HTML UI.")
f.BoolVar(&opt.SkipVerifySSL, "skip-verify-ssl", opt.SkipVerifySSL, "skip verifying the SSL certificate of the LLM provider")
f.BoolVar(&opt.ShowToolOutput, "show-tool-output", opt.ShowToolOutput, "show tool output in the terminal UI")
f.StringVar(&opt.Sandbox, "sandbox", opt.Sandbox, "execute tools in a sandbox environment (k8s, seatbelt)")
f.StringVar(&opt.SandboxImage, "sandbox-image", opt.SandboxImage, "container image to use for the sandbox")
f.StringVar(&opt.ResumeSession, "resume-session", opt.ResumeSession, "ID of session to resume (use 'latest' for the most recent session)")
f.BoolVar(&opt.ListSessions, "list-sessions", opt.ListSessions, "list all available sessions")
f.StringVar(&opt.DeleteSession, "delete-session", opt.DeleteSession, "delete a session by ID")
f.BoolVar(&opt.NewSession, "new-session", opt.NewSession, "start a new persistent session")
f.StringVar(&opt.SessionBackend, "session-backend", opt.SessionBackend,
"session backend to use (memory or filesystem)")
return nil
}
func RunRootCommand(ctx context.Context, opt Options, args []string) error {
var err error
// Automatically upgrade backend to filesystem if session persistence flags are requested explicitly
if (opt.NewSession || opt.ResumeSession != "" || opt.ListSessions || opt.DeleteSession != "") && opt.SessionBackend == "memory" {
klog.Infof("Upgrading session-backend to 'filesystem' based on provided flags")
opt.SessionBackend = "filesystem"
}
// Validate flag combinations
if opt.ExternalTools && !opt.MCPServer {
return fmt.Errorf("--external-tools can only be used with --mcp-server")
}
// resolve kubeconfig path with priority: flag/env > KUBECONFIG > default path
if err = resolveKubeConfigPath(&opt); err != nil {
return fmt.Errorf("failed to resolve kubeconfig path: %w", err)
}
if opt.MCPServer {
if err = startMCPServer(ctx, opt); err != nil {
return fmt.Errorf("failed to start MCP server: %w", err)
}
return nil // MCP server mode blocks, so we return here
}
if opt.ListSessions {
return handleListSessions(opt)
}
if opt.DeleteSession != "" {
return handleDeleteSession(opt)
}
if err := handleCustomTools(opt.ToolConfigPaths); err != nil {
return fmt.Errorf("failed to process custom tools: %w", err)
}
// After reading stdin, it is consumed
var hasInputData bool
hasInputData, err = hasStdInData()
if err != nil {
return fmt.Errorf("failed to check if stdin has data: %w", err)
}
// Handles positional args or stdin
var queryFromCmd string
queryFromCmd, err = resolveQueryInput(hasInputData, args)
if err != nil {
return fmt.Errorf("failed to resolve query input %w", err)
}
klog.Info("Application started", "pid", os.Getpid())
var recorder journal.Recorder
if opt.TracePath != "" {
var fileRecorder journal.Recorder
fileRecorder, err = journal.NewFileRecorder(opt.TracePath)
if err != nil {
return fmt.Errorf("creating trace recorder: %w", err)
}
defer fileRecorder.Close()
recorder = fileRecorder
} else {
// Ensure we always have a recorder, to avoid nil checks
recorder = &journal.LogRecorder{}
defer recorder.Close()
}
// Initialize session management
var session *api.Session
var sessionManager *sessions.SessionManager
sessionManager, err = sessions.NewSessionManager(opt.SessionBackend)
if err != nil {
return fmt.Errorf("failed to create session manager: %w", err)
}
// Build agentFactory for new agents
agentFactory := func(ctx context.Context) (*agent.Agent, error) {
var client gollm.Client
var err error
if opt.SkipVerifySSL {
client, err = gollm.NewClient(ctx, opt.ProviderID, gollm.WithSkipVerifySSL())
} else {
client, err = gollm.NewClient(ctx, opt.ProviderID)
}
if err != nil {
return nil, fmt.Errorf("creating llm client: %w", err)
}
return &agent.Agent{
Model: opt.ModelID,
Provider: opt.ProviderID,
Kubeconfig: opt.KubeConfigPath,
LLM: client,
MaxIterations: opt.MaxIterations,
PromptTemplateFile: opt.PromptTemplateFilePath,
ExtraPromptPaths: opt.ExtraPromptPaths,
Tools: tools.Default(),
Recorder: recorder,
RemoveWorkDir: opt.RemoveWorkDir,
SkipPermissions: opt.SkipPermissions,
EnableToolUseShim: opt.EnableToolUseShim,
MCPClientEnabled: opt.MCPClient,
Sandbox: opt.Sandbox,
SandboxImage: opt.SandboxImage,
SessionBackend: opt.SessionBackend,
RunOnce: opt.Quiet,
InitialQuery: queryFromCmd,
}, nil
}
agentManager := agent.NewAgentManager(agentFactory, sessionManager)
// Register cleanup for all sessions and agents
defer agentManager.Close()
if opt.ResumeSession != "" {
if opt.ResumeSession == "latest" {
session, err = sessionManager.GetLatestSession()
if err != nil {
return fmt.Errorf("failed to get latest session: %w", err)
}
if session == nil {
// No latest session found, create a new one
klog.Info("No previous session found to resume. Creating new session.")
}
} else {
session, err = sessionManager.FindSessionByID(opt.ResumeSession)
if err != nil {
return fmt.Errorf("session %s not found: %w", opt.ResumeSession, err)
}
}
}
var defaultAgent *agent.Agent
// If no session loaded (or resume failed/not requested), create a new one
if session == nil {
meta := sessions.Metadata{
ModelID: opt.ModelID,
ProviderID: opt.ProviderID,
}
session, err = sessionManager.NewSession(meta)
if err != nil {
return fmt.Errorf("failed to create a new session: %w", err)
}
defaultAgent, err = agentManager.GetAgent(ctx, session.ID)
if err != nil {
return fmt.Errorf("failed to get agent for new session: %w", err)
}
klog.Infof("Created new session: %s\n", session.ID)
} else {
// Update last accessed for resumed session
if err := sessionManager.UpdateLastAccessed(session); err != nil {
klog.Warningf("Failed to update session last accessed time: %v", err)
}
klog.Infof("Resuming session: %s\n", session.ID)
defaultAgent, err = agentManager.GetAgent(ctx, session.ID)
if err != nil {
return fmt.Errorf("failed to get agent for session: %w", err)
}
}
var userInterface ui.UI
switch opt.UIType {
case ui.UITypeTerminal:
// since stdin is already consumed, we use TTY for taking input from user
useTTYForInput := hasInputData
userInterface, err = ui.NewTerminalUI(defaultAgent, useTTYForInput, opt.ShowToolOutput, recorder)
if err != nil {
return fmt.Errorf("creating terminal UI: %w", err)
}
case ui.UITypeWeb:
userInterface, err = html.NewHTMLUserInterface(agentManager, sessionManager, opt.ModelID, opt.ProviderID, opt.UIListenAddress, recorder)
if err != nil {
return fmt.Errorf("creating web UI: %w", err)
}
case ui.UITypeTUI:
userInterface = ui.NewTUI(defaultAgent)
default:
return fmt.Errorf("ui-type mode %q is not known", opt.UIType)
}
err = userInterface.Run(ctx)
if err != nil && !errors.Is(err, context.Canceled) {
return fmt.Errorf("running UI: %w", err)
}
return nil
}
func handleCustomTools(toolConfigPaths []string) error {
// resolve tool config paths, and then load and register custom tools from config files and dirs
for _, path := range toolConfigPaths {
pathWithPlaceholdersExpanded := path
if strings.Contains(pathWithPlaceholdersExpanded, "{CONFIG}") {
configDir, err := os.UserConfigDir()
if err != nil {
klog.Warningf("Failed to get user config directory for tools path %q: %v", path, err)
continue
}
pathWithPlaceholdersExpanded = strings.ReplaceAll(pathWithPlaceholdersExpanded, "{CONFIG}", configDir)
}
if strings.Contains(pathWithPlaceholdersExpanded, "{HOME}") {
homeDir, err := os.UserHomeDir()
if err != nil {
klog.Warningf("Failed to get user home directory for tools path %q: %v", path, err)
continue
}
pathWithPlaceholdersExpanded = strings.ReplaceAll(pathWithPlaceholdersExpanded, "{HOME}", homeDir)
}
cleanedPath := filepath.Clean(pathWithPlaceholdersExpanded)
klog.Infof("Attempting to load custom tools from processed path: %q (original value from config: %q)", cleanedPath, path)
if err := tools.LoadAndRegisterCustomTools(cleanedPath); err != nil {
if errors.Is(err, os.ErrNotExist) && !slices.Contains(defaultToolConfigPaths, path) {
// user specified a directory that does not exist, we must error out
return fmt.Errorf("custom tools directory not found (original value: %q, processed path: %q)", path, cleanedPath)
} else {
klog.Warningf("Failed to load or register custom tools (original value: %q, processed path: %q): %v", path, cleanedPath, err)
}
}
}
return nil
}
// Redirect standard log output to our custom klog writer
// This is primarily to suppress warning messages from
// genai library https://github.com/googleapis/go-genai/blob/6ac4afc0168762dc3b7a4d940fc463cc1854f366/types.go#L1633
func redirectStdLogToKlog() {
log.SetOutput(klogWriter{})
// Disable standard log's prefixes (date, time, file info)
// because klog will add its own more detailed prefix.
log.SetFlags(0)
}
// Define a custom writer that forwards messages to klog.Warning
type klogWriter struct{}
func (writer klogWriter) Write(data []byte) (n int, err error) {
// We trim the trailing newline because klog adds its own.
message := string(bytes.TrimSuffix(data, []byte("\n")))
klog.Warning(message)
return len(data), nil
}
func hasStdInData() (bool, error) {
hasData := false
stat, err := os.Stdin.Stat()
if err != nil {
return hasData, fmt.Errorf("checking stdin: %w", err)
}
hasData = (stat.Mode() & os.ModeCharDevice) == 0
return hasData, nil
}
// resolveQueryInput determines the query input from positional args and/or stdin.
// It supports:
// - 1 positional arg only -> kubectl-ai "get pods"
// - stdin only -> echo "get pods" | kubectl-ai
// - 1 positional arg + stdin (combined) -> kubectl-ai get <<< "pods" or kubectl-ai "get" <<< "pods"
// As default no positional arg nor stdin
func resolveQueryInput(hasStdInData bool, args []string) (string, error) {
switch {
case len(args) == 1 && !hasStdInData:
// Use argument directly
return args[0], nil
case len(args) == 1 && hasStdInData:
// Combine arg + stdin
var b strings.Builder
b.WriteString(args[0])
b.WriteString("\n")
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
b.WriteString(scanner.Text())
b.WriteString("\n")
}
if err := scanner.Err(); err != nil {
return "", fmt.Errorf("reading stdin: %w", err)
}
query := strings.TrimSpace(b.String())
if query == "" {
return "", fmt.Errorf("no query provided from stdin")
}
return query, nil
case len(args) == 0 && hasStdInData:
// Read stdin only
b, err := io.ReadAll(os.Stdin)
if err != nil {
return "", fmt.Errorf("reading stdin: %w", err)
}
query := strings.TrimSpace(string(b))
if query == "" {
return "", fmt.Errorf("no query provided from stdin")
}
return query, nil
default:
// Case: No input at all — return empty string, no error
return "", nil
}
}
func resolveKubeConfigPath(opt *Options) error {
switch {
case opt.KubeConfigPath != "":
// Already set from flag or viper env
case os.Getenv("KUBECONFIG") != "":
opt.KubeConfigPath = os.Getenv("KUBECONFIG")
default:
home, err := os.UserHomeDir()
if err != nil {
return fmt.Errorf("failed to get user home directory: %w", err)
}
defaultPath := filepath.Join(home, ".kube", "config")
// Only use the default path if it exists
if _, err := os.Stat(defaultPath); err == nil {
opt.KubeConfigPath = defaultPath
}
}
// We resolve the kubeconfig path to an absolute path, so we can run kubectl from any working directory.
if opt.KubeConfigPath != "" {
p, err := filepath.Abs(opt.KubeConfigPath)
if err != nil {
return fmt.Errorf("failed to get absolute path for kubeconfig file %q: %w", opt.KubeConfigPath, err)
}
opt.KubeConfigPath = p
}
return nil
}
func startMCPServer(ctx context.Context, opt Options) error {
workDir := filepath.Join(os.TempDir(), "kubectl-ai-mcp")
if err := os.MkdirAll(workDir, 0o755); err != nil {
return fmt.Errorf("error creating work directory: %w", err)
}
mcpServer, err := newKubectlMCPServer(ctx, opt.KubeConfigPath, tools.Default(), workDir, opt.ExternalTools, opt.MCPServerMode, opt.HTTPPort)
if err != nil {
return fmt.Errorf("creating mcp server: %w", err)
}
return mcpServer.Serve(ctx)
}
// handleListSessions lists all available sessions with their metadata.
func handleListSessions(opt Options) error {
manager, err := sessions.NewSessionManager(opt.SessionBackend)
if err != nil {
return fmt.Errorf("failed to create session manager: %w", err)
}
sessionList, err := manager.ListSessions()
if err != nil {
return fmt.Errorf("failed to list sessions: %w", err)
}
if len(sessionList) == 0 {
fmt.Println("No sessions found.")
return nil
}
fmt.Println("Available sessions:")
fmt.Println("ID\t\tCreated\t\t\tLast Accessed\t\tModel\t\tProvider")
fmt.Println("--\t\t-------\t\t\t-------------\t\t-----\t\t--------")
for _, session := range sessionList {
fmt.Printf("%s\t%s\t%s\t%s\t%s\n",
session.ID,
session.CreatedAt.Format("2006-01-02 15:04:05"),
session.LastModified.Format("2006-01-02 15:04:05"),
session.ModelID,
session.ProviderID)
}
return nil
}
// handleDeleteSession deletes a session by ID.
func handleDeleteSession(opt Options) error {
manager, err := sessions.NewSessionManager(opt.SessionBackend)
if err != nil {
return fmt.Errorf("failed to create session manager: %w", err)
}
session, err := manager.FindSessionByID(opt.DeleteSession)
if err != nil {
return fmt.Errorf("session %s not found: %w", opt.DeleteSession, err)
}
fmt.Printf("Deleting session %s:\n", opt.DeleteSession)
fmt.Printf(" Model: %s\n", session.ModelID)
fmt.Printf(" Provider: %s\n", session.ProviderID)
fmt.Printf(" Created: %s\n", session.CreatedAt.Format("2006-01-02 15:04:05"))
fmt.Print("Are you sure you want to delete this session? (y/N): ")
var response string
fmt.Scanln(&response)
if response != "y" && response != "Y" {
fmt.Println("Deletion cancelled.")
return nil
}
if err := manager.DeleteSession(opt.DeleteSession); err != nil {
return fmt.Errorf("failed to delete session: %w", err)
}
fmt.Printf("Session %s deleted successfully.\n", opt.DeleteSession)
return nil
}
================================================
FILE: cmd/mcp.go
================================================
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"context"
"fmt"
"github.com/GoogleCloudPlatform/kubectl-ai/gollm"
"github.com/GoogleCloudPlatform/kubectl-ai/pkg/mcp"
"github.com/GoogleCloudPlatform/kubectl-ai/pkg/tools"
mcpgo "github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
"k8s.io/klog/v2"
)
type kubectlMCPServer struct {
kubectlConfig string
server *server.MCPServer
tools tools.Tools
workDir string
mcpManager *mcp.Manager // Add MCP manager for external tool calls
mcpServerMode string // Server mode (e.g., "streamable-http", "stdio")
httpPort int // Port for HTTP-based server modes
}
func newKubectlMCPServer(ctx context.Context, kubectlConfig string, tools tools.Tools, workDir string, exposeExternalTools bool, serverMode string, httpPort int) (*kubectlMCPServer, error) {
s := &kubectlMCPServer{
kubectlConfig: kubectlConfig,
workDir: workDir,
server: server.NewMCPServer(
"kubectl-ai",
"0.0.1",
server.WithToolCapabilities(true),
),
tools: tools,
mcpServerMode: serverMode,
httpPort: httpPort,
}
// Add built-in tools
for _, tool := range s.tools.AllTools() {
toolDefn := tool.FunctionDefinition()
toolInputSchema, err := toolDefn.Parameters.ToRawSchema()
if err != nil {
return nil, fmt.Errorf("converting tool schema to json.RawMessage: %w", err)
}
s.server.AddTool(mcpgo.NewToolWithRawSchema(
toolDefn.Name,
toolDefn.Description,
toolInputSchema,
), s.handleToolCall)
}
// Only discover external MCP tools if explicitly enabled
if exposeExternalTools {
// Initialize MCP manager to get client tools
manager, err := mcp.InitializeManager()
if err != nil {
klog.Warningf("Failed to initialize MCP manager: %v", err)
return s, nil // Return server with just built-in tools
}
// Store the manager for later use in tool calls
s.mcpManager = manager
// Connect to MCP servers and get their tools
if err := manager.DiscoverAndConnectServers(ctx); err != nil {
klog.Warningf("Failed to connect to MCP servers: %v", err)
return s, nil // Return server with just built-in tools
}
// Get tools from all connected MCP servers
serverTools, err := manager.ListAvailableTools(ctx)
if err != nil {
klog.Warningf("Failed to list tools from MCP servers: %v", err)
return s, nil // Return server with just built-in tools
}
// Add tools from MCP servers
totalToolsRegistered := 0
for serverName, tools := range serverTools {
klog.V(2).Infof("Processing tools from MCP server %s: %d tools found", serverName, len(tools))
for _, tool := range tools {
// Create unique tool name to avoid conflicts with built-in tools or from other servers
uniqueToolName := fmt.Sprintf("%s_%s", serverName, tool.Name)
// Use the actual tool schema instead of creating a generic wrapper
var schema *gollm.FunctionDefinition
if tool.InputSchema != nil {
// Use the real schema from the external tool
schema = &gollm.FunctionDefinition{
Name: uniqueToolName,
Description: fmt.Sprintf("%s (from %s)", tool.Description, serverName),
Parameters: tool.InputSchema,
}
} else {
// Fallback to generic schema if no schema provided
klog.V(2).Infof("External tool %s from %s has no schema, using generic wrapper", tool.Name, serverName)
schema = &gollm.FunctionDefinition{
Name: uniqueToolName,
Description: fmt.Sprintf("%s (from %s)", tool.Description, serverName),
Parameters: &gollm.Schema{
Type: gollm.TypeObject,
Properties: map[string]*gollm.Schema{
"args": {
Type: gollm.TypeObject,
Description: "Tool arguments",
},
},
},
}
}
toolInputSchema, err := schema.Parameters.ToRawSchema()
if err != nil {
klog.Errorf("Failed to convert tool schema for %s from %s: %v - skipping tool", tool.Name, serverName, err)
continue
}
// Add the tool to the server
s.server.AddTool(mcpgo.NewToolWithRawSchema(
uniqueToolName,
schema.Description,
toolInputSchema,
), s.handleToolCall)
totalToolsRegistered++
klog.V(3).Infof("Registered tool: %s from server %s", uniqueToolName, serverName)
}
}
klog.Infof("MCP server initialized with external tool discovery enabled - registered %d tools from %d servers", totalToolsRegistered, len(serverTools))
} else {
klog.Infof("MCP server initialized with external tool discovery disabled")
}
return s, nil
}
func (s *kubectlMCPServer) Serve(ctx context.Context) error {
// Ensure proper cleanup of MCP manager on shutdown
if s.mcpManager != nil {
defer func() {
if err := s.mcpManager.Close(); err != nil {
klog.Warningf("Failed to close MCP manager: %v", err)
}
}()
}
klog.Info("Starting kubectl-ai MCP server")
switch s.mcpServerMode {
case "streamable-http":
// Start the server in streamable HTTP mode
klog.Infof("Starting MCP server in streamable HTTP mode on port %d", s.httpPort)
httpServer := server.NewStreamableHTTPServer(s.server)
endpoint := fmt.Sprintf(":%d", s.httpPort)
klog.Infof("Listening for streamable HTTP connections on port %d", s.httpPort)
return httpServer.Start(endpoint)
default:
return server.ServeStdio(s.server)
}
}
func (s *kubectlMCPServer) handleToolCall(ctx context.Context, request mcpgo.CallToolRequest) (*mcpgo.CallToolResult, error) {
toolName := request.Params.Name
// First, try to find the tool in our built-in tools collection
builtinTool := s.tools.Lookup(toolName)
if builtinTool != nil {
return s.handleBuiltinToolCall(ctx, request, builtinTool)
}
// If not a built-in tool, try to handle as external MCP tool
if s.mcpManager != nil {
return s.handleExternalMCPToolCall(ctx, request)
}
// Tool not found
return &mcpgo.CallToolResult{
IsError: true,
Content: []mcpgo.Content{
mcpgo.TextContent{
Type: "text",
Text: fmt.Sprintf("tool %q not found", toolName),
},
},
}, nil
}
// handleBuiltinToolCall handles calls to built-in kubectl-ai tools
func (s *kubectlMCPServer) handleBuiltinToolCall(ctx context.Context, request mcpgo.CallToolRequest, tool tools.Tool) (*mcpgo.CallToolResult, error) {
// Set up context for built-in tools
ctx = context.WithValue(ctx, tools.KubeconfigKey, s.kubectlConfig)
ctx = context.WithValue(ctx, tools.WorkDirKey, s.workDir)
// Convert arguments to the expected type
args, ok := request.Params.Arguments.(map[string]any)
if !ok {
return &mcpgo.CallToolResult{
IsError: true,
Content: []mcpgo.Content{
mcpgo.TextContent{
Type: "text",
Text: fmt.Sprintf("invalid arguments type: expected map[string]any, got %T", request.Params.Arguments),
},
},
}, nil
}
// Execute the built-in tool
result, err := tool.Run(ctx, args)
if err != nil {
return &mcpgo.CallToolResult{
IsError: true,
Content: []mcpgo.Content{
mcpgo.TextContent{
Type: "text",
Text: err.Error(),
},
},
}, nil
}
// Convert result to string
var resultStr string
switch v := result.(type) {
case string:
resultStr = v
default:
resultStr = fmt.Sprintf("%v", v)
}
return &mcpgo.CallToolResult{
Content: []mcpgo.Content{
mcpgo.TextContent{
Type: "text",
Text: resultStr,
},
},
}, nil
}
// handleExternalMCPToolCall handles calls to external MCP tools
func (s *kubectlMCPServer) handleExternalMCPToolCall(ctx context.Context, request mcpgo.CallToolRequest) (*mcpgo.CallToolResult, error) {
toolName := request.Params.Name
// Find which server provides this tool
serverTools, err := s.mcpManager.ListAvailableTools(ctx)
if err != nil {
return &mcpgo.CallToolResult{
IsError: true,
Content: []mcpgo.Content{
mcpgo.TextContent{
Type: "text",
Text: fmt.Sprintf("failed to list available tools: %v", err),
},
},
}, nil
}
var targetServerName string
var originalToolName string
// Look for the tool by checking both original name and server-prefixed name
for serverName, tools := range serverTools {
for _, tool := range tools {
uniqueToolName := fmt.Sprintf("%s_%s", serverName, tool.Name)
if uniqueToolName == toolName {
targetServerName = serverName
originalToolName = tool.Name // Use the original tool name for the MCP call
break
}
}
if targetServerName != "" {
break
}
}
if targetServerName == "" {
return &mcpgo.CallToolResult{
IsError: true,
Content: []mcpgo.Content{
mcpgo.TextContent{
Type: "text",
Text: fmt.Sprintf("external MCP tool %q not found", toolName),
},
},
}, nil
}
// Get the client for the target server
client, exists := s.mcpManager.GetClient(targetServerName)
if !exists {
return &mcpgo.CallToolResult{
IsError: true,
Content: []mcpgo.Content{
mcpgo.TextContent{
Type: "text",
Text: fmt.Sprintf("MCP client for server %q not found", targetServerName),
},
},
}, nil
}
// Extract arguments - handle the args wrapper for external tools and empty/nil input
var toolArgs map[string]any
if request.Params.Arguments == nil {
// Handle nil arguments as empty map
toolArgs = make(map[string]any)
} else if args, ok := request.Params.Arguments.(map[string]any); ok {
if argsValue, hasArgs := args["args"]; hasArgs {
if argsMap, ok := argsValue.(map[string]any); ok {
toolArgs = argsMap
} else {
toolArgs = args // Fallback to using args directly
}
} else {
toolArgs = args // Use arguments directly if no "args" wrapper
}
} else {
return &mcpgo.CallToolResult{
IsError: true,
Content: []mcpgo.Content{
mcpgo.TextContent{
Type: "text",
Text: fmt.Sprintf("invalid arguments type: expected map[string]any, got %T", request.Params.Arguments),
},
},
}, nil
}
// Call the external MCP tool using the original tool name
result, err := client.CallTool(ctx, originalToolName, toolArgs)
if err != nil {
return &mcpgo.CallToolResult{
IsError: true,
Content: []mcpgo.Content{
mcpgo.TextContent{
Type: "text",
Text: fmt.Sprintf("error calling external MCP tool %q on server %q: %v", originalToolName, targetServerName, err),
},
},
}, nil
}
// Return successful result
return &mcpgo.CallToolResult{
Content: []mcpgo.Content{
mcpgo.TextContent{
Type: "text",
Text: result,
},
},
}, nil
}
================================================
FILE: cmd/mcp_test.go
================================================
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"context"
"fmt"
"net"
"testing"
"time"
"github.com/GoogleCloudPlatform/kubectl-ai/gollm"
"github.com/GoogleCloudPlatform/kubectl-ai/pkg/mcp"
"github.com/GoogleCloudPlatform/kubectl-ai/pkg/tools"
)
func TestKubectlMCPServerHTTPClientIntegration(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
toolset := tools.Tools{}
toolset.Init()
toolset.RegisterTool(&stubTool{})
port := getFreePort(t)
workDir := t.TempDir()
server, err := newKubectlMCPServer(ctx, "", toolset, workDir, false, "streamable-http", port)
if err != nil {
t.Fatalf("failed to create MCP server: %v", err)
}
serverErr := make(chan error, 1)
go func() {
serverErr <- server.Serve(ctx)
}()
waitForHTTPServer(t, port)
select {
case err := <-serverErr:
if err != nil {
t.Fatalf("server exited early: %v", err)
}
default:
}
clientConfig := mcp.ClientConfig{
Name: "test-client",
URL: fmt.Sprintf("http://127.0.0.1:%d/mcp", port),
UseStreaming: true,
Timeout: 5,
}
client := mcp.NewClient(clientConfig)
connectCtx, connectCancel := context.WithTimeout(ctx, 5*time.Second)
defer connectCancel()
t.Log("connecting client")
if err := client.Connect(connectCtx); err != nil {
t.Fatalf("failed to connect client to MCP server: %v", err)
}
defer func() {
if err := client.Close(); err != nil {
t.Errorf("failed to close MCP client: %v", err)
}
}()
toolsCtx, toolsCancel := context.WithTimeout(ctx, 5*time.Second)
defer toolsCancel()
t.Log("listing tools")
availableTools, err := client.ListTools(toolsCtx)
if err != nil {
t.Fatalf("failed to list tools from MCP server: %v", err)
}
t.Logf("retrieved %d tool(s)", len(availableTools))
if !toolExists("stub", availableTools) {
t.Fatalf("expected to find stub tool, got %v", availableTools)
}
cancel()
select {
case <-serverErr:
case <-time.After(500 * time.Millisecond):
}
}
func waitForHTTPServer(t *testing.T, port int) {
t.Helper()
deadline := time.Now().Add(5 * time.Second)
address := fmt.Sprintf("127.0.0.1:%d", port)
for {
conn, err := net.DialTimeout("tcp", address, 100*time.Millisecond)
if err == nil {
conn.Close()
return
}
if time.Now().After(deadline) {
t.Fatalf("server did not start listening on %s: %v", address, err)
}
time.Sleep(50 * time.Millisecond)
}
}
func getFreePort(t *testing.T) int {
t.Helper()
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("failed to acquire free port: %v", err)
}
defer l.Close()
return l.Addr().(*net.TCPAddr).Port
}
func toolExists(name string, tools []mcp.Tool) bool {
for _, tool := range tools {
if tool.Name == name {
return true
}
}
return false
}
type stubTool struct{}
func (stubTool) Name() string {
return "stub"
}
func (stubTool) Description() string {
return "stub tool"
}
func (stubTool) FunctionDefinition() *gollm.FunctionDefinition {
return &gollm.FunctionDefinition{
Name: "stub",
Description: "stub tool",
Parameters: &gollm.Schema{
Type: gollm.TypeObject,
},
}
}
func (stubTool) Run(context.Context, map[string]any) (any, error) {
return "ok", nil
}
func (stubTool) IsInteractive(map[string]any) (bool, error) {
return false, nil
}
func (stubTool) CheckModifiesResource(map[string]any) string {
return "no"
}
================================================
FILE: contributing.md
================================================
# How to Contribute
We would love to accept your patches and contributions to this project.
## Before you begin
### Sign our Contributor License Agreement
Contributions to this project must be accompanied by a
[Contributor License Agreement](https://cla.developers.google.com/about) (CLA).
You (or your employer) retain the copyright to your contribution; this simply
gives us permission to use and redistribute your contributions as part of the
project.
If you or your current employer have already signed the Google CLA (even if it
was for a different project), you probably don't need to do it again.
Visit <https://cla.developers.google.com/> to see your current agreements or to
sign a new one.
### Review our Community Guidelines
This project follows [Google's Open Source Community
Guidelines](https://opensource.google/conduct/).
## Contribution process
### Code Reviews
All submissions, including submissions by project members, require review. We
use [GitHub pull requests](https://docs.github.com/articles/about-pull-requests)
for this purpose.
## Understand the repo
An AI-generated overview of the system architecture for this repository is
available [here](https://deepwiki.com/GoogleCloudPlatform/kubectl-ai/). This can
provide an interactive way to explore the codebase.
Quick notes about the various directories:
- Source code for `kubectl-ai` CLI lives under `cmd/` and `pkg/` directories.
- gollm directory is an independent Go module that implements LLM clients for
different LLM providers.
- `modelserving` directory contains utilities and configuration to build and run
open source AI models locally or in a kubernetes cluster.
- `kubectl-utils` is an independent Go package/binary to help with the benchmarks tasks
that evaluates various conditions involving properties of kubernetes resources.
- User guides/design docs/proposals live under `docs` directory.
- `dev` directory scripts for project related tasks (adhoc/CI).
================================================
FILE: dev/ci/periodics/analyze-evals.sh
================================================
#!/bin/bash
set -o errexit
set -o nounset
set -o pipefail
set -x
REPO_ROOT="$(git rev-parse --show-toplevel)"
cd ${REPO_ROOT}
if [[ -z "${OUTPUT_DIR:-}" ]]; then
OUTPUT_DIR="${REPO_ROOT}/.build/k8s-ai-bench"
mkdir -p "${OUTPUT_DIR}"
fi
BINDIR="${REPO_ROOT}/.build/bin"
mkdir -p "${BINDIR}"
K8S_AI_BENCH_SRC="${REPO_ROOT}/.build/k8s-ai-bench-src"
rm -rf "${K8S_AI_BENCH_SRC}"
git clone https://github.com/gke-labs/k8s-ai-bench "${K8S_AI_BENCH_SRC}"
cd "${K8S_AI_BENCH_SRC}"
GOWORK=off go build -o "${BINDIR}/k8s-ai-bench" .
cd "${REPO_ROOT}"
# Pass --show-failures flag to the analyze command if it's set
ANALYZE_ARGS=""
if [[ "$*" == *"--show-failures"* ]]; then
ANALYZE_ARGS="--show-failures"
fi
"${BINDIR}/k8s-ai-bench" analyze --input-dir "${OUTPUT_DIR}" ${TEST_ARGS:-} -results-filepath ${REPO_ROOT}/.build/k8s-ai-bench.md --output-format markdown ${ANALYZE_ARGS}
"${BINDIR}/k8s-ai-bench" analyze --input-dir "${OUTPUT_DIR}" ${TEST_ARGS:-} -results-filepath ${REPO_ROOT}/.build/k8s-ai-bench.json --output-format json
================================================
FILE: dev/ci/periodics/run-evals.sh
================================================
#!/bin/bash
set -o errexit
set -o nounset
set -o pipefail
set -x
REPO_ROOT="$(git rev-parse --show-toplevel)"
cd ${REPO_ROOT}
if [[ -z "${OUTPUT_DIR:-}" ]]; then
OUTPUT_DIR="${REPO_ROOT}/.build/k8s-ai-bench"
mkdir -p "${OUTPUT_DIR}"
fi
echo "Writing results to ${OUTPUT_DIR}"
BINDIR="${REPO_ROOT}/.build/bin"
mkdir -p "${BINDIR}"
curl -sSL https://raw.githubusercontent.com/GoogleCloudPlatform/kubectl-ai/main/install.sh | bash
K8S_AI_BENCH_SRC="${REPO_ROOT}/.build/k8s-ai-bench-src"
rm -rf "${K8S_AI_BENCH_SRC}"
git clone https://github.com/gke-labs/k8s-ai-bench "${K8S_AI_BENCH_SRC}"
cd "${K8S_AI_BENCH_SRC}"
GOWORK=off go build -o "${BINDIR}/k8s-ai-bench" .
"${BINDIR}/k8s-ai-bench" run --agent-bin kubectl-ai --kubeconfig "${KUBECONFIG:-~/.kube/config}" --output-dir "${OUTPUT_DIR}" ${TEST_ARGS:-}
================================================
FILE: dev/ci/presubmits/go-build.sh
================================================
#!/usr/bin/env bash
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
REPO_ROOT="$(git rev-parse --show-toplevel)"
cd ${REPO_ROOT}
for f in $(find ${REPO_ROOT} -name go.mod); do
cd $(dirname ${f})
go build ./...
done
================================================
FILE: dev/ci/presubmits/go-vet.sh
================================================
#!/usr/bin/env bash
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
REPO_ROOT="$(git rev-parse --show-toplevel)"
cd ${REPO_ROOT}
for f in $(find ${REPO_ROOT} -name go.mod); do
cd $(dirname ${f})
go vet ./...
done
================================================
FILE: dev/ci/presubmits/verify-autogen.sh
================================================
#!/usr/bin/env bash
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
REPO_ROOT="$(git rev-parse --show-toplevel)"
cd ${REPO_ROOT}
dev/tasks/generate-github-actions.sh
changes=$(git status --porcelain)
if [[ -n "${changes}" ]]; then
echo "FAIL: Changes detected from dev/tasks/generate-github-actions.sh:"
git diff | head -n60
echo "${changes}"
exit 1
fi
================================================
FILE: dev/ci/presubmits/verify-format.sh
================================================
#!/usr/bin/env bash
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
REPO_ROOT="$(git rev-parse --show-toplevel)"
cd ${REPO_ROOT}
dev/tasks/format.sh
changes=$(git status --porcelain)
if [[ -n "${changes}" ]]; then
echo "FAIL: Changes detected from dev/tasks/format.sh:"
git diff | head -n60
echo "${changes}"
exit 1
fi
================================================
FILE: dev/ci/presubmits/verify-gomod.sh
================================================
#!/usr/bin/env bash
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
REPO_ROOT="$(git rev-parse --show-toplevel)"
cd ${REPO_ROOT}
dev/tasks/gomod.sh
changes=$(git status --porcelain)
if [[ -n "${changes}" ]]; then
echo "FAIL: Changes detected from dev/tasks/gomod.sh:"
git diff | head -n60
echo "${changes}"
exit 1
fi
================================================
FILE: dev/ci/presubmits/verify-mocks.sh
================================================
#!/usr/bin/env bash
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
REPO_ROOT="$(git rev-parse --show-toplevel)"
cd ${REPO_ROOT}
if ! command -v mockgen &> /dev/null; then
echo "mockgen not found, installing..."
go install go.uber.org/mock/mockgen@latest
fi
# We run generate to see if it creates any diffs.
go generate ./internal/mocks
if ! git diff --quiet --exit-code -- internal/mocks; then
echo "Mocks are stale. Commit the changes to the generated files."
exit 1
fi
echo "Mocks are up to date."
================================================
FILE: dev/tasks/build-images
================================================
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
REPO_ROOT="$(git rev-parse --show-toplevel)"
SRC_DIR=${REPO_ROOT}/
cd "${SRC_DIR}"
if [[ -z "${IMAGE_PREFIX:-}" ]]; then
IMAGE_PREFIX=""
fi
echo "Building images with prefix ${IMAGE_PREFIX}"
if [[ -z "${TAG:-}" ]]; then
TAG=latest
fi
if [[ -z "${BUILDX_ARGS:-}" ]]; then
BUILDX_ARGS="--load"
fi
if [[ -z "${DOCKER:-}" ]]; then
DOCKER="docker"
fi
echo "Using container tool: ${DOCKER}"
if [[ "${DOCKER}" == "podman" ]]; then
# Podman doesn't support buildx, use regular build command
if [[ "${BUILDX_ARGS}" == "--push" ]]; then
# For podman, build and then push separately
echo "Building with podman..."
${DOCKER} build \
--platform linux/amd64 \
-f images/kubectl-ai/Dockerfile \
-t ${IMAGE_PREFIX}kubectl-ai:${TAG} \
.
echo "Pushing with podman..."
${DOCKER} push ${IMAGE_PREFIX}kubectl-ai:${TAG}
else
# For --load or other args, just build
${DOCKER} build \
--platform linux/amd64 \
-f images/kubectl-ai/Dockerfile \
-t ${IMAGE_PREFIX}kubectl-ai:${TAG} \
.
fi
else
# Use docker buildx for docker
# Specify platform to avoid multi-arch build requirements
${DOCKER} buildx build ${BUILDX_ARGS} \
--platform linux/amd64 \
-f images/kubectl-ai/Dockerfile \
-t ${IMAGE_PREFIX}kubectl-ai:${TAG} \
--progress=plain .
fi
================================================
FILE: dev/tasks/demo.md
================================================
# steps to produce the demo gif
1. Use [asciinema](asciinema.org) to record a screencast.
2. Use agg to produce the gif
```shell
agg .github/kubectl-ai.cast --speed 1.3 --idle-time-limit 1 --theme monokai --font-size 24 --cols 100 --rows 25 .github/kubectl-ai.gif
```
================================================
FILE: dev/tasks/deploy-to-gke
================================================
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
REPO_ROOT="$(git rev-parse --show-toplevel)"
SRC_DIR=${REPO_ROOT}
cd "${SRC_DIR}"
# Get GCP project ID
if [[ -z "${GCP_PROJECT_ID:-}" ]]; then
GCP_PROJECT_ID=$(gcloud config get project)
fi
echo "Using GCP_PROJECT_ID=${GCP_PROJECT_ID}"
# Build kubectl command args based on available configuration
KUBECTL_ARGS=""
if [[ -n "${KUBECONFIG:-}" ]]; then
echo "Using KUBECONFIG: ${KUBECONFIG}"
else
echo "Using default kubeconfig (~/.kube/config)"
fi
if [[ -n "${KUBE_CONTEXT:-}" ]]; then
KUBECTL_ARGS="--context=${KUBE_CONTEXT}"
echo "Using kube context: ${KUBE_CONTEXT}"
elif [[ -z "${KUBECONFIG:-}" ]]; then
# Only require KUBE_CONTEXT if KUBECONFIG is not specified
echo "Listing GKE clusters in project ${GCP_PROJECT_ID}:"
gcloud container clusters list --project=${GCP_PROJECT_ID}
echo ""
echo "Please set KUBE_CONTEXT to kubectl context to use, or set KUBECONFIG to use a specific kubeconfig file"
exit 1
else
echo "Using current context from KUBECONFIG"
fi
if [[ -z "${NAMESPACE:-}" ]]; then
NAMESPACE=kubectl-ai
echo "Defaulting to namespace: ${NAMESPACE}"
fi
# Pick a probably-unique tag
export TAG=`date +%Y%m%d%H%M%S`
# Set up image registry - default to GCR, but allow override
if [[ -z "${IMAGE_REGISTRY:-}" ]]; then
IMAGE_REGISTRY=gcr.io/${GCP_PROJECT_ID}
fi
echo "Using image registry: ${IMAGE_REGISTRY}"
# Configure authentication for the container registry before building
echo "Configuring authentication for ${IMAGE_REGISTRY}"
if [[ "${IMAGE_REGISTRY}" == gcr.io/* ]]; then
# Configure GCR authentication
gcloud auth configure-docker --quiet
elif [[ "${IMAGE_REGISTRY}" == *-docker.pkg.dev/* ]]; then
# Configure Artifact Registry authentication
gcloud auth configure-docker ${IMAGE_REGISTRY%%/*} --quiet
fi
# Configure podman authentication before building if needed
if [[ "${DOCKER:-docker}" == "podman" ]]; then
echo "Using podman: configuring registry authentication"
# For podman, we need to configure the auth helper
if [[ "${IMAGE_REGISTRY}" == gcr.io/* ]]; then
echo "$(gcloud auth print-access-token)" | podman login -u oauth2accesstoken --password-stdin gcr.io
elif [[ "${IMAGE_REGISTRY}" == *-docker.pkg.dev/* ]]; then
echo "$(gcloud auth print-access-token)" | podman login -u oauth2accesstoken --password-stdin ${IMAGE_REGISTRY%%/*}
fi
fi
# Build the image
echo "Building images"
export IMAGE_PREFIX=${IMAGE_REGISTRY}/
if [[ -n "${DOCKER:-}" ]]; then
echo "Using container tool: ${DOCKER}"
export DOCKER
fi
# For GKE, we need to push images, so use --push instead of --load
BUILDX_ARGS=--push dev/tasks/build-images
KUBECTL_AI_IMAGE="${IMAGE_PREFIX}kubectl-ai:${TAG}"
echo "Built and pushed image: ${KUBECTL_AI_IMAGE}"
# Create the namespace if it doesn't exist
echo "Creating namespace: ${NAMESPACE}"
kubectl create namespace ${NAMESPACE} ${KUBECTL_ARGS} --dry-run=client -oyaml | kubectl apply ${KUBECTL_ARGS} --server-side -f -
# Note: No secret needed for Vertex AI - uses Workload Identity for authentication
# Create a cluster role binding so kubectl can "see" the current cluster
# For production GKE, consider using more restrictive permissions
echo "Creating cluster role binding as view"
cat <<EOF | kubectl apply ${KUBECTL_ARGS} --namespace=${NAMESPACE} --server-side -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: ${NAMESPACE}:kubectl-ai:view
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: view
subjects:
- kind: ServiceAccount
name: kubectl-ai
namespace: ${NAMESPACE}
EOF
# Deploy manifests
echo "Deploying manifests"
cat k8s/kubectl-ai-gke.yaml | \
sed s@kubectl-ai:latest@${KUBECTL_AI_IMAGE}@g | \
sed s@PROJECT_ID@${GCP_PROJECT_ID}@g | \
kubectl apply ${KUBECTL_ARGS} --namespace=${NAMESPACE} --server-side -f -
echo ""
echo "Deployment completed successfully!"
echo "Image: ${KUBECTL_AI_IMAGE}"
echo "Namespace: ${NAMESPACE}"
echo ""
echo "Using GKE Workload Identity Federation for Vertex AI access."
echo "Make sure your GKE cluster has Workload Identity enabled and configured for Vertex AI."
echo ""
echo "To access the service:"
echo " kubectl port-forward ${KUBECTL_ARGS} -n ${NAMESPACE} service/kubectl-ai 8080:80"
echo " Then open http://localhost:8080 in your browser"
================================================
FILE: dev/tasks/deploy-to-kind
================================================
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
REPO_ROOT="$(git rev-parse --show-toplevel)"
SRC_DIR=${REPO_ROOT}
cd "${SRC_DIR}"
# Pick a probably-unique tag
export TAG=`date +%Y%m%d%H%M%S`
# Build kubectl command args based on available configuration
KUBECTL_ARGS=""
if [[ -n "${KUBECONFIG:-}" ]]; then
echo "Using KUBECONFIG: ${KUBECONFIG}"
else
echo "Using default kubeconfig (~/.kube/config)"
fi
if [[ -n "${KUBE_CONTEXT:-}" ]]; then
KUBECTL_ARGS="--context=${KUBE_CONTEXT}"
echo "Using kube context: ${KUBE_CONTEXT}"
elif [[ -z "${KUBECONFIG:-}" ]]; then
# Only set default context if KUBECONFIG is not specified
KUBE_CONTEXT=kind-kind
KUBECTL_ARGS="--context=${KUBE_CONTEXT}"
echo "Defaulting to kube context: ${KUBE_CONTEXT}"
fi
if [[ -z "${NAMESPACE:-}" ]]; then
NAMESPACE=kubectl-ai
echo "Defaulting to namespace: ${NAMESPACE}"
fi
# Build the image
echo "Building images"
export IMAGE_PREFIX=fake.registry/
if [[ -n "${DOCKER:-}" ]]; then
echo "Using container tool: ${DOCKER}"
export DOCKER
fi
BUILDX_ARGS=--load dev/tasks/build-images
KUBECTL_AI_IMAGE="${IMAGE_PREFIX:-}kubectl-ai:${TAG}"
# Determine the kind cluster name
KIND_CLUSTER_NAME=""
if [[ -n "${KUBE_CONTEXT:-}" ]] && [[ "${KUBE_CONTEXT}" == kind-* ]]; then
# Extract cluster name from context (kind-clustername -> clustername)
KIND_CLUSTER_NAME="${KUBE_CONTEXT#kind-}"
else
# Try to find any available kind cluster
AVAILABLE_CLUSTERS=$(kind get clusters 2>/dev/null || echo "")
if [[ -n "${AVAILABLE_CLUSTERS}" ]]; then
KIND_CLUSTER_NAME=$(echo "${AVAILABLE_CLUSTERS}" | head -n1)
echo "Auto-detected kind cluster: ${KIND_CLUSTER_NAME}"
else
echo "ERROR: No kind clusters found. Please create one with 'kind create cluster'"
exit 1
fi
fi
# Load the image into kind
echo "Loading images into kind cluster '${KIND_CLUSTER_NAME}': ${KUBECTL_AI_IMAGE}"
if [[ "${DOCKER:-docker}" == "podman" ]]; then
# For podman, we need to save and load the image via archive
echo "Using podman: saving image to archive first"
podman save ${KUBECTL_AI_IMAGE} -o /tmp/kubectl-ai-image.tar
kind load image-archive /tmp/kubectl-ai-image.tar --name ${KIND_CLUSTER_NAME}
rm -f /tmp/kubectl-ai-image.tar
else
# For docker, use the standard approach
kind load docker-image ${KUBECTL_AI_IMAGE} --name ${KIND_CLUSTER_NAME}
fi
# Create the namespace if it doesn't exist
echo "Creating namespace: ${NAMESPACE}"
kubectl create namespace ${NAMESPACE} ${KUBECTL_ARGS} --dry-run=client -oyaml | kubectl apply ${KUBECTL_ARGS} --server-side -f -
# Create the secret if it doesn't exist,
# including the GEMINI_API_KEY environment variable if set.
# (This is for kind, on a GKE cluster, we probably want to use Workload Identity instead)
echo "Creating secret: kubectl-ai"
cat <<EOF | kubectl apply ${KUBECTL_ARGS} --namespace=${NAMESPACE} --server-side -f -
kind: Secret
apiVersion: v1
metadata:
name: kubectl-ai
labels:
app: kubectl-ai
type: Opaque
stringData:
GEMINI_API_KEY: ${GEMINI_API_KEY}
EOF
# Create a role binding so kubectl can "see" the current cluster
# Again, this makes sense for kind but we will probably have a different approach for GKE
echo "Creating cluster role binding as view"
cat <<EOF | kubectl apply ${KUBECTL_ARGS} --namespace=${NAMESPACE} --server-side -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: ${NAMESPACE}:kubectl-ai:view
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: view
subjects:
- kind: ServiceAccount
name: kubectl-ai
namespace: ${NAMESPACE}
EOF
# Deploy manifests
echo "Deploying manifests"
cat k8s/kubectl-ai.yaml | sed s@kubectl-ai:latest@${KUBECTL_AI_IMAGE}@g | \
kubectl apply ${KUBECTL_ARGS} --namespace=${NAMESPACE} --server-side -f -
================================================
FILE: dev/tasks/format.sh
================================================
#!/usr/bin/env bash
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
REPO_ROOT="$(git rev-parse --show-toplevel)"
cd ${REPO_ROOT}
find . -name "*.go" | xargs go run github.com/google/addlicense@master -c "Google LLC" -l apache
for f in $(find ${REPO_ROOT} -name go.mod); do
cd $(dirname ${f})
gofmt -w .
done
================================================
FILE: dev/tasks/generate-github-actions.sh
================================================
#!/usr/bin/env bash
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
set -x
REPO_ROOT="$(git rev-parse --show-toplevel)"
cd ${REPO_ROOT}
cat > ${REPO_ROOT}/.github/workflows/ci-presubmit.yaml <<EOF
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Generated by dev/tasks/generate-github-actions
name: ci-presubmit
on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches: ["main"]
jobs:
EOF
for f in $(find dev/ci/presubmits -type f | sort ); do
filename=$(basename ${f})
name="${filename%.*}"
cat >> ${REPO_ROOT}/.github/workflows/ci-presubmit.yaml <<EOF
${name}:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
- name: "Run ${f}"
run: |
./${f}
EOF
done
cat >> ${REPO_ROOT}/.github/workflows/ci-presubmit.yaml <<'EOF'
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
EOF
================================================
FILE: dev/tasks/gomod.sh
================================================
#!/usr/bin/env bash
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
REPO_ROOT="$(git rev-parse --show-toplevel)"
cd ${REPO_ROOT}
# retry <max_attempts> <delay_seconds> <command...>
# Retries a command on failure with a fixed delay between attempts.
# Useful for transient Go proxy / network errors (e.g. HTTP/2 INTERNAL_ERROR).
retry() {
local max=$1; shift
local delay=$1; shift
local attempt=1
until "$@"; do
if (( attempt >= max )); then
echo "ERROR: command failed after ${max} attempts: $*" >&2
return 1
fi
echo "WARNING: attempt ${attempt}/${max} failed, retrying in ${delay}s: $*" >&2
sleep "${delay}"
(( attempt++ ))
done
}
for f in $(find ${REPO_ROOT} -name go.mod); do
cd $(dirname ${f})
rm go.sum
retry 3 5 go mod tidy
done
================================================
FILE: docs/bedrock.md
================================================
# AWS Bedrock Provider
kubectl-ai supports AWS Bedrock models including Claude Sonnet 4 and Claude 3.7.
## Setup
### AWS Credentials
Configure AWS credentials using standard AWS SDK methods:
```bash
# Option 1: Environment variables
export AWS_ACCESS_KEY_ID="your-access-key"
export AWS_SECRET_ACCESS_KEY="your-secret-key"
export AWS_REGION="us-east-1"
# Option 2: AWS Profile (recommended)
export AWS_PROFILE="your-profile-name"
export AWS_REGION="us-east-1"
# Option 3: Use IAM roles (on EC2/ECS/Lambda)
export AWS_REGION="us-east-1"
```
### Model Configuration
```bash
# Optional: Set default model
export BEDROCK_MODEL="us.anthropic.claude-3-7-sonnet-20250219-v1:0"
```
## Supported Models
See [AWS Bedrock documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids.html) for current model availability and regional support.
Currently supported:
- Claude Sonnet 4: `us.anthropic.claude-sonnet-4-20250514-v1:0` (default)
- Claude 3.7 Sonnet: `us.anthropic.claude-3-7-sonnet-20250219-v1:0`
## Usage
```bash
# Use default model (Claude Sonnet 4)
kubectl-ai --provider bedrock "explain this deployment"
# Specify model explicitly
kubectl-ai --provider bedrock --model us.anthropic.claude-3-7-sonnet-20250219-v1:0 "help me debug this pod"
```
## Authentication
kubectl-ai uses the standard AWS SDK credential provider chain:
1. Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
2. AWS credentials file (~/.aws/credentials)
3. AWS config file (~/.aws/config)
4. IAM roles for EC2 instances
5. IAM roles for ECS tasks
6. IAM roles for Lambda functions
For more details, see [AWS SDK Go Configuration](https://aws.github.io/aws-sdk-go-v2/docs/configuring-sdk/).
## Region Configuration
Bedrock is available in specific AWS regions. Set your region using:
```bash
export AWS_REGION="us-east-1" # Primary Bedrock region
```
Alternatively, configure region in `~/.aws/config`:
```ini
[default]
region = us-east-1
```
================================================
FILE: docs/gke-deployment.md
================================================
# Deploying k8-kate to Google Kubernetes Engine
## Prerequisites
- A GKE cluster (Standard or Autopilot).
- `gcloud` CLI authenticated with `gcloud auth login`.
- Local Docker environment (or Cloud Build) capable of building and pushing container images.
- [`kubectl` configured](https://cloud.google.com/kubernetes-engine/docs/how-to/cluster-access-for-kubectl) to talk to your target cluster.
- A Gemini API key with access to the model you plan to demo.
## 1. Set your Google Cloud context
```bash
export PROJECT_ID="my-gcp-project"
export REGION="us-central1"
export CLUSTER_NAME="kubectl-ai-demo"
gcloud config set project "${PROJECT_ID}"
gcloud container clusters get-credentials "${CLUSTER_NAME}" --region "${REGION}"
```
These commands configure both `gcloud` and `kubectl` to operate on the cluster that will host `kubectl-ai`.
## 2. Build and push the kubectl-ai image
Pick an Artifact Registry repository or another container registry that your cluster can pull from. The snippet below creates an Artifact Registry repo (if needed), builds the image locally, and pushes it.
```bash
# Create an Artifact Registry (skip if you already have one)
gcloud artifacts repositories create kubectl-ai \
--location="${REGION}" \
--repository-format=DOCKER \
--description="kubectl-ai demo images"
# Configure Docker to authenticate to Artifact Registry
gcloud auth configure-docker "${REGION}"-docker.pkg.dev
# Build and push the container
IMAGE="${REGION}-docker.pkg.dev/${PROJECT_ID}/kubectl-ai/kubectl-ai:latest"
docker build -t "${IMAGE}" -f images/kubectl-ai/Dockerfile .
docker push "${IMAGE}"
```
## 3. Prepare cluster namespaces and RBAC
Create the namespaces and RBAC that the hosted agent requires:
```bash
# Sandbox namespace + RBAC (creates `computer` namespace, service account, and reader roles)
kubectl apply -f k8s/sandbox/all-in-one.yaml
```
The sandbox manifest provisions the `computer` namespace and the `normal-user` service account used for sandbox pods. The `kubectl-ai-gke.yaml` manifest will create the `kubectl-ai` namespace automatically.
## 4. Configure the deployment manifest
Copy `k8s/kubectl-ai-gke.yaml` to a working file and edit the following sections:
1. **Container image** – replace the `REPLACE_WITH_YOUR_IMAGE`
2. **Gemini API key** – change `REPLACE_WITH_YOUR_GEMINI_API_KEY` to the key you obtained from Google AI Studio
Review the RBAC objects in the manifest and adjust them if your security posture requires tighter permissions.
## 5. Deploy kubectl-ai
Apply the updated manifest to your cluster:
```bash
kubectl apply -f kubectl-ai-gke.yaml
```
Kubernetes creates the Deployment, ServiceAccount, RBAC bindings, and Service for the hosted agent. You can watch the rollout with:
```bash
kubectl get pods -n kubectl-ai
kubectl describe pod -n kubectl-ai -l app=kubectl-ai | grep -i image
```
## 6. Access the hosted web UI
Port-forward the Service locally to interact with the hosted UI:
```bash
kubectl port-forward svc/kubectl-ai -n kubectl-ai 8080:80
```
Then open [http://localhost:8080](http://localhost:8080) in your browser. Each browser session can create, rename, and delete conversations, and messages stream in real time via Server-Sent Events.
If you prefer to expose the UI via an external Load Balancer, replace the Service type in the manifest with `LoadBalancer` and configure the appropriate firewall rules.
## 7. Verify sandboxed tool execution
When the UI creates a conversation, the agent launches a sandbox pod in the `computer` namespace. You can confirm sandbox activity with:
```bash
kubectl get pods -n computer
```
Pods named `kubectl-ai-sandbox-*` indicate that commands are running inside isolated helper containers. Asking the agent to execute commands such as `uname -a` should produce output that matches the sandbox image (for example `bitnami/kubectl`).
## 8. Cleanup
Remove the deployment and sandbox resources when you are done:
```bash
kubectl delete -f kubectl-ai-gke.yaml
kubectl delete namespace kubectl-ai
kubectl delete -f k8s/sandbox/all-in-one.yaml
```
If you no longer need the Artifact Registry repository or pushed image, delete them using `gcloud artifacts repositories delete` and `gcloud artifacts docker images delete`.
================================================
FILE: docs/mcp-client.md
================================================
# kubectl-ai MCP Client Integration
## Multi-Server Orchestration for Security Automation
The MCP (Model Context Protocol) Client feature enables kubectl-ai to coordinate multiple specialized tools through natural language commands. This integration demonstrates automated security workflows that combine RBAC scanning with email reporting.
**Problem**: Traditional security audits require manual execution of multiple tools, data correlation, and report distribution—a time-consuming process prone to human error.
**Solution**: Single command orchestration across multiple MCP servers:
```bash
kubectl-ai --mcp-client --quiet "scan rbac and send urgent report to incident-team@company.com from sender@company.com"
```
**Architecture Components:**
- **kubectl-ai**: Central orchestrator interpreting natural language commands
- **Permiflow**: RBAC security scanning and analysis
- **Resend**: Automated email delivery service
- **Additional servers**: Documentation, reasoning, and extensible integrations
## Workflow Sequence Diagram
```mermaid
sequenceDiagram
participant User
participant kubectl-ai as kubectl-ai<br/>(MCP Client)
participant Permiflow as Permiflow<br/>(MCP Server)
participant K8s as Kubernetes<br/>Cluster
participant Resend as Resend<br/>(MCP Server)
participant Email as Email<br/>Recipient
User->>kubectl-ai: "scan rbac and send report to admin@company.com"
kubectl-ai->>Permiflow: scan_rbac()
Permiflow->>K8s: Query RBAC policies
K8s-->>Permiflow: Return roles, bindings, permissions
Permiflow->>Permiflow: Analyze security risks
Permiflow-->>kubectl-ai: Security findings report
kubectl-ai->>kubectl-ai: Format report for email
kubectl-ai->>Resend: send_email(to, from, subject, content)
Resend->>Email: Deliver formatted security report
Email-->>User: Email confirmation
kubectl-ai-->>User: "✅ RBAC scan completed and report sent"
```
## Execution Flow
The command execution follows this sequence:
1. **kubectl-ai** parses the natural language request
2. [**Permiflow**](https://github.com/tutran-se/permiflow) performs comprehensive RBAC analysis across cluster resources
3. [**Resend**](https://github.com/resend/mcp-send-email) formats and delivers the security report via email
**Extensibility**: The architecture supports additional MCP servers for Slack notifications, Jira ticket creation, compliance databases, and custom integrations.
## Configuration and Setup
### MCP Server Configuration
Configure the MCP servers in `~/.config/kubectl-ai/mcp.yaml`:
```yaml
servers:
- name: resend
command: node
args:
- "~/mcp-send-email/build/index.js"
env:
RESEND_API_KEY: "api-key-here"
- name: permiflow
url: http://localhost:8080/mcp
```
### Quick Start
```bash
# 1. Start the Permiflow MCP server
permiflow mcp --transport http --http-port 8080
# 2. Execute kubectl-ai with MCP client enabled
kubectl-ai --mcp-client --quiet "scan rbac and send report to admin@company.com from sec@company.com"
```
## Automation Use Cases
### Scheduled Security Monitoring
Implement automated daily security scans using cron:
```bash
# Daily RBAC audit at 9 AM
0 9 * * * kubectl-ai --mcp-client --quiet "scan rbac and send daily report to admin@company.com from sec@company.com"
```
### Incident Response
Execute immediate security assessments during incidents:
```bash
kubectl-ai --mcp-client --quiet "scan rbac for production namespace and send urgent report to incident-team@company.com from sec@company.com"
```
## Usage Examples
### Interactive Mode
Launch kubectl-ai in interactive mode for exploratory analysis:
```bash
kubectl-ai --mcp-client
>>> "scan rbac and send report to admin@company.com"
>>> "analyze RBAC for kubeflow namespace"
>>> "show me the most dangerous permissions in production"
>>> "which service accounts can access secrets across namespaces?"
```
### Direct Commands
Execute specific security queries directly:
```bash
kubectl-ai --mcp-client "show wildcard permissions and suggest fixes"
```
## Extended Integration
### Additional MCP Servers
Expand the automation capabilities by adding specialized servers:
```yaml
servers:
- name: slack-notifier
url: "https://slack-mcp.company.com/mcp"
- name: jira-tickets
url: "https://jira-mcp.company.com/mcp"
- name: trivy-scanner
command: npx
args: ["-y", "@aquasecurity/trivy-mcp"]
```
### Advanced Workflows
**Multi-Channel Incident Response:**
```bash
"scan rbac, create jira ticket, email security team, post to slack"
```
**Compliance Automation:**
```bash
"scan vulnerabilities, update compliance database, email leadership"
```
## Benefits
- **Unified Interface**: Single natural language interface for multiple tools
- **Automation**: Reduces manual security audit processes
- **Consistency**: Standardized security scanning and reporting
- **Extensibility**: Modular architecture supports additional integrations
- **Efficiency**: Rapid security assessment and stakeholder notification
================================================
FILE: docs/mcp-server.md
================================================
# kubectl-ai MCP Server
kubectl-ai can run as an MCP (Model Context Protocol) server, exposing kubectl-ai tools to other MCP clients. The server can run in two modes:
1. **Built-in tools only**: Exposes only kubectl-ai's native tools
2. **External tool discovery**: Additionally discovers and exposes tools from other MCP servers
## Quick Start
### Basic MCP Server (Built-in tools only)
Start the MCP server with only kubectl-ai's built-in tools:
```bash
kubectl-ai --mcp-server
```
### Enhanced MCP Server (With external tool discovery)
Start the MCP server with external MCP tool discovery enabled:
```bash
kubectl-ai --mcp-server --external-tools
```
### Expose an HTTP Endpoint for MCP Clients
Run the server with the streamable HTTP transport to serve compatible MCP clients (including kubectl-ai MCP client mode) over HTTP:
```bash
kubectl-ai --mcp-server --mcp-server-mode streamable-http --http-port 9080
```
This listens on `http://localhost:9080/mcp` by default.
## Configuration
When `--external-tools` is enabled, the enhanced MCP server will automatically discover and expose tools from configured MCP servers. You can configure MCP servers using the standard MCP client configuration file.
### Example MCP Configuration
Create `~/.config/kubectl-ai/mcp.yaml`:
```yaml
servers:
filesystem:
command: "npx"
args:
[
"-y",
"@modelcontextprotocol/server-filesystem",
"/path/to/allowed/files",
]
brave-search:
command: "npx"
args: ["-y", "@modelcontextprotocol/server-brave-search"]
env:
BRAVE_API_KEY: "your-api-key"
```
## Features
### Tool Aggregation
When external tool discovery is enabled with `--external-tools`, the kubectl-ai MCP server acts as a **tool aggregator**, providing:
- All kubectl-ai built-in tools (kubectl, cluster analysis, etc.)
- Tools from external MCP servers (filesystem, web search, etc.)
- Unified interface for all tools through a single MCP endpoint
### Graceful Degradation
The server handles external MCP connection failures gracefully:
- If external MCP servers are unavailable, the server continues with built-in tools only
- Individual tool failures don't affect the overall server operation
- Clear logging for troubleshooting connection issues
### Example Usage in Claude Desktop
Configure Claude Desktop to use kubectl-ai as an MCP server:
**Basic usage (built-in tools only):**
```json
{
"mcpServers": {
"kubectl-ai": {
"command": "kubectl-ai",
"args": ["--mcp-server"]
}
}
}
```
**Enhanced usage (with external tools):**
```json
{
"mcpServers": {
"kubectl-ai": {
"command": "kubectl-ai",
"args": ["--mcp-server", "--external-tools"]
}
}
}
```
## Available Tools
### Built-in Tools
kubectl-ai provides the following native tools:
- `bash`: Executes a bash command. Use this tool only when you need to execute a shell command.
- `kubectl`: Executes a kubectl command against the user's Kubernetes cluster. Use this tool only when you need to query or modify the state of the user's Kubernetes cluster.
### External Tools (when `--external-tools` is enabled)
Additional tools are available depending on the configured MCP servers:
- **Filesystem tools**: Read/write files, list directories
- **Web search tools**: Search the internet for information
- **Database tools**: Query databases
- **API tools**: Interact with external APIs
- **Custom tools**: Any MCP-compatible tools
## Command Line Options
| Flag | Default | Description |
| ------------------- | ---------------- | ---------------------------------------------------------------------- |
| `--mcp-server` | `false` | Run in MCP server mode |
| `--external-tools` | `false` | Discover and expose external MCP tools (requires --mcp-server) |
| `--kubeconfig` | `~/.kube/config` | Path to kubeconfig file |
| `--mcp-server-mode` | `stdio` | Transport for the MCP server (`stdio` or `streamable-http`) |
| `--http-port` | `9080` | Port for the HTTP endpoint when using `streamable-http` modes |
## Architecture
```txt
┌─────────────────┐ ┌───────────────────┐ ┌─────────────────┐
│ MCP Client │───▶│ kubectl-ai Server │───▶│ External Tools │
│ (Claude, etc.) │ │ │ │ (filesystem, │
│ │ │ ┌───────────────┐ │ │ web search, │
│ │ │ │ Built-in │ │ │ etc.) │
│ │ │ │ kubectl tools │ │ │ │
│ │ │ └───────────────┘ │ │ │
└─────────────────┘ └───────────────────┘ └─────────────────┘
```
The kubectl-ai MCP server acts as both:
- An **MCP Server** (exposing tools to clients)
- An **MCP Client** (consuming tools from other servers, when `--external-tools` is enabled)
This creates a powerful tool aggregation pattern where kubectl-ai becomes a central hub for both Kubernetes operations and general-purpose tools.
## Troubleshooting
### External Tools Not Available
If external tools aren't appearing:
1. Ensure you're using both `--mcp-server` and `--external-tools` flags
2. Check MCP configuration file exists and is valid
3. Verify external MCP servers are working independently
4. Check kubectl-ai logs for connection errors
5. Try running with external tools disabled to isolate issues
### Performance Considerations
- Tool discovery adds startup time (usually 2-3 seconds) when `--external-tools` is enabled
- Each external tool call has network overhead
- Consider running without `--external-tools` for faster startup if external tools aren't needed
### Debugging
Enable verbose logging to troubleshoot:
```bash
kubectl-ai --mcp-server --external-tools -v=2
```
This will show:
- MCP server connection attempts
- Tool discovery results
- Tool call routing decisions
================================================
FILE: docs/mocking.md
================================================
# Mocking in kubectl-ai
## Gomock developer workflow
We use [gomock](https://github.com/uber-go/mock) to mock external dependencies. All mocks and generated files live under `internal/mocks/`.
- **Everyday commands**
- Regenerate mocks after changing interfaces or adding new ones: `make generate`
- Verify nothing is stale (locally or in CI): `make verify-mocks`
- Run tests: `go test ./...`
- **Generator install** (if you don’t have it yet):
`go install go.uber.org/mock/mockgen@latest`
- **What `make generate` does**
Runs `go generate ./internal/mocks`. Note: `go generate` is **not** part of `go build/test`; commit generated mocks.
- **Add a new mock**
1. Add a `go:generate` line in `internal/mocks/generate.go`, e.g.:
```go
//go:generate mockgen -destination=tools_mock.go -package=mocks // github.com/GoogleCloudPlatform/kubectl-ai/pkg/tools Tool
```
2. Run `make generate` and import the mocks in tests:
```go
ctrl := gomock.NewController(t)
defer ctrl.Finish()
llm := mocks.NewMockClient(ctrl) // example
llm.EXPECT().NewChat(gomock.Any()).AnyTimes()
```
## When and when not to use gomock
**Use gomock for:**
- **External boundaries / side effects**: `gollm.Client`, `gollm.Chat`, `pkg/tools.Tool`, network/IO, anything slow or flaky.
- **Behavioral checks**: asserting specific calls/arguments or injecting failures/timeouts.
**Prefer fakes/in‑memory over mocks for:**
- **Stateful components with an in‑memory impl** (e.g., session/message store). Don’t mock storage if an in‑memory version exists.
- **Pure functions / simple value types**—call them directly.
**Good practices:**
- Keep expectations minimal—assert only what matters. Use `gomock.Any()` and `AnyTimes()`/`MinTimes(1)` where exact call counts don’t matter.
- Centralize `mockgen` directives in `internal/mocks/generate.go`.
- **If an interface changes**: run `make generate`, fix compile errors in tests (signatures/matchers), update/remove `go:generate` lines if package paths or names changed, and commit the regenerated mocks.
================================================
FILE: docs/tool-samples/argocd.yaml
================================================
- name: argocd
description: "A declarative, GitOps continuous delivery tool for Kubernetes. Use it to manage application deployments from Git repositories."
command: "argocd"
command_desc: |
The argocd command-line interface.
Core subcommands and usage patterns:
- `argocd login <server> [flags]`: Log in to an Argo CD server. This is required before most other commands.
- `argocd app list [flags]`: List all applications managed by Argo CD.
- `argocd app get <app-name> [flags]`: Get detailed information about a specific application.
- `argocd app sync <app-name> [flags]`: Sync an application to its target state defined in the Git repository.
- `argocd app history <app-name> [flags]`: View the deployment history of an application.
- `argocd app rollback <app-name> <history-id>`: Roll back an application to a previous deployed state.
- `argocd app create <app-name> [flags]`: Create a new application.
- `argocd app delete <app-name>`: Delete an application.
Use `argocd --help` or `argocd <command> --help` for full syntax and available flags.
================================================
FILE: docs/tool-samples/gcloud.yaml
================================================
- name: gcloud
description: "The gcloud command-line tool is the primary CLI for Google Cloud. Use it to manage Google Cloud resources, including Google Kubernetes Engine (GKE) clusters, virtual machines, and networking."
command: "gcloud"
command_desc: |
The gcloud CLI manages authentication, local configuration, developer workflow, and interactions with Google Cloud APIs.
For Kubernetes-related tasks, the `gcloud container clusters` command group is the most relevant.
Core subcommands and usage patterns:
- `gcloud container clusters list [flags]`: List all GKE clusters in the configured project and zone/region.
- `gcloud container clusters describe <cluster-name> [flags]`: Get detailed information about a specific GKE cluster.
- `gcloud container clusters get-credentials <cluster-name> [flags]`: Fetch cluster endpoint and auth data and configure kubectl to use the cluster. This is essential for connecting to a GKE cluster.
- `gcloud container clusters create <cluster-name> [flags]`: Create a new GKE cluster.
- `gcloud container clusters delete <cluster-name> [flags]`: Delete an existing GKE cluster.
You can set the default project, region, and zone using:
- `gcloud config set project <project-id>`
- `gcloud config set compute/region <region>`
- `gcloud config set compute/zone <zone>`
Use `gcloud --help` or `gcloud <command> <subcommand> --help` for full syntax and available flags.
================================================
FILE: docs/tool-samples/gh.yaml
================================================
- name: gh
description: "The official GitHub command-line tool. Use it to interact with GitHub repositories, pull requests, issues, actions, and more, directly from the terminal."
command: "gh"
command_desc: |
The gh command-line interface for GitHub.
Core subcommands and usage patterns:
- `gh auth login`: Authenticate with a GitHub host. This is required before most other commands.
- `gh repo view [repo]`: View a repository.
- `gh pr list`: List pull requests in the current repository.
- `gh pr view <number>`: View a specific pull request.
- `gh pr checkout <number>`: Check out a pull request locally.
- `gh pr create [flags]`: Create a new pull request.
- `gh pr merge [flags]`: Merge a pull request.
- `gh issue list`: List issues in the current repository.
- `gh issue view <number>`: View a specific issue.
- `gh workflow list`: List workflows in the current repository.
- `gh workflow run <workflow-name> [flags]`: Trigger a workflow run.
Use `gh --help` or `gh <subcommand> --help` for full syntax and available flags.
================================================
FILE: docs/tool-samples/kustomize.yaml
================================================
- name: kustomize
description: "A tool to customize Kubernetes resource configurations. Use it to render and apply declarative configurations from a directory containing a kustomization.yaml file."
command: "kustomize"
command_desc: |
The kustomize command-line interface.
Core subcommands and usage patterns:
- `kustomize build <kustomization_dir>`: Prints the customized resources to standard output. This is useful for inspecting the final configuration before applying it.
- `kustomize build <kustomization_dir> | kubectl apply -f -`: A common pattern to apply the output directly to the cluster.
Note: `kubectl apply -k <dir>` is a shorthand for the pipe command above and is often preferred.
================================================
FILE: docs/tools.md
================================================
# Custom Tools for kubectl-ai
`kubectl-ai` leverages LLMs to suggest and execute Kubernetes operations using a set of powerful tools. It comes with built-in tools like `kubectl` and `bash`.
The `kubectl-ai` assistant can be extended with custom tools to interact with various command-line interfaces (CLIs) beyond `kubectl`. This allows the AI to perform a wider range of tasks related to infrastructure management, CI/CD, and more.
This document outlines how you can add custom tools by detailing the steps and providing samples.
This document also outlines the available tools, their locations, and how to use them.
## Adding Custom Tools
Custom tools can be added by following these two steps:
- describing or templating the tool through YAML file
- enabling the tool in the configuration file by pointing the **--custom-tools-config** to this file / directory
## Describing the Tool in YAML file
A custom tool can be described by providing the following four pieces of information:
- **name**: name of the tool
- **description**: "A clear description that helps the LLM understand when to use this tool."
- **command** : "your_command" # For example: 'gcloud' or 'gcloud container clusters'
- **command_desc**: "Detailed information for the LLM, including command syntax and usage examples."
Samples are provided in the `pkg/tools/samples` directory. Below is a sample for the `kustomize` tool:
```yaml
- name: kustomize
description: "A tool to customize Kubernetes resource configurations. Use it to render and apply declarative configurations from a directory containing a kustomization.yaml file."
command: "kustomize"
command_desc: |
The kustomize command-line interface.
Core subcommands and usage patterns:
- `kustomize build <kustomization_dir>`: Prints the customized resources to standard output. This is useful for inspecting the final configuration before applying it.
- `kustomize build <kustomization_dir> | kubectl apply -f -`: A common pattern to apply the output directly to the cluster.
Note: `kubectl apply -k <dir>` is a shorthand for the pipe command above and is often preferred.
```
## Enabling the Custom Tool
To enable the custom tools, you must point `kubectl-ai` to the directory containing the tool configuration YAML files using the `--custom-tools-config` flag. `kubectl-ai` can pick up a single YAML file (e.g., `tools.yaml`) containing all the tool descriptions or multiple individual YAML files when pointed to a directory containing them. This example uses multiple YAML files located in a single directory.
In case, you don't want to use a tool (that is provided in samples), just move the file out of the directory into some other location & restart `kubectl-ai`.
### Running from a Local Binary
When running the `kubectl-ai` binary directly, provide the path to your local tools directory.
```sh
./kubectl-ai --custom-tools-config=<path-to-tools-directory> "your prompt here"
```
### Running with Docker Image
When using the Docker image, you can either use the tools baked into the image or mount your own custom directory.
#### Using Built-in Tools
The official Docker image includes the default tool configurations. You can enable them by pointing to the internal path.
```sh
docker run --rm -it your-kubectl-ai-image:latest \
--custom-tools-config=/etc/kubectl-ai/tools \
"list all pull requests on GitHub"
```
#### Using a Local Tools Directory
To use a custom set of tools from your local machine, mount the directory into the container and point the flag to the mounted path. This is useful for developing and testing new tools.
```sh
docker run --rm -it \
-v /path/to/your/local/tools:/my-custom-tools \
your-kubectl-ai-image:latest \
--custom-tools-config=/my-custom-tools \
"your prompt here"
```
## Sample Custom Tools
The following sample custom tools are configured by default.
| Tool | Description | YAML File |
| :--------------------------------------------------------- | :-------------------------------------------------------------- | :------------------------------------------------------ |
| Argo CD (`argocd`) | A declarative, GitOps continuous delivery tool for Kubernetes. | [argocd.yaml](./tool-samples/argocd.yaml) |
| GitHub CLI (`gh`) | The official command-line tool to interact with GitHub. | [gh.yaml](./tool-samples/gh.yaml) |
| Google Cloud CLI (`gcloud`) | The primary CLI for managing Google Cloud resources. | [gcloud.yaml](./tool-samples/gcloud.yaml) |
| Kustomize (`kustomize`) | A tool to customize Kubernetes resource configurations. | [kustomize.yaml](./tool-samples/kustomize.yaml) |
================================================
FILE: go.mod
================================================
module github.com/GoogleCloudPlatform/kubectl-ai
go 1.24.0
toolchain go1.24.3
// Needed for multiple go modules in one repo
replace github.com/GoogleCloudPlatform/kubectl-ai/gollm => ./gollm
require (
github.com/GoogleCloudPlatform/kubectl-ai/gollm v0.0.0-00010101000000-000000000000
github.com/charmbracelet/bubbles v0.21.0
github.com/charmbracelet/bubbletea v1.3.5
github.com/charmbracelet/glamour v0.10.0
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834
github.com/chzyer/readline v1.5.1
github.com/google/uuid v1.6.0
github.com/mark3labs/mcp-go v0.41.1
github.com/spf13/cobra v1.9.1
github.com/spf13/pflag v1.0.6
go.uber.org/mock v0.6.0
golang.org/x/sync v0.16.0
golang.org/x/term v0.33.0
k8s.io/api v0.34.2
k8s.io/apimachinery v0.34.2
k8s.io/client-go v0.34.2
k8s.io/klog/v2 v2.130.1
mvdan.cc/sh/v3 v3.11.0
sigs.k8s.io/yaml v1.6.0
)
require (
cloud.google.com/go v0.118.3 // indirect
cloud.google.com/go/auth v0.15.0 // indirect
cloud.google.com/go/compute/metadata v0.6.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/ai/azopenai v0.7.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/cognitiveservices/armcognitiveservices v1.7.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/subscription/armsubscription v1.2.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect
github.com/alecthomas/chroma/v2 v2.14.0 // indirect
github.com/anthropics/anthropic-sdk-go v1.26.0 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aws/aws-sdk-go-v2 v1.36.6 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11 // indirect
github.com/aws/aws-sdk-go-v2/config v1.29.18 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.71 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.33 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.37 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.37 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.31.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.18 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.25.6 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.34.1 // indirect
github.com/aws/smithy-go v1.22.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
github.com/charmbracelet/x/ansi v0.8.0 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dlclark/regexp2 v1.11.4 // indirect
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/google/gnostic-models v0.7.0 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/invopop/jsonschema v0.13.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/microcosm-cc/bluemonday v1.0.27 // indirect
github.com/moby/spdystream v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/ollama/ollama v0.6.5 // indirect
github.com/openai/openai-go v1.12.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sahilm/fuzzy v0.1.1 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
github.com/yuin/goldmark v1.7.8 // indirect
github.com/yuin/goldmark-emoji v1.0.5 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.40.0 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/text v0.27.0 // indirect
golang.org/x/time v0.10.0 // indirect
google.golang.org/genai v1.8.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250219182151-9fdb1cabc7b2 // indirect
google.golang.org/grpc v1.70.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
)
================================================
FILE: go.sum
================================================
cloud.google.com/go v0.118.3 h1:jsypSnrE/w4mJysioGdMBg4MiW/hHx/sArFpaBWHdME=
cloud.google.com/go v0.118.3/go.mod h1:Lhs3YLnBlwJ4KA6nuObNMZ/fCbOQBPuWKPoE0Wa/9Vc=
cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps=
cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8=
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
github.com/Azure/azure-sdk-for-go/sdk/ai/azopenai v0.7.2 h1:+hDUZnYHHoXu05iXiJcL53MZW7raZZejB8ZtzVW7yyc=
github.com/Azure/azure-sdk-for-go/sdk/ai/azopenai v0.7.2/go.mod h1:49PyorVrwk6G+e8Vghvn7EkAS6wSPdXEu5a8iW2/vC8=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 h1:OVoM452qUFBrX+URdH3VpR299ma4kfom0yB0URYky9g=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0/go.mod h1:kUjrAo8bgEwLeZ/CmHqNl3Z/kPm7y6FKfxxK0izYUg4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/cognitiveservices/armcognitiveservices v1.7.0 h1:4exaC92+n1FzhSKb5Ghino2XEk3cClUtzvveL1U9YeM=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/cognitiveservices/armcognitiveservices v1.7.0/go.mod h1:BkhZrH3JiVTkrTqCeYHOmqReFcZTYEMf8jcFDlrCJLk=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/subscription/armsubscription v1.2.0 h1:UrGzkHueDwAWDdjQxC+QaXHd4tVCkISYE9j7fSSXF8k=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/subscription/armsubscription v1.2.0/go.mod h1:qskvSQeW+cxEE2bcKYyKimB1/KiQ9xpJ99bcHY0BX6c=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/anthropics/anthropic-sdk-go v1.26.0 h1:oUTzFaUpAevfuELAP1sjL6CQJ9HHAfT7CoSYSac11PY=
github.com/anthropics/anthropic-sdk-go v1.26.0/go.mod h1:qUKmaW+uuPB64iy1l+4kOSvaLqPXnHTTBKH6RVZ7q5Q=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aws/aws-sdk-go-v2 v1.36.6 h1:zJqGjVbRdTPojeCGWn5IR5pbJwSQSBh5RWFTQcEQGdU=
github.com/aws/aws-sdk-go-v2 v1.36.6/go.mod h1:EYrzvCCN9CMUTa5+6lf6MM4tq3Zjp8UhSGR/cBsjai0=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11 h1:12SpdwU8Djs+YGklkinSSlcrPyj3H4VifVsKf78KbwA=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11/go.mod h1:dd+Lkp6YmMryke+qxW/VnKyhMBDTYP41Q2Bb+6gNZgY=
github.com/aws/aws-sdk-go-v2/config v1.29.18 h1:x4T1GRPnqKV8HMJOMtNktbpQMl3bIsfx8KbqmveUO2I=
github.com/aws/aws-sdk-go-v2/config v1.29.18/go.mod h1:bvz8oXugIsH8K7HLhBv06vDqnFv3NsGDt2Znpk7zmOU=
github.com/aws/aws-sdk-go-v2/credentials v1.17.71 h1:r2w4mQWnrTMJjOyIsZtGp3R3XGY3nqHn8C26C2lQWgA=
github.com/aws/aws-sdk-go-v2/credentials v1.17.71/go.mod h1:E7VF3acIup4GB5ckzbKFrCK0vTvEQxOxgdq4U3vcMCY=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.33 h1:D9ixiWSG4lyUBL2DDNK924Px9V/NBVpML90MHqyTADY=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.33/go.mod h1:caS/m4DI+cij2paz3rtProRBI4s/+TCiWoaWZuQ9010=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.37 h1:osMWfm/sC/L4tvEdQ65Gri5ZZDCUpuYJZbTTDrsn4I0=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.37/go.mod h1:ZV2/1fbjOPr4G4v38G3Ww5TBT4+hmsK45s/rxu1fGy0=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.37 h1:v+X21AvTb2wZ+ycg1gx+orkB/9U6L7AOp93R7qYxsxM=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.37/go.mod h1:G0uM1kyssELxmJ2VZEfG0q2npObR3BAkF3c1VsfVnfs=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.31.1 h1:JDLT1baDmioiZKa2bZ6J82/Zwfv9cSAjr+LyF47TPYw=
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.31.1/go.mod h1:FvbGcqrU4sC3qjrAKK3FzOmBoucDJF2dXsKVvAbGE8g=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4 h1:CXV68E2dNqhuynZJPB80bhPQwAKqBWVer887figW6Jc=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4/go.mod h1:/xFi9KtvBXP97ppCz1TAEvU1Uf66qvid89rbem3wCzQ=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.18 h1:vvbXsA2TVO80/KT7ZqCbx934dt6PY+vQ8hZpUZ/cpYg=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.18/go.mod h1:m2JJHledjBGNMsLOF1g9gbAxprzq3KjC8e4lxtn+eWg=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.6 h1:rGtWqkQbPk7Bkwuv3NzpE/scwwL9sC1Ul3tn9x83DUI=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.6/go.mod h1:u4ku9OLv4TO4bCPdxf4fA1upaMaJmP9ZijGk3AAOC6Q=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.4 h1:OV/pxyXh+eMA0TExHEC4jyWdumLxNbzz1P0zJoezkJc=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.4/go.mod h1:8Mm5VGYwtm+r305FfPSuc+aFkrypeylGYhFim6XEPoc=
github.com/aws/aws-sdk-go-v2/service/sts v1.34.1 h1:aUrLQwJfZtwv3/ZNG2xRtEen+NqI3iesuacjP51Mv1s=
github.com/aws/aws-sdk-go-v2/service/sts v1.34.1/go.mod h1:3wFBZKoWnX3r+Sm7in79i54fBmNfwhdNdQuscCw7QIk=
github.com/aws/smithy-go v1.22.4 h1:uqXzVZNuNexwc/xrh6Tb56u89WDlJY6HS+KC0S4QSjw=
github.com/aws/smithy-go v1.22.4/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
github.com/charmbracelet/bubbletea v1.3.5 h1:JAMNLTbqMOhSwoELIr0qyP4VidFq72/6E9j7HHmRKQc=
github.com/charmbracelet/bubbletea v1.3.5/go.mod h1:TkCnmH+aBd4LrXhXcqrKiYwRs7qyQx5rBgH5fVY3v54=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
github.com/charmbracelet/glamour v0.10.0 h1:MtZvfwsYCx8jEPFJm3rIBFIMZUfUJ765oX8V6kXldcY=
github.com/charmbracelet/glamour v0.10.0/go.mod h1:f+uf+I/ChNmqo087elLnVdCiVgjSKWuXa/l6NU2ndYk=
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 h1:ZR7e0ro+SZZiIZD7msJyA+NjkCNNavuiPBLgerbOziE=
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834/go.mod h1:aKC/t2arECF6rNOnaKaVU6y4t4ZeHQzqfxedE/VkVhA=
github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ=
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf h1:rLG0Yb6MQSDKdB52aGX55JT1oi0P0Kuaj7wi1bLUpnI=
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf/go.mod h1:B3UgsnsBZS/eX42BlaNiJkD1pPOUa+oF1IYC6Yd2CEU=
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mark3labs/mcp-go v0.41.1 h1:w78eWfiQam2i8ICL7AL0WFiq7KHNJQ6UB53ZVtH4KGA=
github.com/mark3labs/mcp-go v0.41.1/go.mod h1:T7tUa2jO6MavG+3P25Oy/jR7iCeJPHImCZHRymCn39g=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/ollama/ollama v0.6.5 h1:vXKkVX57ql/1ZzMw4SVK866Qfd6pjwEcITVyEpF0QXQ=
github.com/ollama/ollama v0.6.5/go.mod h1:pGgtoNyc9DdM6oZI6yMfI6jTk2Eh4c36c2GpfQCH7PY=
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/openai/openai-go v1.12.0 h1:NBQCnXzqOTv5wsgNC36PrFEiskGfO5wccfCWDo9S1U0=
github.com/openai/openai-go v1.12.0/go.mod h1:g461MYGXEXBVdV5SaR/5tNzNbSfwTBBefwc+LlDCK0Y=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA=
github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark-emoji v1.0.5 h1:EMVWyCGPlXJfUXBXpuMu+ii3TIaxbVBnEX9uaDC4cIk=
github.com/yuin/goldmark-emoji v1.0.5/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4=
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genai v1.8.0 h1:unX2CNWSiKDO2MSTKK3RstXg/vHp9hr42LIcL6f3Cik=
google.golang.org/genai v1.8.0/go.mod h1:TyfOKRz/QyCaj6f/ZDt505x+YreXnY40l2I6k8TvgqY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250219182151-9fdb1cabc7b2 h1:DMTIbak9GhdaSxEjvVzAeNZvyc03I61duqNbnm3SU0M=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250219182151-9fdb1cabc7b2/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY=
k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw=
k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4=
k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M=
k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
mvdan.cc/sh/v3 v3.11.0 h1:q5h+XMDRfUGUedCqFFsjoFjrhwf2Mvtt1rkMvVz0blw=
mvdan.cc/sh/v3 v3.11.0/go.mod h1:LRM+1NjoYCzuq/WZ6y44x14YNAI0NK7FLPeQSaFagGg=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
================================================
FILE: go.work
================================================
go 1.24.0
toolchain go1.24.4
use (
.
./gollm
./kubectl-utils
)
================================================
FILE: go.work.sum
================================================
cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/iam v1.3.1/go.mod h1:3wMtuyT4NcbnYNPLMBzYRFiEfjKfJlLVLrisE7bwm34=
cloud.google.com/go/longrunning v0.5.6/go.mod h1:vUaDrWYOMKRuhiv6JBnn49YxCPz2Ayn9GqyjaBT8/mA=
cloud.google.com/go/monitoring v1.23.0/go.mod h1:034NnlQPDzrQ64G2Gavhl0LUHZs9H3rRmhtnp7jiJgg=
cloud.google.com/go/storage v1.50.0/go.mod h1:l7XeiD//vx5lfqE3RavfmU9yvk5Pp0Zhcv482poyafY=
cloud.google.com/go/translate v1.10.3/go.mod h1:GW0vC1qvPtd3pgtypCv4k4U8B7EdgK9/QEF2aJEUovs=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1/go.mod h1:zGqV2R4Cr/k8Uye5w+dgQ06WJtEcbQG/8J7BB6hnCr4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.0/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0/go.mod h1:6fTWu4m3jocfUZLYF5KsZC1TUfRvEjs7lM4crme/irw=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0/go.mod h1:wRbFgBQUVm1YXrvWKofAEmq9HNJTDphbAaJSSX01KUI=
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
github.com/apache/arrow/go/arrow v0.0.0-20211112161151-bc219186db40/go.mod h1:Q7yQnSMnLvcXlZ8RV+jwz/6y1rQTqbX6C82SndT52Zs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
github.com/chewxy/hm v1.0.0/go.mod h1:qg9YI4q6Fkj/whwHR1D+bOGeF7SniIP40VweVepLjg0=
github.com/chewxy/math32 v1.11.0/go.mod h1:dOB2rcuFrCn6UHrze36WSLVPKtzPMRAQvBvUwkSsLqs=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/d4l3k/go-bfloat16 v0.0.0-20211005043715-690c3bdd05f1/go.mod h1:uw2gLcxEuYUlAd/EXyjc/v55nd3+47YAgWbSXVxPrNI=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/emirpasic/gods/v2 v2.0.0-alpha/go.mod h1:W0y4M2dtBB9U5z3YlghmpuUhiaZT2h6yoeE+C1sCp6A=
github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA=
github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw=
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/cors v1.7.2/go.mod h1:SUJVARKgQ40dmrzgXEVxj2m7Ig1v1qIboQkPDTQ9t2E=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1
gitextract_bjyvth3y/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ ├── actions/
│ │ └── kind-cluster-setup/
│ │ └── action.yaml
│ ├── kubectl-ai.cast
│ └── workflows/
│ ├── ci-periodic.yaml
│ ├── ci-presubmit.yaml
│ ├── k8s-bench-evals.yaml
│ └── release.yaml
├── .gitignore
├── .goreleaser.yaml
├── .krew.yaml
├── CONTAINER.md
├── LICENSE
├── README.md
├── cmd/
│ ├── main.go
│ ├── mcp.go
│ └── mcp_test.go
├── contributing.md
├── dev/
│ ├── ci/
│ │ ├── periodics/
│ │ │ ├── analyze-evals.sh
│ │ │ └── run-evals.sh
│ │ └── presubmits/
│ │ ├── go-build.sh
│ │ ├── go-vet.sh
│ │ ├── verify-autogen.sh
│ │ ├── verify-format.sh
│ │ ├── verify-gomod.sh
│ │ └── verify-mocks.sh
│ └── tasks/
│ ├── build-images
│ ├── demo.md
│ ├── deploy-to-gke
│ ├── deploy-to-kind
│ ├── format.sh
│ ├── generate-github-actions.sh
│ └── gomod.sh
├── docs/
│ ├── bedrock.md
│ ├── gke-deployment.md
│ ├── mcp-client.md
│ ├── mcp-server.md
│ ├── mocking.md
│ ├── tool-samples/
│ │ ├── argocd.yaml
│ │ ├── gcloud.yaml
│ │ ├── gh.yaml
│ │ └── kustomize.yaml
│ └── tools.md
├── go.mod
├── go.sum
├── go.work
├── go.work.sum
├── gollm/
│ ├── README.md
│ ├── anthropic.go
│ ├── anthropic_test.go
│ ├── azopenai.go
│ ├── bedrock.go
│ ├── factory.go
│ ├── factory_test.go
│ ├── gemini.go
│ ├── go.mod
│ ├── go.sum
│ ├── grok.go
│ ├── http_journal.go
│ ├── interfaces.go
│ ├── llamacpp.go
│ ├── ollama.go
│ ├── openai.go
│ ├── openai_response.go
│ ├── openai_test.go
│ ├── persist.go
│ ├── schema.go
│ └── shims.go
├── images/
│ └── kubectl-ai/
│ └── Dockerfile
├── install.sh
├── internal/
│ └── mocks/
│ ├── agent_mock.go
│ ├── generate.go
│ ├── gollm_mock.go
│ └── tools_mock.go
├── k8s/
│ ├── all_in_one.yaml
│ ├── kubectl-ai-gke.yaml
│ ├── kubectl-ai.yaml
│ └── sandbox/
│ ├── all-in-one.yaml
│ ├── cluster_role.yaml
│ ├── cluster_role_binding.yaml
│ ├── namespace.yaml
│ ├── role.yaml
│ ├── role_binding.yaml
│ └── service_account.yaml
├── kubectl-utils/
│ ├── README.md
│ ├── cmd/
│ │ └── kubectl-expect/
│ │ └── main.go
│ ├── go.mod
│ ├── go.sum
│ └── pkg/
│ ├── kel/
│ │ ├── expression.go
│ │ └── info.go
│ └── kube/
│ ├── client.go
│ └── discovery.go
├── makefile
├── modelserving/
│ ├── .gitignore
│ ├── README.md
│ ├── dev/
│ │ └── tasks/
│ │ ├── build-images
│ │ ├── deploy-to-gke
│ │ ├── deploy-to-kind
│ │ ├── download-model
│ │ └── run-local
│ ├── images/
│ │ ├── llamacpp-gemma3-12b-it/
│ │ │ └── Dockerfile
│ │ └── llamacpp-server/
│ │ └── Dockerfile
│ └── k8s/
│ ├── llm-server-cpu.yaml
│ ├── llm-server-rpc.yaml
│ ├── llm-server.yaml
│ ├── rpc-server-cpu.yaml
│ └── rpc-server-cuda.yaml
└── pkg/
├── agent/
│ ├── agent_e2e_test.go
│ ├── conversation.go
│ ├── conversation_test.go
│ ├── manager.go
│ ├── mcp_client.go
│ └── systemprompt_template_default.txt
├── api/
│ └── models.go
├── journal/
│ ├── context.go
│ ├── loader.go
│ ├── log.go
│ └── recorder.go
├── mcp/
│ ├── README.md
│ ├── client.go
│ ├── config.go
│ ├── constants.go
│ ├── default_config.yaml
│ ├── http_client.go
│ ├── interfaces.go
│ ├── manager.go
│ ├── stdio_client.go
│ └── utils.go
├── sandbox/
│ ├── executor.go
│ ├── kubernetes.go
│ ├── local.go
│ ├── seatbelt_executor.go
│ └── seatbelt_executor_others.go
├── sessions/
│ ├── filesystem.go
│ ├── manager.go
│ ├── memory.go
│ └── store.go
├── tools/
│ ├── bash_tool.go
│ ├── custom_tool.go
│ ├── custom_tool_test.go
│ ├── interfaces.go
│ ├── kubectl_filter.go
│ ├── kubectl_filter_test.go
│ ├── kubectl_tool.go
│ ├── mcp_tool.go
│ ├── streaming.go
│ └── tools.go
└── ui/
├── html/
│ ├── htmlui.go
│ └── index.html
├── interfaces.go
├── terminal.go
└── tui.go
SYMBOL INDEX (1015 symbols across 70 files)
FILE: cmd/main.go
function BuildRootCommand (line 55) | func BuildRootCommand(opt *Options) (*cobra.Command, error) {
type Options (line 81) | type Options struct
method InitDefaults (line 150) | func (o *Options) InitDefaults() {
method LoadConfiguration (line 195) | func (o *Options) LoadConfiguration(b []byte) error {
method LoadConfigurationFile (line 202) | func (o *Options) LoadConfigurationFile() error {
method bindCLIFlags (line 307) | func (opt *Options) bindCLIFlags(f *pflag.FlagSet) error {
function main (line 240) | func main() {
function run (line 266) | func run(ctx context.Context) error {
function RunRootCommand (line 345) | func RunRootCommand(ctx context.Context, opt Options, args []string) err...
function handleCustomTools (line 540) | func handleCustomTools(toolConfigPaths []string) error {
function redirectStdLogToKlog (line 582) | func redirectStdLogToKlog() {
type klogWriter (line 591) | type klogWriter struct
method Write (line 593) | func (writer klogWriter) Write(data []byte) (n int, err error) {
function hasStdInData (line 600) | func hasStdInData() (bool, error) {
function resolveQueryInput (line 618) | func resolveQueryInput(hasStdInData bool, args []string) (string, error) {
function resolveKubeConfigPath (line 662) | func resolveKubeConfigPath(opt *Options) error {
function startMCPServer (line 692) | func startMCPServer(ctx context.Context, opt Options) error {
function handleListSessions (line 705) | func handleListSessions(opt Options) error {
function handleDeleteSession (line 738) | func handleDeleteSession(opt Options) error {
FILE: cmd/mcp.go
type kubectlMCPServer (line 29) | type kubectlMCPServer struct
method Serve (line 154) | func (s *kubectlMCPServer) Serve(ctx context.Context) error {
method handleToolCall (line 179) | func (s *kubectlMCPServer) handleToolCall(ctx context.Context, request...
method handleBuiltinToolCall (line 206) | func (s *kubectlMCPServer) handleBuiltinToolCall(ctx context.Context, ...
method handleExternalMCPToolCall (line 259) | func (s *kubectlMCPServer) handleExternalMCPToolCall(ctx context.Conte...
function newKubectlMCPServer (line 39) | func newKubectlMCPServer(ctx context.Context, kubectlConfig string, tool...
FILE: cmd/mcp_test.go
function TestKubectlMCPServerHTTPClientIntegration (line 29) | func TestKubectlMCPServerHTTPClientIntegration(t *testing.T) {
function waitForHTTPServer (line 108) | func waitForHTTPServer(t *testing.T, port int) {
function getFreePort (line 127) | func getFreePort(t *testing.T) int {
function toolExists (line 139) | func toolExists(name string, tools []mcp.Tool) bool {
type stubTool (line 148) | type stubTool struct
method Name (line 150) | func (stubTool) Name() string {
method Description (line 154) | func (stubTool) Description() string {
method FunctionDefinition (line 158) | func (stubTool) FunctionDefinition() *gollm.FunctionDefinition {
method Run (line 168) | func (stubTool) Run(context.Context, map[string]any) (any, error) {
method IsInteractive (line 172) | func (stubTool) IsInteractive(map[string]any) (bool, error) {
method CheckModifiesResource (line 176) | func (stubTool) CheckModifiesResource(map[string]any) string {
FILE: gollm/anthropic.go
function init (line 41) | func init() {
function newAnthropicClientFactory (line 69) | func newAnthropicClientFactory(ctx context.Context, opts ClientOptions) ...
type AnthropicClient (line 74) | type AnthropicClient struct
method Close (line 101) | func (c *AnthropicClient) Close() error {
method StartChat (line 106) | func (c *AnthropicClient) StartChat(systemPrompt, model string) Chat {
method GenerateCompletion (line 121) | func (c *AnthropicClient) GenerateCompletion(ctx context.Context, req ...
method SetResponseSchema (line 131) | func (c *AnthropicClient) SetResponseSchema(schema *Schema) error {
method ListModels (line 137) | func (c *AnthropicClient) ListModels(ctx context.Context) ([]string, e...
function NewAnthropicClient (line 82) | func NewAnthropicClient(ctx context.Context, opts ClientOptions) (*Anthr...
type anthropicChatSession (line 163) | type anthropicChatSession struct
method Initialize (line 177) | func (c *anthropicChatSession) Initialize(history []*api.Message) error {
method SetFunctionDefinitions (line 217) | func (c *anthropicChatSession) SetFunctionDefinitions(functions []*Fun...
method buildSystemBlocks (line 269) | func (c *anthropicChatSession) buildSystemBlocks() []anthropic.TextBlo...
method addContentsToHistory (line 283) | func (c *anthropicChatSession) addContentsToHistory(contents []any) er...
method Send (line 323) | func (c *anthropicChatSession) Send(ctx context.Context, contents ...a...
method SendStreaming (line 366) | func (c *anthropicChatSession) SendStreaming(ctx context.Context, cont...
method IsRetryableError (line 491) | func (c *anthropicChatSession) IsRetryableError(err error) bool {
type anthropicResponse (line 507) | type anthropicResponse struct
method UsageMetadata (line 513) | func (r *anthropicResponse) UsageMetadata() any {
method Candidates (line 520) | func (r *anthropicResponse) Candidates() []Candidate {
type anthropicStreamResponse (line 528) | type anthropicStreamResponse struct
method UsageMetadata (line 536) | func (r *anthropicStreamResponse) UsageMetadata() any {
method Candidates (line 543) | func (r *anthropicStreamResponse) Candidates() []Candidate {
type anthropicCandidate (line 554) | type anthropicCandidate struct
method String (line 560) | func (c *anthropicCandidate) String() string {
method Parts (line 572) | func (c *anthropicCandidate) Parts() []Part {
type anthropicStreamCandidate (line 607) | type anthropicStreamCandidate struct
method String (line 614) | func (c *anthropicStreamCandidate) String() string {
method Parts (line 624) | func (c *anthropicStreamCandidate) Parts() []Part {
type anthropicTextPart (line 636) | type anthropicTextPart struct
method AsText (line 642) | func (p *anthropicTextPart) AsText() (string, bool) {
method AsFunctionCalls (line 646) | func (p *anthropicTextPart) AsFunctionCalls() ([]FunctionCall, bool) {
type anthropicToolPart (line 651) | type anthropicToolPart struct
method AsText (line 657) | func (p *anthropicToolPart) AsText() (string, bool) {
method AsFunctionCalls (line 661) | func (p *anthropicToolPart) AsFunctionCalls() ([]FunctionCall, bool) {
type anthropicCompletionResponse (line 666) | type anthropicCompletionResponse struct
method Response (line 672) | func (r *anthropicCompletionResponse) Response() string {
method UsageMetadata (line 688) | func (r *anthropicCompletionResponse) UsageMetadata() any {
function getAnthropicModel (line 695) | func getAnthropicModel(model string) string {
FILE: gollm/anthropic_test.go
function TestAnthropicProviderRegistration (line 28) | func TestAnthropicProviderRegistration(t *testing.T) {
function TestAnthropicAddContentsToHistory (line 44) | func TestAnthropicAddContentsToHistory(t *testing.T) {
function TestAnthropicBuildSystemBlocks_WithCaching (line 121) | func TestAnthropicBuildSystemBlocks_WithCaching(t *testing.T) {
function TestAnthropicBuildSystemBlocks_WithoutCaching (line 145) | func TestAnthropicBuildSystemBlocks_WithoutCaching(t *testing.T) {
function TestAnthropicBuildSystemBlocks_EmptyPrompt (line 169) | func TestAnthropicBuildSystemBlocks_EmptyPrompt(t *testing.T) {
function TestAnthropicSetFunctionDefinitions_CacheControl (line 183) | func TestAnthropicSetFunctionDefinitions_CacheControl(t *testing.T) {
function TestAnthropicSetFunctionDefinitions_NoCacheControl (line 240) | func TestAnthropicSetFunctionDefinitions_NoCacheControl(t *testing.T) {
function TestAnthropicSetFunctionDefinitions_EmptyList (line 266) | func TestAnthropicSetFunctionDefinitions_EmptyList(t *testing.T) {
function TestAnthropicIsRetryableError (line 281) | func TestAnthropicIsRetryableError(t *testing.T) {
function makeAnthropicAPIError (line 349) | func makeAnthropicAPIError(statusCode int) error {
function TestAnthropicStreamResponseUsageMetadata (line 359) | func TestAnthropicStreamResponseUsageMetadata(t *testing.T) {
function TestAnthropicMaxTokensDefault (line 393) | func TestAnthropicMaxTokensDefault(t *testing.T) {
function TestAnthropicMaxTokensEnvVar (line 406) | func TestAnthropicMaxTokensEnvVar(t *testing.T) {
function TestGetAnthropicModel (line 465) | func TestGetAnthropicModel(t *testing.T) {
FILE: gollm/azopenai.go
function init (line 37) | func init() {
function azureOpenAIFactory (line 47) | func azureOpenAIFactory(ctx context.Context, opts ClientOptions) (Client...
type AzureOpenAIClient (line 51) | type AzureOpenAIClient struct
method Close (line 104) | func (c *AzureOpenAIClient) Close() error {
method GenerateCompletion (line 108) | func (c *AzureOpenAIClient) GenerateCompletion(ctx context.Context, re...
method ListModels (line 128) | func (c *AzureOpenAIClient) ListModels(ctx context.Context) ([]string,...
method SetResponseSchema (line 202) | func (c *AzureOpenAIClient) SetResponseSchema(schema *Schema) error {
method StartChat (line 206) | func (c *AzureOpenAIClient) StartChat(systemPrompt string, model strin...
function NewAzureOpenAIClient (line 60) | func NewAzureOpenAIClient(ctx context.Context, opts ClientOptions) (*Azu...
type AzureOpenAICompletionResponse (line 216) | type AzureOpenAICompletionResponse struct
method Response (line 220) | func (r *AzureOpenAICompletionResponse) Response() string {
method UsageMetadata (line 224) | func (r *AzureOpenAICompletionResponse) UsageMetadata() any {
type AzureOpenAIChat (line 228) | type AzureOpenAIChat struct
method Send (line 235) | func (c *AzureOpenAIChat) Send(ctx context.Context, contents ...any) (...
method IsRetryableError (line 268) | func (c *AzureOpenAIChat) IsRetryableError(err error) bool {
method Initialize (line 273) | func (c *AzureOpenAIChat) Initialize(messages []*api.Message) error {
method SendStreaming (line 278) | func (c *AzureOpenAIChat) SendStreaming(ctx context.Context, contents ...
method SetFunctionDefinitions (line 395) | func (c *AzureOpenAIChat) SetFunctionDefinitions(functionDefinitions [...
type AzureOpenAIChatResponse (line 287) | type AzureOpenAIChatResponse struct
method MarshalJSON (line 293) | func (r *AzureOpenAIChatResponse) MarshalJSON() ([]byte, error) {
method String (line 300) | func (r *AzureOpenAIChatResponse) String() string {
method UsageMetadata (line 304) | func (r *AzureOpenAIChatResponse) UsageMetadata() any {
method Candidates (line 308) | func (r *AzureOpenAIChatResponse) Candidates() []Candidate {
type AzureOpenAICandidate (line 316) | type AzureOpenAICandidate struct
method String (line 320) | func (r *AzureOpenAICandidate) String() string {
method Parts (line 344) | func (r *AzureOpenAICandidate) Parts() []Part {
type AzureOpenAIPart (line 365) | type AzureOpenAIPart struct
method AsText (line 370) | func (p *AzureOpenAIPart) AsText() (string, bool) {
method AsFunctionCalls (line 377) | func (p *AzureOpenAIPart) AsFunctionCalls() ([]FunctionCall, bool) {
function fnDefToAzureOpenAITool (line 404) | func fnDefToAzureOpenAITool(fnDef *FunctionDefinition) *azopenai.ChatCom...
FILE: gollm/bedrock.go
function init (line 36) | func init() {
function newBedrockClientFactory (line 43) | func newBedrockClientFactory(ctx context.Context, opts ClientOptions) (C...
type BedrockClient (line 48) | type BedrockClient struct
method Close (line 77) | func (c *BedrockClient) Close() error {
method StartChat (line 82) | func (c *BedrockClient) StartChat(systemPrompt, model string) Chat {
method GenerateCompletion (line 108) | func (c *BedrockClient) GenerateCompletion(ctx context.Context, req *C...
method SetResponseSchema (line 122) | func (c *BedrockClient) SetResponseSchema(schema *Schema) error {
method ListModels (line 127) | func (c *BedrockClient) ListModels(ctx context.Context) ([]string, err...
function NewBedrockClient (line 56) | func NewBedrockClient(ctx context.Context, opts ClientOptions) (*Bedrock...
type bedrockChat (line 135) | type bedrockChat struct
method Initialize (line 144) | func (cs *bedrockChat) Initialize(history []*api.Message) error {
method Send (line 192) | func (c *bedrockChat) Send(ctx context.Context, contents ...any) (Chat...
method SendStreaming (line 246) | func (c *bedrockChat) SendStreaming(ctx context.Context, contents ...a...
method addContentsToHistory (line 426) | func (c *bedrockChat) addContentsToHistory(contents []any) error {
method SetFunctionDefinitions (line 481) | func (c *bedrockChat) SetFunctionDefinitions(functions []*FunctionDefi...
method IsRetryableError (line 526) | func (c *bedrockChat) IsRetryableError(err error) bool {
type bedrockResponse (line 531) | type bedrockResponse struct
method UsageMetadata (line 537) | func (r *bedrockResponse) UsageMetadata() any {
method Candidates (line 545) | func (r *bedrockResponse) Candidates() []Candidate {
type bedrockStreamResponse (line 562) | type bedrockStreamResponse struct
method UsageMetadata (line 572) | func (r *bedrockStreamResponse) UsageMetadata() any {
method Candidates (line 577) | func (r *bedrockStreamResponse) Candidates() []Candidate {
type bedrockCandidate (line 592) | type bedrockCandidate struct
method String (line 598) | func (c *bedrockCandidate) String() string {
method Parts (line 613) | func (c *bedrockCandidate) Parts() []Part {
type bedrockStreamCandidate (line 631) | type bedrockStreamCandidate struct
method String (line 639) | func (c *bedrockStreamCandidate) String() string {
method Parts (line 644) | func (c *bedrockStreamCandidate) Parts() []Part {
type bedrockTextPart (line 668) | type bedrockTextPart struct
method AsText (line 673) | func (p *bedrockTextPart) AsText() (string, bool) {
method AsFunctionCalls (line 678) | func (p *bedrockTextPart) AsFunctionCalls() ([]FunctionCall, bool) {
type bedrockToolPart (line 683) | type bedrockToolPart struct
method AsText (line 689) | func (p *bedrockToolPart) AsText() (string, bool) {
method AsFunctionCalls (line 694) | func (p *bedrockToolPart) AsFunctionCalls() ([]FunctionCall, bool) {
function getBedrockModel (line 729) | func getBedrockModel(model string) string {
type bedrockCompletionResponse (line 746) | type bedrockCompletionResponse struct
method Response (line 752) | func (r *bedrockCompletionResponse) Response() string {
method UsageMetadata (line 769) | func (r *bedrockCompletionResponse) UsageMetadata() any {
FILE: gollm/factory.go
type registry (line 40) | type registry struct
method listProviders (line 45) | func (r *registry) listProviders() []string {
method RegisterProvider (line 77) | func (r *registry) RegisterProvider(id string, factoryFunc FactoryFunc...
method NewClient (line 92) | func (r *registry) NewClient(ctx context.Context, providerID string, o...
type ClientOptions (line 55) | type ClientOptions struct
type Option (line 62) | type Option
function WithSkipVerifySSL (line 65) | func WithSkipVerifySSL() Option {
type FactoryFunc (line 71) | type FactoryFunc
function RegisterProvider (line 73) | func RegisterProvider(id string, factoryFunc FactoryFunc) error {
function NewClient (line 132) | func NewClient(ctx context.Context, providerID string, opts ...Option) (...
type APIError (line 145) | type APIError struct
method Error (line 151) | func (e *APIError) Error() string {
method Unwrap (line 158) | func (e *APIError) Unwrap() error {
type IsRetryableFunc (line 165) | type IsRetryableFunc
function DefaultIsRetryableError (line 168) | func DefaultIsRetryableError(err error) bool {
function createCustomHTTPClient (line 198) | func createCustomHTTPClient(skipVerify bool) *http.Client {
type RetryConfig (line 213) | type RetryConfig struct
function Retry (line 232) | func Retry[T any](
type retryChat (line 306) | type retryChat struct
function NewRetryChat (line 315) | func NewRetryChat[C Chat](
method Send (line 326) | func (rc *retryChat[C]) Send(ctx context.Context, contents ...any) (Chat...
method SendStreaming (line 337) | func (rc *retryChat[C]) SendStreaming(ctx context.Context, contents ...a...
method SetFunctionDefinitions (line 341) | func (rc *retryChat[C]) SetFunctionDefinitions(functionDefinitions []*Fu...
method IsRetryableError (line 345) | func (rc *retryChat[C]) IsRetryableError(err error) bool {
method Initialize (line 349) | func (rc *retryChat[C]) Initialize(messages []*api.Message) error {
FILE: gollm/factory_test.go
function TestNewClient (line 23) | func TestNewClient(t *testing.T) {
FILE: gollm/gemini.go
function init (line 37) | func init() {
function geminiFactory (line 48) | func geminiFactory(ctx context.Context, opts ClientOptions) (Client, err...
type GeminiAPIClientOptions (line 54) | type GeminiAPIClientOptions struct
function NewGeminiAPIClient (line 60) | func NewGeminiAPIClient(ctx context.Context, opt GeminiAPIClientOptions)...
type VertexAIClientOptions (line 88) | type VertexAIClientOptions struct
function vertexaiViaGeminiFactory (line 97) | func vertexaiViaGeminiFactory(ctx context.Context, opts ClientOptions) (...
function findDefaultGCPProject (line 103) | func findDefaultGCPProject(ctx context.Context) (string, error) {
function NewVertexAIClient (line 136) | func NewVertexAIClient(ctx context.Context, opt VertexAIClientOptions) (...
type GoogleAIClient (line 191) | type GoogleAIClient struct
method ListModels (line 201) | func (c *GoogleAIClient) ListModels(ctx context.Context) (modelNames [...
method Close (line 212) | func (c *GoogleAIClient) Close() error {
method SetResponseSchema (line 218) | func (c *GoogleAIClient) SetResponseSchema(responseSchema *Schema) err...
method GenerateCompletion (line 233) | func (c *GoogleAIClient) GenerateCompletion(ctx context.Context, reque...
method StartChat (line 259) | func (c *GoogleAIClient) StartChat(systemPrompt string, model string) ...
type GeminiChat (line 303) | type GeminiChat struct
method SetFunctionDefinitions (line 312) | func (c *GeminiChat) SetFunctionDefinitions(functionDefinitions []*Fun...
method partsToGemini (line 379) | func (c *GeminiChat) partsToGemini(contents ...any) ([]*genai.Part, er...
method Send (line 403) | func (c *GeminiChat) Send(ctx context.Context, contents ...any) (ChatR...
method SendStreaming (line 431) | func (c *GeminiChat) SendStreaming(ctx context.Context, contents ...an...
method Initialize (line 492) | func (c *GeminiChat) Initialize(messages []*api.Message) error {
method messageToContent (line 505) | func (c *GeminiChat) messageToContent(msg *api.Message) (*genai.Conten...
method IsRetryableError (line 657) | func (c *GeminiChat) IsRetryableError(err error) bool {
function toGeminiSchema (line 337) | func toGeminiSchema(schema *Schema) (*genai.Schema, error) {
type GeminiChatResponse (line 528) | type GeminiChatResponse struct
method MarshalJSON (line 534) | func (r *GeminiChatResponse) MarshalJSON() ([]byte, error) {
method String (line 542) | func (r *GeminiChatResponse) String() string {
method UsageMetadata (line 547) | func (r *GeminiChatResponse) UsageMetadata() any {
method Candidates (line 552) | func (r *GeminiChatResponse) Candidates() []Candidate {
type GeminiCandidate (line 562) | type GeminiCandidate struct
method String (line 567) | func (r *GeminiCandidate) String() string {
method Parts (line 592) | func (r *GeminiCandidate) Parts() []Part {
type GeminiPart (line 604) | type GeminiPart struct
method AsText (line 609) | func (p *GeminiPart) AsText() (string, bool) {
method AsFunctionCalls (line 617) | func (p *GeminiPart) AsFunctionCalls() ([]FunctionCall, bool) {
type GeminiCompletionResponse (line 630) | type GeminiCompletionResponse struct
method MarshalJSON (line 637) | func (r *GeminiCompletionResponse) MarshalJSON() ([]byte, error) {
method Response (line 645) | func (r *GeminiCompletionResponse) Response() string {
method UsageMetadata (line 649) | func (r *GeminiCompletionResponse) UsageMetadata() any {
method String (line 653) | func (r *GeminiCompletionResponse) String() string {
FILE: gollm/grok.go
function init (line 33) | func init() {
function newGrokClientFactory (line 40) | func newGrokClientFactory(ctx context.Context, opts ClientOptions) (Clie...
type GrokClient (line 45) | type GrokClient struct
method Close (line 82) | func (c *GrokClient) Close() error {
method StartChat (line 88) | func (c *GrokClient) StartChat(systemPrompt, model string) Chat {
method GenerateCompletion (line 125) | func (c *GrokClient) GenerateCompletion(ctx context.Context, req *Comp...
method SetResponseSchema (line 157) | func (c *GrokClient) SetResponseSchema(schema *Schema) error {
method ListModels (line 163) | func (c *GrokClient) ListModels(ctx context.Context) ([]string, error) {
function NewGrokClient (line 54) | func NewGrokClient(ctx context.Context, opts ClientOptions) (*GrokClient...
type simpleGrokCompletionResponse (line 110) | type simpleGrokCompletionResponse struct
method Response (line 115) | func (r *simpleGrokCompletionResponse) Response() string {
method UsageMetadata (line 120) | func (r *simpleGrokCompletionResponse) UsageMetadata() any {
type grokChatSession (line 171) | type grokChatSession struct
method SetFunctionDefinitions (line 183) | func (cs *grokChatSession) SetFunctionDefinitions(defs []*FunctionDefi...
method Send (line 216) | func (cs *grokChatSession) Send(ctx context.Context, contents ...any) ...
method SendStreaming (line 281) | func (cs *grokChatSession) SendStreaming(ctx context.Context, contents...
method IsRetryableError (line 376) | func (cs *grokChatSession) IsRetryableError(err error) bool {
method Initialize (line 383) | func (cs *grokChatSession) Initialize(messages []*api.Message) error {
type grokChatResponse (line 390) | type grokChatResponse struct
method UsageMetadata (line 396) | func (r *grokChatResponse) UsageMetadata() any {
method Candidates (line 404) | func (r *grokChatResponse) Candidates() []Candidate {
type grokCandidate (line 415) | type grokCandidate struct
method Parts (line 421) | func (c *grokCandidate) Parts() []Part {
method String (line 439) | func (c *grokCandidate) String() string {
type grokPart (line 452) | type grokPart struct
method AsText (line 459) | func (p *grokPart) AsText() (string, bool) {
method AsFunctionCalls (line 463) | func (p *grokPart) AsFunctionCalls() ([]FunctionCall, bool) {
type grokChatStreamResponse (line 489) | type grokChatStreamResponse struct
method UsageMetadata (line 498) | func (r *grokChatStreamResponse) UsageMetadata() any {
method Candidates (line 506) | func (r *grokChatStreamResponse) Candidates() []Candidate {
type grokStreamCandidate (line 520) | type grokStreamCandidate struct
method String (line 528) | func (c *grokStreamCandidate) String() string {
method Parts (line 534) | func (c *grokStreamCandidate) Parts() []Part {
type grokStreamPart (line 571) | type grokStreamPart struct
method AsText (line 580) | func (p *grokStreamPart) AsText() (string, bool) {
method AsFunctionCalls (line 585) | func (p *grokStreamPart) AsFunctionCalls() ([]FunctionCall, bool) {
FILE: gollm/http_journal.go
type journalingRoundTripper (line 29) | type journalingRoundTripper struct
method RoundTrip (line 36) | func (jrt *journalingRoundTripper) RoundTrip(req *http.Request) (*http...
function withJournaling (line 98) | func withJournaling(client *http.Client) *http.Client {
FILE: gollm/interfaces.go
type Client (line 28) | type Client interface
type Chat (line 47) | type Chat interface
type CompletionRequest (line 68) | type CompletionRequest struct
type CompletionResponse (line 74) | type CompletionResponse interface
type FunctionCall (line 81) | type FunctionCall struct
type FunctionDefinition (line 90) | type FunctionDefinition struct
type Schema (line 97) | type Schema struct
method ToRawSchema (line 106) | func (s *Schema) ToRawSchema() (json.RawMessage, error) {
type SchemaType (line 119) | type SchemaType
constant TypeObject (line 122) | TypeObject SchemaType = "object"
constant TypeArray (line 123) | TypeArray SchemaType = "array"
constant TypeString (line 125) | TypeString SchemaType = "string"
constant TypeBoolean (line 126) | TypeBoolean SchemaType = "boolean"
constant TypeNumber (line 127) | TypeNumber SchemaType = "number"
constant TypeInteger (line 128) | TypeInteger SchemaType = "integer"
type FunctionCallResult (line 133) | type FunctionCallResult struct
type ChatResponse (line 140) | type ChatResponse interface
type ChatResponseIterator (line 149) | type ChatResponseIterator
type Candidate (line 152) | type Candidate interface
type Part (line 166) | type Part interface
FILE: gollm/llamacpp.go
function init (line 32) | func init() {
function llamacppFactory (line 40) | func llamacppFactory(ctx context.Context, opts ClientOptions) (Client, e...
type LlamaCppClient (line 44) | type LlamaCppClient struct
method Close (line 81) | func (c *LlamaCppClient) Close() error {
method GenerateCompletion (line 85) | func (c *LlamaCppClient) GenerateCompletion(ctx context.Context, reque...
method doRequest (line 104) | func (c *LlamaCppClient) doRequest(ctx context.Context, httpMethod, re...
method doCompletion (line 139) | func (c *LlamaCppClient) doCompletion(ctx context.Context, req *llamac...
method doChat (line 147) | func (c *LlamaCppClient) doChat(ctx context.Context, req *llamacppChat...
method ListModels (line 155) | func (c *LlamaCppClient) ListModels(ctx context.Context) ([]string, er...
method SetResponseSchema (line 159) | func (c *LlamaCppClient) SetResponseSchema(responseSchema *Schema) err...
method StartChat (line 165) | func (c *LlamaCppClient) StartChat(systemPrompt, model string) Chat {
type LlamaCppChat (line 50) | type LlamaCppChat struct
method Send (line 190) | func (c *LlamaCppChat) Send(ctx context.Context, contents ...any) (Cha...
method SendStreaming (line 284) | func (c *LlamaCppChat) SendStreaming(ctx context.Context, contents ......
method IsRetryableError (line 293) | func (c *LlamaCppChat) IsRetryableError(err error) bool {
method Initialize (line 298) | func (c *LlamaCppChat) Initialize(messages []*api.Message) error {
method SetFunctionDefinitions (line 383) | func (c *LlamaCppChat) SetFunctionDefinitions(functionDefinitions []*F...
function NewLlamaCppClient (line 61) | func NewLlamaCppClient(ctx context.Context, opts ClientOptions) (*LlamaC...
type LlamaCppCompletionResponse (line 178) | type LlamaCppCompletionResponse struct
method Response (line 182) | func (r *LlamaCppCompletionResponse) Response() string {
method UsageMetadata (line 186) | func (r *LlamaCppCompletionResponse) UsageMetadata() any {
function ptrTo (line 303) | func ptrTo[T any](t T) *T {
type LlamaCppChatResponse (line 307) | type LlamaCppChatResponse struct
method MarshalJSON (line 314) | func (r *LlamaCppChatResponse) MarshalJSON() ([]byte, error) {
method String (line 321) | func (r *LlamaCppChatResponse) String() string {
method UsageMetadata (line 336) | func (r *LlamaCppChatResponse) UsageMetadata() any {
method Candidates (line 340) | func (r *LlamaCppChatResponse) Candidates() []Candidate {
type LlamaCppCandidate (line 348) | type LlamaCppCandidate struct
method String (line 352) | func (r *LlamaCppCandidate) String() string {
method Parts (line 356) | func (r *LlamaCppCandidate) Parts() []Part {
type LlamaCppPart (line 364) | type LlamaCppPart struct
method AsText (line 369) | func (p *LlamaCppPart) AsText() (string, bool) {
method AsFunctionCalls (line 376) | func (p *LlamaCppPart) AsFunctionCalls() ([]FunctionCall, bool) {
function toLlamacppTool (line 392) | func toLlamacppTool(fnDef *FunctionDefinition) llamacppTool {
function toLlamacppSchema (line 410) | func toLlamacppSchema(in *Schema) *llamacppSchema {
type llamacppCompletionRequest (line 432) | type llamacppCompletionRequest struct
type llamacppCompletionResponse (line 440) | type llamacppCompletionResponse struct
type llamacppTimings (line 473) | type llamacppTimings struct
type llamacppChatRequest (line 484) | type llamacppChatRequest struct
type llamacppChatResponse (line 490) | type llamacppChatResponse struct
type llamacppChoice (line 501) | type llamacppChoice struct
type llamacppUsage (line 507) | type llamacppUsage struct
type llamacppChatMessage (line 513) | type llamacppChatMessage struct
type llamacppToolCall (line 520) | type llamacppToolCall struct
type llamacppFunctionCall (line 525) | type llamacppFunctionCall struct
type llamacppTool (line 531) | type llamacppTool struct
type llamacppFunction (line 536) | type llamacppFunction struct
type llamacppSchema (line 542) | type llamacppSchema struct
FILE: gollm/ollama.go
function init (line 29) | func init() {
function ollamaFactory (line 37) | func ollamaFactory(ctx context.Context, opts ClientOptions) (Client, err...
constant defaultOllamaModel (line 42) | defaultOllamaModel = "gemma3:latest"
type OllamaClient (line 45) | type OllamaClient struct
method Close (line 70) | func (c *OllamaClient) Close() error {
method GenerateCompletion (line 74) | func (c *OllamaClient) GenerateCompletion(ctx context.Context, request...
method ListModels (line 96) | func (c *OllamaClient) ListModels(ctx context.Context) ([]string, erro...
method SetResponseSchema (line 110) | func (c *OllamaClient) SetResponseSchema(schema *Schema) error {
method StartChat (line 114) | func (c *OllamaClient) StartChat(systemPrompt, model string) Chat {
type OllamaChat (line 49) | type OllamaChat struct
method Send (line 139) | func (c *OllamaChat) Send(ctx context.Context, contents ...any) (ChatR...
method IsRetryableError (line 198) | func (c *OllamaChat) IsRetryableError(err error) bool {
method SendStreaming (line 203) | func (c *OllamaChat) SendStreaming(ctx context.Context, contents ...an...
method Initialize (line 212) | func (c *OllamaChat) Initialize(messages []*kctlApi.Message) error {
method SetFunctionDefinitions (line 292) | func (c *OllamaChat) SetFunctionDefinitions(functionDefinitions []*Fun...
function NewOllamaClient (line 60) | func NewOllamaClient(ctx context.Context, opts ClientOptions) (*OllamaCl...
type OllamaCompletionResponse (line 127) | type OllamaCompletionResponse struct
method Response (line 131) | func (r *OllamaCompletionResponse) Response() string {
method UsageMetadata (line 135) | func (r *OllamaCompletionResponse) UsageMetadata() any {
type OllamaChatResponse (line 217) | type OllamaChatResponse struct
method MarshalJSON (line 224) | func (r *OllamaChatResponse) MarshalJSON() ([]byte, error) {
method String (line 231) | func (r *OllamaChatResponse) String() string {
method UsageMetadata (line 235) | func (r *OllamaChatResponse) UsageMetadata() any {
method Candidates (line 239) | func (r *OllamaChatResponse) Candidates() []Candidate {
type OllamaCandidate (line 247) | type OllamaCandidate struct
method String (line 251) | func (r *OllamaCandidate) String() string {
method Parts (line 255) | func (r *OllamaCandidate) Parts() []Part {
type OllamaPart (line 266) | type OllamaPart struct
method AsText (line 271) | func (p *OllamaPart) AsText() (string, bool) {
method AsFunctionCalls (line 278) | func (p *OllamaPart) AsFunctionCalls() ([]FunctionCall, bool) {
function fnDefToOllamaTool (line 301) | func fnDefToOllamaTool(fnDef *FunctionDefinition) api.Tool {
FILE: gollm/openai.go
function init (line 47) | func init() {
type OpenAIClient (line 75) | type OpenAIClient struct
method Close (line 116) | func (c *OpenAIClient) Close() error {
method StartChat (line 122) | func (c *OpenAIClient) StartChat(systemPrompt, model string) Chat {
method GenerateCompletion (line 190) | func (c *OpenAIClient) GenerateCompletion(ctx context.Context, req *Co...
method SetResponseSchema (line 220) | func (c *OpenAIClient) SetResponseSchema(schema *Schema) error {
method ListModels (line 228) | func (c *OpenAIClient) ListModels(ctx context.Context) ([]string, erro...
function NewOpenAIClient (line 84) | func NewOpenAIClient(ctx context.Context, opts ClientOptions) (*OpenAICl...
type simpleCompletionResponse (line 175) | type simpleCompletionResponse struct
method Response (line 180) | func (r *simpleCompletionResponse) Response() string {
method UsageMetadata (line 185) | func (r *simpleCompletionResponse) UsageMetadata() any {
type openAIChatSession (line 244) | type openAIChatSession struct
method SetFunctionDefinitions (line 256) | func (cs *openAIChatSession) SetFunctionDefinitions(defs []*FunctionDe...
method Send (line 284) | func (cs *openAIChatSession) Send(ctx context.Context, contents ...any...
method SendStreaming (line 332) | func (cs *openAIChatSession) SendStreaming(ctx context.Context, conten...
method IsRetryableError (line 460) | func (cs *openAIChatSession) IsRetryableError(err error) bool {
method Initialize (line 467) | func (cs *openAIChatSession) Initialize(messages []*api.Message) error {
method convertFunctionParameters (line 720) | func (cs *openAIChatSession) convertFunctionParameters(gollmDef *Funct...
method convertSchemaToBytes (line 791) | func (cs *openAIChatSession) convertSchemaToBytes(schema *Schema, func...
method addContentsToHistory (line 811) | func (cs *openAIChatSession) addContentsToHistory(contents []any) error {
type openAIChatResponse (line 474) | type openAIChatResponse struct
method UsageMetadata (line 480) | func (r *openAIChatResponse) UsageMetadata() any {
method Candidates (line 488) | func (r *openAIChatResponse) Candidates() []Candidate {
type openAICandidate (line 499) | type openAICandidate struct
method Parts (line 505) | func (c *openAICandidate) Parts() []Part {
method String (line 523) | func (c *openAICandidate) String() string {
type openAIPart (line 536) | type openAIPart struct
method AsText (line 543) | func (p *openAIPart) AsText() (string, bool) {
method AsFunctionCalls (line 547) | func (p *openAIPart) AsFunctionCalls() ([]FunctionCall, bool) {
type openAIChatStreamResponse (line 552) | type openAIChatStreamResponse struct
method Candidates (line 560) | func (r *openAIChatStreamResponse) Candidates() []Candidate {
method UsageMetadata (line 605) | func (r *openAIChatStreamResponse) UsageMetadata() any {
type openAIStreamCandidate (line 577) | type openAIStreamCandidate struct
method Parts (line 584) | func (c *openAIStreamCandidate) Parts() []Part {
method String (line 613) | func (c *openAIStreamCandidate) String() string {
type openAIStreamPart (line 619) | type openAIStreamPart struct
method AsText (line 627) | func (p *openAIStreamPart) AsText() (string, bool) {
method AsFunctionCalls (line 631) | func (p *openAIStreamPart) AsFunctionCalls() ([]FunctionCall, bool) {
function convertSchemaForOpenAI (line 637) | func convertSchemaForOpenAI(schema *Schema) (*Schema, error) {
type openAISchema (line 750) | type openAISchema struct
method MarshalJSON (line 755) | func (s openAISchema) MarshalJSON() ([]byte, error) {
function newOpenAIClientFactory (line 806) | func newOpenAIClientFactory(ctx context.Context, opts ClientOptions) (Cl...
function convertToolCallsToFunctionCalls (line 835) | func convertToolCallsToFunctionCalls(toolCalls []openai.ChatCompletionMe...
function getOpenAIModel (line 869) | func getOpenAIModel(model string) string {
FILE: gollm/openai_response.go
type openAIResponseChatSession (line 32) | type openAIResponseChatSession struct
method SetFunctionDefinitions (line 47) | func (cs *openAIResponseChatSession) SetFunctionDefinitions(defs []*Fu...
method Send (line 73) | func (cs *openAIResponseChatSession) Send(ctx context.Context, content...
method SendStreaming (line 81) | func (cs *openAIResponseChatSession) SendStreaming(ctx context.Context...
method IsRetryableError (line 140) | func (cs *openAIResponseChatSession) IsRetryableError(err error) bool {
method Initialize (line 147) | func (cs *openAIResponseChatSession) Initialize(messages []*api.Messag...
method convertFunctionParameters (line 241) | func (cs *openAIResponseChatSession) convertFunctionParameters(gollmDe...
method convertSchemaToBytes (line 271) | func (cs *openAIResponseChatSession) convertSchemaToBytes(schema *Sche...
method addContentsToHistory (line 286) | func (cs *openAIResponseChatSession) addContentsToHistory(contents []a...
type openAIResponseChatResponse (line 153) | type openAIResponseChatResponse struct
method UsageMetadata (line 159) | func (r *openAIResponseChatResponse) UsageMetadata() any {
method Candidates (line 163) | func (r *openAIResponseChatResponse) Candidates() []Candidate {
type openAIResponseCandidate (line 182) | type openAIResponseCandidate struct
method Parts (line 188) | func (c *openAIResponseCandidate) Parts() []Part {
method String (line 221) | func (c *openAIResponseCandidate) String() string {
type openAIResponsePart (line 225) | type openAIResponsePart struct
method AsText (line 232) | func (p *openAIResponsePart) AsText() (string, bool) {
method AsFunctionCalls (line 236) | func (p *openAIResponsePart) AsFunctionCalls() ([]FunctionCall, bool) {
function convertResponseToolCallToFunctionCall (line 318) | func convertResponseToolCallToFunctionCall(responseToolCall responses.Re...
FILE: gollm/openai_test.go
function TestConvertSchemaForOpenAI (line 24) | func TestConvertSchemaForOpenAI(t *testing.T) {
function TestConvertSchemaToBytes (line 389) | func TestConvertSchemaToBytes(t *testing.T) {
function TestConvertToolCallsToFunctionCalls (line 430) | func TestConvertToolCallsToFunctionCalls(t *testing.T) {
FILE: gollm/persist.go
type RecordCompletionResponse (line 20) | type RecordCompletionResponse struct
type RecordChatResponse (line 25) | type RecordChatResponse struct
FILE: gollm/schema.go
function BuildSchemaFor (line 26) | func BuildSchemaFor(t reflect.Type) *Schema {
FILE: gollm/shims.go
function singletonChatResponseIterator (line 17) | func singletonChatResponseIterator(response ChatResponse) ChatResponseIt...
FILE: internal/mocks/agent_mock.go
type MockChatMessageStore (line 20) | type MockChatMessageStore struct
method EXPECT (line 39) | func (m *MockChatMessageStore) EXPECT() *MockChatMessageStoreMockRecor...
method AddChatMessage (line 44) | func (m *MockChatMessageStore) AddChatMessage(record *api.Message) err...
method ChatMessages (line 58) | func (m *MockChatMessageStore) ChatMessages() []*api.Message {
method ClearChatMessages (line 72) | func (m *MockChatMessageStore) ClearChatMessages() error {
method SetChatMessages (line 86) | func (m *MockChatMessageStore) SetChatMessages(newHistory []*api.Messa...
type MockChatMessageStoreMockRecorder (line 27) | type MockChatMessageStoreMockRecorder struct
method AddChatMessage (line 52) | func (mr *MockChatMessageStoreMockRecorder) AddChatMessage(record any)...
method ChatMessages (line 66) | func (mr *MockChatMessageStoreMockRecorder) ChatMessages() *gomock.Call {
method ClearChatMessages (line 80) | func (mr *MockChatMessageStoreMockRecorder) ClearChatMessages() *gomoc...
method SetChatMessages (line 94) | func (mr *MockChatMessageStoreMockRecorder) SetChatMessages(newHistory...
function NewMockChatMessageStore (line 32) | func NewMockChatMessageStore(ctrl *gomock.Controller) *MockChatMessageSt...
FILE: internal/mocks/gollm_mock.go
type MockClient (line 22) | type MockClient struct
method EXPECT (line 41) | func (m *MockClient) EXPECT() *MockClientMockRecorder {
method Close (line 46) | func (m *MockClient) Close() error {
method GenerateCompletion (line 60) | func (m *MockClient) GenerateCompletion(ctx context.Context, req *goll...
method ListModels (line 75) | func (m *MockClient) ListModels(ctx context.Context) ([]string, error) {
method SetResponseSchema (line 90) | func (m *MockClient) SetResponseSchema(schema *gollm.Schema) error {
method StartChat (line 104) | func (m *MockClient) StartChat(systemPrompt, model string) gollm.Chat {
type MockClientMockRecorder (line 29) | type MockClientMockRecorder struct
method Close (line 54) | func (mr *MockClientMockRecorder) Close() *gomock.Call {
method GenerateCompletion (line 69) | func (mr *MockClientMockRecorder) GenerateCompletion(ctx, req any) *go...
method ListModels (line 84) | func (mr *MockClientMockRecorder) ListModels(ctx any) *gomock.Call {
method SetResponseSchema (line 98) | func (mr *MockClientMockRecorder) SetResponseSchema(schema any) *gomoc...
method StartChat (line 112) | func (mr *MockClientMockRecorder) StartChat(systemPrompt, model any) *...
function NewMockClient (line 34) | func NewMockClient(ctrl *gomock.Controller) *MockClient {
type MockChat (line 118) | type MockChat struct
method EXPECT (line 137) | func (m *MockChat) EXPECT() *MockChatMockRecorder {
method Initialize (line 142) | func (m *MockChat) Initialize(messages []*api.Message) error {
method IsRetryableError (line 156) | func (m *MockChat) IsRetryableError(arg0 error) bool {
method Send (line 170) | func (m *MockChat) Send(ctx context.Context, contents ...any) (gollm.C...
method SendStreaming (line 190) | func (m *MockChat) SendStreaming(ctx context.Context, contents ...any)...
method SetFunctionDefinitions (line 210) | func (m *MockChat) SetFunctionDefinitions(functionDefinitions []*gollm...
type MockChatMockRecorder (line 125) | type MockChatMockRecorder struct
method Initialize (line 150) | func (mr *MockChatMockRecorder) Initialize(messages any) *gomock.Call {
method IsRetryableError (line 164) | func (mr *MockChatMockRecorder) IsRetryableError(arg0 any) *gomock.Call {
method Send (line 183) | func (mr *MockChatMockRecorder) Send(ctx any, contents ...any) *gomock...
method SendStreaming (line 203) | func (mr *MockChatMockRecorder) SendStreaming(ctx any, contents ...any...
method SetFunctionDefinitions (line 218) | func (mr *MockChatMockRecorder) SetFunctionDefinitions(functionDefinit...
function NewMockChat (line 130) | func NewMockChat(ctrl *gomock.Controller) *MockChat {
FILE: internal/mocks/tools_mock.go
type MockTool (line 21) | type MockTool struct
method EXPECT (line 40) | func (m *MockTool) EXPECT() *MockToolMockRecorder {
method CheckModifiesResource (line 45) | func (m *MockTool) CheckModifiesResource(args map[string]any) string {
method Description (line 59) | func (m *MockTool) Description() string {
method FunctionDefinition (line 73) | func (m *MockTool) FunctionDefinition() *gollm.FunctionDefinition {
method IsInteractive (line 87) | func (m *MockTool) IsInteractive(args map[string]any) (bool, error) {
method Name (line 102) | func (m *MockTool) Name() string {
method Run (line 116) | func (m *MockTool) Run(ctx context.Context, args map[string]any) (any,...
type MockToolMockRecorder (line 28) | type MockToolMockRecorder struct
method CheckModifiesResource (line 53) | func (mr *MockToolMockRecorder) CheckModifiesResource(args any) *gomoc...
method Description (line 67) | func (mr *MockToolMockRecorder) Description() *gomock.Call {
method FunctionDefinition (line 81) | func (mr *MockToolMockRecorder) FunctionDefinition() *gomock.Call {
method IsInteractive (line 96) | func (mr *MockToolMockRecorder) IsInteractive(args any) *gomock.Call {
method Name (line 110) | func (mr *MockToolMockRecorder) Name() *gomock.Call {
method Run (line 125) | func (mr *MockToolMockRecorder) Run(ctx, args any) *gomock.Call {
function NewMockTool (line 33) | func NewMockTool(ctrl *gomock.Controller) *MockTool {
FILE: kubectl-utils/cmd/kubectl-expect/main.go
function main (line 35) | func main() {
function run (line 42) | func run(ctx context.Context) error {
FILE: kubectl-utils/pkg/kel/expression.go
function NewEnv (line 28) | func NewEnv() (*cel.Env, error) {
type Expression (line 37) | type Expression struct
method Eval (line 61) | func (x *Expression) Eval(ctx context.Context, self *unstructured.Unst...
method buildInputs (line 73) | func (x *Expression) buildInputs(self *unstructured.Unstructured) map[...
function NewExpression (line 44) | func NewExpression(env *cel.Env, celExpression string) (*Expression, err...
type unstructuredToCELAdapter (line 80) | type unstructuredToCELAdapter struct
method NativeToValue (line 83) | func (a *unstructuredToCELAdapter) NativeToValue(value any) ref.Val {
FILE: kubectl-utils/pkg/kel/info.go
type InfoFunction (line 29) | type InfoFunction
method BuildStatusPrinter (line 32) | func (x *Expression) BuildStatusPrinter(ctx context.Context) (InfoFuncti...
method buildFunctionPrinterFor (line 68) | func (x *Expression) buildFunctionPrinterFor(args []*exprpb.Expr) (InfoF...
FILE: kubectl-utils/pkg/kube/client.go
type Client (line 28) | type Client struct
method DefaultNamespace (line 75) | func (c *Client) DefaultNamespace() (string, error) {
method ForGVR (line 88) | func (c *Client) ForGVR(gvr schema.GroupVersionResource, namespace str...
function NewClient (line 34) | func NewClient(kubeconfig string) (*Client, error) {
function loadKubeconfig (line 63) | func loadKubeconfig(kubeconfigPath string) (clientcmd.ClientConfig, erro...
FILE: kubectl-utils/pkg/kube/discovery.go
function buildDiscoveryClient (line 28) | func buildDiscoveryClient(restConfig *rest.Config, httpClient *http.Clie...
method FindResource (line 37) | func (c *Client) FindResource(ctx context.Context, name string) (*metav1...
FILE: pkg/agent/agent_e2e_test.go
function recvMsg (line 30) | func recvMsg(t *testing.T, ctx context.Context, ch <-chan any) *api.Mess...
function recvUntil (line 46) | func recvUntil(t *testing.T, ctx context.Context, ch <-chan any, pred fu...
type fakePart (line 65) | type fakePart struct
method AsText (line 70) | func (p fakePart) AsText() (string, bool) {
method AsFunctionCalls (line 77) | func (p fakePart) AsFunctionCalls() ([]gollm.FunctionCall, bool) {
type fakeCandidate (line 84) | type fakeCandidate struct
method String (line 86) | func (c fakeCandidate) String() string { return "" }
method Parts (line 87) | func (c fakeCandidate) Parts() []gollm.Part { return c.parts }
type fakeChatResponse (line 89) | type fakeChatResponse struct
method UsageMetadata (line 91) | func (r fakeChatResponse) UsageMetadata() any { return nil }
method Candidates (line 92) | func (r fakeChatResponse) Candidates() []gollm.Candidate { return []go...
function fCalls (line 94) | func fCalls(name string, args map[string]any) gollm.Part {
function fText (line 98) | func fText(s string) gollm.Part { return fakePart{text: s} }
function chatWith (line 100) | func chatWith(parts ...gollm.Part) gollm.ChatResponse {
function TestAgentEndToEndToolExecution (line 104) | func TestAgentEndToEndToolExecution(t *testing.T) {
function TestAgentEndToEndMetaClear (line 231) | func TestAgentEndToEndMetaClear(t *testing.T) {
FILE: pkg/agent/conversation.go
type Agent (line 45) | type Agent struct
method GetSession (line 143) | func (s *Agent) GetSession() *api.Session {
method addMessage (line 156) | func (c *Agent) addMessage(source api.MessageSource, messageType api.M...
method setAgentState (line 177) | func (c *Agent) setAgentState(newState api.AgentState) {
method AgentState (line 187) | func (c *Agent) AgentState() api.AgentState {
method agentState (line 195) | func (c *Agent) agentState() api.AgentState {
method Init (line 199) | func (s *Agent) Init(ctx context.Context) error {
method Close (line 344) | func (c *Agent) Close() error {
method LastErr (line 381) | func (c *Agent) LastErr() error {
method Run (line 385) | func (c *Agent) Run(ctx context.Context, initialQuery string) error {
method handleMetaQuery (line 817) | func (c *Agent) handleMetaQuery(ctx context.Context, query string) (an...
method NewSession (line 896) | func (c *Agent) NewSession() (string, error) {
method SaveSession (line 957) | func (c *Agent) SaveSession() (string, error) {
method LoadSession (line 1002) | func (c *Agent) LoadSession(sessionID string) error {
method ListSessions (line 1057) | func (c *Agent) ListSessions() ([]api.SessionInfo, error) {
method listModels (line 1087) | func (c *Agent) listModels(ctx context.Context) ([]string, error) {
method DispatchToolCalls (line 1098) | func (c *Agent) DispatchToolCalls(ctx context.Context) error {
method analyzeToolCalls (line 1165) | func (c *Agent) analyzeToolCalls(ctx context.Context, toolCalls []goll...
method handleChoice (line 1183) | func (c *Agent) handleChoice(ctx context.Context, choice *api.UserChoi...
method generatePrompt (line 1221) | func (a *Agent) generatePrompt(_ context.Context, defaultPromptTemplat...
type ToolCallAnalysis (line 1157) | type ToolCallAnalysis struct
type PromptData (line 1253) | type PromptData struct
method ToolsAsJSON (line 1261) | func (a *PromptData) ToolsAsJSON() string {
method ToolNames (line 1275) | func (a *PromptData) ToolNames() string {
type ReActResponse (line 1279) | type ReActResponse struct
type Action (line 1285) | type Action struct
function extractJSON (line 1292) | func extractJSON(s string) (string, bool) {
function parseReActResponse (line 1309) | func parseReActResponse(input string) (*ReActResponse, error) {
function toMap (line 1326) | func toMap(v any) (map[string]any, error) {
function candidateToShimCandidate (line 1338) | func candidateToShimCandidate(iterator gollm.ChatResponseIterator) (goll...
type ShimResponse (line 1380) | type ShimResponse struct
method UsageMetadata (line 1384) | func (r *ShimResponse) UsageMetadata() any {
method Candidates (line 1388) | func (r *ShimResponse) Candidates() []gollm.Candidate {
type ShimCandidate (line 1392) | type ShimCandidate struct
method String (line 1396) | func (c *ShimCandidate) String() string {
method Parts (line 1400) | func (c *ShimCandidate) Parts() []gollm.Part {
type ShimPart (line 1414) | type ShimPart struct
method AsText (line 1419) | func (p *ShimPart) AsText() (string, bool) {
method AsFunctionCalls (line 1423) | func (p *ShimPart) AsFunctionCalls() ([]gollm.FunctionCall, bool) {
FILE: pkg/agent/conversation_test.go
function TestHandleMetaQuery (line 31) | func TestHandleMetaQuery(t *testing.T) {
function TestAgent_NewSession (line 252) | func TestAgent_NewSession(t *testing.T) {
function TestAgent_LoadSession_ResetsState (line 285) | func TestAgent_LoadSession_ResetsState(t *testing.T) {
function TestAgent_Init_CreatesSessionInStore (line 317) | func TestAgent_Init_CreatesSessionInStore(t *testing.T) {
function TestAgent_NewSession_NoDeadlock (line 356) | func TestAgent_NewSession_NoDeadlock(t *testing.T) {
FILE: pkg/agent/manager.go
type Factory (line 28) | type Factory
type AgentManager (line 31) | type AgentManager struct
method SetAgentCreatedCallback (line 50) | func (sm *AgentManager) SetAgentCreatedCallback(cb func(*Agent)) {
method GetAgent (line 60) | func (sm *AgentManager) GetAgent(ctx context.Context, sessionID string...
method Close (line 83) | func (sm *AgentManager) Close() error {
method ListSessions (line 99) | func (sm *AgentManager) ListSessions() ([]*api.Session, error) {
method FindSessionByID (line 104) | func (sm *AgentManager) FindSessionByID(id string) (*api.Session, erro...
method DeleteSession (line 109) | func (sm *AgentManager) DeleteSession(id string) error {
method UpdateLastAccessed (line 120) | func (sm *AgentManager) UpdateLastAccessed(session *api.Session) error {
method startAgent (line 124) | func (sm *AgentManager) startAgent(ctx context.Context, session *api.S...
function NewAgentManager (line 40) | func NewAgentManager(factory Factory, sessionManager *sessions.SessionMa...
FILE: pkg/agent/mcp_client.go
method InitializeMCPClient (line 30) | func (a *Agent) InitializeMCPClient(ctx context.Context) error {
method UpdateMCPStatus (line 68) | func (a *Agent) UpdateMCPStatus(ctx context.Context, mcpClientEnabled bo...
method getMCPStatus (line 89) | func (a *Agent) getMCPStatus(ctx context.Context, mcpClientEnabled bool)...
method convertMCPStatus (line 111) | func (a *Agent) convertMCPStatus(mcpStatus *mcp.MCPStatus) *api.MCPStatus {
method GetMCPStatusText (line 151) | func (a *Agent) GetMCPStatusText() string {
function extractCommandName (line 206) | func extractCommandName(command string) string {
method CloseMCPClient (line 220) | func (a *Agent) CloseMCPClient() error {
FILE: pkg/api/models.go
type Session (line 22) | type Session struct
method AllMessages (line 152) | func (s *Session) AllMessages() []*Message {
method String (line 159) | func (s *Session) String() string {
type AgentState (line 36) | type AgentState
constant AgentStateIdle (line 39) | AgentStateIdle AgentState = "idle"
constant AgentStateWaitingForInput (line 40) | AgentStateWaitingForInput AgentState = "waiting-for-input"
constant AgentStateRunning (line 41) | AgentStateRunning AgentState = "running"
constant AgentStateInitializing (line 42) | AgentStateInitializing AgentState = "initializing"
constant AgentStateDone (line 43) | AgentStateDone AgentState = "done"
constant AgentStateExited (line 44) | AgentStateExited AgentState = "exited"
type MessageType (line 47) | type MessageType
constant MessageTypeText (line 50) | MessageTypeText MessageType = "text"
constant MessageTypeError (line 51) | MessageTypeError MessageType = "error"
constant MessageTypeToolCallRequest (line 52) | MessageTypeToolCallRequest MessageType = "tool-call-request"
constant MessageTypeToolCallResponse (line 53) | MessageTypeToolCallResponse MessageType = "tool-call-response"
constant MessageTypeUserInputRequest (line 54) | MessageTypeUserInputRequest MessageType = "user-input-request"
constant MessageTypeUserInputResponse (line 55) | MessageTypeUserInputResponse MessageType = "user-input-response"
constant MessageTypeUserChoiceRequest (line 56) | MessageTypeUserChoiceRequest MessageType = "user-choice-request"
constant MessageTypeUserChoiceResponse (line 57) | MessageTypeUserChoiceResponse MessageType = "user-choice-response"
constant MessageTypeSessionPickerRequest (line 58) | MessageTypeSessionPickerRequest MessageType = "session-picker-request"
constant MessageTypeSessionPickerResponse (line 59) | MessageTypeSessionPickerResponse MessageType = "session-picker-response"
type Message (line 62) | type Message struct
type MessageSource (line 70) | type MessageSource
constant MessageSourceUser (line 73) | MessageSourceUser MessageSource = "user"
constant MessageSourceAgent (line 74) | MessageSourceAgent MessageSource = "agent"
constant MessageSourceModel (line 75) | MessageSourceModel MessageSource = "model"
type UserChoiceRequest (line 78) | type UserChoiceRequest struct
type UserChoiceOption (line 83) | type UserChoiceOption struct
type UserChoiceResponse (line 88) | type UserChoiceResponse struct
type UserInputResponse (line 92) | type UserInputResponse struct
type SessionPickerRequest (line 97) | type SessionPickerRequest struct
type SessionInfo (line 102) | type SessionInfo struct
type SessionPickerResponse (line 113) | type SessionPickerResponse struct
type MCPStatus (line 119) | type MCPStatus struct
type ServerConnectionInfo (line 129) | type ServerConnectionInfo struct
type MCPTool (line 138) | type MCPTool struct
type ChatMessageStore (line 145) | type ChatMessageStore interface
FILE: pkg/journal/context.go
type contextKey (line 21) | type contextKey
constant RecorderKey (line 23) | RecorderKey contextKey = "journal-recorder"
function RecorderFromContext (line 26) | func RecorderFromContext(ctx context.Context) Recorder {
function ContextWithRecorder (line 35) | func ContextWithRecorder(ctx context.Context, recorder Recorder) context...
FILE: pkg/journal/loader.go
function ParseEventsFromFile (line 28) | func ParseEventsFromFile(p string) ([]*Event, error) {
function ParseEvents (line 39) | func ParseEvents(r io.Reader) ([]*Event, error) {
function splitYAML (line 64) | func splitYAML(data []byte, atEOF bool) (advance int, token []byte, err ...
FILE: pkg/journal/log.go
type LogRecorder (line 23) | type LogRecorder struct
method Write (line 26) | func (r *LogRecorder) Write(ctx context.Context, event *Event) error {
method Close (line 33) | func (r *LogRecorder) Close() error {
FILE: pkg/journal/recorder.go
type Recorder (line 29) | type Recorder interface
type FileRecorder (line 37) | type FileRecorder struct
method Close (line 53) | func (r *FileRecorder) Close() error {
method Write (line 57) | func (r *FileRecorder) Write(ctx context.Context, event *Event) error {
function NewFileRecorder (line 42) | func NewFileRecorder(path string) (*FileRecorder, error) {
type Event (line 73) | type Event struct
method GetString (line 89) | func (e *Event) GetString(key string) (string, bool) {
constant ActionHTTPRequest (line 80) | ActionHTTPRequest = "http.request"
constant ActionHTTPResponse (line 81) | ActionHTTPResponse = "http.response"
constant ActionHTTPError (line 82) | ActionHTTPError = "http.error"
constant ActionUIRender (line 86) | ActionUIRender = "ui.render"
FILE: pkg/mcp/client.go
type Client (line 34) | type Client struct
method Connect (line 96) | func (c *Client) Connect(ctx context.Context) error {
method Close (line 112) | func (c *Client) Close() error {
method ListTools (line 131) | func (c *Client) ListTools(ctx context.Context) ([]Tool, error) {
method CallTool (line 148) | func (c *Client) CallTool(ctx context.Context, toolName string, argume...
type Tool (line 44) | type Tool struct
method WithServer (line 164) | func (t Tool) WithServer(server string) Tool {
method ID (line 171) | func (t Tool) ID() string {
method String (line 179) | func (t Tool) String() string {
method AsBasicTool (line 187) | func (t Tool) AsBasicTool() Tool {
method IsFromServer (line 194) | func (t Tool) IsFromServer(server string) bool {
function NewClient (line 54) | func NewClient(config ClientConfig) *Client {
function CreateStdioClient (line 73) | func CreateStdioClient(name, command string, args []string, env map[stri...
function convertMCPToolsToTools (line 199) | func convertMCPToolsToTools(mcpTools []mcp.Tool) ([]Tool, error) {
function convertMCPInputSchema (line 225) | func convertMCPInputSchema(mcpInputSchema *mcp.ToolInputSchema) (*gollm....
function convertMCPMapSchema (line 263) | func convertMCPMapSchema(key string, schemaMap map[string]interface{}) (...
function ensureClientConnected (line 338) | func ensureClientConnected(client *mcpclient.Client) error {
function initializeClientConnection (line 346) | func initializeClientConnection(ctx context.Context, client *mcpclient.C...
function verifyClientConnection (line 365) | func verifyClientConnection(ctx context.Context, client *mcpclient.Clien...
function cleanupClient (line 379) | func cleanupClient(client **mcpclient.Client) {
function processToolResponse (line 388) | func processToolResponse(result any) (string, error) {
function listClientTools (line 442) | func listClientTools(ctx context.Context, client *mcpclient.Client, serv...
FILE: pkg/mcp/config.go
type Config (line 29) | type Config struct
method Save (line 173) | func (c *Config) Save(path string) error {
method ValidateConfig (line 244) | func (c *Config) ValidateConfig() error {
type ServerConfig (line 35) | type ServerConfig struct
function loadDefaultConfig (line 63) | func loadDefaultConfig() (*Config, error) {
function DefaultConfigPath (line 82) | func DefaultConfigPath() (string, error) {
function LoadConfig (line 115) | func LoadConfig(path string) (*Config, error) {
function atomicWriteFile (line 203) | func atomicWriteFile(path string, data []byte, perm os.FileMode) error {
function ValidateServerConfig (line 266) | func ValidateServerConfig(config ServerConfig) error {
function applyEnvironmentVariables (line 290) | func applyEnvironmentVariables(config *Config) {
function applyServerEnvironment (line 298) | func applyServerEnvironment(server *ServerConfig) {
function applyAuthEnvironmentVariables (line 319) | func applyAuthEnvironmentVariables(server *ServerConfig, prefix string) {
function applyCommandEnvironmentVariables (line 348) | func applyCommandEnvironmentVariables(server *ServerConfig, prefix strin...
FILE: pkg/mcp/constants.go
constant DefaultConnectionTimeout (line 22) | DefaultConnectionTimeout = 30 * time.Second
constant DefaultVerificationTimeout (line 25) | DefaultVerificationTimeout = 10 * time.Second
constant DefaultPingTimeout (line 28) | DefaultPingTimeout = 5 * time.Second
constant DefaultStabilizationDelay (line 31) | DefaultStabilizationDelay = 2 * time.Second
constant ErrServerConnectionFmt (line 36) | ErrServerConnectionFmt = "connecting to MCP server %q: %w"
constant ErrServerCloseFmt (line 37) | ErrServerCloseFmt = "closing MCP client %q: %w"
constant ErrToolCallFmt (line 38) | ErrToolCallFmt = "calling tool %q: %w"
constant ErrPathCheckFmt (line 39) | ErrPathCheckFmt = "checking path %q: %w"
constant ClientName (line 44) | ClientName = "kubectl-ai-mcp-client"
constant ClientVersion (line 45) | ClientVersion = "1.0.0"
constant ConfigFilePermissions (line 50) | ConfigFilePermissions = 0600
constant ConfigDirPermissions (line 51) | ConfigDirPermissions = 0755
constant EnvMCPServerPrefix (line 57) | EnvMCPServerPrefix = "MCP_"
FILE: pkg/mcp/http_client.go
type httpClient (line 36) | type httpClient struct
method getUnderlyingClient (line 63) | func (c *httpClient) getUnderlyingClient() *mcpclient.Client {
method ensureConnected (line 68) | func (c *httpClient) ensureConnected() error {
method Name (line 73) | func (c *httpClient) Name() string {
method Connect (line 78) | func (c *httpClient) Connect(ctx context.Context) error {
method createStreamingClient (line 119) | func (c *httpClient) createStreamingClient() (*mcpclient.Client, error) {
method createStandardClient (line 195) | func (c *httpClient) createStandardClient() (*mcpclient.Client, error) {
method createOAuthClient (line 202) | func (c *httpClient) createOAuthClient(ctx context.Context) (*mcpclien...
method initializeConnection (line 240) | func (c *httpClient) initializeConnection(ctx context.Context) error {
method verifyConnection (line 245) | func (c *httpClient) verifyConnection(ctx context.Context) error {
method cleanup (line 250) | func (c *httpClient) cleanup() {
method Close (line 255) | func (c *httpClient) Close() error {
method ListTools (line 272) | func (c *httpClient) ListTools(ctx context.Context) ([]Tool, error) {
method CallTool (line 283) | func (c *httpClient) CallTool(ctx context.Context, toolName string, ar...
function NewHTTPClient (line 49) | func NewHTTPClient(config ClientConfig) MCPClient {
FILE: pkg/mcp/interfaces.go
type MCPClient (line 25) | type MCPClient interface
type ClientConfig (line 49) | type ClientConfig struct
type AuthConfig (line 71) | type AuthConfig struct
type OAuthConfig (line 81) | type OAuthConfig struct
function NewMCPClient (line 91) | func NewMCPClient(config ClientConfig) (MCPClient, error) {
FILE: pkg/mcp/manager.go
type ServerConnectionInfo (line 31) | type ServerConnectionInfo struct
type MCPStatus (line 40) | type MCPStatus struct
type Manager (line 54) | type Manager struct
method ConnectAll (line 87) | func (m *Manager) ConnectAll(ctx context.Context) error {
method Close (line 139) | func (m *Manager) Close() error {
method GetClient (line 160) | func (m *Manager) GetClient(name string) (*Client, bool) {
method ListClients (line 169) | func (m *Manager) ListClients() []*Client {
method DiscoverAndConnectServers (line 187) | func (m *Manager) DiscoverAndConnectServers(ctx context.Context) error {
method ListAvailableTools (line 207) | func (m *Manager) ListAvailableTools(ctx context.Context) (map[string]...
method RefreshToolDiscovery (line 231) | func (m *Manager) RefreshToolDiscovery(ctx context.Context) (map[strin...
method RegisterTools (line 265) | func (m *Manager) RegisterTools(ctx context.Context, registerCallback ...
method GetStatus (line 296) | func (m *Manager) GetStatus(ctx context.Context, mcpClientEnabled bool...
method LogConfig (line 370) | func (m *Manager) LogConfig(mcpConfigPath string) error {
method RegisterWithToolSystem (line 412) | func (m *Manager) RegisterWithToolSystem(ctx context.Context, register...
function NewManager (line 61) | func NewManager(config *Config) *Manager {
function InitializeManager (line 70) | func InitializeManager() (*Manager, error) {
FILE: pkg/mcp/stdio_client.go
type stdioClient (line 31) | type stdioClient struct
method getUnderlyingClient (line 50) | func (c *stdioClient) getUnderlyingClient() *mcpclient.Client {
method ensureConnected (line 55) | func (c *stdioClient) ensureConnected() error {
method Name (line 60) | func (c *stdioClient) Name() string {
method Connect (line 65) | func (c *stdioClient) Connect(ctx context.Context) error {
method initializeConnection (line 102) | func (c *stdioClient) initializeConnection(ctx context.Context) error {
method verifyConnection (line 107) | func (c *stdioClient) verifyConnection(ctx context.Context) error {
method cleanup (line 112) | func (c *stdioClient) cleanup() {
method Close (line 117) | func (c *stdioClient) Close() error {
method ListTools (line 134) | func (c *stdioClient) ListTools(ctx context.Context) ([]Tool, error) {
method CallTool (line 145) | func (c *stdioClient) CallTool(ctx context.Context, toolName string, a...
function NewStdioClient (line 40) | func NewStdioClient(config ClientConfig) MCPClient {
FILE: pkg/mcp/utils.go
type RetryConfig (line 32) | type RetryConfig struct
function DefaultRetryConfig (line 41) | func DefaultRetryConfig(description string) RetryConfig {
function RetryOperation (line 52) | func RetryOperation(ctx context.Context, config RetryConfig, operation f...
function calculateBackoffDelay (line 93) | func calculateBackoffDelay(attempt int, config RetryConfig) time.Duration {
function SanitizeServerName (line 104) | func SanitizeServerName(name string) string {
function GroupToolsByServer (line 126) | func GroupToolsByServer(tools map[string][]Tool) map[string]int {
function mergeEnvironmentVariables (line 137) | func mergeEnvironmentVariables(processEnv, customEnv []string) []string {
function expandPath (line 165) | func expandPath(path string) (string, error) {
function withTimeout (line 234) | func withTimeout(ctx context.Context, timeout time.Duration) (context.Co...
method ensureConnected (line 239) | func (c *Client) ensureConnected() error {
type FunctionDefinition (line 252) | type FunctionDefinition interface
type SchemaProperty (line 258) | type SchemaProperty interface
type SchemaBuilder (line 264) | type SchemaBuilder
function ConvertArgs (line 268) | func ConvertArgs(args map[string]any) map[string]any {
function SnakeToCamel (line 287) | func SnakeToCamel(s string) string {
function ConvertValue (line 305) | func ConvertValue(paramName string, value any) any {
function IsNumberParam (line 346) | func IsNumberParam(name string) bool {
function IsBoolParam (line 357) | func IsBoolParam(name string) bool {
FILE: pkg/sandbox/executor.go
type Executor (line 23) | type Executor interface
type ExecResult (line 32) | type ExecResult struct
method String (line 41) | func (e *ExecResult) String() string {
FILE: pkg/sandbox/kubernetes.go
type KubernetesSandbox (line 41) | type KubernetesSandbox struct
method Execute (line 51) | func (s *KubernetesSandbox) Execute(ctx context.Context, command strin...
method Close (line 81) | func (s *KubernetesSandbox) Close(ctx context.Context) error {
method Command (line 166) | func (s *KubernetesSandbox) Command(name string, arg ...string) *Cmd {
method CommandContext (line 176) | func (s *KubernetesSandbox) CommandContext(ctx context.Context, name s...
method Delete (line 187) | func (s *KubernetesSandbox) Delete(ctx context.Context) error {
method deleteKubeconfigMap (line 428) | func (s *KubernetesSandbox) deleteKubeconfigMap(ctx context.Context, n...
type Cmd (line 87) | type Cmd struct
method Run (line 228) | func (c *Cmd) Run() error {
method Output (line 233) | func (c *Cmd) Output() ([]byte, error) {
method CombinedOutput (line 240) | func (c *Cmd) CombinedOutput() ([]byte, error) {
method execute (line 247) | func (c *Cmd) execute(stdout, stderr io.Writer) error {
method getPod (line 296) | func (c *Cmd) getPod() (*corev1.Pod, error) {
method createPod (line 309) | func (c *Cmd) createPod() error {
method createKubeconfigMap (line 390) | func (c *Cmd) createKubeconfigMap(name string) error {
method waitForPodReady (line 437) | func (c *Cmd) waitForPodReady() error {
method executeInPod (line 462) | func (c *Cmd) executeInPod(stdout, stderr io.Writer) error {
type Option (line 99) | type Option
function NewKubernetesSandbox (line 102) | func NewKubernetesSandbox(name string, opts ...Option) (*KubernetesSandb...
function WithKubeconfig (line 133) | func WithKubeconfig(kubeconfig string) Option {
function WithName (line 141) | func WithName(name string) Option {
function WithNamespace (line 149) | func WithNamespace(namespace string) Option {
function WithImage (line 157) | func WithImage(image string) Option {
FILE: pkg/sandbox/local.go
constant defaultBashBin (line 28) | defaultBashBin = "/bin/bash"
type Local (line 32) | type Local struct
method Execute (line 40) | func (e *Local) Execute(ctx context.Context, command string, env []str...
method Close (line 80) | func (e *Local) Close(ctx context.Context) error {
function NewLocalExecutor (line 35) | func NewLocalExecutor() *Local {
function lookupBashBin (line 85) | func lookupBashBin() string {
FILE: pkg/sandbox/seatbelt_executor.go
type Seatbelt (line 27) | type Seatbelt struct
method Execute (line 39) | func (e *Seatbelt) Execute(ctx context.Context, command string, env []...
method Close (line 78) | func (e *Seatbelt) Close(ctx context.Context) error {
function NewSeatbeltExecutor (line 32) | func NewSeatbeltExecutor() *Seatbelt {
FILE: pkg/sandbox/seatbelt_executor_others.go
type Seatbelt (line 25) | type Seatbelt struct
method Execute (line 33) | func (e *Seatbelt) Execute(ctx context.Context, command string, env []...
method Close (line 38) | func (e *Seatbelt) Close(ctx context.Context) error {
function NewSeatbeltExecutor (line 28) | func NewSeatbeltExecutor() *Seatbelt {
FILE: pkg/sessions/filesystem.go
type filesystemStore (line 30) | type filesystemStore struct
method GetSession (line 38) | func (f *filesystemStore) GetSession(id string) (*api.Session, error) {
method CreateSession (line 67) | func (f *filesystemStore) CreateSession(session *api.Session) error {
method UpdateSession (line 91) | func (f *filesystemStore) UpdateSession(session *api.Session) error {
method ListSessions (line 120) | func (f *filesystemStore) ListSessions() ([]*api.Session, error) {
method DeleteSession (line 149) | func (f *filesystemStore) DeleteSession(id string) error {
function newFilesystemStore (line 34) | func newFilesystemStore(basePath string) Store {
type FileChatMessageStore (line 155) | type FileChatMessageStore struct
method HistoryPath (line 166) | func (s *FileChatMessageStore) HistoryPath() string {
method AddChatMessage (line 171) | func (s *FileChatMessageStore) AddChatMessage(record *api.Message) err...
method SetChatMessages (line 224) | func (s *FileChatMessageStore) SetChatMessages(newHistory []*api.Messa...
method ChatMessages (line 232) | func (s *FileChatMessageStore) ChatMessages() []*api.Message {
method ClearChatMessages (line 244) | func (s *FileChatMessageStore) ClearChatMessages() error {
method readMessages (line 251) | func (s *FileChatMessageStore) readMessages() ([]*api.Message, error) {
method writeMessages (line 316) | func (s *FileChatMessageStore) writeMessages(messages []*api.Message) ...
function NewFileChatMessageStore (line 161) | func NewFileChatMessageStore(path string) *FileChatMessageStore {
FILE: pkg/sessions/manager.go
type SessionManager (line 25) | type SessionManager struct
method NewSession (line 50) | func (sm *SessionManager) NewSession(meta Metadata) (*api.Session, err...
method ListSessions (line 72) | func (sm *SessionManager) ListSessions() ([]*api.Session, error) {
method FindSessionByID (line 76) | func (sm *SessionManager) FindSessionByID(id string) (*api.Session, er...
method DeleteSession (line 80) | func (sm *SessionManager) DeleteSession(id string) error {
method GetLatestSession (line 84) | func (sm *SessionManager) GetLatestSession() (*api.Session, error) {
method UpdateLastAccessed (line 104) | func (sm *SessionManager) UpdateLastAccessed(session *api.Session) err...
function NewSessionManager (line 29) | func NewSessionManager(backend string) (*SessionManager, error) {
FILE: pkg/sessions/memory.go
type memoryStore (line 25) | type memoryStore struct
method GetSession (line 34) | func (m *memoryStore) GetSession(id string) (*api.Session, error) {
method CreateSession (line 45) | func (m *memoryStore) CreateSession(session *api.Session) error {
method UpdateSession (line 61) | func (m *memoryStore) UpdateSession(session *api.Session) error {
method ListSessions (line 73) | func (m *memoryStore) ListSessions() ([]*api.Session, error) {
method DeleteSession (line 89) | func (m *memoryStore) DeleteSession(id string) error {
function newMemoryStore (line 30) | func newMemoryStore() Store {
type InMemoryChatStore (line 103) | type InMemoryChatStore struct
method AddChatMessage (line 116) | func (s *InMemoryChatStore) AddChatMessage(record *api.Message) error {
method SetChatMessages (line 124) | func (s *InMemoryChatStore) SetChatMessages(newHistory []*api.Message)...
method ChatMessages (line 132) | func (s *InMemoryChatStore) ChatMessages() []*api.Message {
method ClearChatMessages (line 141) | func (s *InMemoryChatStore) ClearChatMessages() error {
function NewInMemoryChatStore (line 109) | func NewInMemoryChatStore() *InMemoryChatStore {
FILE: pkg/sessions/store.go
constant sessionsDirName (line 27) | sessionsDirName = "sessions"
type Metadata (line 29) | type Metadata struct
type Store (line 38) | type Store interface
function NewStore (line 46) | func NewStore(backend string) (Store, error) {
function defaultFilesystemBasePath (line 64) | func defaultFilesystemBasePath() (string, error) {
FILE: pkg/tools/bash_tool.go
constant defaultBashBin (line 30) | defaultBashBin = "/bin/bash"
function expandShellVar (line 34) | func expandShellVar(value string) (string, error) {
type BashTool (line 47) | type BashTool struct
method Name (line 55) | func (t *BashTool) Name() string {
method Description (line 59) | func (t *BashTool) Description() string {
method FunctionDefinition (line 63) | func (t *BashTool) FunctionDefinition() *gollm.FunctionDefinition {
method Run (line 88) | func (t *BashTool) Run(ctx context.Context, args map[string]any) (any,...
method IsInteractive (line 120) | func (t *BashTool) IsInteractive(args map[string]any) (bool, error) {
method CheckModifiesResource (line 137) | func (t *BashTool) CheckModifiesResource(args map[string]any) string {
function NewBashTool (line 51) | func NewBashTool(executor sandbox.Executor) *BashTool {
function validateCommand (line 110) | func validateCommand(command string) error {
FILE: pkg/tools/custom_tool.go
type CustomToolConfig (line 29) | type CustomToolConfig struct
type CustomTool (line 38) | type CustomTool struct
method Name (line 56) | func (t *CustomTool) Name() string {
method Description (line 61) | func (t *CustomTool) Description() string {
method FunctionDefinition (line 66) | func (t *CustomTool) FunctionDefinition() *gollm.FunctionDefinition {
method addCommandPrefix (line 96) | func (t *CustomTool) addCommandPrefix(inputCmd string) (string, error) {
method Run (line 127) | func (t *CustomTool) Run(ctx context.Context, args map[string]any) (an...
method CheckModifiesResource (line 157) | func (t *CustomTool) CheckModifiesResource(args map[string]any) string {
method CloneWithExecutor (line 164) | func (t *CustomTool) CloneWithExecutor(executor sandbox.Executor) *Cus...
function NewCustomTool (line 44) | func NewCustomTool(config CustomToolConfig) (*CustomTool, error) {
FILE: pkg/tools/custom_tool_test.go
function TestCustomTool_AddCommandPrefix (line 25) | func TestCustomTool_AddCommandPrefix(t *testing.T) {
type MockExecutor (line 120) | type MockExecutor struct
method Execute (line 126) | func (m *MockExecutor) Execute(ctx context.Context, command string, en...
method Close (line 133) | func (m *MockExecutor) Close(ctx context.Context) error {
function TestCustomTool_CloneWithExecutor (line 137) | func TestCustomTool_CloneWithExecutor(t *testing.T) {
FILE: pkg/tools/interfaces.go
type Tool (line 23) | type Tool interface
FILE: pkg/tools/kubectl_filter.go
function kubectlModifiesResource (line 64) | func kubectlModifiesResource(command string) string {
function analyzeCall (line 122) | func analyzeCall(call *syntax.CallExpr) string {
function parseKubectlArgs (line 200) | func parseKubectlArgs(args []string) (verb, subVerb string, hasDryRun bo...
FILE: pkg/tools/kubectl_filter_test.go
function TestKubectlModifiesResource (line 25) | func TestKubectlModifiesResource(t *testing.T) {
function TestKubectlAnalyzerComponents (line 159) | func TestKubectlAnalyzerComponents(t *testing.T) {
function TestKubectlCommandParsing (line 214) | func TestKubectlCommandParsing(t *testing.T) {
function TestKubectlPathHandling (line 284) | func TestKubectlPathHandling(t *testing.T) {
function TestKubectlDetectionLogic (line 322) | func TestKubectlDetectionLogic(t *testing.T) {
function TestShellParserBehavior (line 370) | func TestShellParserBehavior(t *testing.T) {
function TestSimplifiedKubectlDetection (line 480) | func TestSimplifiedKubectlDetection(t *testing.T) {
function TestKubectlAlwaysAtPosition0 (line 529) | func TestKubectlAlwaysAtPosition0(t *testing.T) {
FILE: pkg/tools/kubectl_tool.go
type Kubectl (line 27) | type Kubectl struct
method Name (line 35) | func (t *Kubectl) Name() string {
method Description (line 39) | func (t *Kubectl) Description() string {
method FunctionDefinition (line 53) | func (t *Kubectl) FunctionDefinition() *gollm.FunctionDefinition {
method Run (line 100) | func (t *Kubectl) Run(ctx context.Context, args map[string]any) (any, ...
method IsInteractive (line 151) | func (t *Kubectl) IsInteractive(args map[string]any) (bool, error) {
method CheckModifiesResource (line 168) | func (t *Kubectl) CheckModifiesResource(args map[string]any) string {
function NewKubectlTool (line 31) | func NewKubectlTool(executor sandbox.Executor) *Kubectl {
function DetectKubectlStreaming (line 134) | func DetectKubectlStreaming(command string) (bool, string) {
function validateKubectlCommand (line 177) | func validateKubectlCommand(command string) error {
FILE: pkg/tools/mcp_tool.go
function ConvertToolToGollm (line 32) | func ConvertToolToGollm(mcpTool *mcp.Tool) (*gollm.FunctionDefinition, e...
type MCPTool (line 47) | type MCPTool struct
method Name (line 67) | func (t *MCPTool) Name() string {
method UniqueToolName (line 71) | func (t *MCPTool) UniqueToolName() string {
method ServerName (line 76) | func (t *MCPTool) ServerName() string {
method Description (line 81) | func (t *MCPTool) Description() string {
method FunctionDefinition (line 86) | func (t *MCPTool) FunctionDefinition() *gollm.FunctionDefinition {
method IsInteractive (line 92) | func (t *MCPTool) IsInteractive(args map[string]any) (bool, error) {
method CheckModifiesResource (line 100) | func (t *MCPTool) CheckModifiesResource(args map[string]any) string {
method Run (line 107) | func (t *MCPTool) Run(ctx context.Context, args map[string]any) (any, ...
function NewMCPTool (line 56) | func NewMCPTool(serverName, toolName, description string, schema *gollm....
FILE: pkg/tools/streaming.go
type StreamDetector (line 26) | type StreamDetector
function ExecuteWithStreamingHandling (line 31) | func ExecuteWithStreamingHandling(ctx context.Context, executor sandbox....
FILE: pkg/tools/tools.go
type ContextKey (line 36) | type ContextKey
constant KubeconfigKey (line 39) | KubeconfigKey ContextKey = "kubeconfig"
constant WorkDirKey (line 40) | WorkDirKey ContextKey = "work_dir"
constant ExecutorKey (line 41) | ExecutorKey ContextKey = "executor"
function Lookup (line 44) | func Lookup(name string) Tool {
function Default (line 52) | func Default() Tools {
function RegisterTool (line 57) | func RegisterTool(tool Tool) {
type Tools (line 61) | type Tools struct
method Init (line 65) | func (t *Tools) Init() {
method Lookup (line 69) | func (t *Tools) Lookup(name string) Tool {
method AllTools (line 73) | func (t *Tools) AllTools() []Tool {
method Names (line 77) | func (t *Tools) Names() []string {
method RegisterTool (line 86) | func (t *Tools) RegisterTool(tool Tool) {
method CloneWithExecutor (line 103) | func (t *Tools) CloneWithExecutor(executor sandbox.Executor) Tools {
method ParseToolInvocation (line 158) | func (t *Tools) ParseToolInvocation(ctx context.Context, name string, ...
type ToolCall (line 120) | type ToolCall struct
method Description (line 131) | func (t *ToolCall) Description() string {
method InvokeTool (line 194) | func (t *ToolCall) InvokeTool(ctx context.Context, opt InvokeToolOptio...
method GetTool (line 326) | func (t *ToolCall) GetTool() Tool {
type InvokeToolOptions (line 171) | type InvokeToolOptions struct
type ToolRequestEvent (line 181) | type ToolRequestEvent struct
type ToolResponseEvent (line 187) | type ToolResponseEvent struct
function ToolResultToMap (line 235) | func ToolResultToMap(result any) (map[string]any, error) {
function LoadAndRegisterCustomTools (line 262) | func LoadAndRegisterCustomTools(configPath string) error {
method IsInteractive (line 320) | func (t *CustomTool) IsInteractive(args map[string]any) (bool, error) {
function ExpandShellVar (line 331) | func ExpandShellVar(value string) (string, error) {
function IsInteractiveCommand (line 344) | func IsInteractiveCommand(command string) (bool, error) {
FILE: pkg/ui/html/htmlui.go
type Broadcaster (line 41) | type Broadcaster struct
method Run (line 61) | func (b *Broadcaster) Run(ctx context.Context) {
method Broadcast (line 90) | func (b *Broadcaster) Broadcast(msg []byte) {
function NewBroadcaster (line 50) | func NewBroadcaster() *Broadcaster {
type HTMLUserInterface (line 94) | type HTMLUserInterface struct
method Run (line 169) | func (u *HTMLUserInterface) Run(ctx context.Context) error {
method serveIndex (line 197) | func (u *HTMLUserInterface) serveIndex(w http.ResponseWriter, req *htt...
method handleSessionStream (line 202) | func (u *HTMLUserInterface) handleSessionStream(w http.ResponseWriter,...
method handleListSessions (line 258) | func (u *HTMLUserInterface) handleListSessions(w http.ResponseWriter, ...
method handleCreateSession (line 275) | func (u *HTMLUserInterface) handleCreateSession(w http.ResponseWriter,...
method handleRenameSession (line 301) | func (u *HTMLUserInterface) handleRenameSession(w http.ResponseWriter,...
method handleDeleteSession (line 345) | func (u *HTMLUserInterface) handleDeleteSession(w http.ResponseWriter,...
method handlePOSTSendMessage (line 374) | func (u *HTMLUserInterface) handlePOSTSendMessage(w http.ResponseWrite...
method handlePOSTChooseOption (line 410) | func (u *HTMLUserInterface) handlePOSTChooseOption(w http.ResponseWrit...
method Close (line 451) | func (u *HTMLUserInterface) Close() error {
method ClearScreen (line 472) | func (u *HTMLUserInterface) ClearScreen() {
method getSessionStateJSON (line 476) | func (u *HTMLUserInterface) getSessionStateJSON(session *api.Session) ...
method getBroadcaster (line 497) | func (u *HTMLUserInterface) getBroadcaster(sessionID string) *Broadcas...
method ensureAgentListener (line 521) | func (u *HTMLUserInterface) ensureAgentListener(a *agent.Agent) {
function NewHTMLUserInterface (line 114) | func NewHTMLUserInterface(manager *agent.AgentManager, sessionManager *s...
FILE: pkg/ui/interfaces.go
type UI (line 24) | type UI interface
type Type (line 33) | type Type
method Set (line 42) | func (u *Type) Set(s string) error {
method String (line 52) | func (u *Type) String() string {
method Type (line 56) | func (u *Type) Type() string {
constant UITypeTerminal (line 36) | UITypeTerminal Type = "terminal"
constant UITypeWeb (line 37) | UITypeWeb Type = "web"
constant UITypeTUI (line 38) | UITypeTUI Type = "tui"
FILE: pkg/ui/terminal.go
type computedStyle (line 39) | type computedStyle struct
type colorValue (line 44) | type colorValue
constant colorGreen (line 47) | colorGreen colorValue = "green"
constant colorWhite (line 48) | colorWhite colorValue = "white"
constant colorRed (line 49) | colorRed colorValue = "red"
type styleOption (line 52) | type styleOption
function foreground (line 54) | func foreground(color colorValue) styleOption {
function renderMarkdown (line 60) | func renderMarkdown() styleOption {
type TerminalUI (line 67) | type TerminalUI struct
method Run (line 143) | func (u *TerminalUI) Run(ctx context.Context) error {
method ttyReader (line 190) | func (u *TerminalUI) ttyReader() (*bufio.Reader, error) {
method readlineInstance (line 204) | func (u *TerminalUI) readlineInstance() (*readline.Instance, error) {
method Close (line 229) | func (u *TerminalUI) Close() error {
method handleMessage (line 246) | func (u *TerminalUI) handleMessage(msg *api.Message) {
method ClearScreen (line 470) | func (u *TerminalUI) ClearScreen() {
function getCustomTerminalWidth (line 88) | func getCustomTerminalWidth() int {
function NewTerminalUI (line 113) | func NewTerminalUI(agent *agent.Agent, useTTYForInput bool, showToolOutp...
function formatToolCallResponse (line 474) | func formatToolCallResponse(payload map[string]any) string {
FILE: pkg/ui/tui.go
constant logo (line 38) | logo = `
type item (line 90) | type item
method FilterValue (line 92) | func (i item) FilterValue() string { return "" }
type itemDelegate (line 94) | type itemDelegate struct
method Height (line 96) | func (d itemDelegate) Height() int { retur...
method Spacing (line 97) | func (d itemDelegate) Spacing() int { retur...
method Update (line 98) | func (d itemDelegate) Update(_ tea.Msg, _ *list.Model) tea.Cmd { retur...
method Render (line 99) | func (d itemDelegate) Render(w io.Writer, m list.Model, idx int, li li...
type TUI (line 112) | type TUI struct
method Run (line 124) | func (u *TUI) Run(ctx context.Context) error {
method ClearScreen (line 152) | func (u *TUI) ClearScreen() {}
function NewTUI (line 117) | func NewTUI(agent *agent.Agent) *TUI {
type sessionListMsg (line 154) | type sessionListMsg
type tickMsg (line 167) | type tickMsg
type renderCache (line 170) | type renderCache struct
method get (line 181) | func (rc *renderCache) get(id string) (string, bool) {
method set (line 188) | func (rc *renderCache) set(id, content string) {
method getRenderer (line 194) | func (rc *renderCache) getRenderer(width int) (*glamour.TermRenderer, ...
function newRenderCache (line 177) | func newRenderCache() *renderCache {
type model (line 214) | type model struct
method fetchSessions (line 156) | func (m *model) fetchSessions() tea.Msg {
method Init (line 271) | func (m model) Init() tea.Cmd {
method tick (line 275) | func (m model) tick() tea.Cmd {
method Update (line 279) | func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
method resize (line 349) | func (m *model) resize() {
method updateViewportHeight (line 358) | func (m *model) updateViewportHeight() {
method navigateList (line 366) | func (m *model) navigateList(keyType tea.KeyType) tea.Cmd {
method handleKey (line 374) | func (m *model) handleKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
method handleEnter (line 421) | func (m *model) handleEnter() (tea.Model, tea.Cmd) {
method handleAgentMsg (line 486) | func (m *model) handleAgentMsg(msg *api.Message) (tea.Model, tea.Cmd) {
method refresh (line 542) | func (m *model) refresh() {
method renderMessages (line 550) | func (m model) renderMessages() string {
method renderMessage (line 588) | func (m model) renderMessage(msg *api.Message, r *glamour.TermRenderer...
method renderTextMsg (line 627) | func (m model) renderTextMsg(msg *api.Message, r *glamour.TermRenderer...
method renderToolCall (line 651) | func (m model) renderToolCall(msg *api.Message, w int) string {
method renderError (line 660) | func (m model) renderError(msg *api.Message, w int) string {
method View (line 669) | func (m model) View() string {
method viewStatus (line 685) | func (m model) viewStatus(session *api.Session) string {
method viewState (line 707) | func (m model) viewState(state api.AgentState) string {
method viewDivider (line 730) | func (m model) viewDivider() string {
method viewInput (line 734) | func (m model) viewInput(state api.AgentState) string {
method viewHelp (line 754) | func (m model) viewHelp(state api.AgentState) string {
function newModel (line 235) | func newModel(agent *agent.Agent) model {
function formatDuration (line 769) | func formatDuration(d time.Duration) string {
Condensed preview — 152 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (996K chars).
[
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 618,
"preview": "---\nname: 🐛 Bug Report\nabout: Report a reproducible bug or issue\ntitle: \"[Bug]: <describe bug>\"\nlabels: bug\n---\n\n**Envir"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 539,
"preview": "---\nname: 🚀 Feature Request\nabout: Suggest an idea for a new feature or improvement\ntitle: \"[Feature]: <describe your id"
},
{
"path": ".github/actions/kind-cluster-setup/action.yaml",
"chars": 671,
"preview": "name: Kind Cluster Setup\ndescription: \"Sets up a Kind Kubernetes cluster and authenticates with GCP\"\ninputs:\n cluster_n"
},
{
"path": ".github/kubectl-ai.cast",
"chars": 4273,
"preview": "{\"version\": 2, \"width\": 190, \"height\": 49, \"timestamp\": 1744912218, \"env\": {\"SHELL\": \"/bin/bash\", \"TERM\": \"screen-256col"
},
{
"path": ".github/workflows/ci-periodic.yaml",
"chars": 2641,
"preview": "# Copyright 2025 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
},
{
"path": ".github/workflows/ci-presubmit.yaml",
"chars": 2714,
"preview": "# Copyright 2025 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
},
{
"path": ".github/workflows/k8s-bench-evals.yaml",
"chars": 2646,
"preview": "# Copyright 2025 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
},
{
"path": ".github/workflows/release.yaml",
"chars": 791,
"preview": "name: Release\n\non:\n push:\n tags:\n - 'v*'\n\npermissions:\n contents: write\n packages: write\n\njobs:\n goreleaser:"
},
{
"path": ".gitignore",
"chars": 356,
"preview": "# Binary\n./kubectl-ai\nbin/\n.build/\n\n# Log files\n*.log\ntrace.log\nprompt.log\napp.log\n\n# OS specific files\n.DS_Store\n.env\n\n"
},
{
"path": ".goreleaser.yaml",
"chars": 1436,
"preview": "# This is an example .goreleaser.yml file with some sensible defaults.\n# Make sure to check the documentation at https:/"
},
{
"path": ".krew.yaml",
"chars": 2447,
"preview": "apiVersion: krew.googlecontainertools.github.com/v1alpha2\nkind: Plugin\nmetadata:\n name: ai\nspec:\n version: {{ .TagName"
},
{
"path": "CONTAINER.md",
"chars": 1954,
"preview": "# Running kubectl-ai in a Docker Container\n\n## 1. Build the Docker Image\n\nFirst, clone the `kubectl-ai` repository and b"
},
{
"path": "LICENSE",
"chars": 11358,
"preview": "\n Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 16198,
"preview": "# kubectl-ai\n\n[](https://gore"
},
{
"path": "cmd/main.go",
"chars": 27273,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "cmd/mcp.go",
"chars": 11003,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "cmd/mcp_test.go",
"chars": 3956,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "contributing.md",
"chars": 1961,
"preview": "# How to Contribute\n\nWe would love to accept your patches and contributions to this project.\n\n## Before you begin\n\n### S"
},
{
"path": "dev/ci/periodics/analyze-evals.sh",
"chars": 1042,
"preview": "#!/bin/bash\n\nset -o errexit\nset -o nounset\nset -o pipefail\n\nset -x\n\nREPO_ROOT=\"$(git rev-parse --show-toplevel)\"\ncd ${RE"
},
{
"path": "dev/ci/periodics/run-evals.sh",
"chars": 818,
"preview": "#!/bin/bash\n\nset -o errexit\nset -o nounset\nset -o pipefail\n\nset -x\n\nREPO_ROOT=\"$(git rev-parse --show-toplevel)\"\ncd ${RE"
},
{
"path": "dev/ci/presubmits/go-build.sh",
"chars": 796,
"preview": "#!/usr/bin/env bash\n# Copyright 2025 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# yo"
},
{
"path": "dev/ci/presubmits/go-vet.sh",
"chars": 794,
"preview": "#!/usr/bin/env bash\n# Copyright 2025 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# yo"
},
{
"path": "dev/ci/presubmits/verify-autogen.sh",
"chars": 939,
"preview": "#!/usr/bin/env bash\n# Copyright 2025 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# yo"
},
{
"path": "dev/ci/presubmits/verify-format.sh",
"chars": 905,
"preview": "#!/usr/bin/env bash\n# Copyright 2025 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# yo"
},
{
"path": "dev/ci/presubmits/verify-gomod.sh",
"chars": 903,
"preview": "#!/usr/bin/env bash\n# Copyright 2025 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# yo"
},
{
"path": "dev/ci/presubmits/verify-mocks.sh",
"chars": 1087,
"preview": "#!/usr/bin/env bash\n# Copyright 2025 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# yo"
},
{
"path": "dev/tasks/build-images",
"chars": 1425,
"preview": "#!/usr/bin/env bash\n\nset -o errexit\nset -o nounset\nset -o pipefail\n\nREPO_ROOT=\"$(git rev-parse --show-toplevel)\"\nSRC_DIR"
},
{
"path": "dev/tasks/demo.md",
"chars": 270,
"preview": "# steps to produce the demo gif\n\n1. Use [asciinema](asciinema.org) to record a screencast.\n\n2. Use agg to produce the gi"
},
{
"path": "dev/tasks/deploy-to-gke",
"chars": 4351,
"preview": "#!/usr/bin/env bash\n\nset -o errexit\nset -o nounset\nset -o pipefail\n\nREPO_ROOT=\"$(git rev-parse --show-toplevel)\"\nSRC_DIR"
},
{
"path": "dev/tasks/deploy-to-kind",
"chars": 3809,
"preview": "#!/usr/bin/env bash\n\nset -o errexit\nset -o nounset\nset -o pipefail\n\nREPO_ROOT=\"$(git rev-parse --show-toplevel)\"\nSRC_DIR"
},
{
"path": "dev/tasks/format.sh",
"chars": 890,
"preview": "#!/usr/bin/env bash\n# Copyright 2025 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# yo"
},
{
"path": "dev/tasks/generate-github-actions.sh",
"chars": 2142,
"preview": "#!/usr/bin/env bash\n# Copyright 2025 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# yo"
},
{
"path": "dev/tasks/gomod.sh",
"chars": 1361,
"preview": "#!/usr/bin/env bash\n# Copyright 2025 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# yo"
},
{
"path": "docs/bedrock.md",
"chars": 1970,
"preview": "# AWS Bedrock Provider\n\nkubectl-ai supports AWS Bedrock models including Claude Sonnet 4 and Claude 3.7.\n\n## Setup\n\n### "
},
{
"path": "docs/gke-deployment.md",
"chars": 4255,
"preview": "# Deploying k8-kate to Google Kubernetes Engine\n\n## Prerequisites\n\n- A GKE cluster (Standard or Autopilot).\n- `gcloud` C"
},
{
"path": "docs/mcp-client.md",
"chars": 5049,
"preview": "# kubectl-ai MCP Client Integration\n\n## Multi-Server Orchestration for Security Automation\n\nThe MCP (Model Context Proto"
},
{
"path": "docs/mcp-server.md",
"chars": 6075,
"preview": "# kubectl-ai MCP Server\n\nkubectl-ai can run as an MCP (Model Context Protocol) server, exposing kubectl-ai tools to othe"
},
{
"path": "docs/mocking.md",
"chars": 2091,
"preview": "# Mocking in kubectl-ai\n\n## Gomock developer workflow\n\nWe use [gomock](https://github.com/uber-go/mock) to mock external"
},
{
"path": "docs/tool-samples/argocd.yaml",
"chars": 1104,
"preview": "- name: argocd\n description: \"A declarative, GitOps continuous delivery tool for Kubernetes. Use it to manage applicati"
},
{
"path": "docs/tool-samples/gcloud.yaml",
"chars": 1468,
"preview": "- name: gcloud\n description: \"The gcloud command-line tool is the primary CLI for Google Cloud. Use it to manage Google"
},
{
"path": "docs/tool-samples/gh.yaml",
"chars": 1097,
"preview": "- name: gh\n description: \"The official GitHub command-line tool. Use it to interact with GitHub repositories, pull requ"
},
{
"path": "docs/tool-samples/kustomize.yaml",
"chars": 734,
"preview": "- name: kustomize\n description: \"A tool to customize Kubernetes resource configurations. Use it to render and apply dec"
},
{
"path": "docs/tools.md",
"chars": 4890,
"preview": "# Custom Tools for kubectl-ai\n\n`kubectl-ai` leverages LLMs to suggest and execute Kubernetes operations using a set of p"
},
{
"path": "go.mod",
"chars": 7576,
"preview": "module github.com/GoogleCloudPlatform/kubectl-ai\n\ngo 1.24.0\n\ntoolchain go1.24.3\n\n// Needed for multiple go modules in on"
},
{
"path": "go.sum",
"chars": 37698,
"preview": "cloud.google.com/go v0.118.3 h1:jsypSnrE/w4mJysioGdMBg4MiW/hHx/sArFpaBWHdME=\ncloud.google.com/go v0.118.3/go.mod h1:Lhs3"
},
{
"path": "go.work",
"chars": 68,
"preview": "go 1.24.0\n\ntoolchain go1.24.4\n\nuse (\n\t.\n\t./gollm\n\t./kubectl-utils\n)\n"
},
{
"path": "go.work.sum",
"chars": 17381,
"preview": "cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=\ncloud.google.com/go/auth/oauth2adapt v0.2.7/"
},
{
"path": "gollm/README.md",
"chars": 11899,
"preview": "# gollm\n\nA Go library for calling into multiple Large Language Model (LLM) providers with a unified interface.\n\nThis lib"
},
{
"path": "gollm/anthropic.go",
"chars": 19280,
"preview": "// Copyright 2026 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "gollm/anthropic_test.go",
"chars": 14074,
"preview": "// Copyright 2026 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "gollm/azopenai.go",
"chars": 12617,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "gollm/bedrock.go",
"chars": 22279,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "gollm/factory.go",
"chars": 10227,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "gollm/factory_test.go",
"chars": 1049,
"preview": "// Copyright 2026 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "gollm/gemini.go",
"chars": 18842,
"preview": "// Copyright 2024 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "gollm/go.mod",
"chars": 3442,
"preview": "module github.com/GoogleCloudPlatform/kubectl-ai/gollm\n\ngo 1.24.0\n\ntoolchain go1.24.3\n\nrequire (\n\tgithub.com/Azure/azure"
},
{
"path": "gollm/go.sum",
"chars": 15714,
"preview": "cloud.google.com/go v0.118.3 h1:jsypSnrE/w4mJysioGdMBg4MiW/hHx/sArFpaBWHdME=\ncloud.google.com/go v0.118.3/go.mod h1:Lhs3"
},
{
"path": "gollm/grok.go",
"chars": 20370,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "gollm/http_journal.go",
"chars": 3397,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "gollm/interfaces.go",
"chars": 6063,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "gollm/llamacpp.go",
"chars": 16286,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "gollm/ollama.go",
"chars": 8182,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "gollm/openai.go",
"chars": 27752,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "gollm/openai_response.go",
"chars": 11480,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "gollm/openai_test.go",
"chars": 17490,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "gollm/persist.go",
"chars": 952,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "gollm/schema.go",
"chars": 1829,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "gollm/shims.go",
"chars": 785,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "images/kubectl-ai/Dockerfile",
"chars": 1675,
"preview": "ARG GO_VERSION=\"1.24.3\"\nARG GCLOUD_CLI_VERSION=\"530.0.0-stable\"\n\nFROM golang:${GO_VERSION}-bookworm AS builder\n\nWORKDIR "
},
{
"path": "install.sh",
"chars": 3530,
"preview": "#!/usr/bin/env bash\nset -euo pipefail\n\n# Check for required commands\nfor cmd in curl tar; do\n if ! command -v \"$cmd\" >/"
},
{
"path": "internal/mocks/agent_mock.go",
"chars": 3395,
"preview": "// Code generated by MockGen. DO NOT EDIT.\n// Source: github.com/GoogleCloudPlatform/kubectl-ai/pkg/api (interfaces: Cha"
},
{
"path": "internal/mocks/generate.go",
"chars": 1298,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "internal/mocks/gollm_mock.go",
"chars": 7465,
"preview": "// Code generated by MockGen. DO NOT EDIT.\n// Source: github.com/GoogleCloudPlatform/kubectl-ai/gollm (interfaces: Clien"
},
{
"path": "internal/mocks/tools_mock.go",
"chars": 4017,
"preview": "// Code generated by MockGen. DO NOT EDIT.\n// Source: github.com/GoogleCloudPlatform/kubectl-ai/pkg/tools (interfaces: T"
},
{
"path": "k8s/all_in_one.yaml",
"chars": 2213,
"preview": "apiVersion: v1\nkind: Namespace\nmetadata:\n name: computer\n labels:\n name: computer\n---\napiVersion: v1\nkind: ServiceA"
},
{
"path": "k8s/kubectl-ai-gke.yaml",
"chars": 3363,
"preview": "apiVersion: v1\nkind: Namespace\nmetadata:\n name: kubectl-ai\n---\nkind: ServiceAccount\napiVersion: v1\nmetadata:\n name: ku"
},
{
"path": "k8s/kubectl-ai.yaml",
"chars": 1029,
"preview": "kind: Deployment\napiVersion: apps/v1\nmetadata:\n name: kubectl-ai\nspec:\n replicas: 1\n selector:\n matchLabels:\n "
},
{
"path": "k8s/sandbox/all-in-one.yaml",
"chars": 2807,
"preview": "apiVersion: v1\nkind: Namespace\nmetadata:\n name: computer\n labels:\n name: computer\n---\napiVersion: v1\nkind: ServiceA"
},
{
"path": "k8s/sandbox/cluster_role.yaml",
"chars": 999,
"preview": "apiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n name: reader-cluster-resources\nrules:\n- apiGroups"
},
{
"path": "k8s/sandbox/cluster_role_binding.yaml",
"chars": 293,
"preview": "apiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRoleBinding\nmetadata:\n name: normal-user-cluster-reader-binding\ns"
},
{
"path": "k8s/sandbox/namespace.yaml",
"chars": 87,
"preview": "apiVersion: v1\nkind: Namespace\nmetadata:\n name: computer\n labels:\n name: computer "
},
{
"path": "k8s/sandbox/role.yaml",
"chars": 1027,
"preview": "apiVersion: rbac.authorization.k8s.io/v1\nkind: Role\nmetadata:\n namespace: computer\n name: reader-all-but-secrets\nrules"
},
{
"path": "k8s/sandbox/role_binding.yaml",
"chars": 291,
"preview": "apiVersion: rbac.authorization.k8s.io/v1\nkind: RoleBinding\nmetadata:\n name: normal-user-reader-binding\n namespace: com"
},
{
"path": "k8s/sandbox/service_account.yaml",
"chars": 87,
"preview": "apiVersion: v1\nkind: ServiceAccount\nmetadata:\n name: normal-user\n namespace: computer"
},
{
"path": "kubectl-utils/README.md",
"chars": 368,
"preview": "kubectl-utils contains some experimental kubectl extensions that should help us write simpler evals for kubectl-ai\n\nThey"
},
{
"path": "kubectl-utils/cmd/kubectl-expect/main.go",
"chars": 3837,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "kubectl-utils/go.mod",
"chars": 2302,
"preview": "module github.com/GoogleCloudPlatform/kubectl-ai/kubectl-utils\n\ngo 1.24.0\n\ntoolchain go1.24.3\n\nrequire (\n\tgithub.com/goo"
},
{
"path": "kubectl-utils/go.sum",
"chars": 15520,
"preview": "cel.dev/expr v0.23.1 h1:K4KOtPCJQjVggkARsjG9RWXP6O4R73aHeJMa/dmCQQg=\ncel.dev/expr v0.23.1/go.mod h1:hLPLo1W4QUmuYdA72RBX"
},
{
"path": "kubectl-utils/pkg/kel/expression.go",
"chars": 2581,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "kubectl-utils/pkg/kel/info.go",
"chars": 3957,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "kubectl-utils/pkg/kube/client.go",
"chars": 2821,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "kubectl-utils/pkg/kube/discovery.go",
"chars": 2146,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "makefile",
"chars": 5377,
"preview": "# Makefile for kubectl-ai\n#\n# This Makefile provides a set of commands to build, test, run,\n# and manage the kubectl-ai "
},
{
"path": "modelserving/.gitignore",
"chars": 14,
"preview": ".build\n.cache\n"
},
{
"path": "modelserving/README.md",
"chars": 1001,
"preview": "# Model Serving\n\nThis directory provides components to build and deploy Large Language Model (LLM) serving endpoints.\n\n-"
},
{
"path": "modelserving/dev/tasks/build-images",
"chars": 3670,
"preview": "#!/usr/bin/env bash\n\nset -o errexit\nset -o nounset\nset -o pipefail\n\nREPO_ROOT=\"$(git rev-parse --show-toplevel)\"\nSRC_DIR"
},
{
"path": "modelserving/dev/tasks/deploy-to-gke",
"chars": 1247,
"preview": "#!/usr/bin/env bash\n\nset -o errexit\nset -o nounset\nset -o pipefail\n\nREPO_ROOT=\"$(git rev-parse --show-toplevel)\"\nSRC_DIR"
},
{
"path": "modelserving/dev/tasks/deploy-to-kind",
"chars": 1385,
"preview": "#!/usr/bin/env bash\n\nset -o errexit\nset -o nounset\nset -o pipefail\n\nREPO_ROOT=\"$(git rev-parse --show-toplevel)\"\nSRC_DIR"
},
{
"path": "modelserving/dev/tasks/download-model",
"chars": 1580,
"preview": "#!/usr/bin/env bash\n\nset -o errexit\nset -o nounset\nset -o pipefail\n\nREPO_ROOT=\"$(git rev-parse --show-toplevel)\"\nSRC_DIR"
},
{
"path": "modelserving/dev/tasks/run-local",
"chars": 845,
"preview": "#!/usr/bin/env bash\n\nset -o errexit\nset -o nounset\nset -o pipefail\n\nREPO_ROOT=\"$(git rev-parse --show-toplevel)\"\nSRC_DIR"
},
{
"path": "modelserving/images/llamacpp-gemma3-12b-it/Dockerfile",
"chars": 308,
"preview": "ARG BASE_IMAGE\nFROM ${BASE_IMAGE}\n\n# TODO: Add checksum\nCOPY .cache/gemma-3-12b-it-Q4_K_M.gguf /gemma-3-12b-it-Q4_K_M.gg"
},
{
"path": "modelserving/images/llamacpp-server/Dockerfile",
"chars": 959,
"preview": "ARG BUILDER_IMAGE\nARG BASE_IMAGE\n\nFROM ${BUILDER_IMAGE} AS builder\n\nARG CMAKE_ARGS\nARG LLAMACPP_TAG\n\nRUN apt-get update\n"
},
{
"path": "modelserving/k8s/llm-server-cpu.yaml",
"chars": 795,
"preview": "kind: Deployment\napiVersion: apps/v1\nmetadata:\n name: llm-server\nspec:\n replicas: 1\n selector:\n matchLabels:\n "
},
{
"path": "modelserving/k8s/llm-server-rpc.yaml",
"chars": 1150,
"preview": "kind: Deployment\napiVersion: apps/v1\nmetadata:\n name: llm-server-rpc\nspec:\n replicas: 1\n selector:\n matchLabels:\n "
},
{
"path": "modelserving/k8s/llm-server.yaml",
"chars": 1050,
"preview": "kind: Deployment\napiVersion: apps/v1\nmetadata:\n name: llm-server\nspec:\n replicas: 1\n selector:\n matchLabels:\n "
},
{
"path": "modelserving/k8s/rpc-server-cpu.yaml",
"chars": 744,
"preview": "kind: ServiceAccount\napiVersion: v1\nmetadata:\n name: rpc-server\n\n---\n\nkind: Service\napiVersion: v1\nmetadata:\n name: rp"
},
{
"path": "modelserving/k8s/rpc-server-cuda.yaml",
"chars": 905,
"preview": "kind: ServiceAccount\napiVersion: v1\nmetadata:\n name: rpc-server\n\n---\n\nkind: Service\napiVersion: v1\nmetadata:\n name: rp"
},
{
"path": "pkg/agent/agent_e2e_test.go",
"chars": 9464,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/agent/conversation.go",
"chars": 46625,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/agent/conversation_test.go",
"chars": 10983,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/agent/manager.go",
"chars": 4120,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/agent/mcp_client.go",
"chars": 6406,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/agent/systemprompt_template_default.txt",
"chars": 6582,
"preview": "You are `kubectl-ai`, an AI assistant with expertise in operating and performing actions against a kubernetes cluster. Y"
},
{
"path": "pkg/api/models.go",
"chars": 5164,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/journal/context.go",
"chars": 1137,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/journal/loader.go",
"chars": 1932,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/journal/log.go",
"chars": 893,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/journal/recorder.go",
"chars": 2563,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/mcp/README.md",
"chars": 8832,
"preview": "# MCP (Model Context Protocol) Client\n\nThis package provides functionality to interact with MCP (Model Context Protocol)"
},
{
"path": "pkg/mcp/client.go",
"chars": 14460,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/mcp/config.go",
"chars": 11491,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/mcp/constants.go",
"chars": 1749,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/mcp/default_config.yaml",
"chars": 136,
"preview": "servers:\n - name: sequential-thinking\n command: npx\n args:\n - -y\n - \"@modelcontextprotocol/server-seque"
},
{
"path": "pkg/mcp/http_client.go",
"chars": 9247,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/mcp/interfaces.go",
"chars": 3301,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/mcp/manager.go",
"chars": 12058,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/mcp/stdio_client.go",
"chars": 4698,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/mcp/utils.go",
"chars": 10622,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/sandbox/executor.go",
"chars": 1543,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/sandbox/kubernetes.go",
"chars": 14572,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/sandbox/local.go",
"chars": 2276,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/sandbox/seatbelt_executor.go",
"chars": 2108,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/sandbox/seatbelt_executor_others.go",
"chars": 1208,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/sessions/filesystem.go",
"chars": 7861,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/sessions/manager.go",
"chars": 2515,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/sessions/memory.go",
"chars": 3593,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/sessions/store.go",
"chars": 1840,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/tools/bash_tool.go",
"chars": 4069,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/tools/custom_tool.go",
"chars": 5243,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/tools/custom_tool_test.go",
"chars": 4782,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/tools/interfaces.go",
"chars": 1965,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/tools/kubectl_filter.go",
"chars": 6490,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/tools/kubectl_filter_test.go",
"chars": 24831,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/tools/kubectl_tool.go",
"chars": 6169,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/tools/mcp_tool.go",
"chars": 4300,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/tools/streaming.go",
"chars": 2369,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/tools/tools.go",
"chars": 9680,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/ui/html/htmlui.go",
"chars": 13789,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/ui/html/index.html",
"chars": 53426,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-widt"
},
{
"path": "pkg/ui/interfaces.go",
"chars": 1439,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/ui/terminal.go",
"chars": 13137,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
},
{
"path": "pkg/ui/tui.go",
"chars": 20943,
"preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
}
]
About this extraction
This page contains the full source code of the GoogleCloudPlatform/kubectl-ai GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 152 files (899.0 KB), approximately 263.7k tokens, and a symbol index with 1015 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.