mirror of
https://github.com/mark3labs/kit.git
synced 2026-06-14 19:50:13 +00:00
Compare commits
545 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9d38349091 | |||
| fec8bac800 | |||
| e76f5f3d45 | |||
| 1ad493c5c7 | |||
| ea6ddc8792 | |||
| 6d4e8bcec5 | |||
| e2ed345280 | |||
| e542eb797e | |||
| e631fc1b17 | |||
| 290c5a4774 | |||
| 287d60c31e | |||
| 3d45d98895 | |||
| db4be4f9a2 | |||
| 80093e69ed | |||
| ef519ba517 | |||
| d79eb1f0fa | |||
| ac8ee6525d | |||
| e35e8382d6 | |||
| fbb3408a25 | |||
| 44fed9a647 | |||
| e7f11487b9 | |||
| 054c417603 | |||
| 94d62a6ef0 | |||
| 91e6dfd2c8 | |||
| b6a0c4b44c | |||
| 8eb0fa855a | |||
| 3bf696c546 | |||
| 3e461a0539 | |||
| a2ece01ecf | |||
| 623c9fb5ad | |||
| 139506f336 | |||
| 6d424554ad | |||
| 5a3d3fdd7d | |||
| c91225629d | |||
| 5a71cde5ff | |||
| 044d3eb206 | |||
| 80f3a642a3 | |||
| 26f0969e3e | |||
| 4af75901b5 | |||
| 49ff4c0678 | |||
| b0802a5c32 | |||
| dfe65ca227 | |||
| d4ec756ce5 | |||
| 2971e73ee8 | |||
| 5aa6c9e116 | |||
| bca08476de | |||
| 6a599d86af | |||
| fd6f200659 | |||
| b295a25946 | |||
| f0e4e2f757 | |||
| d25249506a | |||
| 971521f534 | |||
| 8c00682367 | |||
| 58caf155c1 | |||
| 3f08bf2424 | |||
| 9fbbab05f6 | |||
| b0991c7aa6 | |||
| 9c90563765 | |||
| f36166bee5 | |||
| 879e81f9b5 | |||
| 727b42acfe | |||
| 4830981570 | |||
| dcfebafcc5 | |||
| 1f5c103667 | |||
| 4caa8ba3dc | |||
| 15ef8ad78b | |||
| 551f2710d9 | |||
| 67bda5cad5 | |||
| 01d7d754ef | |||
| c6304f1e92 | |||
| bc3c733ae3 | |||
| 428ee2b8be | |||
| eb1d7fd07e | |||
| 1e3e5cafd3 | |||
| 0b93e58fb9 | |||
| 2bb01ed72c | |||
| b6ecc36ea1 | |||
| d4f27bc912 | |||
| f12e195390 | |||
| b68b3dd0bf | |||
| 48521bf76d | |||
| 16df3a738c | |||
| 9d0b8c8cef | |||
| d9326fcf21 | |||
| 22c479277e | |||
| 8ae204f12f | |||
| 8b1665a4ce | |||
| 941f1daf0b | |||
| ab7e2bda61 | |||
| 741520927c | |||
| 4c1bda9541 | |||
| 3b69b13556 | |||
| 83a959a379 | |||
| 3491e05e9e | |||
| 0a54a8aa05 | |||
| 3cb3e5dba1 | |||
| 31966c469f | |||
| f03625d6e5 | |||
| d06641dc0a | |||
| bbf1106e27 | |||
| babed03a3d | |||
| 1cd074836f | |||
| ab3ce260c8 | |||
| 8e8cc3946d | |||
| e18e36625e | |||
| be55bc03f1 | |||
| 09919b6307 | |||
| 7a2de4cc3c | |||
| acd7fd7f45 | |||
| 3446f38516 | |||
| db4bb19bac | |||
| d1cffb85ef | |||
| 329cd4ea4a | |||
| 4e779d576f | |||
| fc054f50e8 | |||
| d8f1b32885 | |||
| 1e2a3e2589 | |||
| c7f43917b1 | |||
| 6a8833a7b1 | |||
| 82cbf1d457 | |||
| ab09d5c9e4 | |||
| 2347e0e506 | |||
| 3e1c19442b | |||
| 3fc0ad906e | |||
| f373c34f54 | |||
| 1206837af4 | |||
| f79601feb1 | |||
| eb3219e7ca | |||
| 7e7632ad3c | |||
| 0ef46a75f2 | |||
| 7f9a9da40a | |||
| 7ff9e84894 | |||
| 017eb99d44 | |||
| 15a1550205 | |||
| 2d14b3461f | |||
| b99aafaeaa | |||
| a55f6d3d9a | |||
| 027c2de849 | |||
| d24540693c | |||
| f7c8e7757b | |||
| 0d5374b17b | |||
| 25f17a104d | |||
| 20125f939b | |||
| d3b67ffd14 | |||
| 915dc066dd | |||
| 3b14814740 | |||
| a1decf9cff | |||
| ec4ac64343 | |||
| a95117686e | |||
| c0880e1ef6 | |||
| 4e66c0b4f7 | |||
| 131ce8f2cc | |||
| 3d0f3358cb | |||
| 25da02fa65 | |||
| 4ae03aab7c | |||
| 93895392e6 | |||
| 473070e78b | |||
| 12268a777f | |||
| 351c10d814 | |||
| 9de3843605 | |||
| 1d5473e111 | |||
| b6adcf159e | |||
| b1da4a28e6 | |||
| 95abb6fa6e | |||
| a9970cf346 | |||
| 13060a20f9 | |||
| adf603e944 | |||
| af486133a5 | |||
| a97cd47ced | |||
| 68518a2bdb | |||
| fd61db3e12 | |||
| e49066a119 | |||
| efaff7f44f | |||
| d3c970b607 | |||
| 23254fee64 | |||
| fe072ad2e1 | |||
| 8840cbfabc | |||
| a11b41cda4 | |||
| 8b7be8b735 | |||
| caa6d1c178 | |||
| 001156053d | |||
| 54717e32bc | |||
| 5b214b9fdf | |||
| c5e6ca6e4d | |||
| 419a139137 | |||
| 7b963624c1 | |||
| 66f2ba543b | |||
| 6dd052b990 | |||
| ef8628eecc | |||
| 3167222b72 | |||
| e3b37191b1 | |||
| 41d5f5e0fb | |||
| 3ad0b3616d | |||
| 8831b49b51 | |||
| c94edc929b | |||
| e49194a0d4 | |||
| 46b1acf444 | |||
| 6a6d201a50 | |||
| 930cbcb4f2 | |||
| 12e1ef2036 | |||
| a05da5f3ab | |||
| fefbf19b42 | |||
| 93905d4d77 | |||
| 7268ccdf4d | |||
| 9f59fa42dc | |||
| 8af7ca8455 | |||
| 424847f0db | |||
| 4c126ca41b | |||
| 4bdc4f75cc | |||
| bbd8975ca0 | |||
| e613a07773 | |||
| 1d3b4f8d56 | |||
| 118af2e152 | |||
| c46687fc44 | |||
| aeaa5368af | |||
| 4966c0ca2a | |||
| f3ea18ae3a | |||
| 24ea2c94e3 | |||
| 4577d218d3 | |||
| bd48457b27 | |||
| 84298a0743 | |||
| 393074447b | |||
| 879723fe90 | |||
| 57250a3a3d | |||
| 7e1686e572 | |||
| 4a8b10cde7 | |||
| cc5611eff7 | |||
| 51c70b63a7 | |||
| c9ee80d98a | |||
| 3ecedcbc2d | |||
| dbfa410fc1 | |||
| 512ecb92dc | |||
| aede76d807 | |||
| 9e1df38836 | |||
| 8f5efee837 | |||
| a392d3e572 | |||
| c40dc2f4fb | |||
| 37e82781b1 | |||
| 23c16bb197 | |||
| 9449f1fcdf | |||
| dc59cfc81e | |||
| 8407d924b9 | |||
| 91474af503 | |||
| e252791b3a | |||
| 1880523422 | |||
| eeecd5a843 | |||
| 7747fc2033 | |||
| 864230bd0a | |||
| 0de0040e63 | |||
| 98efaae960 | |||
| 53ae47a1bd | |||
| 584b215803 | |||
| 3009b5530b | |||
| 1309c4bd12 | |||
| 2a829fb98f | |||
| ad07086900 | |||
| 596eeede2f | |||
| 879ec65609 | |||
| 2fce8731e1 | |||
| c925a69aec | |||
| 25a3523c56 | |||
| 16ce5f3627 | |||
| f6ddd0b785 | |||
| 6d18ded8f9 | |||
| d6f8020554 | |||
| bc57364017 | |||
| 2cf7464e76 | |||
| 215a3186ff | |||
| 3804daa6fa | |||
| 078daaf8ce | |||
| 199297ce7e | |||
| 6aa54d0898 | |||
| 35452cc21b | |||
| 470ec43636 | |||
| 6c069907dd | |||
| 744642f2ee | |||
| 4ecdee7e25 | |||
| 4f0cd2ca6e | |||
| aeedf00f0f | |||
| 8275e217a8 | |||
| cf1c367713 | |||
| d8f40039fe | |||
| 9e5cf9dd4c | |||
| 144f0524d5 | |||
| 626f1105c9 | |||
| fb3608326f | |||
| 6ac8d3983a | |||
| f12950c0b6 | |||
| 7a6c0aba61 | |||
| f42d487214 | |||
| dd018b65ec | |||
| 268f3de34e | |||
| 6fdc6f7e5e | |||
| a3bad94821 | |||
| 3f2a399e47 | |||
| 30f368de24 | |||
| 46e1a1d66f | |||
| 7fc94018a9 | |||
| 7483bef1fa | |||
| c5b75674a3 | |||
| 41f1198cb6 | |||
| 7244485ce2 | |||
| 89b3adcc64 | |||
| 9b430f3883 | |||
| 8c140b89c2 | |||
| d818eddecb | |||
| 711aefba5e | |||
| a2c8201ec4 | |||
| ee5f41764b | |||
| faea00795e | |||
| 948919d60b | |||
| 2f8be19c83 | |||
| ac6d7f8f53 | |||
| d6b710542f | |||
| f57aaf31d0 | |||
| 38e4aa36e6 | |||
| 69fba663ef | |||
| ee21408546 | |||
| 4d9bf8a276 | |||
| acc8843ec0 | |||
| d405bc12d2 | |||
| cd4b5ab851 | |||
| 18bedca479 | |||
| 26b40aa613 | |||
| c1b3fa1dfc | |||
| 4f96acc217 | |||
| 80d5300feb | |||
| 0c3d240519 | |||
| cf2c026182 | |||
| 4450945b4f | |||
| 977f6c75bd | |||
| 00771cf291 | |||
| 04a0689775 | |||
| c15ff612b8 | |||
| ee66477498 | |||
| 154d693a8e | |||
| 371fc04477 | |||
| 6f518b74d6 | |||
| 355198cd3a | |||
| 12bed9b515 | |||
| d813c2c3d4 | |||
| 512aa735bf | |||
| 591d7905d2 | |||
| 3f2a0aaa92 | |||
| c7585b17b8 | |||
| 302b85ab31 | |||
| 7216c86db2 | |||
| 8303ee1dc5 | |||
| 0cf4e60a78 | |||
| a99f11a2f7 | |||
| dbf1dd52e1 | |||
| 6398b3e637 | |||
| be77e716cb | |||
| b23b8db3d6 | |||
| d4b393c95d | |||
| b96fe071ba | |||
| 676a6b0e63 | |||
| 443410018c | |||
| 71bdc768be | |||
| ccef91e69c | |||
| 29a46f6f98 | |||
| 72fa1ff029 | |||
| e62ce679fe | |||
| be9162637b | |||
| 0703dd1602 | |||
| ce32cea7ee | |||
| d24d49854f | |||
| bf2f44190d | |||
| 191dcea159 | |||
| a77aab3d90 | |||
| c4aa911e3a | |||
| 7ece291d9a | |||
| f9485cbe59 | |||
| c284a20dab | |||
| 1b3a8c171a | |||
| 4891f87038 | |||
| f240b9a8c1 | |||
| cab31935f1 | |||
| d3cae7c016 | |||
| 8c1f548eac | |||
| 63704f55b5 | |||
| d3281a2f01 | |||
| 1b7cc2ef6e | |||
| d56335e807 | |||
| 13acf44131 | |||
| dec5f08f80 | |||
| 81ec644cb9 | |||
| f778379dea | |||
| 4709716f5e | |||
| 433bdece70 | |||
| cc1ab91e64 | |||
| 4efc2e271c | |||
| 4f2f61c673 | |||
| bce9221916 | |||
| d3c04b12bc | |||
| 2f428997b3 | |||
| 4808ed2138 | |||
| d3f9759eb4 | |||
| 5d83df457b | |||
| 76741ab037 | |||
| fe4db1998d | |||
| d30f15269e | |||
| 0211e9ff7e | |||
| cdc4abfb36 | |||
| 66b7a72281 | |||
| 4227926dda | |||
| e40e44da48 | |||
| 2fd34068dd | |||
| df9337d085 | |||
| f3fd2822de | |||
| b0761f7ada | |||
| 8eb5060dc4 | |||
| 5c041394b8 | |||
| df29dc1673 | |||
| a87c772fd0 | |||
| 9159f226f4 | |||
| ac954068a9 | |||
| 4f524d3b16 | |||
| 32c46e4dc3 | |||
| 053e6c32b0 | |||
| ddd7856f9b | |||
| 47718a7fed | |||
| 7b2592bbdc | |||
| 00aa3c6995 | |||
| 76dd18030a | |||
| 42af56963f | |||
| be5af5469f | |||
| 6a1b2f65d1 | |||
| acade83e37 | |||
| daf5c41ac9 | |||
| ec620e4e88 | |||
| a66c55e175 | |||
| 6219b84937 | |||
| 30f720ef7b | |||
| b3c26150c7 | |||
| 904cbc6b37 | |||
| b56fb3c597 | |||
| 4d543da3b9 | |||
| 7d6abbf4a0 | |||
| b25bda58ab | |||
| a2346721cb | |||
| 0972a1600a | |||
| e5b6e7e123 | |||
| a46615abb0 | |||
| 3fcee53836 | |||
| 84fd21fc42 | |||
| d9983b9524 | |||
| 0d6742286b | |||
| ec81875390 | |||
| 679709d078 | |||
| ff49415679 | |||
| 7686577d9a | |||
| 1f407bf7fb | |||
| 33df79adb6 | |||
| d7f4c7a61c | |||
| 3faf46ff44 | |||
| cda80f1572 | |||
| aa43421b81 | |||
| 9e87977822 | |||
| 78a12147d7 | |||
| 6fb31c16a7 | |||
| 57f11899bf | |||
| 45b9ad9d5c | |||
| 34ea275a0c | |||
| 7ec2c5e9ed | |||
| 1a5d4ccf8a | |||
| 354e8a09fb | |||
| cd04879d7b | |||
| d273436091 | |||
| 798b1ede0e | |||
| d4910a9164 | |||
| 5ac43f1646 | |||
| 4b23623270 | |||
| 21f60e3560 | |||
| c159972306 | |||
| 824694bacb | |||
| ec5c1ef03e | |||
| 42eb398148 | |||
| 4133bbcb60 | |||
| 87274cb960 | |||
| 306fbdb3d0 | |||
| e0c20348b4 | |||
| 6a362d2f5e | |||
| cff5c2deda | |||
| 67dabdfeb2 | |||
| 82b50fbf0e | |||
| 57efdb5332 | |||
| 13ede07ea5 | |||
| 33e3e4fbb0 | |||
| 5959f15f0a | |||
| 6c4bcce030 | |||
| a88a2fc05c | |||
| 887622b5c4 | |||
| 45999161e5 | |||
| 8f8169a090 | |||
| 5d5cb58eb9 | |||
| 6bc360d375 | |||
| e37c7952dd | |||
| 8645a8c36a | |||
| 6c8f1114e8 | |||
| 7a81dc3ccf | |||
| b4750c5852 | |||
| 009beb5b64 | |||
| 494851a71f | |||
| beaa623fc5 | |||
| 4facfd6151 | |||
| c47f090061 | |||
| 4fe66b099e | |||
| ec996fd1e1 | |||
| c572625257 | |||
| f0be8458aa | |||
| 3076686b18 | |||
| b4057a917a | |||
| f1ed1fcb5c | |||
| d8583a7883 | |||
| d58251f53e | |||
| 54ff95c421 | |||
| e614a4d061 | |||
| 32a4831179 | |||
| 9681971ee9 | |||
| c4ed189f48 | |||
| 88a8c7bf15 | |||
| b32fd143c6 | |||
| 9a50bcbcbe | |||
| e1a974511d | |||
| 5c9246cd5f | |||
| 5ac7908227 | |||
| e3a64a107d | |||
| fa7cbb0b55 | |||
| 0bbe5f15c1 | |||
| e69bbb8354 | |||
| 5201b57ec6 | |||
| e7cebe73da | |||
| b79b521911 | |||
| 57fe2c6795 | |||
| ea68e00db6 | |||
| 87ffdec8f0 | |||
| 2d79e5989c | |||
| 6b2180ac7c | |||
| 8b0001184f | |||
| 8350b7a55a | |||
| 986f5560c5 | |||
| 8301aa1a68 | |||
| aca9d750ba | |||
| d80a14f660 |
@@ -0,0 +1,32 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.26"
|
||||
|
||||
- uses: golangci/golangci-lint-action@v7
|
||||
with:
|
||||
version: v2.10.1
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.26"
|
||||
|
||||
- run: go test -race ./...
|
||||
@@ -0,0 +1,32 @@
|
||||
name: Build and Deploy Docs to GitHub Pages
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: Install Dependencies
|
||||
working-directory: ./www
|
||||
run: bun install
|
||||
|
||||
- name: Build
|
||||
working-directory: ./www
|
||||
run: bun run build
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: JamesIves/github-pages-deploy-action@v4
|
||||
with:
|
||||
folder: www/out
|
||||
branch: gh-pages
|
||||
@@ -0,0 +1,105 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Tag to use for npm publish'
|
||||
required: true
|
||||
skip_goreleaser:
|
||||
description: 'Skip goreleaser job (npm-only release)'
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write # Required for npm trusted publishing (OIDC)
|
||||
|
||||
jobs:
|
||||
goreleaser:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name == 'push' || !inputs.skip_goreleaser }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.26"
|
||||
|
||||
- uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
distribution: goreleaser
|
||||
version: "~> v2"
|
||||
args: release --clean
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
npm-publish:
|
||||
runs-on: ubuntu-latest
|
||||
needs: goreleaser
|
||||
if: ${{ always() && (needs.goreleaser.result == 'success' || needs.goreleaser.result == 'skipped') }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "24"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
- name: Set version from tag
|
||||
working-directory: npm
|
||||
run: |
|
||||
TAG=${{ inputs.tag || github.ref_name }}
|
||||
VERSION=${TAG#v}
|
||||
echo "Setting npm version to $VERSION"
|
||||
npm version $VERSION --no-git-tag-version
|
||||
|
||||
- name: Publish to npm
|
||||
working-directory: npm
|
||||
run: npm publish --access public
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
notify:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [goreleaser, npm-publish]
|
||||
if: ${{ always() && (needs.goreleaser.result == 'success' || needs.goreleaser.result == 'skipped') && (needs.npm-publish.result == 'success') }}
|
||||
steps:
|
||||
- name: Send Discord Notification
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.RELEASES_WEBHOOK }}
|
||||
TAG_NAME: ${{ inputs.tag || github.ref_name }}
|
||||
RELEASE_URL: https://github.com/${{ github.repository }}/releases/tag/${{ inputs.tag || github.ref_name }}
|
||||
run: |
|
||||
curl -H "Content-Type: application/json" \
|
||||
-X POST \
|
||||
-d "{
|
||||
\"embeds\": [{
|
||||
\"title\": \"New Release: $TAG_NAME\",
|
||||
\"description\": \"A new version of kit has been released!\",
|
||||
\"color\": 5814783,
|
||||
\"fields\": [
|
||||
{
|
||||
\"name\": \"Version\",
|
||||
\"value\": \"$TAG_NAME\",
|
||||
\"inline\": true
|
||||
},
|
||||
{
|
||||
\"name\": \"Repository\",
|
||||
\"value\": \"[kit](https://github.com/${{ github.repository }})\",
|
||||
\"inline\": true
|
||||
}
|
||||
],
|
||||
\"footer\": {
|
||||
\"text\": \"Released via GitHub Actions\"
|
||||
},
|
||||
\"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)\",
|
||||
\"url\": \"$RELEASE_URL\"
|
||||
}]
|
||||
}" \
|
||||
$DISCORD_WEBHOOK
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
.aider*
|
||||
.task/
|
||||
.env
|
||||
.kit/*
|
||||
!.kit/extensions/
|
||||
!.kit/prompts/
|
||||
aidocs/
|
||||
*.log
|
||||
/kit
|
||||
.idea
|
||||
build/
|
||||
dist/
|
||||
contribute/output/
|
||||
CONTEXT.md
|
||||
output/
|
||||
.agents/
|
||||
skills-lock.json
|
||||
@@ -0,0 +1,9 @@
|
||||
version: "2"
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- modernize
|
||||
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
@@ -0,0 +1,70 @@
|
||||
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
|
||||
version: 2
|
||||
|
||||
builds:
|
||||
- main: ./cmd/kit
|
||||
binary: kit
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- darwin
|
||||
- linux
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -X main.version={{.Version}}
|
||||
- -X main.commit={{.Commit}}
|
||||
- -X main.date={{.Date}}
|
||||
|
||||
archives:
|
||||
- formats: [tar.gz]
|
||||
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
formats: [zip]
|
||||
|
||||
checksum:
|
||||
name_template: checksums.txt
|
||||
|
||||
release:
|
||||
header: |
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# Bun / npm
|
||||
bun add -g @mark3labs/kit
|
||||
# or: npm install -g @mark3labs/kit
|
||||
|
||||
# Go install (requires Go 1.26+)
|
||||
go install github.com/mark3labs/kit/cmd/kit@latest
|
||||
|
||||
# Or download from assets below
|
||||
```
|
||||
|
||||
changelog:
|
||||
sort: asc
|
||||
use: github
|
||||
filters:
|
||||
exclude:
|
||||
- "^Merge"
|
||||
- "^merge"
|
||||
- "^wip"
|
||||
- "^WIP"
|
||||
groups:
|
||||
- title: "Features"
|
||||
regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$'
|
||||
order: 0
|
||||
- title: "Bug Fixes"
|
||||
regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$'
|
||||
order: 1
|
||||
- title: "Documentation"
|
||||
regexp: '^.*?docs(\([[:word:]]+\))??!?:.+$'
|
||||
order: 2
|
||||
- title: "Maintenance"
|
||||
regexp: '^.*?(chore|refactor|style|ci|build)(\([[:word:]]+\))??!?:.+$'
|
||||
order: 3
|
||||
- title: "Other Changes"
|
||||
order: 999
|
||||
@@ -0,0 +1 @@
|
||||
{"Created":"2026-02-25T21:53:09.725157606Z","name":"iteratr_events","subjects":["iteratr.\u003e"],"retention":"limits","max_consumers":-1,"max_msgs":-1,"max_bytes":-1,"max_age":2592000000000000,"max_msgs_per_subject":-1,"max_msg_size":-1,"discard":"old","storage":"file","num_replicas":1,"duplicate_window":120000000000,"compression":"none","allow_direct":false,"mirror_direct":false,"sealed":false,"deny_delete":false,"deny_purge":false,"allow_rollup_hdrs":false,"consumer_limits":{}}
|
||||
@@ -0,0 +1 @@
|
||||
0d94ed0d599988d1
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
{"Created":"0001-01-01T00:00:00Z","Name":"","name":"7UfQJgqo","deliver_policy":"all","ack_policy":"explicit","ack_wait":30000000000,"max_deliver":-1,"filter_subject":"iteratr.unified-bubbletea-architecture.\u003e","replay_policy":"instant","max_waiting":512,"max_ack_pending":1000,"inactive_threshold":5000000000,"num_replicas":0}
|
||||
@@ -0,0 +1 @@
|
||||
0a75862a3a998ab5
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
{"Created":"0001-01-01T00:00:00Z","Name":"","name":"EnXXTfbX","deliver_policy":"all","ack_policy":"explicit","ack_wait":30000000000,"max_deliver":-1,"filter_subject":"iteratr.unified-bubbletea-architecture.\u003e","replay_policy":"instant","max_waiting":512,"max_ack_pending":1000,"inactive_threshold":5000000000,"num_replicas":0}
|
||||
@@ -0,0 +1 @@
|
||||
3288f873d6615ff9
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
39825
|
||||
@@ -0,0 +1,228 @@
|
||||
//go:build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"kit/ext"
|
||||
)
|
||||
|
||||
const (
|
||||
diagnosticsTimeout = 20 * time.Second
|
||||
maxOutputBytes = 12_000
|
||||
)
|
||||
|
||||
type toolPathInput struct {
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
type lintResult struct {
|
||||
Output string
|
||||
Err error
|
||||
}
|
||||
|
||||
func Init(api ext.API) {
|
||||
api.OnSessionStart(func(_ ext.SessionStartEvent, ctx ext.Context) {
|
||||
ctx.Print("go-edit-lint extension loaded - will run gopls and golangci-lint on Go file edits")
|
||||
})
|
||||
|
||||
api.OnToolResult(func(e ext.ToolResultEvent, ctx ext.Context) *ext.ToolResultResult {
|
||||
if e.IsError || !isEditOrWrite(e.ToolName) {
|
||||
return nil
|
||||
}
|
||||
|
||||
absPath, ok := resolveGoFilePath(e.Input, ctx.CWD)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
report := runGoDiagnostics(ctx.CWD, absPath)
|
||||
|
||||
// Check if there are issues and add explicit prompt for the LLM to react
|
||||
goplsIssues, lintIssues := countIssues(report)
|
||||
hasIssues := goplsIssues > 0 || lintIssues > 0
|
||||
|
||||
var enhanced string
|
||||
if hasIssues {
|
||||
enhanced = e.Content + "\n\n" + report + "\n\n⚠️ DIAGNOSTICS FOUND: Please review the issues above and fix them before proceeding."
|
||||
} else {
|
||||
enhanced = e.Content + "\n\n" + report
|
||||
}
|
||||
|
||||
// Show TUI message block for diagnostics visibility (only if there are issues)
|
||||
if hasIssues {
|
||||
var msgLines []string
|
||||
msgLines = append(msgLines, fmt.Sprintf("File: %s", filepath.Base(absPath)))
|
||||
if goplsIssues > 0 {
|
||||
msgLines = append(msgLines, fmt.Sprintf("gopls: %d issue(s)", goplsIssues))
|
||||
}
|
||||
if lintIssues > 0 {
|
||||
msgLines = append(msgLines, fmt.Sprintf("golangci-lint: %d issue(s)", lintIssues))
|
||||
}
|
||||
msgLines = append(msgLines, "", "⚠️ Please fix these issues before proceeding.")
|
||||
|
||||
borderColor := "#f9e2af" // yellow
|
||||
if goplsIssues > 0 && lintIssues > 0 {
|
||||
borderColor = "#f38ba8" // red
|
||||
}
|
||||
|
||||
ctx.PrintBlock(ext.PrintBlockOpts{
|
||||
Text: strings.Join(msgLines, "\n"),
|
||||
BorderColor: borderColor,
|
||||
Subtitle: "go-edit-lint",
|
||||
})
|
||||
}
|
||||
|
||||
return &ext.ToolResultResult{Content: &enhanced}
|
||||
})
|
||||
}
|
||||
|
||||
func isEditOrWrite(toolName string) bool {
|
||||
return strings.EqualFold(toolName, "edit") || strings.EqualFold(toolName, "write")
|
||||
}
|
||||
|
||||
func resolveGoFilePath(inputJSON, cwd string) (string, bool) {
|
||||
var args toolPathInput
|
||||
if err := json.Unmarshal([]byte(inputJSON), &args); err != nil || args.Path == "" {
|
||||
return "", false
|
||||
}
|
||||
|
||||
absPath := args.Path
|
||||
if !filepath.IsAbs(absPath) {
|
||||
absPath = filepath.Join(cwd, absPath)
|
||||
}
|
||||
|
||||
if strings.ToLower(filepath.Ext(absPath)) != ".go" {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return absPath, true
|
||||
}
|
||||
|
||||
func runGoDiagnostics(cwd, absPath string) string {
|
||||
gopls := runGopls(cwd, absPath)
|
||||
lint := runGolangCILint(cwd, "./...")
|
||||
|
||||
return fmt.Sprintf(
|
||||
"<go_diagnostics file=%q>\n[gopls]\n%s\n\n[golangci-lint]\n%s\n</go_diagnostics>",
|
||||
filepath.Base(absPath),
|
||||
formatToolResult(gopls, "No diagnostics."),
|
||||
formatToolResult(lint, "No lint issues."),
|
||||
)
|
||||
}
|
||||
|
||||
func runGopls(cwd, absPath string) lintResult {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), diagnosticsTimeout)
|
||||
defer cancel()
|
||||
|
||||
cmd := exec.CommandContext(ctx, "gopls", "check", absPath)
|
||||
cmd.Dir = cwd
|
||||
out, err := cmd.CombinedOutput()
|
||||
|
||||
if ctx.Err() == context.DeadlineExceeded {
|
||||
return lintResult{Err: fmt.Errorf("timed out after %s", diagnosticsTimeout)}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return lintResult{Output: truncate(string(out), maxOutputBytes), Err: fmt.Errorf("failed to run gopls check: %w", err)}
|
||||
}
|
||||
|
||||
return lintResult{Output: truncate(string(out), maxOutputBytes)}
|
||||
}
|
||||
|
||||
func runGolangCILint(cwd, target string) lintResult {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), diagnosticsTimeout)
|
||||
defer cancel()
|
||||
|
||||
args := []string{
|
||||
"run",
|
||||
target,
|
||||
"--show-stats=false",
|
||||
"--output.text.path", "stdout",
|
||||
"--output.text.colors=false",
|
||||
"--output.text.print-issued-lines=false",
|
||||
}
|
||||
cmd := exec.CommandContext(ctx, "golangci-lint", args...)
|
||||
cmd.Dir = cwd
|
||||
out, err := cmd.CombinedOutput()
|
||||
|
||||
if ctx.Err() == context.DeadlineExceeded {
|
||||
return lintResult{Err: fmt.Errorf("timed out after %s", diagnosticsTimeout)}
|
||||
}
|
||||
|
||||
trimmed := truncate(string(out), maxOutputBytes)
|
||||
if err == nil {
|
||||
return lintResult{Output: trimmed}
|
||||
}
|
||||
|
||||
exitErr, ok := err.(*exec.ExitError)
|
||||
if ok && exitErr.ExitCode() == 1 {
|
||||
return lintResult{Output: trimmed}
|
||||
}
|
||||
|
||||
return lintResult{Output: trimmed, Err: fmt.Errorf("failed to run golangci-lint: %w", err)}
|
||||
}
|
||||
|
||||
func formatToolResult(res lintResult, emptyFallback string) string {
|
||||
var lines []string
|
||||
if res.Err != nil {
|
||||
lines = append(lines, "ERROR: "+res.Err.Error())
|
||||
}
|
||||
out := strings.TrimSpace(res.Output)
|
||||
if out == "" {
|
||||
if res.Err == nil {
|
||||
lines = append(lines, emptyFallback)
|
||||
}
|
||||
} else {
|
||||
lines = append(lines, out)
|
||||
}
|
||||
if len(lines) == 0 {
|
||||
return emptyFallback
|
||||
}
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
func truncate(s string, max int) string {
|
||||
if len(s) <= max {
|
||||
return s
|
||||
}
|
||||
return s[:max] + "\n... output truncated ..."
|
||||
}
|
||||
|
||||
func countIssues(report string) (goplsCount, lintCount int) {
|
||||
// Extract gopls section
|
||||
goplsStart := strings.Index(report, "[gopls]")
|
||||
lintStart := strings.Index(report, "[golangci-lint]")
|
||||
endTag := strings.Index(report, "</go_diagnostics>")
|
||||
|
||||
if goplsStart != -1 && lintStart != -1 {
|
||||
goplsSection := report[goplsStart:lintStart]
|
||||
// Count non-empty lines excluding the header and "No diagnostics." message
|
||||
for _, line := range strings.Split(goplsSection, "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if line != "" && line != "[gopls]" && line != "No diagnostics." {
|
||||
goplsCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if lintStart != -1 && endTag != -1 {
|
||||
lintSection := report[lintStart:endTag]
|
||||
// Count non-empty lines excluding the header and "No lint issues." message
|
||||
for _, line := range strings.Split(lintSection, "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if line != "" && line != "[golangci-lint]" && line != "No lint issues." {
|
||||
lintCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return goplsCount, lintCount
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
//go:build ignore
|
||||
|
||||
// subagent-monitor — live horizontal widget strip for spawned subagents
|
||||
//
|
||||
// Subscribes to subagents spawned by the main Kit agent and displays a
|
||||
// single widget just above the input box. Each subagent occupies one column
|
||||
// in a side-by-side horizontal layout. Columns show scrolling real-time
|
||||
// output as the subagent works. When a subagent finishes its column is
|
||||
// removed automatically.
|
||||
//
|
||||
// Yaegi-safe design notes:
|
||||
// - No sync.Mutex (Yaegi has reflection issues with sync primitives)
|
||||
// - No channels in maps (Yaegi panics on range over map[string]chan)
|
||||
// - All ctx.* calls guarded with nil checks
|
||||
// - Simple data structures only
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"kit/ext"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Per-subagent state
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
type submonEntry struct {
|
||||
id int
|
||||
callID string
|
||||
task string
|
||||
lines []string
|
||||
started time.Time
|
||||
elapsed time.Duration
|
||||
}
|
||||
|
||||
const (
|
||||
submonColWidth = 34 // visible character width per column
|
||||
submonMaxLines = 5 // scrolling output lines per column
|
||||
submonColGap = 2 // spaces between columns
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Package-level state - all simple types
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
var (
|
||||
submonCtx ext.Context
|
||||
submonHasCtx bool
|
||||
submonEntries []*submonEntry
|
||||
submonNextID int
|
||||
)
|
||||
|
||||
func submonInit() {
|
||||
submonEntries = nil
|
||||
submonNextID = 1
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// String helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func submonPad(s string, w int) string {
|
||||
r := []rune(s)
|
||||
if len(r) >= w {
|
||||
return string(r[:w])
|
||||
}
|
||||
return s + strings.Repeat(" ", w-len(r))
|
||||
}
|
||||
|
||||
func submonTrunc(s string, w int) string {
|
||||
r := []rune(s)
|
||||
if len(r) <= w {
|
||||
return s
|
||||
}
|
||||
if w <= 1 {
|
||||
return "…"
|
||||
}
|
||||
return string(r[:w-1]) + "…"
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Widget rendering
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func submonRenderColumn(e *submonEntry) []string {
|
||||
var rows []string
|
||||
|
||||
// Calculate elapsed time on-demand to avoid race conditions with ticker
|
||||
elapsed := e.elapsed
|
||||
if elapsed == 0 && !e.started.IsZero() {
|
||||
elapsed = time.Since(e.started)
|
||||
}
|
||||
secs := int(elapsed.Seconds())
|
||||
timeStr := fmt.Sprintf("%ds", secs)
|
||||
taskMax := submonColWidth - len(timeStr) - 3
|
||||
taskPart := submonTrunc(e.task, taskMax)
|
||||
header := fmt.Sprintf("#%d %s %s", e.id, taskPart, timeStr)
|
||||
rows = append(rows, submonPad(header, submonColWidth))
|
||||
|
||||
display := e.lines
|
||||
if len(display) > submonMaxLines {
|
||||
display = display[len(display)-submonMaxLines:]
|
||||
}
|
||||
for _, l := range display {
|
||||
rows = append(rows, submonPad(" "+submonTrunc(l, submonColWidth-2), submonColWidth))
|
||||
}
|
||||
for len(rows) < submonMaxLines+1 {
|
||||
if len(rows) == 1 && len(e.lines) == 0 {
|
||||
rows = append(rows, submonPad(" waiting…", submonColWidth))
|
||||
} else {
|
||||
rows = append(rows, strings.Repeat(" ", submonColWidth))
|
||||
}
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
func submonBuildWidget() string {
|
||||
if len(submonEntries) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
numCols := len(submonEntries)
|
||||
numRows := submonMaxLines + 1
|
||||
cols := make([][]string, numCols)
|
||||
for i, e := range submonEntries {
|
||||
rows := submonRenderColumn(e)
|
||||
col := make([]string, numRows)
|
||||
for j := 0; j < numRows; j++ {
|
||||
if j < len(rows) {
|
||||
col[j] = rows[j]
|
||||
} else {
|
||||
col[j] = strings.Repeat(" ", submonColWidth)
|
||||
}
|
||||
}
|
||||
cols[i] = col
|
||||
}
|
||||
|
||||
gap := strings.Repeat(" ", submonColGap)
|
||||
var sb strings.Builder
|
||||
for row := 0; row < numRows; row++ {
|
||||
for ci := range cols {
|
||||
if ci > 0 {
|
||||
sb.WriteString(gap)
|
||||
}
|
||||
sb.WriteString(cols[ci][row])
|
||||
}
|
||||
if row < numRows-1 {
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func submonPushWidget() {
|
||||
if !submonHasCtx {
|
||||
return
|
||||
}
|
||||
if submonCtx.SetWidget == nil {
|
||||
return
|
||||
}
|
||||
|
||||
text := submonBuildWidget()
|
||||
if len(submonEntries) == 0 {
|
||||
if submonCtx.RemoveWidget != nil {
|
||||
submonCtx.RemoveWidget("submon")
|
||||
}
|
||||
return
|
||||
}
|
||||
submonCtx.SetWidget(ext.WidgetConfig{
|
||||
ID: "submon",
|
||||
Placement: ext.WidgetAbove,
|
||||
Content: ext.WidgetContent{Text: text},
|
||||
Style: ext.WidgetStyle{BorderColor: "#89b4fa"},
|
||||
Priority: 0,
|
||||
})
|
||||
}
|
||||
|
||||
func submonAppendLine(e *submonEntry, line string) {
|
||||
line = strings.TrimRight(line, "\r\n")
|
||||
if strings.TrimSpace(line) == "" {
|
||||
return
|
||||
}
|
||||
e.lines = append(e.lines, line)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Init
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func Init(api ext.API) {
|
||||
submonInit()
|
||||
|
||||
api.OnSessionStart(func(_ ext.SessionStartEvent, ctx ext.Context) {
|
||||
submonCtx = ctx
|
||||
submonHasCtx = true
|
||||
submonInit()
|
||||
if ctx.RemoveWidget != nil {
|
||||
ctx.RemoveWidget("submon")
|
||||
}
|
||||
})
|
||||
|
||||
api.OnAgentEnd(func(_ ext.AgentEndEvent, ctx ext.Context) {
|
||||
submonCtx = ctx
|
||||
submonHasCtx = true
|
||||
})
|
||||
|
||||
// ── SubagentStart ────────────────────────────────────────────────────────
|
||||
api.OnSubagentStart(func(e ext.SubagentStartEvent, ctx ext.Context) {
|
||||
submonCtx = ctx
|
||||
submonHasCtx = true
|
||||
|
||||
id := submonNextID
|
||||
submonNextID++
|
||||
entry := &submonEntry{
|
||||
id: id,
|
||||
callID: e.ToolCallID,
|
||||
task: e.Task,
|
||||
started: time.Now(),
|
||||
}
|
||||
submonEntries = append(submonEntries, entry)
|
||||
|
||||
submonPushWidget()
|
||||
})
|
||||
|
||||
// ── SubagentChunk ────────────────────────────────────────────────────────
|
||||
api.OnSubagentChunk(func(e ext.SubagentChunkEvent, ctx ext.Context) {
|
||||
submonCtx = ctx
|
||||
submonHasCtx = true
|
||||
|
||||
var entry *submonEntry
|
||||
for _, en := range submonEntries {
|
||||
if en.callID == e.ToolCallID {
|
||||
entry = en
|
||||
break
|
||||
}
|
||||
}
|
||||
if entry == nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch e.ChunkType {
|
||||
case "text":
|
||||
for _, line := range strings.Split(e.Content, "\n") {
|
||||
submonAppendLine(entry, line)
|
||||
}
|
||||
case "tool_call":
|
||||
submonAppendLine(entry, "→ "+e.ToolName)
|
||||
case "tool_execution_start":
|
||||
submonAppendLine(entry, "⚙ "+e.ToolName)
|
||||
case "tool_result":
|
||||
if e.IsError {
|
||||
submonAppendLine(entry, "✗ "+e.ToolName)
|
||||
} else {
|
||||
submonAppendLine(entry, "✓ "+e.ToolName)
|
||||
}
|
||||
}
|
||||
|
||||
submonPushWidget()
|
||||
})
|
||||
|
||||
// ── SubagentEnd ──────────────────────────────────────────────────────────
|
||||
api.OnSubagentEnd(func(e ext.SubagentEndEvent, ctx ext.Context) {
|
||||
submonCtx = ctx
|
||||
submonHasCtx = true
|
||||
|
||||
var entry *submonEntry
|
||||
for _, en := range submonEntries {
|
||||
if en.callID == e.ToolCallID {
|
||||
entry = en
|
||||
break
|
||||
}
|
||||
}
|
||||
if entry != nil {
|
||||
entry.elapsed = time.Since(entry.started)
|
||||
if e.ErrorMsg != "" {
|
||||
submonAppendLine(entry, "✗ "+submonTrunc(e.ErrorMsg, submonColWidth-2))
|
||||
}
|
||||
}
|
||||
|
||||
submonPushWidget()
|
||||
|
||||
// Remove the entry immediately (no goroutine to avoid races)
|
||||
newEntries := submonEntries[:0]
|
||||
for _, en := range submonEntries {
|
||||
if en.callID != e.ToolCallID {
|
||||
newEntries = append(newEntries, en)
|
||||
}
|
||||
}
|
||||
submonEntries = newEntries
|
||||
submonPushWidget()
|
||||
})
|
||||
|
||||
// ── SessionShutdown ──────────────────────────────────────────────────────
|
||||
api.OnSessionShutdown(func(_ ext.SessionShutdownEvent, ctx ext.Context) {
|
||||
submonInit()
|
||||
// Guard ctx access - may be nil during shutdown
|
||||
if ctx.RemoveWidget != nil {
|
||||
ctx.RemoveWidget("submon")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
description: Run ACP smoke test against opencode/kimi-k2.5 to verify JSON-RPC stdio works
|
||||
---
|
||||
|
||||
Run the ACP smoke test to verify the Kit ACP server works correctly over JSON-RPC stdio with streaming responses.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Build the kit binary:
|
||||
```bash
|
||||
go build -o output/kit ./cmd/kit
|
||||
```
|
||||
|
||||
2. Run the smoke test Python script against opencode/kimi-k2.5:
|
||||
```bash
|
||||
python3 scripts/acp_smoke_test.py
|
||||
```
|
||||
|
||||
3. Verify the output shows:
|
||||
- `session/new` returns a valid `sessionId`
|
||||
- `session/prompt` streams `agent_thought_chunk` notifications (reasoning)
|
||||
- `session/prompt` streams `agent_message_chunk` notifications (response)
|
||||
- Final result has `stopReason: "end_turn"`
|
||||
- `✓ SMOKE TEST PASSED` at the end
|
||||
|
||||
4. If the test fails, check:
|
||||
- `output/kit` binary exists and is executable
|
||||
- `OPENCODE_API_KEY` or `OPENCODE_ZEN_API_KEY` environment variable is set
|
||||
- `scripts/acp_smoke_test.py` exists
|
||||
- The model `opencode/kimi-k2.5` is available (`kit models opencode | grep kimi-k2.5`)
|
||||
|
||||
5. For testing with a different model, edit the script or set the `MODEL` variable:
|
||||
```bash
|
||||
MODEL=anthropic/claude-sonnet-4-5 python3 scripts/acp_smoke_test.py
|
||||
```
|
||||
|
||||
The smoke test exercises the full ACP protocol: session lifecycle, streaming notifications, and tool-free prompt completion.
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
description: Stage, commit, and push changes with an auto-generated conventional commit message
|
||||
---
|
||||
|
||||
Review the current git status and diff, then stage all changes, write a concise conventional commit message, commit, and push to the current branch.
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Check status**: `git status` — understand what has changed
|
||||
2. **Review the diff**: `git diff` (and `git diff --cached` if anything is already staged) — read the actual changes
|
||||
3. **Stage everything**: `git add -A`
|
||||
4. **Craft the commit message** following Conventional Commits:
|
||||
- Format: `<type>(<scope>): <short summary>`
|
||||
- Types: `feat`, `fix`, `refactor`, `chore`, `docs`, `test`, `perf`, `build`
|
||||
- Scope: optional, the subsystem affected (e.g. `ui`, `cmd`, `config`)
|
||||
- Summary: imperative mood, lowercase, no trailing period, ≤72 chars
|
||||
- Body: add a blank line then bullet points for non-trivial changes
|
||||
- Do **not** include "Generated by" or similar noise
|
||||
5. **Commit**: `git commit -m "<message>"`
|
||||
6. **Push**: `git push`
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Read the actual diff — do not guess from filenames alone
|
||||
- Prefer one well-scoped commit; do not split unless the changes are clearly unrelated
|
||||
- Keep the subject line under 72 characters
|
||||
- Use the body to explain *what* and *why*, not *how*
|
||||
- If there is nothing to commit, say so and stop
|
||||
|
||||
$@
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
description: Scaffold a new prompt template in .kit/prompts/
|
||||
---
|
||||
|
||||
Create a new kit prompt template. The user wants a prompt that does: $@
|
||||
|
||||
## What a prompt template is
|
||||
|
||||
A prompt template is a `.md` file in `.kit/prompts/` (project-local) or `~/.kit/prompts/` (global).
|
||||
It becomes a `/slug` slash command in the kit input box — typed as `/filename` with optional arguments.
|
||||
|
||||
## File format
|
||||
|
||||
```
|
||||
---
|
||||
description: One-line description shown in autocomplete
|
||||
---
|
||||
|
||||
Body text of the prompt. Use $@ for all user-supplied arguments,
|
||||
$1 $2 etc. for positional arguments.
|
||||
```
|
||||
|
||||
- **Filename** → slug: `commit-push.md` becomes `/commit-push`
|
||||
- **Frontmatter**: only `description` is recognised; keep it under ~80 chars
|
||||
- **Body**: plain markdown; the full text is submitted as the user's message when the template fires
|
||||
- **Arguments**: `$@` expands to everything the user typed after the slash command name;
|
||||
`$1`, `$2` for individual positional args; omit entirely if no arguments are needed
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Understand the workflow** the user described in `$@` — ask a clarifying question if the intent is ambiguous
|
||||
2. **Choose a filename**: short, lowercase, hyphen-separated, descriptive (e.g. `code-review.md`)
|
||||
3. **Write the description**: one sentence, imperative, fits in autocomplete
|
||||
4. **Draft the body**:
|
||||
- Open with a single sentence stating the goal
|
||||
- Use `## Steps` for multi-step workflows; use plain prose for simple prompts
|
||||
- Be specific: name commands, flags, and file paths where relevant
|
||||
- End with `$@` on its own line if the user might want to pass context or a hint; omit if the prompt is self-contained
|
||||
5. **Write the file** to `.kit/prompts/<slug>.md`
|
||||
6. **Confirm** by showing the final file content and the slash command that activates it
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Keep prompts action-oriented — they should tell kit *what to do*, not just *what to think about*
|
||||
- Prefer concrete steps over vague instructions
|
||||
- A prompt that does one thing well beats one that tries to cover every edge case
|
||||
- If the workflow already exists as a prompt, suggest extending it instead of duplicating
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
description: Semantic version tagging workflow - analyzes commits and tags releases
|
||||
---
|
||||
|
||||
# Release Tagging Workflow
|
||||
|
||||
Tag a new version of this Go project following semantic versioning.
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Fetch remote tags**: `git fetch --tags origin`
|
||||
|
||||
2. **Find latest version**: `git tag -l | sort -V | tail -5` to see recent tags
|
||||
|
||||
3. **Analyze changes since last tag**:
|
||||
- `git log <latest-tag>..HEAD --oneline` - list commits
|
||||
- `git diff <latest-tag>..HEAD --stat` - see file stats
|
||||
- `git diff <latest-tag>..HEAD --name-only` - see changed files
|
||||
|
||||
4. **Determine version bump** (Semantic Versioning):
|
||||
- **MAJOR (X.0.0)**: Breaking API changes, incompatible modifications
|
||||
- **MINOR (0.X.0)**: New features, backward-compatible additions
|
||||
- **PATCH (0.0.X)**: Bug fixes, backward-compatible fixes
|
||||
|
||||
Look for indicators:
|
||||
- `feat:` or `feature:` commits → MINOR
|
||||
- `fix:` or `bugfix:` commits → PATCH
|
||||
- `breaking:` or `BREAKING CHANGE:` → MAJOR
|
||||
- Breaking API changes in `pkg/` or public interfaces → MAJOR
|
||||
- New commands, flags, or features → MINOR
|
||||
- Documentation-only changes → PATCH (or skip)
|
||||
|
||||
5. **Calculate new version**: Increment appropriate segment, reset lower segments to 0
|
||||
|
||||
6. **Draft tag message**:
|
||||
- Summarize key changes from commits
|
||||
- Group by type (Features, Fixes, Breaking Changes)
|
||||
- Keep concise but informative
|
||||
|
||||
7. **Create annotated tag**: `git tag -a vX.Y.Z -m "vX.Y.Z - <summary>\n\n<detailed list>"`
|
||||
|
||||
8. **Push tag**: `git push origin vX.Y.Z`
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Always fetch remote tags first to avoid conflicts
|
||||
- Use annotated tags (`-a`) with descriptive messages
|
||||
- Follow semver strictly - when in doubt, prefer conservative bump (patch over minor)
|
||||
- For Go projects, changes to `pkg/` or exported APIs warrant careful version consideration
|
||||
- If no changes since last tag, suggest skipping the release
|
||||
- Include commit summaries in the tag message body
|
||||
|
||||
## Example Tag Message Format
|
||||
|
||||
```
|
||||
v0.30.1 - Bug fixes for model handling and UI improvements
|
||||
|
||||
Fixes:
|
||||
- Properly handle think tags from Qwen/DeepSeek models
|
||||
- Handle custom provider model persistence and bare model names
|
||||
|
||||
Improvements:
|
||||
- UI style refactoring and cleanup
|
||||
```
|
||||
|
||||
Wait for the user to confirm the version and message before executing tag commands.
|
||||
|
||||
---
|
||||
|
||||
$@
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"permission": {
|
||||
"external_directory": {
|
||||
"~/go/**": "deny"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
# KIT Agent Guidelines
|
||||
|
||||
## Build/Test Commands
|
||||
- **Build**: `go build -o output/kit ./cmd/kit`
|
||||
- **Test all**: `go test -race ./...`
|
||||
- **Test single**: `go test -race ./cmd -run TestScriptExecution`
|
||||
- **Lint**: `go vet ./...`
|
||||
- **Format**: `go fmt ./...`
|
||||
|
||||
## Code Style
|
||||
- **Imports**: stdlib → third-party → local (blank lines between)
|
||||
- **Naming**: camelCase (unexported), PascalCase (exported)
|
||||
- **Errors**: Always check, wrap with `fmt.Errorf("context: %w", err)`
|
||||
- **Logging**: Use `github.com/charmbracelet/log` structured logging
|
||||
- **Types**: Prefer `any` over `interface{}`
|
||||
- **JSON**: snake_case tags with `omitempty` where appropriate
|
||||
- **Context**: First parameter for blocking operations
|
||||
|
||||
## Architecture
|
||||
- Multi-provider LLM support via `llm.Provider` interface
|
||||
- MCP client-server for tool integration
|
||||
- Builtin servers: bash, fetch, todo, fs
|
||||
- **Extension system** (`internal/extensions/`): Yaegi-interpreted Go, 13 lifecycle events, custom tools/commands/widgets/overlays/editor interceptors
|
||||
- **TUI** (`internal/ui/`): Bubble Tea v2 parent-child model (`AppModel` → `InputComponent`, `StreamComponent`, etc.)
|
||||
- **Decoupling pattern**: `cmd/root.go` has converter functions (e.g. `widgetProviderForUI()`) that bridge `internal/extensions/` types to `internal/ui/` types — the UI never imports extensions directly
|
||||
- **Public SDK** (`pkg/kit/`): The public-facing Go SDK for embedding Kit as a library. See rules below.
|
||||
|
||||
## Public SDK (`pkg/kit/`) Rules
|
||||
|
||||
`pkg/kit/` is the **public API surface** consumed by external Go developers. All exported symbols, types, function names, and godoc comments in this package are part of the SDK contract.
|
||||
|
||||
### No Dependency Name Leakage
|
||||
Internal dependency names (e.g. `charm.land/fantasy`, library-specific jargon) **must not** appear in:
|
||||
- **Exported function/method names** — use generic terms (`LLM`, `Provider`, `Message`) instead of library names
|
||||
- **Exported type names** — type aliases should use domain names (e.g. `LLMMessage`, not `FantasyMessage`)
|
||||
- **Godoc comments** on exported symbols — these are visible in `go doc` output and pkg.go.dev
|
||||
- **Struct field names and tags** on exported types
|
||||
|
||||
Using dependency types directly in **function bodies** (private implementation) is fine — that's invisible to SDK consumers.
|
||||
|
||||
### Naming Conventions for SDK Symbols
|
||||
- Type aliases re-exporting dependency types: use `LLM*` prefix (e.g. `LLMMessage`, `LLMUsage`, `LLMResponse`)
|
||||
- Conversion helpers: use `ConvertToLLM*` / `ConvertFromLLM*` (not the dependency name)
|
||||
- Provider queries: use `GetLLMProviders` (not `GetFantasyProviders`)
|
||||
- When wrapping internal methods, the `pkg/kit/` name should be dependency-agnostic even if the `internal/` method still uses the old name
|
||||
|
||||
### Deprecation Pattern
|
||||
When renaming a public SDK symbol, keep the old name as a deprecated wrapper for one release cycle:
|
||||
```go
|
||||
// Deprecated: Use NewName instead.
|
||||
func OldName() { return NewName() }
|
||||
```
|
||||
|
||||
## Key Patterns
|
||||
|
||||
### Yaegi (Extension Interpreter) Gotchas
|
||||
- **No interfaces across boundary**: All extension-facing API types must be concrete structs, never interfaces. Yaegi crashes on interface wrapper generation.
|
||||
- **Function field bug**: Named function references assigned to struct fields return zero values across the interpreter boundary. Always use anonymous closure literals:
|
||||
```go
|
||||
// WRONG: ctx.SetEditor(ext.EditorConfig{HandleKey: myHandler})
|
||||
// RIGHT: ctx.SetEditor(ext.EditorConfig{HandleKey: func(k, t string) ext.EditorKeyAction { return myHandler(k, t) }})
|
||||
```
|
||||
- **Symbol exports**: Every new type exposed to extensions must be added to `internal/extensions/symbols.go`
|
||||
|
||||
### BubbleTea Integration
|
||||
- **No `prog.Send()` from inside `Update()`**: Calling `prog.Send()` synchronously within a BubbleTea `Update()` handler deadlocks the event loop. Use `go appInstance.NotifyWidgetUpdate()` (async goroutine) instead.
|
||||
- **Height measurement**: `distributeHeight()` in `model.go` must measure using the same render path as `View()`. If an interceptor wraps rendering, measure with the wrapper too, or layout will mismatch.
|
||||
- **Channel-based prompts**: Extension prompt calls (PromptSelect, etc.) block on a `chan PromptResponse`. Extension slash commands run in dedicated goroutines (not `tea.Cmd`) to avoid stalling BubbleTea's Cmd scheduler.
|
||||
|
||||
### Extension State Management
|
||||
- **Thread-safe maps on Runner**: Widget/header/footer/editor state lives on the Runner with `sync.RWMutex`, queried by UI via callbacks
|
||||
- **Context function fields**: The `Context` struct uses function fields (`Print func(string)`, `SetWidget func(WidgetConfig)`) wired by closures in `cmd/root.go`
|
||||
- **Package-level vars in extensions**: Yaegi supports package-level variables captured in closures — this is how extensions maintain state across event callbacks
|
||||
|
||||
### Unicode in Widget Text
|
||||
- Widget content renders through `lipgloss.Style.Render()` which preserves ANSI escape codes
|
||||
- Use rune-based width calculations (`len([]rune(s))`) not byte length (`len(s)`) when aligning box-drawing characters or multi-byte symbols
|
||||
|
||||
## Testing
|
||||
|
||||
### Interactive TUI Testing with tmux
|
||||
Use tmux to test Kit interactively without blocking the agent:
|
||||
```bash
|
||||
tmux new-session -d -s kittest -x 120 -y 40 "output/kit -e examples/extensions/my-ext.go --no-session 2>kit_stderr.log"
|
||||
sleep 3
|
||||
tmux capture-pane -t kittest -p # read screen
|
||||
tmux send-keys -t kittest '/command' Enter # send input
|
||||
tmux kill-session -t kittest # cleanup
|
||||
```
|
||||
|
||||
### Non-Interactive Kit (Subprocess Spawning)
|
||||
Extensions can spawn Kit as a subprocess for sub-agent patterns:
|
||||
```bash
|
||||
kit --quiet --no-session --no-extensions --system-prompt /path/to/prompt.txt --model provider/model "question"
|
||||
```
|
||||
Positional args are the prompt. `@file` args attach file content. Key flags: `--quiet` (stdout only, no TUI), `--no-session` (ephemeral), `--no-extensions` (prevent recursive loading), `--system-prompt` (string or file path).
|
||||
|
||||
## External Repo Research
|
||||
- **ALWAYS use `btca`** to search external repos (e.g. iteratr, other reference codebases)
|
||||
- Never guess or manually search the filesystem for external projects
|
||||
- Example: `btca ask -r https://github.com/user/repo -q "How does X work?"`
|
||||
- See `.agents/skills/btca-cli/SKILL.md` for full btca usage
|
||||
|
||||
## BTCA Configured Resources
|
||||
The following external repositories are configured in `btca.config.jsonc` for research:
|
||||
|
||||
- bubbletea
|
||||
- lipgloss
|
||||
- bubbles
|
||||
- glamour
|
||||
- fantasy
|
||||
- catwalk
|
||||
- crush
|
||||
- pi
|
||||
- iteratr
|
||||
- yaegi
|
||||
- acp-go-sdk
|
||||
- opencode
|
||||
- herald
|
||||
- herald-md
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Mark III Labs, LLC.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,792 @@
|
||||
<p align="center">
|
||||
<img src="logo.jpg" alt="KIT" width="400">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/mark3labs/kit/actions/workflows/ci.yml"><img src="https://github.com/mark3labs/kit/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
||||
<a href="https://github.com/mark3labs/kit/releases/latest"><img src="https://img.shields.io/github/v/release/mark3labs/kit?style=flat&color=blue" alt="Release"></a>
|
||||
<a href="https://www.npmjs.com/package/@mark3labs/kit"><img src="https://img.shields.io/npm/v/@mark3labs/kit?style=flat&color=cb3837" alt="npm"></a>
|
||||
<a href="https://pkg.go.dev/github.com/mark3labs/kit"><img src="https://pkg.go.dev/badge/github.com/mark3labs/kit.svg" alt="Go Reference"></a>
|
||||
<a href="https://github.com/mark3labs/kit/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mark3labs/kit?style=flat" alt="License"></a>
|
||||
<a href="https://discord.gg/RqSS2NQVsY"><img src="https://img.shields.io/badge/Discord-community-5865F2?style=flat&logo=discord&logoColor=white" alt="Discord"></a>
|
||||
</p>
|
||||
|
||||
# KIT (Knowledge Inference Tool)
|
||||
|
||||
A powerful, extensible AI coding agent CLI with multi-provider support, built-in tools, and a rich extension system.
|
||||
|
||||
## Features
|
||||
|
||||
- **Multi-Provider LLM Support**: Anthropic, OpenAI, Google Gemini, Ollama, Azure OpenAI, AWS Bedrock, OpenRouter, and more
|
||||
- **Built-in Core Tools**: bash, read, write, edit, grep, find, ls, subagent - no MCP overhead
|
||||
- **MCP Integration**: Connect external MCP servers for expanded capabilities
|
||||
- **Extension System**: Write custom tools, commands, widgets, and UI modifications in Go
|
||||
- **Theming**: 22 built-in color themes (KITT, Catppuccin, Dracula, Nord, etc.) with runtime switching, persistence, and custom theme files
|
||||
- **Model Persistence**: Model and thinking level selections are automatically saved and restored across sessions
|
||||
- **Prompt Templates**: Create reusable prompt templates with shell-style argument substitution
|
||||
- **Interactive TUI**: Rich terminal interface powered by Bubble Tea with streaming, syntax highlighting, and custom rendering
|
||||
- **Session Management**: Tree-based conversation history with branching support
|
||||
- **Non-Interactive Mode**: Script-friendly positional args with JSON output
|
||||
- **ACP Server**: Run Kit as an [Agent Client Protocol](https://agentclientprotocol.com) agent over stdio
|
||||
- **Go SDK**: Embed Kit in your own applications
|
||||
|
||||
## Installation
|
||||
|
||||
### Using npm / bun / pnpm
|
||||
|
||||
```bash
|
||||
npm install -g @mark3labs/kit
|
||||
# or
|
||||
bun install -g @mark3labs/kit
|
||||
# or
|
||||
pnpm install -g @mark3labs/kit
|
||||
```
|
||||
|
||||
### Using Go
|
||||
|
||||
```bash
|
||||
go install github.com/mark3labs/kit/cmd/kit@latest
|
||||
```
|
||||
|
||||
### Building from source
|
||||
|
||||
```bash
|
||||
git clone https://github.com/mark3labs/kit.git
|
||||
cd kit
|
||||
go build -o kit ./cmd/kit
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```bash
|
||||
# Start interactive session
|
||||
kit
|
||||
|
||||
# Run a one-off prompt
|
||||
kit "List files in src/"
|
||||
|
||||
# Attach files as context
|
||||
kit @main.go @test.go "Review these files"
|
||||
|
||||
# Continue the most recent session
|
||||
kit --continue
|
||||
|
||||
# Model and thinking level selections are automatically persisted
|
||||
# across sessions and restored on next launch
|
||||
|
||||
# Use specific model
|
||||
kit --model anthropic/claude-sonnet-latest
|
||||
```
|
||||
|
||||
### Non-Interactive Mode
|
||||
|
||||
```bash
|
||||
# Get JSON output for scripting
|
||||
kit "Explain main.go" --json
|
||||
|
||||
# Quiet mode (final response only)
|
||||
kit "Run tests" --quiet
|
||||
|
||||
# Ephemeral mode (no session file)
|
||||
kit "Quick question" --no-session
|
||||
```
|
||||
|
||||
### ACP Server Mode
|
||||
|
||||
Kit can run as an [ACP (Agent Client Protocol)](https://agentclientprotocol.com) agent server, enabling ACP-compatible clients (such as [OpenCode](https://github.com/sst/opencode)) to drive Kit as a remote coding agent over stdio.
|
||||
|
||||
```bash
|
||||
# Start Kit as an ACP server (communicates via JSON-RPC 2.0 on stdin/stdout)
|
||||
kit acp
|
||||
|
||||
# With debug logging to stderr
|
||||
kit acp --debug
|
||||
```
|
||||
|
||||
The ACP server exposes Kit's full capabilities — LLM execution, tool calls (bash, read, write, edit, grep, etc.), and session persistence — over the standard ACP protocol. Sessions are persisted to Kit's normal JSONL session files, so they can be resumed later.
|
||||
|
||||
## Configuration
|
||||
|
||||
Kit looks for configuration in the following locations (in order of priority):
|
||||
|
||||
1. CLI flags
|
||||
2. Environment variables (with `KIT_` prefix)
|
||||
3. `./.kit.yml` / `./.kit.yaml` / `./.kit.json` (project-local)
|
||||
4. `~/.kit.yml` / `~/.kit.yaml` / `~/.kit.json` (global)
|
||||
|
||||
### Basic Configuration
|
||||
|
||||
Create `~/.kit.yml`:
|
||||
|
||||
```yaml
|
||||
model: anthropic/claude-sonnet-latest
|
||||
max-tokens: 4096
|
||||
temperature: 0.7
|
||||
stream: true
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
```bash
|
||||
export ANTHROPIC_API_KEY="sk-..."
|
||||
export OPENAI_API_KEY="sk-..."
|
||||
export KIT_MODEL="openai/gpt-4o"
|
||||
```
|
||||
|
||||
### MCP Server Configuration
|
||||
|
||||
Add external MCP servers to `.kit.yml`:
|
||||
|
||||
```yaml
|
||||
mcpServers:
|
||||
filesystem:
|
||||
type: local
|
||||
command: ["npx", "-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed"]
|
||||
environment:
|
||||
LOG_LEVEL: "info"
|
||||
allowedTools: ["read_file", "write_file"]
|
||||
|
||||
search:
|
||||
type: remote
|
||||
url: "https://mcp.example.com/search"
|
||||
```
|
||||
|
||||
## CLI Reference
|
||||
|
||||
### Global Flags
|
||||
|
||||
```bash
|
||||
# Model and provider
|
||||
--model, -m Model to use (provider/model format)
|
||||
--provider-api-key API key for the provider
|
||||
--provider-url Base URL for provider API
|
||||
--tls-skip-verify Skip TLS certificate verification
|
||||
|
||||
# Session management
|
||||
--session, -s Open specific JSONL session file
|
||||
--continue, -c Resume most recent session for current directory
|
||||
--resume, -r Interactive session picker
|
||||
--no-session Ephemeral mode, no persistence
|
||||
|
||||
# Behavior (non-interactive: pass prompt as positional arg)
|
||||
--quiet Suppress all output (non-interactive only)
|
||||
--json Output response as JSON (non-interactive only)
|
||||
--no-exit Enter interactive mode after prompt completes
|
||||
--max-steps Maximum agent steps (0 for unlimited)
|
||||
--stream Enable streaming output (default: true)
|
||||
--compact Enable compact output mode
|
||||
--auto-compact Auto-compact conversation near context limit
|
||||
|
||||
# Extensions
|
||||
--extension, -e Load additional extension file(s) (repeatable)
|
||||
--no-extensions Disable all extensions
|
||||
--prompt-template Load a specific prompt template by name
|
||||
--no-prompt-templates Disable prompt template loading
|
||||
|
||||
# Generation parameters
|
||||
--max-tokens Maximum tokens in response (default: 4096)
|
||||
--temperature Randomness 0.0-1.0 (default: 0.7)
|
||||
--top-p Nucleus sampling 0.0-1.0 (default: 0.95)
|
||||
--top-k Limit top K tokens (default: 40)
|
||||
--stop-sequences Custom stop sequences (comma-separated)
|
||||
--thinking-level Extended thinking level: off, minimal, low, medium, high (default: off)
|
||||
|
||||
# System
|
||||
--config Config file path (default: ~/.kit.yml)
|
||||
--system-prompt System prompt text or file path
|
||||
--debug Enable debug logging
|
||||
```
|
||||
|
||||
### Commands
|
||||
|
||||
```bash
|
||||
# Authentication (for OAuth-enabled providers)
|
||||
kit auth login [provider] # Start OAuth flow (e.g., anthropic)
|
||||
kit auth logout [provider] # Remove credentials for provider
|
||||
kit auth status # Check authentication status
|
||||
|
||||
# Model database
|
||||
kit models [provider] # List available models (optionally filter by provider)
|
||||
kit models --all # Show all providers (not just LLM-compatible)
|
||||
kit update-models [source] # Update model database (from models.dev, URL, file, or 'embedded')
|
||||
|
||||
# Extension management
|
||||
kit extensions list # List discovered extensions
|
||||
kit extensions validate # Validate extension files
|
||||
kit extensions init # Generate example extension template
|
||||
kit install <git-url> # Install extensions from git repositories
|
||||
kit install -l <git-url> # Install to project-local .kit/git/ directory
|
||||
kit install -u <git-url> # Update an already-installed package
|
||||
kit install --uninstall <pkg> # Remove an installed package
|
||||
|
||||
# Skills
|
||||
kit skill # Install the Kit extensions skill via skills.sh
|
||||
|
||||
# ACP server
|
||||
kit acp # Start as ACP agent (stdio JSON-RPC)
|
||||
kit acp --debug # With debug logging to stderr
|
||||
```
|
||||
|
||||
## Themes
|
||||
|
||||
Kit ships with 22 built-in color themes that control all UI elements. Switch at runtime:
|
||||
|
||||
```
|
||||
/theme dracula
|
||||
/theme catppuccin
|
||||
/theme tokyonight
|
||||
```
|
||||
|
||||
Theme selections are automatically saved and restored on next launch (stored in `~/.config/kit/preferences.yml`). This persistence also applies to **model** and **thinking level** selections — all are saved together and restored on startup.
|
||||
|
||||
### Custom themes
|
||||
|
||||
Drop a `.yml` file in `~/.config/kit/themes/` (user) or `.kit/themes/` (project):
|
||||
|
||||
```yaml
|
||||
# ~/.config/kit/themes/my-theme.yml
|
||||
primary:
|
||||
light: "#8839ef"
|
||||
dark: "#cba6f7"
|
||||
success:
|
||||
light: "#40a02b"
|
||||
dark: "#a6e3a1"
|
||||
```
|
||||
|
||||
Built-in themes: `kitt`, `catppuccin`, `dracula`, `tokyonight`, `nord`, `gruvbox`, `monokai`, `solarized`, `github`, `one-dark`, `rose-pine`, `ayu`, `material`, `everforest`, `kanagawa`, `amoled`, `synthwave`, `vesper`, `flexoki`, `matrix`, `vercel`, `zenburn`
|
||||
|
||||
## Extension System
|
||||
|
||||
Extensions are Go source files that run via Yaegi interpreter. They can add custom tools, slash commands, widgets, keyboard shortcuts, themes, and intercept lifecycle events.
|
||||
|
||||
### Minimal Extension
|
||||
|
||||
```go
|
||||
//go:build ignore
|
||||
|
||||
package main
|
||||
|
||||
import "kit/ext"
|
||||
|
||||
func Init(api ext.API) {
|
||||
api.OnSessionStart(func(_ ext.SessionStartEvent, ctx ext.Context) {
|
||||
ctx.SetFooter(ext.HeaderFooterConfig{
|
||||
Content: ext.WidgetContent{Text: "Custom Footer"},
|
||||
})
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
|
||||
```bash
|
||||
kit -e examples/extensions/minimal.go
|
||||
```
|
||||
|
||||
### Extension Capabilities
|
||||
|
||||
**Lifecycle Events**: OnSessionStart, OnSessionShutdown, OnBeforeAgentStart, OnAgentStart, OnAgentEnd, OnToolCall, OnToolExecutionStart, OnToolOutput, OnToolExecutionEnd, OnToolResult, OnInput, OnMessageStart, OnMessageUpdate, OnMessageEnd, OnModelChange, OnContextPrepare, OnBeforeFork, OnBeforeSessionSwitch, OnBeforeCompact, OnCustomEvent, OnSubagentStart, OnSubagentChunk, OnSubagentEnd
|
||||
|
||||
**Custom Components**:
|
||||
- **Tools**: Add new tools the LLM can invoke
|
||||
- **Commands**: Register slash commands (e.g., `/mycommand`)
|
||||
- **Options**: Register configurable extension options
|
||||
- **Widgets**: Persistent status displays above/below input
|
||||
- **Headers/Footers**: Persistent content above/below the conversation
|
||||
- **Status Bar**: Custom status bar entries
|
||||
- **Shortcuts**: Global keyboard shortcuts
|
||||
- **Overlays**: Modal dialogs with markdown content
|
||||
- **Tool Renderers**: Customize how tool calls display
|
||||
- **Message Renderers**: Custom rendering for assistant messages
|
||||
- **Editor Interceptors**: Handle key events and wrap rendering
|
||||
- **Interactive Prompts**: Select, confirm, input, and multi-select dialogs
|
||||
- **Subagents**: Spawn in-process child Kit instances
|
||||
- **LLM Completion**: Direct model calls via `Complete()`
|
||||
- **Themes**: Register and switch color themes via `RegisterTheme`, `SetTheme`, `ListThemes`
|
||||
- **Custom Events**: Inter-extension communication via `EmitCustomEvent`
|
||||
|
||||
**Bridged SDK APIs** (NEW): Extensions can now access internal SDK capabilities:
|
||||
- **Tree Navigation**: Navigate conversation history (`GetTreeNode`, `GetCurrentBranch`, `NavigateTo`), summarize branches (`SummarizeBranch`), and implement fresh context loops (`CollapseBranch`)
|
||||
- **Skill Loading**: Dynamically load and inject skills at runtime (`LoadSkill`, `DiscoverSkills`, `InjectSkillAsContext`)
|
||||
- **Template Parsing**: Parse and render templates with `{{variables}}` (`ParseTemplate`, `RenderTemplate`), parse CLI-style arguments (`ParseArguments`, `SimpleParseArguments`), and evaluate model conditionals (`EvaluateModelConditional`, `RenderWithModelConditionals`)
|
||||
- **Model Resolution**: Resolve model fallback chains (`ResolveModelChain`), query model capabilities (`GetModelCapabilities`, `CheckModelAvailable`), and extract provider/model ID (`GetCurrentProvider`, `GetCurrentModelID`)
|
||||
|
||||
### Extension Examples
|
||||
|
||||
See the `examples/extensions/` directory:
|
||||
|
||||
- `minimal.go` - Clean UI with custom footer
|
||||
- `auto-commit.go` - Auto-commit on shutdown
|
||||
- `bookmark.go` - Bookmark conversations
|
||||
- `branded-output.go` - Branded output rendering
|
||||
- `compact-notify.go` - Notification on compaction
|
||||
- `confirm-destructive.go` - Confirm destructive operations
|
||||
- `context-inject.go` - Inject context into conversations
|
||||
- `conversation-manager.go` - **NEW** Tree navigation, branch summarization, and fresh context loops
|
||||
- `custom-editor-demo.go` - Vim-like modal editor
|
||||
- `dev-reload.go` - Development live-reload
|
||||
- `header-footer-demo.go` - Custom headers and footers
|
||||
- `inline-bash.go` - Inline bash execution
|
||||
- `interactive-shell.go` - Interactive shell integration
|
||||
- `kit-kit.go` - Kit-in-Kit (sub-agent spawning)
|
||||
- `lsp-diagnostics.go` - LSP diagnostic integration
|
||||
- `notify.go` - Desktop notifications
|
||||
- `overlay-demo.go` - Modal dialogs
|
||||
- `permission-gate.go` - Permission gating for tools
|
||||
- `pirate.go` - Pirate-themed personality
|
||||
- `plan-mode.go` - Read-only planning mode
|
||||
- `project-rules.go` - Project-specific rules
|
||||
- `prompt-demo.go` - Interactive prompts (select/confirm/input)
|
||||
- `prompt-templates.go` - **NEW** Frontmatter-driven templates with model switching and skill injection
|
||||
- `protected-paths.go` - Path protection for sensitive files
|
||||
- `subagent-widget.go` - Multi-agent orchestration with status widget
|
||||
- `subagent-test.go` - Subagent testing utilities
|
||||
- `summarize.go` - Conversation summarization
|
||||
- `tool-logger.go` - Log all tool calls
|
||||
- `neon-theme.go` - Custom theme registration and switching
|
||||
- `tool-renderer-demo.go` - Custom tool call rendering
|
||||
- `widget-status.go` - Persistent status widgets
|
||||
|
||||
Also see `.kit/extensions/go-edit-lint.go` (in this repo) for a project-local extension example that runs gopls and golangci-lint on Go file edits.
|
||||
|
||||
### Loading Extensions
|
||||
|
||||
**Auto-discovery** (loads automatically):
|
||||
- `~/.config/kit/extensions/*.go` (global single files)
|
||||
- `~/.config/kit/extensions/*/main.go` (global subdirectory extensions)
|
||||
- `.kit/extensions/*.go` (project-local single files)
|
||||
- `.kit/extensions/*/main.go` (project-local subdirectory extensions)
|
||||
- `~/.local/share/kit/git/` (global git-installed packages)
|
||||
- `.kit/git/` (project-local git-installed packages)
|
||||
|
||||
**Explicit loading**:
|
||||
```bash
|
||||
kit -e path/to/extension.go
|
||||
kit -e ext1.go -e ext2.go # Multiple extensions
|
||||
```
|
||||
|
||||
**Disable auto-load**:
|
||||
```bash
|
||||
kit --no-extensions
|
||||
```
|
||||
|
||||
### Testing Extensions
|
||||
|
||||
Kit provides a testing package to help you write unit tests for your extensions:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"github.com/mark3labs/kit/pkg/extensions/test"
|
||||
"github.com/mark3labs/kit/internal/extensions"
|
||||
)
|
||||
|
||||
func TestMyExtension(t *testing.T) {
|
||||
harness := test.New(t)
|
||||
harness.LoadFile("my-ext.go")
|
||||
|
||||
// Emit events and verify behavior
|
||||
_, err := harness.Emit(extensions.SessionStartEvent{SessionID: "test"})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// Verify the extension printed something
|
||||
test.AssertPrinted(t, harness, "session started")
|
||||
}
|
||||
```
|
||||
|
||||
**Available assertions:**
|
||||
- `AssertBlocked()`, `AssertNotBlocked()` — Verify tool blocking
|
||||
- `AssertWidgetSet()`, `AssertWidgetText()` — Verify widget content
|
||||
- `AssertPrinted()`, `AssertPrintedContains()` — Verify output
|
||||
- `AssertToolRegistered()`, `AssertCommandRegistered()` — Verify registration
|
||||
|
||||
See `examples/extensions/tool-logger_test.go` for a complete example with 14 test cases covering tool calls, input handling, and session lifecycle.
|
||||
|
||||
### Prompt Templates
|
||||
|
||||
Create reusable prompt templates with shell-style argument substitution. Templates are loaded from `~/.kit/prompts/*.md` and `.kit/prompts/*.md`.
|
||||
|
||||
**Example template** (`~/.kit/prompts/review.md`):
|
||||
```markdown
|
||||
---
|
||||
description: Review code for issues
|
||||
---
|
||||
Review the following code for bugs and security issues.
|
||||
Focus on $1 specifically.
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```
|
||||
/review error handling
|
||||
```
|
||||
|
||||
**Argument placeholders:**
|
||||
- `$1`, `$2`, etc. — Individual arguments
|
||||
- `$@` or `$ARGUMENTS` — All arguments
|
||||
- `${@:2}` — Arguments from position 2 onwards
|
||||
- `${@:1:3}` — 3 arguments starting at position 1
|
||||
|
||||
Disable templates with `--no-prompt-templates` or load a specific template with `--prompt-template <name>`.
|
||||
|
||||
## Session Management
|
||||
|
||||
Kit uses a tree-based session model that supports branching and forking conversations.
|
||||
|
||||
### Session Locations
|
||||
|
||||
- Default: `~/.kit/sessions/<cwd-path>/<timestamp>_<id>.jsonl`
|
||||
- Path separators in the working directory are replaced with `--` (e.g., `/home/user/project` becomes `home--user--project`)
|
||||
- Each line is a session entry (messages, tool calls, extension data)
|
||||
- Supports branching from any message to explore alternate paths
|
||||
|
||||
### Session Commands
|
||||
|
||||
```bash
|
||||
# Resume most recent session for current directory
|
||||
kit --continue
|
||||
kit -c
|
||||
|
||||
# Interactive session picker
|
||||
kit --resume
|
||||
kit -r
|
||||
|
||||
# Open specific session file
|
||||
kit --session path/to/session.jsonl
|
||||
kit -s path/to/session.jsonl
|
||||
|
||||
# Ephemeral mode (no file persistence)
|
||||
kit --no-session
|
||||
```
|
||||
|
||||
### Interactive Session Commands
|
||||
|
||||
During an interactive session, use these slash commands:
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `/name [name]` | Set or display the session's display name |
|
||||
| `/session` | Show session info (path, ID, message count) |
|
||||
| `/resume` | Open the session picker to switch sessions |
|
||||
| `/export [path]` | Export session as JSONL (auto-generates path if omitted) |
|
||||
| `/import <path>` | Import and switch to a session from a JSONL file |
|
||||
| `/share` | Upload session to GitHub Gist and get a shareable viewer URL |
|
||||
| `/tree` | Navigate the session tree |
|
||||
| `/fork` | Branch from an earlier message |
|
||||
| `/new` | Start a fresh session |
|
||||
|
||||
## Go SDK
|
||||
|
||||
Embed Kit in your Go applications:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
kit "github.com/mark3labs/kit/pkg/kit"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
|
||||
// Create Kit instance with default configuration
|
||||
host, err := kit.New(ctx, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer func() { _ = host.Close() }()
|
||||
|
||||
// Send a prompt
|
||||
response, err := host.Prompt(ctx, "What is 2+2?")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
println(response)
|
||||
}
|
||||
```
|
||||
|
||||
### With Options
|
||||
|
||||
```go
|
||||
host, err := kit.New(ctx, &kit.Options{
|
||||
Model: "ollama/llama3",
|
||||
SystemPrompt: "You are a helpful bot",
|
||||
ConfigFile: "/path/to/config.yml",
|
||||
MaxSteps: 10,
|
||||
Streaming: true,
|
||||
Quiet: true,
|
||||
|
||||
// Session options
|
||||
SessionPath: "./session.jsonl", // Open specific session
|
||||
Continue: true, // Resume most recent session
|
||||
NoSession: true, // Ephemeral mode
|
||||
|
||||
// Tool options
|
||||
ExtraTools: []kit.Tool{...}, // Additional tools alongside defaults
|
||||
|
||||
// Compaction
|
||||
AutoCompact: true, // Auto-compact near context limit
|
||||
|
||||
Debug: true, // Debug logging
|
||||
})
|
||||
```
|
||||
|
||||
### With Callbacks
|
||||
|
||||
```go
|
||||
unsub := host.OnToolCall(func(e kit.ToolCallEvent) {
|
||||
println("Calling tool:", e.ToolName)
|
||||
})
|
||||
defer unsub()
|
||||
|
||||
unsub2 := host.OnToolResult(func(e kit.ToolResultEvent) {
|
||||
if e.IsError {
|
||||
println("Tool failed:", e.ToolName)
|
||||
}
|
||||
})
|
||||
defer unsub2()
|
||||
|
||||
unsub3 := host.OnStreaming(func(e kit.MessageUpdateEvent) {
|
||||
print(e.Chunk)
|
||||
})
|
||||
defer unsub3()
|
||||
|
||||
response, err := host.Prompt(
|
||||
ctx,
|
||||
"List files in current directory",
|
||||
)
|
||||
```
|
||||
|
||||
### Session Management
|
||||
|
||||
```go
|
||||
// Multi-turn conversations retain context automatically
|
||||
host.Prompt(ctx, "My name is Alice")
|
||||
response, _ := host.Prompt(ctx, "What's my name?")
|
||||
|
||||
// Sessions are persisted automatically to JSONL files.
|
||||
// Access session info:
|
||||
path := host.GetSessionPath()
|
||||
id := host.GetSessionID()
|
||||
|
||||
// Clear conversation history
|
||||
host.ClearSession()
|
||||
```
|
||||
|
||||
Session persistence is configured via `Options`:
|
||||
|
||||
```go
|
||||
host, _ := kit.New(ctx, &kit.Options{
|
||||
SessionPath: "./my-session.jsonl", // Open specific session
|
||||
Continue: true, // Resume most recent session
|
||||
NoSession: true, // Ephemeral mode
|
||||
})
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Subagent Pattern
|
||||
|
||||
Spawn Kit as a subprocess for multi-agent orchestration:
|
||||
|
||||
```bash
|
||||
kit "Analyze codebase" \
|
||||
--json \
|
||||
--no-session \
|
||||
--no-extensions \
|
||||
--quiet \
|
||||
--model anthropic/claude-haiku-3-5-20241022
|
||||
```
|
||||
|
||||
Parse the JSON output:
|
||||
|
||||
```json
|
||||
{
|
||||
"response": "Final assistant response text",
|
||||
"model": "anthropic/claude-haiku-3-5-20241022",
|
||||
"stop_reason": "end_turn",
|
||||
"session_id": "a1b2c3d4e5f6",
|
||||
"usage": {
|
||||
"input_tokens": 1024,
|
||||
"output_tokens": 512,
|
||||
"total_tokens": 1536,
|
||||
"cache_read_tokens": 0,
|
||||
"cache_creation_tokens": 0
|
||||
},
|
||||
"messages": [
|
||||
{
|
||||
"role": "assistant",
|
||||
"parts": [
|
||||
{"type": "text", "data": "..."},
|
||||
{"type": "tool_call", "data": {"name": "...", "args": "..."}},
|
||||
{"type": "tool_result", "data": {"name": "...", "result": "..."}}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Testing with tmux
|
||||
|
||||
Test the TUI non-interactively:
|
||||
|
||||
```bash
|
||||
# Start Kit in detached tmux session
|
||||
tmux new-session -d -s kittest -x 120 -y 40 \
|
||||
"kit -e ext.go --no-session 2>kit.log"
|
||||
|
||||
# Wait for startup
|
||||
sleep 3
|
||||
|
||||
# Capture screen
|
||||
tmux capture-pane -t kittest -p
|
||||
|
||||
# Send input
|
||||
tmux send-keys -t kittest '/command' Enter
|
||||
|
||||
# Cleanup
|
||||
tmux kill-session -t kittest
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
### Build and Test
|
||||
|
||||
```bash
|
||||
# Build
|
||||
go build -o output/kit ./cmd/kit
|
||||
|
||||
# Run tests
|
||||
go test -race ./...
|
||||
|
||||
# Run specific test
|
||||
go test -race ./cmd -run TestScriptExecution
|
||||
|
||||
# Lint
|
||||
go vet ./...
|
||||
|
||||
# Format
|
||||
go fmt ./...
|
||||
```
|
||||
|
||||
### Project Structure
|
||||
|
||||
```
|
||||
cmd/kit/ - CLI entry point (main.go)
|
||||
cmd/ - CLI command implementations (root, auth, models, etc.)
|
||||
pkg/kit/ - Go SDK for embedding Kit
|
||||
internal/app/ - Application orchestrator (agent loop, message store, queue)
|
||||
internal/agent/ - Agent execution and tool dispatch
|
||||
internal/auth/ - OAuth authentication and credential storage
|
||||
internal/acpserver/ - ACP (Agent Client Protocol) server
|
||||
internal/clipboard/ - Cross-platform clipboard operations
|
||||
internal/compaction/ - Conversation compaction and summarization
|
||||
internal/config/ - Configuration management
|
||||
internal/core/ - Built-in tools (bash, read, write, edit, grep, find, ls)
|
||||
internal/extensions/ - Yaegi extension system
|
||||
internal/kitsetup/ - Initial setup wizard
|
||||
internal/message/ - Message content types and structured content blocks
|
||||
internal/models/ - Provider and model management
|
||||
internal/session/ - Session persistence (tree-based JSONL)
|
||||
internal/skills/ - Skill loading and system prompt composition
|
||||
internal/tools/ - MCP tool integration
|
||||
internal/ui/ - Bubble Tea TUI components
|
||||
examples/extensions/ - Example extension files
|
||||
npm/ - NPM package wrapper for distribution
|
||||
```
|
||||
|
||||
## Supported Providers
|
||||
|
||||
- **Anthropic** - Claude models (native, prompt caching, OAuth)
|
||||
- **OpenAI** - GPT models
|
||||
- **Google** - Gemini models
|
||||
- **Ollama** - Local models
|
||||
- **Azure OpenAI** - Azure-hosted OpenAI
|
||||
- **AWS Bedrock** - Bedrock models
|
||||
- **Google Vertex** - Claude on Vertex AI
|
||||
- **OpenRouter** - Multi-provider router
|
||||
- **Vercel AI** - Vercel AI SDK models
|
||||
- **Custom** - Any OpenAI-compatible endpoint via `--provider-url`
|
||||
- **Auto-routed** - Any provider from models.dev database
|
||||
|
||||
### Custom Provider
|
||||
|
||||
Use `custom/custom` when pointing Kit at any OpenAI-compatible endpoint with `--provider-url`:
|
||||
|
||||
```bash
|
||||
kit --provider-url "http://localhost:8080/v1" "Hello"
|
||||
```
|
||||
|
||||
This automatically defaults to `custom/custom` without needing to specify a model. The custom provider routes through the `openaicompat` provider and supports:
|
||||
|
||||
- Zero cost tracking (input/output = 0)
|
||||
- 262K context window, 65K output limit
|
||||
- Reasoning and temperature support
|
||||
- Optional `CUSTOM_API_KEY` environment variable or `--provider-api-key` flag
|
||||
|
||||
### Model String Format
|
||||
|
||||
```bash
|
||||
provider/model # Standard format
|
||||
anthropic/claude-sonnet-latest
|
||||
openai/gpt-4o
|
||||
ollama/llama3
|
||||
google/gemini-2.0-flash-exp
|
||||
```
|
||||
|
||||
### Model Aliases
|
||||
|
||||
```bash
|
||||
# Anthropic Claude
|
||||
claude-opus-latest → claude-opus-4-6
|
||||
claude-sonnet-latest → claude-sonnet-4-6
|
||||
claude-haiku-latest → claude-haiku-4-5
|
||||
claude-4-opus-latest → claude-opus-4-6
|
||||
claude-4-sonnet-latest → claude-sonnet-4-6
|
||||
claude-4-haiku-latest → claude-haiku-4-5
|
||||
claude-3-7-sonnet-latest → claude-3-7-sonnet-20250219
|
||||
claude-3-5-sonnet-latest → claude-3-5-sonnet-20241022
|
||||
claude-3-5-haiku-latest → claude-3-5-haiku-20241022
|
||||
claude-3-opus-latest → claude-3-opus-20240229
|
||||
|
||||
# OpenAI GPT
|
||||
o1-latest → o1
|
||||
o3-latest → o3
|
||||
o4-latest → o4-mini
|
||||
gpt-5-latest → gpt-5.4
|
||||
gpt-5-chat-latest → gpt-5.4
|
||||
gpt-4-latest → gpt-4o
|
||||
gpt-4 → gpt-4o
|
||||
gpt-3.5-latest → gpt-3.5-turbo
|
||||
gpt-3.5 → gpt-3.5-turbo
|
||||
codex-latest → codex-mini-latest
|
||||
|
||||
# Google Gemini
|
||||
gemini-pro-latest → gemini-2.5-pro
|
||||
gemini-flash-latest → gemini-2.5-flash
|
||||
gemini-flash → gemini-2.5-flash
|
||||
gemini-pro → gemini-2.5-pro
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please see the [contribution guide](contribute/contribute.md) for guidelines.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
## Community
|
||||
|
||||
- [Discord](https://discord.gg/RqSS2NQVsY)
|
||||
- [GitHub Issues](https://github.com/mark3labs/kit/issues)
|
||||
- [Documentation](https://github.com/mark3labs/kit/wiki)
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
version: "3"
|
||||
|
||||
vars:
|
||||
BINARY: kit
|
||||
OUTPUT_DIR: output
|
||||
BUILD_FLAGS: -o {{.OUTPUT_DIR}}/{{.BINARY}}
|
||||
LDFLAGS: -s -w -X main.version=dev
|
||||
|
||||
tasks:
|
||||
default:
|
||||
desc: Show available tasks
|
||||
cmds:
|
||||
- task --list-all
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Build
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
build:
|
||||
desc: Build the kit binary
|
||||
cmds:
|
||||
- go build {{.BUILD_FLAGS}} -ldflags "{{.LDFLAGS}}" ./cmd/kit
|
||||
sources:
|
||||
- "**/*.go"
|
||||
- go.mod
|
||||
- go.sum
|
||||
generates:
|
||||
- "{{.OUTPUT_DIR}}/{{.BINARY}}"
|
||||
|
||||
build-all:
|
||||
desc: Build for all platforms (linux, darwin, windows)
|
||||
cmds:
|
||||
- GOOS=linux GOARCH=amd64 go build -o {{.OUTPUT_DIR}}/{{.BINARY}}-linux-amd64 -ldflags "{{.LDFLAGS}}" ./cmd/kit
|
||||
- GOOS=linux GOARCH=arm64 go build -o {{.OUTPUT_DIR}}/{{.BINARY}}-linux-arm64 -ldflags "{{.LDFLAGS}}" ./cmd/kit
|
||||
- GOOS=darwin GOARCH=amd64 go build -o {{.OUTPUT_DIR}}/{{.BINARY}}-darwin-amd64 -ldflags "{{.LDFLAGS}}" ./cmd/kit
|
||||
- GOOS=darwin GOARCH=arm64 go build -o {{.OUTPUT_DIR}}/{{.BINARY}}-darwin-arm64 -ldflags "{{.LDFLAGS}}" ./cmd/kit
|
||||
- GOOS=windows GOARCH=amd64 go build -o {{.OUTPUT_DIR}}/{{.BINARY}}-windows-amd64.exe -ldflags "{{.LDFLAGS}}" ./cmd/kit
|
||||
|
||||
install:
|
||||
desc: Install kit to $GOPATH/bin
|
||||
cmds:
|
||||
- go install -ldflags "{{.LDFLAGS}}" ./cmd/kit
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Test
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
test:
|
||||
desc: Run all tests with race detector
|
||||
cmds:
|
||||
- go test -race ./...
|
||||
|
||||
test-v:
|
||||
desc: Run all tests (verbose)
|
||||
cmds:
|
||||
- go test -race -v ./...
|
||||
|
||||
test-short:
|
||||
desc: Run tests in short mode (skip long-running tests)
|
||||
cmds:
|
||||
- go test -race -short ./...
|
||||
|
||||
test-pkg:
|
||||
desc: "Run tests for a specific package (usage: task test-pkg -- ./internal/config)"
|
||||
cmds:
|
||||
- go test -race -v {{.CLI_ARGS}}
|
||||
|
||||
test-run:
|
||||
desc: "Run a single test by name (usage: task test-run -- TestScriptExecution)"
|
||||
cmds:
|
||||
- go test -race -v ./... -run {{.CLI_ARGS}}
|
||||
|
||||
test-cover:
|
||||
desc: Run tests with coverage report
|
||||
cmds:
|
||||
- mkdir -p {{.OUTPUT_DIR}}
|
||||
- go test -race -coverprofile={{.OUTPUT_DIR}}/coverage.out ./...
|
||||
- go tool cover -html={{.OUTPUT_DIR}}/coverage.out -o {{.OUTPUT_DIR}}/coverage.html
|
||||
- echo "Coverage report written to {{.OUTPUT_DIR}}/coverage.html"
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Code quality
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
lint:
|
||||
desc: Run golangci-lint and go vet
|
||||
cmds:
|
||||
- golangci-lint run ./...
|
||||
- go vet ./...
|
||||
|
||||
fmt:
|
||||
desc: Format all Go files
|
||||
cmds:
|
||||
- go fmt ./...
|
||||
|
||||
fmt-check:
|
||||
desc: Check formatting (fails if files need formatting)
|
||||
cmds:
|
||||
- test -z "$(gofmt -l .)" || (echo "Files need formatting:" && gofmt -l . && exit 1)
|
||||
|
||||
tidy:
|
||||
desc: Tidy go.mod and go.sum
|
||||
cmds:
|
||||
- go mod tidy
|
||||
|
||||
check:
|
||||
desc: Run all quality checks (fmt, vet, test)
|
||||
cmds:
|
||||
- task: fmt-check
|
||||
- task: lint
|
||||
- task: test
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Development
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
dev:
|
||||
desc: Build and run kit with optional args
|
||||
cmds:
|
||||
- task: build
|
||||
- "{{.OUTPUT_DIR}}/{{.BINARY}} {{.CLI_ARGS}}"
|
||||
|
||||
watch:
|
||||
desc: Watch for changes and rebuild (requires watchexec)
|
||||
cmds:
|
||||
- watchexec -e go -r -- task build
|
||||
|
||||
clean:
|
||||
desc: Remove build artifacts
|
||||
cmds:
|
||||
- rm -rf {{.OUTPUT_DIR}}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Release
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
release-snapshot:
|
||||
desc: Build a release snapshot (no publish)
|
||||
cmds:
|
||||
- goreleaser release --snapshot --clean
|
||||
|
||||
release-check:
|
||||
desc: Validate goreleaser config
|
||||
cmds:
|
||||
- goreleaser check
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
{"version":"1.5.2","languages":{"en":{"hash":"en_80f5da5232","wasm":"en","page_count":21}},"include_characters":["_","‿","⁀","⁔","︳","︴","﹍","﹎","﹏","_"]}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,214 +0,0 @@
|
||||
:root {
|
||||
--pagefind-ui-scale: 0.8;
|
||||
--pagefind-ui-primary: #034AD8;
|
||||
--pagefind-ui-fade: #707070;
|
||||
--pagefind-ui-text: #393939;
|
||||
--pagefind-ui-background: #ffffff;
|
||||
--pagefind-ui-border: #eeeeee;
|
||||
--pagefind-ui-tag: #eeeeee;
|
||||
--pagefind-ui-border-width: 2px;
|
||||
--pagefind-ui-border-radius: 8px;
|
||||
--pagefind-ui-image-border-radius: 8px;
|
||||
--pagefind-ui-image-box-ratio: 3 / 2;
|
||||
--pagefind-ui-font: system, -apple-system, ".SFNSText-Regular",
|
||||
"San Francisco", "Roboto", "Segoe UI", "Helvetica Neue",
|
||||
"Lucida Grande", sans-serif;
|
||||
}
|
||||
|
||||
[data-pfmod-hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
[data-pfmod-suppressed] {
|
||||
opacity: 0 !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
[data-pfmod-sr-hidden] {
|
||||
-webkit-clip: rect(0 0 0 0) !important;
|
||||
clip: rect(0 0 0 0) !important;
|
||||
-webkit-clip-path: inset(100%) !important;
|
||||
clip-path: inset(100%) !important;
|
||||
height: 1px !important;
|
||||
overflow: hidden !important;
|
||||
overflow: clip !important;
|
||||
position: absolute !important;
|
||||
white-space: nowrap !important;
|
||||
width: 1px !important;
|
||||
}
|
||||
|
||||
[data-pfmod-loading] {
|
||||
color: var(--pagefind-ui-text);
|
||||
background-color: var(--pagefind-ui-text);
|
||||
border-radius: var(--pagefind-ui-border-radius);
|
||||
opacity: 0.1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Input */
|
||||
|
||||
.pagefind-modular-input-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.pagefind-modular-input-wrapper::before {
|
||||
background-color: var(--pagefind-ui-text);
|
||||
width: calc(18px * var(--pagefind-ui-scale));
|
||||
height: calc(18px * var(--pagefind-ui-scale));
|
||||
top: calc(23px * var(--pagefind-ui-scale));
|
||||
left: calc(20px * var(--pagefind-ui-scale));
|
||||
content: "";
|
||||
position: absolute;
|
||||
display: block;
|
||||
opacity: 0.7;
|
||||
-webkit-mask-image: url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.7549 11.255H11.9649L11.6849 10.985C12.6649 9.845 13.2549 8.365 13.2549 6.755C13.2549 3.165 10.3449 0.255005 6.75488 0.255005C3.16488 0.255005 0.254883 3.165 0.254883 6.755C0.254883 10.345 3.16488 13.255 6.75488 13.255C8.36488 13.255 9.84488 12.665 10.9849 11.685L11.2549 11.965V12.755L16.2549 17.745L17.7449 16.255L12.7549 11.255ZM6.75488 11.255C4.26488 11.255 2.25488 9.245 2.25488 6.755C2.25488 4.26501 4.26488 2.255 6.75488 2.255C9.24488 2.255 11.2549 4.26501 11.2549 6.755C11.2549 9.245 9.24488 11.255 6.75488 11.255Z' fill='%23000000'/%3E%3C/svg%3E%0A");
|
||||
mask-image: url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.7549 11.255H11.9649L11.6849 10.985C12.6649 9.845 13.2549 8.365 13.2549 6.755C13.2549 3.165 10.3449 0.255005 6.75488 0.255005C3.16488 0.255005 0.254883 3.165 0.254883 6.755C0.254883 10.345 3.16488 13.255 6.75488 13.255C8.36488 13.255 9.84488 12.665 10.9849 11.685L11.2549 11.965V12.755L16.2549 17.745L17.7449 16.255L12.7549 11.255ZM6.75488 11.255C4.26488 11.255 2.25488 9.245 2.25488 6.755C2.25488 4.26501 4.26488 2.255 6.75488 2.255C9.24488 2.255 11.2549 4.26501 11.2549 6.755C11.2549 9.245 9.24488 11.255 6.75488 11.255Z' fill='%23000000'/%3E%3C/svg%3E%0A");
|
||||
-webkit-mask-size: 100%;
|
||||
mask-size: 100%;
|
||||
z-index: 9;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.pagefind-modular-input {
|
||||
height: calc(64px * var(--pagefind-ui-scale));
|
||||
padding: 0 calc(70px * var(--pagefind-ui-scale)) 0 calc(54px * var(--pagefind-ui-scale));
|
||||
background-color: var(--pagefind-ui-background);
|
||||
border: var(--pagefind-ui-border-width) solid var(--pagefind-ui-border);
|
||||
border-radius: var(--pagefind-ui-border-radius);
|
||||
font-size: calc(21px * var(--pagefind-ui-scale));
|
||||
position: relative;
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.pagefind-modular-input::placeholder {
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.pagefind-modular-input-clear {
|
||||
position: absolute;
|
||||
top: calc(2px * var(--pagefind-ui-scale));
|
||||
right: calc(2px * var(--pagefind-ui-scale));
|
||||
height: calc(60px * var(--pagefind-ui-scale));
|
||||
border-radius: var(--pagefind-ui-border-radius);
|
||||
padding: 0 calc(15px * var(--pagefind-ui-scale)) 0 calc(2px * var(--pagefind-ui-scale));
|
||||
color: var(--pagefind-ui-text);
|
||||
font-size: calc(14px * var(--pagefind-ui-scale));
|
||||
cursor: pointer;
|
||||
background-color: var(--pagefind-ui-background);
|
||||
border: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
/* ResultList */
|
||||
|
||||
.pagefind-modular-list-result {
|
||||
list-style-type: none;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: min(calc(40px * var(--pagefind-ui-scale)), 3%);
|
||||
padding: calc(30px * var(--pagefind-ui-scale)) 0 calc(40px * var(--pagefind-ui-scale));
|
||||
border-top: solid var(--pagefind-ui-border-width) var(--pagefind-ui-border);
|
||||
}
|
||||
|
||||
.pagefind-modular-list-result:last-of-type {
|
||||
border-bottom: solid var(--pagefind-ui-border-width) var(--pagefind-ui-border);
|
||||
}
|
||||
|
||||
.pagefind-modular-list-thumb {
|
||||
width: min(30%,
|
||||
calc((30% - (100px * var(--pagefind-ui-scale))) * 100000));
|
||||
max-width: calc(120px * var(--pagefind-ui-scale));
|
||||
margin-top: calc(10px * var(--pagefind-ui-scale));
|
||||
aspect-ratio: var(--pagefind-ui-image-box-ratio);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.pagefind-modular-list-image {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 0;
|
||||
width: auto;
|
||||
height: auto;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
border-radius: var(--pagefind-ui-image-border-radius);
|
||||
}
|
||||
|
||||
.pagefind-modular-list-inner {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
margin-top: calc(10px * var(--pagefind-ui-scale));
|
||||
}
|
||||
|
||||
.pagefind-modular-list-title {
|
||||
display: inline-block;
|
||||
font-weight: 700;
|
||||
font-size: calc(21px * var(--pagefind-ui-scale));
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.pagefind-modular-list-link {
|
||||
color: var(--pagefind-ui-text);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.pagefind-modular-list-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.pagefind-modular-list-excerpt {
|
||||
display: inline-block;
|
||||
font-weight: 400;
|
||||
font-size: calc(16px * var(--pagefind-ui-scale));
|
||||
margin-top: calc(4px * var(--pagefind-ui-scale));
|
||||
margin-bottom: 0;
|
||||
min-width: calc(250px * var(--pagefind-ui-scale));
|
||||
}
|
||||
|
||||
/* FilterPills */
|
||||
|
||||
.pagefind-modular-filter-pills-wrapper {
|
||||
overflow-x: scroll;
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.pagefind-modular-filter-pills {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.pagefind-modular-filter-pill {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: none;
|
||||
appearance: none;
|
||||
padding: 0 calc(24px * var(--pagefind-ui-scale));
|
||||
background-color: var(--pagefind-ui-background);
|
||||
color: var(--pagefind-ui-fade);
|
||||
border: var(--pagefind-ui-border-width) solid var(--pagefind-ui-border);
|
||||
border-radius: calc(25px * var(--pagefind-ui-scale));
|
||||
font-size: calc(18px * var(--pagefind-ui-scale));
|
||||
height: calc(50px * var(--pagefind-ui-scale));
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pagefind-modular-filter-pill:hover {
|
||||
border-color: var(--pagefind-ui-primary);
|
||||
}
|
||||
|
||||
.pagefind-modular-filter-pill[aria-pressed="true"] {
|
||||
border-color: var(--pagefind-ui-primary);
|
||||
color: var(--pagefind-ui-primary);
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -1,428 +0,0 @@
|
||||
const e={frontmatter:{title:"Commands",description:"Complete reference for all Kit CLI subcommands.",hidden:!1,toc:!0,draft:!1},html:`<h1 id="commands"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#commands"><span class="icon icon-link"></span></a>Commands</h1>
|
||||
<h2 id="authentication"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#authentication"><span class="icon icon-link"></span></a>Authentication</h2>
|
||||
<p>For OAuth-enabled providers like Anthropic.</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> auth</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> login</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> [provider] </span><span style="color:#6A737D;--shiki-dark:#6A737D"># Start OAuth flow (e.g., anthropic)</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> auth</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> login</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> [provider] --set-default </span><span style="color:#6A737D;--shiki-dark:#6A737D"># Set provider's default model as system default</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> auth</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> logout</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> [provider] </span><span style="color:#6A737D;--shiki-dark:#6A737D"># Remove credentials for provider</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> auth</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> status</span><span style="color:#6A737D;--shiki-dark:#6A737D"> # Check authentication status</span></span></code></pre>
|
||||
<h2 id="model-database"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#model-database"><span class="icon icon-link"></span></a>Model database</h2>
|
||||
<p>Manage the local model database that maps provider names to API configurations.</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> models</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> [provider] </span><span style="color:#6A737D;--shiki-dark:#6A737D"># List available models (optionally filter by provider)</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> models</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --all</span><span style="color:#6A737D;--shiki-dark:#6A737D"> # Show all providers (not just LLM-compatible)</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> update-models</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> [source] </span><span style="color:#6A737D;--shiki-dark:#6A737D"># Update model database</span></span></code></pre>
|
||||
<p>The <code>update-models</code> command accepts an optional source argument:</p>
|
||||
<ul>
|
||||
<li><em>(none)</em> — update from <a href="https://models.dev">models.dev</a></li>
|
||||
<li>A URL — fetch from a custom endpoint</li>
|
||||
<li>A file path — load from a local file</li>
|
||||
<li><code>embedded</code> — reset to the bundled database</li>
|
||||
</ul>
|
||||
<h2 id="extension-management"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#extension-management"><span class="icon icon-link"></span></a>Extension management</h2>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> extensions</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> list</span><span style="color:#6A737D;--shiki-dark:#6A737D"> # List discovered extensions</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> extensions</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> validate</span><span style="color:#6A737D;--shiki-dark:#6A737D"> # Validate extension files</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> extensions</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> init</span><span style="color:#6A737D;--shiki-dark:#6A737D"> # Generate example extension template</span></span></code></pre>
|
||||
<h3 id="installing-extensions-from-git"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#installing-extensions-from-git"><span class="icon icon-link"></span></a>Installing extensions from git</h3>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="color:#D73A49;--shiki-dark:#F97583"> <</span><span style="color:#032F62;--shiki-dark:#9ECBFF">git-ur</span><span style="color:#24292E;--shiki-dark:#E1E4E8">l</span><span style="color:#D73A49;--shiki-dark:#F97583">></span><span style="color:#6A737D;--shiki-dark:#6A737D"> # Install extensions from git repositories</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -l</span><span style="color:#D73A49;--shiki-dark:#F97583"> <</span><span style="color:#032F62;--shiki-dark:#9ECBFF">git-ur</span><span style="color:#24292E;--shiki-dark:#E1E4E8">l</span><span style="color:#D73A49;--shiki-dark:#F97583">></span><span style="color:#6A737D;--shiki-dark:#6A737D"> # Install to project-local .kit/git/ directory</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -u</span><span style="color:#D73A49;--shiki-dark:#F97583"> <</span><span style="color:#032F62;--shiki-dark:#9ECBFF">git-ur</span><span style="color:#24292E;--shiki-dark:#E1E4E8">l</span><span style="color:#D73A49;--shiki-dark:#F97583">></span><span style="color:#6A737D;--shiki-dark:#6A737D"> # Update an already-installed package</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --uninstall</span><span style="color:#D73A49;--shiki-dark:#F97583"> <</span><span style="color:#032F62;--shiki-dark:#9ECBFF">pk</span><span style="color:#24292E;--shiki-dark:#E1E4E8">g</span><span style="color:#D73A49;--shiki-dark:#F97583">></span><span style="color:#6A737D;--shiki-dark:#6A737D"> # Remove an installed package</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --all</span><span style="color:#6A737D;--shiki-dark:#6A737D"> # Install all extensions without prompting</span></span></code></pre>
|
||||
<h2 id="skills"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#skills"><span class="icon icon-link"></span></a>Skills</h2>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> skill</span><span style="color:#6A737D;--shiki-dark:#6A737D"> # Install the Kit extensions skill via skills.sh</span></span></code></pre>
|
||||
<h3 id="skills-cli-flags"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#skills-cli-flags"><span class="icon icon-link"></span></a>Skills CLI flags</h3>
|
||||
<p>Control which skills are loaded at startup:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Load a specific skill file</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --skill</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> path/to/skill.md</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "prompt"</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Load multiple skill files or directories (flag is repeatable)</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --skill</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> ./skill1.md</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --skill</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> ./skill2.md</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "prompt"</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Load all skills from a custom directory instead of the default locations</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --skills-dir</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> /path/to/skills</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "prompt"</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Disable all skill loading (auto-discovery and explicit)</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --no-skills</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "prompt"</span></span></code></pre>
|
||||
<p>Skills are auto-discovered from <code>~/.config/kit/skills/</code>, <code>.kit/skills/</code>, and <code>.agents/skills/</code> by default. Use <code>--skills-dir</code> to override the project-local search root, or <code>--skill</code> to load files explicitly (which disables auto-discovery). <code>--no-skills</code> suppresses all skill loading regardless of other flags.</p>
|
||||
<h2 id="interactive-slash-commands"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#interactive-slash-commands"><span class="icon icon-link"></span></a>Interactive slash commands</h2>
|
||||
<p>These commands are available inside the Kit TUI during an interactive session:</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Command</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>/help</code></td>
|
||||
<td>Show available commands</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/tools</code></td>
|
||||
<td>List available MCP tools</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/servers</code></td>
|
||||
<td>Show connected MCP servers</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/model [name]</code></td>
|
||||
<td>Switch model or open model selector</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/theme [name]</code></td>
|
||||
<td>Switch color theme or list available themes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/thinking [level]</code></td>
|
||||
<td>Set thinking level (off, none, minimal, low, medium, high)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/compact [focus]</code></td>
|
||||
<td>Summarize older messages to free context</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/clear</code></td>
|
||||
<td>Clear conversation</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/clear-queue</code></td>
|
||||
<td>Clear queued messages</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/usage</code></td>
|
||||
<td>Show token usage</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/reset-usage</code></td>
|
||||
<td>Reset usage statistics</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/tree</code></td>
|
||||
<td>Navigate session tree</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/fork</code></td>
|
||||
<td>Fork to new session from an earlier message</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/new</code></td>
|
||||
<td>Start a new session (creates new session file)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/name [name]</code></td>
|
||||
<td>Set or show session display name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/resume</code></td>
|
||||
<td>Open session picker to switch sessions (alias: <code>/r</code>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/session</code></td>
|
||||
<td>Show session info</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/export [path]</code></td>
|
||||
<td>Export session as JSONL (default: auto-generated path)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/import <path></code></td>
|
||||
<td>Import a session from a JSONL file</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/share</code></td>
|
||||
<td>Upload session to GitHub Gist and get a shareable viewer URL</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/quit</code></td>
|
||||
<td>Exit Kit</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 id="prompt-history"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#prompt-history"><span class="icon icon-link"></span></a>Prompt history</h3>
|
||||
<p>Use <strong>↑</strong> and <strong>↓</strong> arrow keys to navigate through previously submitted prompts. Kit keeps the last 100 entries. Consecutive duplicates are skipped.</p>
|
||||
<h3 id="cancelling-operations"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#cancelling-operations"><span class="icon icon-link"></span></a>Cancelling operations</h3>
|
||||
<p>Press <strong>ESC twice</strong> to cancel the current operation:</p>
|
||||
<ul>
|
||||
<li>During a tool call: rolls back the entire turn to maintain API message pairing</li>
|
||||
<li>During streaming: stops the response generation</li>
|
||||
</ul>
|
||||
<p>This ensures that <code>tool_use</code> and <code>tool_result</code> messages are always sent to the API as matched pairs, avoiding errors from orphaned tool calls.</p>
|
||||
<h3 id="external-editor"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#external-editor"><span class="icon icon-link"></span></a>External editor</h3>
|
||||
<p>Press <strong>Ctrl+X e</strong> to open your <code>$VISUAL</code> or <code>$EDITOR</code> in a temporary file pre-populated with the current input text. On save and quit, the edited content replaces the input textarea. On error exit (e.g., <code>:cq</code> in Vim), the original input is preserved.</p>
|
||||
<h3 id="mid-turn-steering"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#mid-turn-steering"><span class="icon icon-link"></span></a>Mid-turn steering</h3>
|
||||
<p>Press <strong>Ctrl+X s</strong> during streaming to inject a system-level instruction mid-turn. This allows you to steer the conversation direction without waiting for the model to finish:</p>
|
||||
<ul>
|
||||
<li>Works during streaming output</li>
|
||||
<li>Sends a steering instruction as a system message</li>
|
||||
<li>Model continues from the interruption point with the new guidance</li>
|
||||
</ul>
|
||||
<p>Example: While the model is writing code, press Ctrl+X s and type "Use async/await instead" to change the implementation approach.</p>
|
||||
<h3 id="image-attachments"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#image-attachments"><span class="icon icon-link"></span></a>Image attachments</h3>
|
||||
<p>Attach images to your next prompt straight from the clipboard:</p>
|
||||
<ul>
|
||||
<li>Copy an image (e.g. a screenshot) to the system clipboard, then press <strong>Ctrl+V</strong> in the input to attach it.</li>
|
||||
<li>Press <strong>Ctrl+U</strong> to clear all pending image attachments.</li>
|
||||
<li>Attachments are sent alongside your text when you submit, and cleared afterward.</li>
|
||||
</ul>
|
||||
<p>When a terminal supports color, Kit renders a small low-resolution <strong>thumbnail preview</strong> of each pending image directly in the input, below the <code>[N image(s) attached]</code> indicator, so you can confirm the right image was attached before sending.</p>
|
||||
<p>The preview is drawn with Unicode half-block characters and ordinary terminal colors — not a graphics protocol — so it renders correctly inside terminal multiplexers like <strong>tmux</strong> and <strong>zellij</strong>. Thumbnails are capped to a small cell box for a glanceable, low-res look.</p>
|
||||
<ul>
|
||||
<li>Best fidelity needs a <strong>truecolor</strong> terminal (<code>COLORTERM=truecolor</code>); Kit degrades to 256-color where truecolor is unavailable.</li>
|
||||
<li>On terminals with neither, the preview is skipped and the <code>[N image(s) attached]</code> text indicator is shown alone.</li>
|
||||
</ul>
|
||||
<p>You can also attach image files by referencing them with <code>@path/to/image.png</code> — binary files are auto-detected by MIME type. See <a href="/quick-start">Quick Start</a> for the <code>@</code> attachment syntax.</p>
|
||||
<h2 id="prompt-templates"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#prompt-templates"><span class="icon icon-link"></span></a>Prompt templates</h2>
|
||||
<h3 id="creating-templates"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#creating-templates"><span class="icon icon-link"></span></a>Creating templates</h3>
|
||||
<p>Templates use YAML frontmatter for metadata and support argument placeholders:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">---</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">description</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">Review code for issues</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">---</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">Review the following code for bugs and security issues.</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">Focus on $1 specifically.</span></span></code></pre>
|
||||
<p>Save to <code>~/.kit/prompts/review.md</code> or <code>.kit/prompts/review.md</code>.</p>
|
||||
<h3 id="using-templates"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#using-templates"><span class="icon icon-link"></span></a>Using templates</h3>
|
||||
<p>Templates appear as slash commands:</p>
|
||||
<pre><code>/review error handling
|
||||
</code></pre>
|
||||
<h3 id="argument-placeholders"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#argument-placeholders"><span class="icon icon-link"></span></a>Argument placeholders</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Placeholder</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>$1</code>, <code>$2</code>, etc.</td>
|
||||
<td>Individual arguments by position</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>$@</code>, <code>$ARGUMENTS</code></td>
|
||||
<td>All arguments joined with spaces (zero or more)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>$+</code></td>
|
||||
<td>All arguments joined with spaces (one or more required)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>\${@:N}</code></td>
|
||||
<td>Arguments from position N onwards</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>\${@:N:L}</code></td>
|
||||
<td>L arguments starting at position N</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Placeholders inside fenced code blocks (<code>\`\`\`</code>) and inline code spans are ignored, so documentation examples won't be substituted.</p>
|
||||
<h3 id="cli-flags"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#cli-flags"><span class="icon icon-link"></span></a>CLI flags</h3>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Load a specific template by name</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --prompt-template</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> review</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Disable template loading</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --no-prompt-templates</span></span></code></pre>
|
||||
<h2 id="acp-server"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#acp-server"><span class="icon icon-link"></span></a>ACP server</h2>
|
||||
<p>Run Kit as an <a href="https://agentclientprotocol.com">ACP (Agent Client Protocol)</a> agent server. ACP-compatible clients communicate with Kit over JSON-RPC 2.0 on stdin/stdout.</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> acp</span><span style="color:#6A737D;--shiki-dark:#6A737D"> # Start as ACP agent</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> acp</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --debug</span><span style="color:#6A737D;--shiki-dark:#6A737D"> # With debug logging to stderr</span></span></code></pre>`,headings:[{depth:2,text:"Authentication",id:"authentication"},{depth:2,text:"Model database",id:"model-database"},{depth:2,text:"Extension management",id:"extension-management"},{depth:3,text:"Installing extensions from git",id:"installing-extensions-from-git"},{depth:2,text:"Skills",id:"skills"},{depth:3,text:"Skills CLI flags",id:"skills-cli-flags"},{depth:2,text:"Interactive slash commands",id:"interactive-slash-commands"},{depth:3,text:"Prompt history",id:"prompt-history"},{depth:3,text:"Cancelling operations",id:"cancelling-operations"},{depth:3,text:"External editor",id:"external-editor"},{depth:3,text:"Mid-turn steering",id:"mid-turn-steering"},{depth:3,text:"Image attachments",id:"image-attachments"},{depth:2,text:"Prompt templates",id:"prompt-templates"},{depth:3,text:"Creating templates",id:"creating-templates"},{depth:3,text:"Using templates",id:"using-templates"},{depth:3,text:"Argument placeholders",id:"argument-placeholders"},{depth:3,text:"CLI flags",id:"cli-flags"},{depth:2,text:"ACP server",id:"acp-server"}],raw:`
|
||||
# Commands
|
||||
|
||||
## Authentication
|
||||
|
||||
For OAuth-enabled providers like Anthropic.
|
||||
|
||||
\`\`\`bash
|
||||
kit auth login [provider] # Start OAuth flow (e.g., anthropic)
|
||||
kit auth login [provider] --set-default # Set provider's default model as system default
|
||||
kit auth logout [provider] # Remove credentials for provider
|
||||
kit auth status # Check authentication status
|
||||
\`\`\`
|
||||
|
||||
## Model database
|
||||
|
||||
Manage the local model database that maps provider names to API configurations.
|
||||
|
||||
\`\`\`bash
|
||||
kit models [provider] # List available models (optionally filter by provider)
|
||||
kit models --all # Show all providers (not just LLM-compatible)
|
||||
kit update-models [source] # Update model database
|
||||
\`\`\`
|
||||
|
||||
The \`update-models\` command accepts an optional source argument:
|
||||
- *(none)* — update from [models.dev](https://models.dev)
|
||||
- A URL — fetch from a custom endpoint
|
||||
- A file path — load from a local file
|
||||
- \`embedded\` — reset to the bundled database
|
||||
|
||||
## Extension management
|
||||
|
||||
\`\`\`bash
|
||||
kit extensions list # List discovered extensions
|
||||
kit extensions validate # Validate extension files
|
||||
kit extensions init # Generate example extension template
|
||||
\`\`\`
|
||||
|
||||
### Installing extensions from git
|
||||
|
||||
\`\`\`bash
|
||||
kit install <git-url> # Install extensions from git repositories
|
||||
kit install -l <git-url> # Install to project-local .kit/git/ directory
|
||||
kit install -u <git-url> # Update an already-installed package
|
||||
kit install --uninstall <pkg> # Remove an installed package
|
||||
kit install --all # Install all extensions without prompting
|
||||
\`\`\`
|
||||
|
||||
## Skills
|
||||
|
||||
\`\`\`bash
|
||||
kit skill # Install the Kit extensions skill via skills.sh
|
||||
\`\`\`
|
||||
|
||||
### Skills CLI flags
|
||||
|
||||
Control which skills are loaded at startup:
|
||||
|
||||
\`\`\`bash
|
||||
# Load a specific skill file
|
||||
kit --skill path/to/skill.md "prompt"
|
||||
|
||||
# Load multiple skill files or directories (flag is repeatable)
|
||||
kit --skill ./skill1.md --skill ./skill2.md "prompt"
|
||||
|
||||
# Load all skills from a custom directory instead of the default locations
|
||||
kit --skills-dir /path/to/skills "prompt"
|
||||
|
||||
# Disable all skill loading (auto-discovery and explicit)
|
||||
kit --no-skills "prompt"
|
||||
\`\`\`
|
||||
|
||||
Skills are auto-discovered from \`~/.config/kit/skills/\`, \`.kit/skills/\`, and \`.agents/skills/\` by default. Use \`--skills-dir\` to override the project-local search root, or \`--skill\` to load files explicitly (which disables auto-discovery). \`--no-skills\` suppresses all skill loading regardless of other flags.
|
||||
|
||||
## Interactive slash commands
|
||||
|
||||
These commands are available inside the Kit TUI during an interactive session:
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| \`/help\` | Show available commands |
|
||||
| \`/tools\` | List available MCP tools |
|
||||
| \`/servers\` | Show connected MCP servers |
|
||||
| \`/model [name]\` | Switch model or open model selector |
|
||||
| \`/theme [name]\` | Switch color theme or list available themes |
|
||||
| \`/thinking [level]\` | Set thinking level (off, none, minimal, low, medium, high) |
|
||||
| \`/compact [focus]\` | Summarize older messages to free context |
|
||||
| \`/clear\` | Clear conversation |
|
||||
| \`/clear-queue\` | Clear queued messages |
|
||||
| \`/usage\` | Show token usage |
|
||||
| \`/reset-usage\` | Reset usage statistics |
|
||||
| \`/tree\` | Navigate session tree |
|
||||
| \`/fork\` | Fork to new session from an earlier message |
|
||||
| \`/new\` | Start a new session (creates new session file) |
|
||||
| \`/name [name]\` | Set or show session display name |
|
||||
| \`/resume\` | Open session picker to switch sessions (alias: \`/r\`) |
|
||||
| \`/session\` | Show session info |
|
||||
| \`/export [path]\` | Export session as JSONL (default: auto-generated path) |
|
||||
| \`/import <path>\` | Import a session from a JSONL file |
|
||||
| \`/share\` | Upload session to GitHub Gist and get a shareable viewer URL |
|
||||
| \`/quit\` | Exit Kit |
|
||||
|
||||
### Prompt history
|
||||
|
||||
Use **↑** and **↓** arrow keys to navigate through previously submitted prompts. Kit keeps the last 100 entries. Consecutive duplicates are skipped.
|
||||
|
||||
### Cancelling operations
|
||||
|
||||
Press **ESC twice** to cancel the current operation:
|
||||
- During a tool call: rolls back the entire turn to maintain API message pairing
|
||||
- During streaming: stops the response generation
|
||||
|
||||
This ensures that \`tool_use\` and \`tool_result\` messages are always sent to the API as matched pairs, avoiding errors from orphaned tool calls.
|
||||
|
||||
### External editor
|
||||
|
||||
Press **Ctrl+X e** to open your \`$VISUAL\` or \`$EDITOR\` in a temporary file pre-populated with the current input text. On save and quit, the edited content replaces the input textarea. On error exit (e.g., \`:cq\` in Vim), the original input is preserved.
|
||||
|
||||
### Mid-turn steering
|
||||
|
||||
Press **Ctrl+X s** during streaming to inject a system-level instruction mid-turn. This allows you to steer the conversation direction without waiting for the model to finish:
|
||||
|
||||
- Works during streaming output
|
||||
- Sends a steering instruction as a system message
|
||||
- Model continues from the interruption point with the new guidance
|
||||
|
||||
Example: While the model is writing code, press Ctrl+X s and type "Use async/await instead" to change the implementation approach.
|
||||
|
||||
### Image attachments
|
||||
|
||||
Attach images to your next prompt straight from the clipboard:
|
||||
|
||||
- Copy an image (e.g. a screenshot) to the system clipboard, then press **Ctrl+V** in the input to attach it.
|
||||
- Press **Ctrl+U** to clear all pending image attachments.
|
||||
- Attachments are sent alongside your text when you submit, and cleared afterward.
|
||||
|
||||
When a terminal supports color, Kit renders a small low-resolution **thumbnail preview** of each pending image directly in the input, below the \`[N image(s) attached]\` indicator, so you can confirm the right image was attached before sending.
|
||||
|
||||
The preview is drawn with Unicode half-block characters and ordinary terminal colors — not a graphics protocol — so it renders correctly inside terminal multiplexers like **tmux** and **zellij**. Thumbnails are capped to a small cell box for a glanceable, low-res look.
|
||||
|
||||
- Best fidelity needs a **truecolor** terminal (\`COLORTERM=truecolor\`); Kit degrades to 256-color where truecolor is unavailable.
|
||||
- On terminals with neither, the preview is skipped and the \`[N image(s) attached]\` text indicator is shown alone.
|
||||
|
||||
You can also attach image files by referencing them with \`@path/to/image.png\` — binary files are auto-detected by MIME type. See [Quick Start](/quick-start) for the \`@\` attachment syntax.
|
||||
|
||||
## Prompt templates
|
||||
|
||||
### Creating templates
|
||||
|
||||
Templates use YAML frontmatter for metadata and support argument placeholders:
|
||||
|
||||
\`\`\`markdown
|
||||
---
|
||||
description: Review code for issues
|
||||
---
|
||||
Review the following code for bugs and security issues.
|
||||
Focus on $1 specifically.
|
||||
\`\`\`
|
||||
|
||||
Save to \`~/.kit/prompts/review.md\` or \`.kit/prompts/review.md\`.
|
||||
|
||||
### Using templates
|
||||
|
||||
Templates appear as slash commands:
|
||||
|
||||
\`\`\`
|
||||
/review error handling
|
||||
\`\`\`
|
||||
|
||||
### Argument placeholders
|
||||
|
||||
| Placeholder | Description |
|
||||
|-------------|-------------|
|
||||
| \`$1\`, \`$2\`, etc. | Individual arguments by position |
|
||||
| \`$@\`, \`$ARGUMENTS\` | All arguments joined with spaces (zero or more) |
|
||||
| \`$+\` | All arguments joined with spaces (one or more required) |
|
||||
| \`\${@:N}\` | Arguments from position N onwards |
|
||||
| \`\${@:N:L}\` | L arguments starting at position N |
|
||||
|
||||
Placeholders inside fenced code blocks (\`\` \`\`\` \`\`) and inline code spans are ignored, so documentation examples won't be substituted.
|
||||
|
||||
### CLI flags
|
||||
|
||||
\`\`\`bash
|
||||
# Load a specific template by name
|
||||
kit --prompt-template review
|
||||
|
||||
# Disable template loading
|
||||
kit --no-prompt-templates
|
||||
\`\`\`
|
||||
|
||||
## ACP server
|
||||
|
||||
Run Kit as an [ACP (Agent Client Protocol)](https://agentclientprotocol.com) agent server. ACP-compatible clients communicate with Kit over JSON-RPC 2.0 on stdin/stdout.
|
||||
|
||||
\`\`\`bash
|
||||
kit acp # Start as ACP agent
|
||||
kit acp --debug # With debug logging to stderr
|
||||
\`\`\`
|
||||
`};export{e as default};
|
||||
File diff suppressed because one or more lines are too long
@@ -1,138 +0,0 @@
|
||||
const e={frontmatter:{title:"Development",description:"Build, test, and contribute to Kit.",hidden:!1,toc:!0,draft:!1},html:`<h1 id="development"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#development"><span class="icon icon-link"></span></a>Development</h1>
|
||||
<h2 id="build-and-test"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#build-and-test"><span class="icon icon-link"></span></a>Build and test</h2>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Build</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">go</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> build</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -o</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> output/kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> ./cmd/kit</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Run all tests</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">go</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> test</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -race</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> ./...</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Run a specific test</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">go</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> test</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -race</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> ./cmd</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -run</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> TestScriptExecution</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Lint</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">go</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> vet</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> ./...</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Format</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">go</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> fmt</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> ./...</span></span></code></pre>
|
||||
<h2 id="project-structure"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#project-structure"><span class="icon icon-link"></span></a>Project structure</h2>
|
||||
<pre><code>cmd/kit/ - CLI entry point (main.go)
|
||||
cmd/ - CLI command implementations (root, auth, models, etc.)
|
||||
pkg/kit/ - Go SDK for embedding Kit
|
||||
internal/app/ - Application orchestrator (agent loop, message store, queue)
|
||||
internal/agent/ - Agent execution and tool dispatch
|
||||
internal/auth/ - OAuth authentication and credential storage
|
||||
internal/acpserver/ - ACP (Agent Client Protocol) server
|
||||
internal/clipboard/ - Cross-platform clipboard operations
|
||||
internal/compaction/ - Conversation compaction and summarization
|
||||
internal/config/ - Configuration management
|
||||
internal/core/ - Built-in tools (bash with sudo password prompt, read, write, edit, grep, find, ls)
|
||||
internal/extensions/ - Yaegi extension system
|
||||
internal/kitsetup/ - Initial setup wizard
|
||||
internal/message/ - Message content types and structured content blocks
|
||||
internal/models/ - Provider and model management
|
||||
internal/session/ - Session persistence (tree-based JSONL)
|
||||
internal/skills/ - Skill loading and system prompt composition
|
||||
internal/tools/ - MCP tool integration
|
||||
internal/ui/ - Bubble Tea TUI components
|
||||
examples/extensions/ - Example extension files
|
||||
npm/ - NPM package wrapper for distribution
|
||||
</code></pre>
|
||||
<h2 id="architecture-overview"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#architecture-overview"><span class="icon icon-link"></span></a>Architecture overview</h2>
|
||||
<p>Kit is built around a few key architectural patterns:</p>
|
||||
<h3 id="multi-provider-llm-support"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#multi-provider-llm-support"><span class="icon icon-link"></span></a>Multi-provider LLM support</h3>
|
||||
<p>The <code>llm.Provider</code> interface abstracts different LLM providers. Each provider implements message formatting, tool calling, and streaming for its specific API.</p>
|
||||
<h3 id="mcp-client-server-model"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#mcp-client-server-model"><span class="icon icon-link"></span></a>MCP client-server model</h3>
|
||||
<p>External tools are integrated via the Model Context Protocol (MCP). Kit acts as an MCP client, connecting to MCP servers configured in <code>.kit.yml</code>.</p>
|
||||
<h3 id="extension-system"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#extension-system"><span class="icon icon-link"></span></a>Extension system</h3>
|
||||
<p>Extensions are Go source files interpreted at runtime by Yaegi. The <code>internal/extensions/</code> package manages loading, symbol export, and lifecycle dispatch. See the <a href="/extensions/overview">Extension System</a> docs for details.</p>
|
||||
<h3 id="tui-architecture"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#tui-architecture"><span class="icon icon-link"></span></a>TUI architecture</h3>
|
||||
<p>The interactive terminal UI is built with <a href="https://github.com/charmbracelet/bubbletea">Bubble Tea v2</a>, using a parent-child model where <code>AppModel</code> manages child components (<code>InputComponent</code>, <code>StreamComponent</code>, etc.).</p>
|
||||
<h3 id="decoupling-pattern"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#decoupling-pattern"><span class="icon icon-link"></span></a>Decoupling pattern</h3>
|
||||
<p><code>cmd/root.go</code> contains converter functions (e.g., <code>widgetProviderForUI()</code>) that bridge <code>internal/extensions/</code> types to <code>internal/ui/</code> types. The UI never imports the extensions package directly.</p>
|
||||
<h2 id="contributing"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#contributing"><span class="icon icon-link"></span></a>Contributing</h2>
|
||||
<p>Contributions are welcome! Please see the <a href="https://github.com/mark3labs/kit/blob/master/contribute/contribute.md">contribution guide</a> for guidelines.</p>
|
||||
<h2 id="community"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#community"><span class="icon icon-link"></span></a>Community</h2>
|
||||
<ul>
|
||||
<li><a href="https://discord.gg/RqSS2NQVsY">Discord</a></li>
|
||||
<li><a href="https://github.com/mark3labs/kit/issues">GitHub Issues</a></li>
|
||||
</ul>`,headings:[{depth:2,text:"Build and test",id:"build-and-test"},{depth:2,text:"Project structure",id:"project-structure"},{depth:2,text:"Architecture overview",id:"architecture-overview"},{depth:3,text:"Multi-provider LLM support",id:"multi-provider-llm-support"},{depth:3,text:"MCP client-server model",id:"mcp-client-server-model"},{depth:3,text:"Extension system",id:"extension-system"},{depth:3,text:"TUI architecture",id:"tui-architecture"},{depth:3,text:"Decoupling pattern",id:"decoupling-pattern"},{depth:2,text:"Contributing",id:"contributing"},{depth:2,text:"Community",id:"community"}],raw:`
|
||||
# Development
|
||||
|
||||
## Build and test
|
||||
|
||||
\`\`\`bash
|
||||
# Build
|
||||
go build -o output/kit ./cmd/kit
|
||||
|
||||
# Run all tests
|
||||
go test -race ./...
|
||||
|
||||
# Run a specific test
|
||||
go test -race ./cmd -run TestScriptExecution
|
||||
|
||||
# Lint
|
||||
go vet ./...
|
||||
|
||||
# Format
|
||||
go fmt ./...
|
||||
\`\`\`
|
||||
|
||||
## Project structure
|
||||
|
||||
\`\`\`
|
||||
cmd/kit/ - CLI entry point (main.go)
|
||||
cmd/ - CLI command implementations (root, auth, models, etc.)
|
||||
pkg/kit/ - Go SDK for embedding Kit
|
||||
internal/app/ - Application orchestrator (agent loop, message store, queue)
|
||||
internal/agent/ - Agent execution and tool dispatch
|
||||
internal/auth/ - OAuth authentication and credential storage
|
||||
internal/acpserver/ - ACP (Agent Client Protocol) server
|
||||
internal/clipboard/ - Cross-platform clipboard operations
|
||||
internal/compaction/ - Conversation compaction and summarization
|
||||
internal/config/ - Configuration management
|
||||
internal/core/ - Built-in tools (bash with sudo password prompt, read, write, edit, grep, find, ls)
|
||||
internal/extensions/ - Yaegi extension system
|
||||
internal/kitsetup/ - Initial setup wizard
|
||||
internal/message/ - Message content types and structured content blocks
|
||||
internal/models/ - Provider and model management
|
||||
internal/session/ - Session persistence (tree-based JSONL)
|
||||
internal/skills/ - Skill loading and system prompt composition
|
||||
internal/tools/ - MCP tool integration
|
||||
internal/ui/ - Bubble Tea TUI components
|
||||
examples/extensions/ - Example extension files
|
||||
npm/ - NPM package wrapper for distribution
|
||||
\`\`\`
|
||||
|
||||
## Architecture overview
|
||||
|
||||
Kit is built around a few key architectural patterns:
|
||||
|
||||
### Multi-provider LLM support
|
||||
|
||||
The \`llm.Provider\` interface abstracts different LLM providers. Each provider implements message formatting, tool calling, and streaming for its specific API.
|
||||
|
||||
### MCP client-server model
|
||||
|
||||
External tools are integrated via the Model Context Protocol (MCP). Kit acts as an MCP client, connecting to MCP servers configured in \`.kit.yml\`.
|
||||
|
||||
### Extension system
|
||||
|
||||
Extensions are Go source files interpreted at runtime by Yaegi. The \`internal/extensions/\` package manages loading, symbol export, and lifecycle dispatch. See the [Extension System](/extensions/overview) docs for details.
|
||||
|
||||
### TUI architecture
|
||||
|
||||
The interactive terminal UI is built with [Bubble Tea v2](https://github.com/charmbracelet/bubbletea), using a parent-child model where \`AppModel\` manages child components (\`InputComponent\`, \`StreamComponent\`, etc.).
|
||||
|
||||
### Decoupling pattern
|
||||
|
||||
\`cmd/root.go\` contains converter functions (e.g., \`widgetProviderForUI()\`) that bridge \`internal/extensions/\` types to \`internal/ui/\` types. The UI never imports the extensions package directly.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please see the [contribution guide](https://github.com/mark3labs/kit/blob/master/contribute/contribute.md) for guidelines.
|
||||
|
||||
## Community
|
||||
|
||||
- [Discord](https://discord.gg/RqSS2NQVsY)
|
||||
- [GitHub Issues](https://github.com/mark3labs/kit/issues)
|
||||
`};export{e as default};
|
||||
File diff suppressed because one or more lines are too long
@@ -1,292 +0,0 @@
|
||||
const t={frontmatter:{title:"Global Flags",description:"Complete reference for all Kit CLI flags.",hidden:!1,toc:!0,draft:!1},html:`<h1 id="global-flags"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#global-flags"><span class="icon icon-link"></span></a>Global Flags</h1>
|
||||
<p>All flags can be passed to the root <code>kit</code> command.</p>
|
||||
<h2 id="model-and-provider"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#model-and-provider"><span class="icon icon-link"></span></a>Model and provider</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Flag</th>
|
||||
<th>Short</th>
|
||||
<th>Default</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>--model</code></td>
|
||||
<td><code>-m</code></td>
|
||||
<td><code>anthropic/claude-sonnet-latest</code></td>
|
||||
<td>Model to use (provider/model format)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--provider-api-key</code></td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>API key for the provider</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--provider-url</code></td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>Base URL for provider API</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--tls-skip-verify</code></td>
|
||||
<td>—</td>
|
||||
<td><code>false</code></td>
|
||||
<td>Skip TLS certificate verification</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2 id="session-management"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#session-management"><span class="icon icon-link"></span></a>Session management</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Flag</th>
|
||||
<th>Short</th>
|
||||
<th>Default</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>--session</code></td>
|
||||
<td><code>-s</code></td>
|
||||
<td>—</td>
|
||||
<td>Open specific JSONL session file</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--continue</code></td>
|
||||
<td><code>-c</code></td>
|
||||
<td><code>false</code></td>
|
||||
<td>Resume most recent session for current directory</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--resume</code></td>
|
||||
<td><code>-r</code></td>
|
||||
<td><code>false</code></td>
|
||||
<td>Interactive session picker</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--no-session</code></td>
|
||||
<td>—</td>
|
||||
<td><code>false</code></td>
|
||||
<td>Ephemeral mode, no persistence</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2 id="behavior"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#behavior"><span class="icon icon-link"></span></a>Behavior</h2>
|
||||
<p>These flags control Kit's behavior. When a prompt is passed as a positional argument, Kit runs in non-interactive mode.</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Flag</th>
|
||||
<th>Short</th>
|
||||
<th>Default</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>--quiet</code></td>
|
||||
<td>—</td>
|
||||
<td><code>false</code></td>
|
||||
<td>Suppress all output (non-interactive only)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--json</code></td>
|
||||
<td>—</td>
|
||||
<td><code>false</code></td>
|
||||
<td>Output response as JSON (non-interactive only)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--no-exit</code></td>
|
||||
<td>—</td>
|
||||
<td><code>false</code></td>
|
||||
<td>Enter interactive mode after prompt completes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--max-steps</code></td>
|
||||
<td>—</td>
|
||||
<td><code>0</code></td>
|
||||
<td>Maximum agent steps (0 for unlimited)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--stream</code></td>
|
||||
<td>—</td>
|
||||
<td><code>true</code></td>
|
||||
<td>Enable streaming output</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--compact</code></td>
|
||||
<td>—</td>
|
||||
<td><code>false</code></td>
|
||||
<td>Enable compact output mode</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--auto-compact</code></td>
|
||||
<td>—</td>
|
||||
<td><code>false</code></td>
|
||||
<td>Auto-compact conversation near context limit</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2 id="extensions"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#extensions"><span class="icon icon-link"></span></a>Extensions</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Flag</th>
|
||||
<th>Short</th>
|
||||
<th>Default</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>--extension</code></td>
|
||||
<td><code>-e</code></td>
|
||||
<td>—</td>
|
||||
<td>Load additional extension file(s) (repeatable)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--no-extensions</code></td>
|
||||
<td>—</td>
|
||||
<td><code>false</code></td>
|
||||
<td>Disable all extensions</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--prompt-template</code></td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>Load a specific prompt template by name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--no-prompt-templates</code></td>
|
||||
<td>—</td>
|
||||
<td><code>false</code></td>
|
||||
<td>Disable prompt template loading</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2 id="skills"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#skills"><span class="icon icon-link"></span></a>Skills</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Flag</th>
|
||||
<th>Short</th>
|
||||
<th>Default</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>--skill</code></td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>Load skill file or directory (repeatable)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--skills-dir</code></td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>Override the project-local skills directory for auto-discovery</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--no-skills</code></td>
|
||||
<td>—</td>
|
||||
<td><code>false</code></td>
|
||||
<td>Disable skill loading (auto-discovery and explicit)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2 id="generation-parameters"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#generation-parameters"><span class="icon icon-link"></span></a>Generation parameters</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Flag</th>
|
||||
<th>Short</th>
|
||||
<th>Default</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>--max-tokens</code></td>
|
||||
<td>—</td>
|
||||
<td><code>8192</code></td>
|
||||
<td>Base cap for output tokens. Auto-raised per-model up to 32768 when the model's catalog ceiling is higher and no explicit value is set.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--temperature</code></td>
|
||||
<td>—</td>
|
||||
<td><code>0.7</code></td>
|
||||
<td>Randomness 0.0–1.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--top-p</code></td>
|
||||
<td>—</td>
|
||||
<td><code>0.95</code></td>
|
||||
<td>Nucleus sampling 0.0–1.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--top-k</code></td>
|
||||
<td>—</td>
|
||||
<td><code>40</code></td>
|
||||
<td>Limit top K tokens</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--stop-sequences</code></td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>Custom stop sequences (comma-separated)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--frequency-penalty</code></td>
|
||||
<td>—</td>
|
||||
<td><code>0.0</code></td>
|
||||
<td>Penalize frequent tokens (0.0–2.0)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--presence-penalty</code></td>
|
||||
<td>—</td>
|
||||
<td><code>0.0</code></td>
|
||||
<td>Penalize present tokens (0.0–2.0)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--thinking-level</code></td>
|
||||
<td>—</td>
|
||||
<td><code>off</code></td>
|
||||
<td>Extended thinking level: off, none, minimal, low, medium, high</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2 id="system"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#system"><span class="icon icon-link"></span></a>System</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Flag</th>
|
||||
<th>Short</th>
|
||||
<th>Default</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>--config</code></td>
|
||||
<td>—</td>
|
||||
<td><code>~/.kit.yml</code></td>
|
||||
<td>Config file path</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--system-prompt</code></td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>System prompt text or file path</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--debug</code></td>
|
||||
<td>—</td>
|
||||
<td><code>false</code></td>
|
||||
<td>Enable debug logging</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>`,headings:[{depth:2,text:"Model and provider",id:"model-and-provider"},{depth:2,text:"Session management",id:"session-management"},{depth:2,text:"Behavior",id:"behavior"},{depth:2,text:"Extensions",id:"extensions"},{depth:2,text:"Skills",id:"skills"},{depth:2,text:"Generation parameters",id:"generation-parameters"},{depth:2,text:"System",id:"system"}],raw:"\n# Global Flags\n\nAll flags can be passed to the root `kit` command.\n\n## Model and provider\n\n| Flag | Short | Default | Description |\n|------|-------|---------|-------------|\n| `--model` | `-m` | `anthropic/claude-sonnet-latest` | Model to use (provider/model format) |\n| `--provider-api-key` | — | — | API key for the provider |\n| `--provider-url` | — | — | Base URL for provider API |\n| `--tls-skip-verify` | — | `false` | Skip TLS certificate verification |\n\n## Session management\n\n| Flag | Short | Default | Description |\n|------|-------|---------|-------------|\n| `--session` | `-s` | — | Open specific JSONL session file |\n| `--continue` | `-c` | `false` | Resume most recent session for current directory |\n| `--resume` | `-r` | `false` | Interactive session picker |\n| `--no-session` | — | `false` | Ephemeral mode, no persistence |\n\n## Behavior\n\nThese flags control Kit's behavior. When a prompt is passed as a positional argument, Kit runs in non-interactive mode.\n\n| Flag | Short | Default | Description |\n|------|-------|---------|-------------|\n| `--quiet` | — | `false` | Suppress all output (non-interactive only) |\n| `--json` | — | `false` | Output response as JSON (non-interactive only) |\n| `--no-exit` | — | `false` | Enter interactive mode after prompt completes |\n| `--max-steps` | — | `0` | Maximum agent steps (0 for unlimited) |\n| `--stream` | — | `true` | Enable streaming output |\n| `--compact` | — | `false` | Enable compact output mode |\n| `--auto-compact` | — | `false` | Auto-compact conversation near context limit |\n\n## Extensions\n\n| Flag | Short | Default | Description |\n|------|-------|---------|-------------|\n| `--extension` | `-e` | — | Load additional extension file(s) (repeatable) |\n| `--no-extensions` | — | `false` | Disable all extensions |\n| `--prompt-template` | — | — | Load a specific prompt template by name |\n| `--no-prompt-templates` | — | `false` | Disable prompt template loading |\n\n## Skills\n\n| Flag | Short | Default | Description |\n|------|-------|---------|-------------|\n| `--skill` | — | — | Load skill file or directory (repeatable) |\n| `--skills-dir` | — | — | Override the project-local skills directory for auto-discovery |\n| `--no-skills` | — | `false` | Disable skill loading (auto-discovery and explicit) |\n\n## Generation parameters\n\n| Flag | Short | Default | Description |\n|------|-------|---------|-------------|\n| `--max-tokens` | — | `8192` | Base cap for output tokens. Auto-raised per-model up to 32768 when the model's catalog ceiling is higher and no explicit value is set. |\n| `--temperature` | — | `0.7` | Randomness 0.0–1.0 |\n| `--top-p` | — | `0.95` | Nucleus sampling 0.0–1.0 |\n| `--top-k` | — | `40` | Limit top K tokens |\n| `--stop-sequences` | — | — | Custom stop sequences (comma-separated) |\n| `--frequency-penalty` | — | `0.0` | Penalize frequent tokens (0.0–2.0) |\n| `--presence-penalty` | — | `0.0` | Penalize present tokens (0.0–2.0) |\n| `--thinking-level` | — | `off` | Extended thinking level: off, none, minimal, low, medium, high |\n\n## System\n\n| Flag | Short | Default | Description |\n|------|-------|---------|-------------|\n| `--config` | — | `~/.kit.yml` | Config file path |\n| `--system-prompt` | — | — | System prompt text or file path |\n| `--debug` | — | `false` | Enable debug logging |\n"};export{t as default};
|
||||
@@ -1 +0,0 @@
|
||||
:root,:root[data-theme=dark],:root[data-theme=light],html,html[data-theme=dark],#tome-root,#tome-root *,[data-theme],body,div{--bg: #08080a !important;--sf: #0e0e12 !important;--sfH: #141418 !important;--bd: #1a1a22 !important;--tx: #e8e0e0 !important;--tx2: #8a8090 !important;--txM: #6a6070 !important;--ac: #e03030 !important;--acD: rgba(224, 48, 48, .12) !important;--acT: #ff4444 !important;--cdBg: #0a0a0e !important;--cdTx: #c8a0a0 !important;--sbBg: #0a0a0d !important;--hdBg: rgba(8, 8, 10, .92) !important}h1,h2,h3,h4,h5,h6{color:#e8e0e0!important;font-style:normal!important}
|
||||
@@ -1,77 +0,0 @@
|
||||
const t={frontmatter:{title:"Kit",description:"Kit is a powerful, extensible AI coding agent CLI with multi-provider support, built-in tools, and a rich extension system.",hidden:!1,toc:!1,draft:!1},html:`<div style="text-align: center; margin: 2rem 0;">
|
||||
<img src="/logo.jpg" alt="KIT" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;">
|
||||
</div>
|
||||
<p>A powerful, extensible AI coding agent CLI with multi-provider support, built-in tools, and a rich extension system.</p>
|
||||
<h2 id="features"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#features"><span class="icon icon-link"></span></a>Features</h2>
|
||||
<ul>
|
||||
<li><strong>Multi-Provider LLM Support</strong> — Anthropic, OpenAI, Google Gemini, Ollama, Azure OpenAI, AWS Bedrock, OpenRouter, and more</li>
|
||||
<li><strong>Built-in Core Tools</strong> — bash (with interactive sudo password prompt), read, write, edit, grep, find, ls, subagent with no MCP overhead</li>
|
||||
<li><strong>Smart @ Attachments</strong> — Binary files auto-detected via MIME type, MCP resources via <code>@mcp:server:uri</code></li>
|
||||
<li><strong>MCP Integration</strong> — Connect external MCP servers for expanded capabilities (tools, prompts, and resources)</li>
|
||||
<li><strong>Extension System</strong> — Write custom tools, commands, widgets, and UI modifications in Go</li>
|
||||
<li><strong>Interactive TUI</strong> — Rich terminal interface powered by Bubble Tea with streaming, syntax highlighting, and custom rendering</li>
|
||||
<li><strong>Session Management</strong> — Tree-based conversation history with branching support</li>
|
||||
<li><strong>Non-Interactive Mode</strong> — Script-friendly positional args with JSON output</li>
|
||||
<li><strong>ACP Server</strong> — Run Kit as an <a href="https://agentclientprotocol.com">Agent Client Protocol</a> agent over stdio</li>
|
||||
<li><strong>Go SDK</strong> — Embed Kit in your own applications</li>
|
||||
</ul>
|
||||
<h2 id="quick-links"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#quick-links"><span class="icon icon-link"></span></a>Quick links</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Resource</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a href="/installation">Installation</a></td>
|
||||
<td>Get Kit up and running</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="/quick-start">Quick Start</a></td>
|
||||
<td>Your first Kit session</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="/configuration">Configuration</a></td>
|
||||
<td>Customize Kit for your workflow</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="/extensions/overview">Extensions</a></td>
|
||||
<td>Build custom tools and UI components</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="/sdk/overview">Go SDK</a></td>
|
||||
<td>Embed Kit in your applications</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>`,headings:[{depth:2,text:"Features",id:"features"},{depth:2,text:"Quick links",id:"quick-links"}],raw:`
|
||||
<div style="text-align: center; margin: 2rem 0;">
|
||||
<img src="/logo.jpg" alt="KIT" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" />
|
||||
</div>
|
||||
|
||||
A powerful, extensible AI coding agent CLI with multi-provider support, built-in tools, and a rich extension system.
|
||||
|
||||
## Features
|
||||
|
||||
- **Multi-Provider LLM Support** — Anthropic, OpenAI, Google Gemini, Ollama, Azure OpenAI, AWS Bedrock, OpenRouter, and more
|
||||
- **Built-in Core Tools** — bash (with interactive sudo password prompt), read, write, edit, grep, find, ls, subagent with no MCP overhead
|
||||
- **Smart @ Attachments** — Binary files auto-detected via MIME type, MCP resources via \`@mcp:server:uri\`
|
||||
- **MCP Integration** — Connect external MCP servers for expanded capabilities (tools, prompts, and resources)
|
||||
- **Extension System** — Write custom tools, commands, widgets, and UI modifications in Go
|
||||
- **Interactive TUI** — Rich terminal interface powered by Bubble Tea with streaming, syntax highlighting, and custom rendering
|
||||
- **Session Management** — Tree-based conversation history with branching support
|
||||
- **Non-Interactive Mode** — Script-friendly positional args with JSON output
|
||||
- **ACP Server** — Run Kit as an [Agent Client Protocol](https://agentclientprotocol.com) agent over stdio
|
||||
- **Go SDK** — Embed Kit in your own applications
|
||||
|
||||
## Quick links
|
||||
|
||||
| Resource | Description |
|
||||
|----------|-------------|
|
||||
| [Installation](/installation) | Get Kit up and running |
|
||||
| [Quick Start](/quick-start) | Your first Kit session |
|
||||
| [Configuration](/configuration) | Customize Kit for your workflow |
|
||||
| [Extensions](/extensions/overview) | Build custom tools and UI components |
|
||||
| [Go SDK](/sdk/overview) | Embed Kit in your applications |
|
||||
`};export{t as default};
|
||||
File diff suppressed because one or more lines are too long
@@ -1,88 +0,0 @@
|
||||
const s={frontmatter:{title:"Installation",description:"Install Kit using npm, bun, pnpm, Go, or build from source.",hidden:!1,toc:!0,draft:!1},html:`<h1 id="installation"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#installation"><span class="icon icon-link"></span></a>Installation</h1>
|
||||
<h2 id="using-npm--bun--pnpm"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#using-npm--bun--pnpm"><span class="icon icon-link"></span></a>Using npm / bun / pnpm</h2>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">npm</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -g</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> @mark3labs/kit</span></span></code></pre>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">bun</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -g</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> @mark3labs/kit</span></span></code></pre>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -g</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> @mark3labs/kit</span></span></code></pre>
|
||||
<h2 id="using-go"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#using-go"><span class="icon icon-link"></span></a>Using Go</h2>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">go</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> github.com/mark3labs/kit/cmd/kit@latest</span></span></code></pre>
|
||||
<h2 id="building-from-source"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#building-from-source"><span class="icon icon-link"></span></a>Building from source</h2>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">git</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> https://github.com/mark3labs/kit.git</span></span>
|
||||
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> kit</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">go</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> build</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -o</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> ./cmd/kit</span></span></code></pre>
|
||||
<h2 id="verifying-the-installation"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#verifying-the-installation"><span class="icon icon-link"></span></a>Verifying the installation</h2>
|
||||
<p>After installing, verify Kit is available:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --help</span></span></code></pre>
|
||||
<h2 id="setting-up-a-provider"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#setting-up-a-provider"><span class="icon icon-link"></span></a>Setting up a provider</h2>
|
||||
<p>Kit needs at least one LLM provider configured. Set an API key for your preferred provider:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Anthropic (default provider)</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ANTHROPIC_API_KEY</span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"sk-..."</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># OpenAI</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> OPENAI_API_KEY</span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"sk-..."</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Google Gemini</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> GOOGLE_API_KEY</span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"..."</span></span></code></pre>
|
||||
<p>For OAuth-enabled providers like Anthropic, you can also authenticate interactively:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> auth</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> login</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> anthropic</span></span></code></pre>
|
||||
<p>See <a href="/providers">Providers</a> for the full list of supported providers and their configuration.</p>`,headings:[{depth:2,text:"Using npm / bun / pnpm",id:"using-npm--bun--pnpm"},{depth:2,text:"Using Go",id:"using-go"},{depth:2,text:"Building from source",id:"building-from-source"},{depth:2,text:"Verifying the installation",id:"verifying-the-installation"},{depth:2,text:"Setting up a provider",id:"setting-up-a-provider"}],raw:`
|
||||
# Installation
|
||||
|
||||
## Using npm / bun / pnpm
|
||||
|
||||
\`\`\`bash
|
||||
npm install -g @mark3labs/kit
|
||||
\`\`\`
|
||||
|
||||
\`\`\`bash
|
||||
bun install -g @mark3labs/kit
|
||||
\`\`\`
|
||||
|
||||
\`\`\`bash
|
||||
pnpm install -g @mark3labs/kit
|
||||
\`\`\`
|
||||
|
||||
## Using Go
|
||||
|
||||
\`\`\`bash
|
||||
go install github.com/mark3labs/kit/cmd/kit@latest
|
||||
\`\`\`
|
||||
|
||||
## Building from source
|
||||
|
||||
\`\`\`bash
|
||||
git clone https://github.com/mark3labs/kit.git
|
||||
cd kit
|
||||
go build -o kit ./cmd/kit
|
||||
\`\`\`
|
||||
|
||||
## Verifying the installation
|
||||
|
||||
After installing, verify Kit is available:
|
||||
|
||||
\`\`\`bash
|
||||
kit --help
|
||||
\`\`\`
|
||||
|
||||
## Setting up a provider
|
||||
|
||||
Kit needs at least one LLM provider configured. Set an API key for your preferred provider:
|
||||
|
||||
\`\`\`bash
|
||||
# Anthropic (default provider)
|
||||
export ANTHROPIC_API_KEY="sk-..."
|
||||
|
||||
# OpenAI
|
||||
export OPENAI_API_KEY="sk-..."
|
||||
|
||||
# Google Gemini
|
||||
export GOOGLE_API_KEY="..."
|
||||
\`\`\`
|
||||
|
||||
For OAuth-enabled providers like Anthropic, you can also authenticate interactively:
|
||||
|
||||
\`\`\`bash
|
||||
kit auth login anthropic
|
||||
\`\`\`
|
||||
|
||||
See [Providers](/providers) for the full list of supported providers and their configuration.
|
||||
`};export{s as default};
|
||||
@@ -1,240 +0,0 @@
|
||||
const s={frontmatter:{title:"JSON Output",description:"Machine-readable JSON output for scripting and automation.",hidden:!1,toc:!0,draft:!1},html:`<h1 id="json-output"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#json-output"><span class="icon icon-link"></span></a>JSON Output</h1>
|
||||
<p>Use the <code>--json</code> flag to get structured output for scripting and automation:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "Explain main.go"</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --json</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --quiet</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --no-session</span></span></code></pre>
|
||||
<h2 id="response-format"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#response-format"><span class="icon icon-link"></span></a>Response format</h2>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF"> "response"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Final assistant response text"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF"> "model"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"anthropic/claude-haiku-latest"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF"> "stop_reason"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"end_turn"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF"> "session_id"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"a1b2c3d4e5f6"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF"> "usage"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: {</span></span>
|
||||
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF"> "input_tokens"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">1024</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF"> "output_tokens"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">512</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF"> "total_tokens"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">1536</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF"> "cache_read_tokens"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">0</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF"> "cache_creation_tokens"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">0</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
|
||||
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF"> "messages"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: [</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
|
||||
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF"> "role"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"assistant"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF"> "parts"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: [</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span><span style="color:#005CC5;--shiki-dark:#79B8FF">"type"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"text"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">"data"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"..."</span><span style="color:#24292E;--shiki-dark:#E1E4E8">},</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span><span style="color:#005CC5;--shiki-dark:#79B8FF">"type"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"tool_call"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">"data"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: {</span><span style="color:#005CC5;--shiki-dark:#79B8FF">"name"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"..."</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">"args"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"..."</span><span style="color:#24292E;--shiki-dark:#E1E4E8">}},</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span><span style="color:#005CC5;--shiki-dark:#79B8FF">"type"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"tool_result"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">"data"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: {</span><span style="color:#005CC5;--shiki-dark:#79B8FF">"name"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"..."</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">"result"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"..."</span><span style="color:#24292E;--shiki-dark:#E1E4E8">}}</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> ]</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> }</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> ]</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
|
||||
<h2 id="fields"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#fields"><span class="icon icon-link"></span></a>Fields</h2>
|
||||
<h3 id="top-level"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#top-level"><span class="icon icon-link"></span></a>Top-level</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>response</code></td>
|
||||
<td>string</td>
|
||||
<td>The final assistant response text</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>model</code></td>
|
||||
<td>string</td>
|
||||
<td>The model that was used</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>stop_reason</code></td>
|
||||
<td>string</td>
|
||||
<td>Why the model stopped (e.g., <code>end_turn</code>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>session_id</code></td>
|
||||
<td>string</td>
|
||||
<td>Session identifier (omitted in <code>--no-session</code> mode)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>usage</code></td>
|
||||
<td>object</td>
|
||||
<td>Token usage statistics</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>messages</code></td>
|
||||
<td>array</td>
|
||||
<td>Full conversation history</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 id="usage"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#usage"><span class="icon icon-link"></span></a>Usage</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>input_tokens</code></td>
|
||||
<td>int</td>
|
||||
<td>Tokens sent to the model</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>output_tokens</code></td>
|
||||
<td>int</td>
|
||||
<td>Tokens generated by the model</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>total_tokens</code></td>
|
||||
<td>int</td>
|
||||
<td>Sum of input and output tokens</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>cache_read_tokens</code></td>
|
||||
<td>int</td>
|
||||
<td>Tokens read from prompt cache</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>cache_creation_tokens</code></td>
|
||||
<td>int</td>
|
||||
<td>Tokens written to prompt cache</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 id="message-parts"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#message-parts"><span class="icon icon-link"></span></a>Message parts</h3>
|
||||
<p>Each message contains a <code>parts</code> array with typed entries:</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>text</code></td>
|
||||
<td>Assistant text content</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>tool_call</code></td>
|
||||
<td>Tool invocation with name and args</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>tool_result</code></td>
|
||||
<td>Tool execution result</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>reasoning</code></td>
|
||||
<td>Extended thinking content</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>finish</code></td>
|
||||
<td>End-of-turn marker</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2 id="parsing-in-scripts"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#parsing-in-scripts"><span class="icon icon-link"></span></a>Parsing in scripts</h2>
|
||||
<h3 id="bash--jq"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#bash--jq"><span class="icon icon-link"></span></a>bash + jq</h3>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">result</span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8">$(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "Count files"</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --json</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --quiet</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --no-session</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">response</span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8">$(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">echo</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "</span><span style="color:#24292E;--shiki-dark:#E1E4E8">$result</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> jq</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -r</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> '.response'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">tokens</span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8">$(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">echo</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "</span><span style="color:#24292E;--shiki-dark:#E1E4E8">$result</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> jq</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> '.usage.total_tokens'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span></code></pre>
|
||||
<h3 id="go-sdk"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#go-sdk"><span class="icon icon-link"></span></a>Go SDK</h3>
|
||||
<p>For Go programs, use the SDK's <code>PromptResult</code> method instead of parsing JSON:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">result, err </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">PromptResult</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Count files"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">fmt.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Println</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(result.Response)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">fmt.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Println</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(result.Usage.TotalTokens)</span></span></code></pre>`,headings:[{depth:2,text:"Response format",id:"response-format"},{depth:2,text:"Fields",id:"fields"},{depth:3,text:"Top-level",id:"top-level"},{depth:3,text:"Usage",id:"usage"},{depth:3,text:"Message parts",id:"message-parts"},{depth:2,text:"Parsing in scripts",id:"parsing-in-scripts"},{depth:3,text:"bash + jq",id:"bash--jq"},{depth:3,text:"Go SDK",id:"go-sdk"}],raw:`
|
||||
# JSON Output
|
||||
|
||||
Use the \`--json\` flag to get structured output for scripting and automation:
|
||||
|
||||
\`\`\`bash
|
||||
kit "Explain main.go" --json --quiet --no-session
|
||||
\`\`\`
|
||||
|
||||
## Response format
|
||||
|
||||
\`\`\`json
|
||||
{
|
||||
"response": "Final assistant response text",
|
||||
"model": "anthropic/claude-haiku-latest",
|
||||
"stop_reason": "end_turn",
|
||||
"session_id": "a1b2c3d4e5f6",
|
||||
"usage": {
|
||||
"input_tokens": 1024,
|
||||
"output_tokens": 512,
|
||||
"total_tokens": 1536,
|
||||
"cache_read_tokens": 0,
|
||||
"cache_creation_tokens": 0
|
||||
},
|
||||
"messages": [
|
||||
{
|
||||
"role": "assistant",
|
||||
"parts": [
|
||||
{"type": "text", "data": "..."},
|
||||
{"type": "tool_call", "data": {"name": "...", "args": "..."}},
|
||||
{"type": "tool_result", "data": {"name": "...", "result": "..."}}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## Fields
|
||||
|
||||
### Top-level
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| \`response\` | string | The final assistant response text |
|
||||
| \`model\` | string | The model that was used |
|
||||
| \`stop_reason\` | string | Why the model stopped (e.g., \`end_turn\`) |
|
||||
| \`session_id\` | string | Session identifier (omitted in \`--no-session\` mode) |
|
||||
| \`usage\` | object | Token usage statistics |
|
||||
| \`messages\` | array | Full conversation history |
|
||||
|
||||
### Usage
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| \`input_tokens\` | int | Tokens sent to the model |
|
||||
| \`output_tokens\` | int | Tokens generated by the model |
|
||||
| \`total_tokens\` | int | Sum of input and output tokens |
|
||||
| \`cache_read_tokens\` | int | Tokens read from prompt cache |
|
||||
| \`cache_creation_tokens\` | int | Tokens written to prompt cache |
|
||||
|
||||
### Message parts
|
||||
|
||||
Each message contains a \`parts\` array with typed entries:
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| \`text\` | Assistant text content |
|
||||
| \`tool_call\` | Tool invocation with name and args |
|
||||
| \`tool_result\` | Tool execution result |
|
||||
| \`reasoning\` | Extended thinking content |
|
||||
| \`finish\` | End-of-turn marker |
|
||||
|
||||
## Parsing in scripts
|
||||
|
||||
### bash + jq
|
||||
|
||||
\`\`\`bash
|
||||
result=$(kit "Count files" --json --quiet --no-session)
|
||||
response=$(echo "$result" | jq -r '.response')
|
||||
tokens=$(echo "$result" | jq '.usage.total_tokens')
|
||||
\`\`\`
|
||||
|
||||
### Go SDK
|
||||
|
||||
For Go programs, use the SDK's \`PromptResult\` method instead of parsing JSON:
|
||||
|
||||
\`\`\`go
|
||||
result, err := host.PromptResult(ctx, "Count files")
|
||||
fmt.Println(result.Response)
|
||||
fmt.Println(result.Usage.TotalTokens)
|
||||
\`\`\`
|
||||
`};export{s as default};
|
||||
@@ -1,213 +0,0 @@
|
||||
const s={frontmatter:{title:"Loading Extensions",description:"How Kit discovers and loads extensions.",hidden:!1,toc:!0,draft:!1},html:`<h1 id="loading-extensions"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#loading-extensions"><span class="icon icon-link"></span></a>Loading Extensions</h1>
|
||||
<h2 id="auto-discovery"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#auto-discovery"><span class="icon icon-link"></span></a>Auto-discovery</h2>
|
||||
<p>Kit automatically discovers and loads extensions from these paths, in order:</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Path</th>
|
||||
<th>Scope</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>~/.config/kit/extensions/*.go</code></td>
|
||||
<td>Global single files</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>~/.config/kit/extensions/*/main.go</code></td>
|
||||
<td>Global subdirectory extensions</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>.kit/extensions/*.go</code></td>
|
||||
<td>Project-local single files</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>.kit/extensions/*/main.go</code></td>
|
||||
<td>Project-local subdirectory extensions</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>~/.local/share/kit/git/</code></td>
|
||||
<td>Global git-installed packages</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>.kit/git/</code></td>
|
||||
<td>Project-local git-installed packages</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2 id="explicit-loading"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#explicit-loading"><span class="icon icon-link"></span></a>Explicit loading</h2>
|
||||
<p>Load extensions by path using the <code>-e</code> flag:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> path/to/extension.go</span></span></code></pre>
|
||||
<p>Load multiple extensions:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> ext1.go</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> ext2.go</span></span></code></pre>
|
||||
<h2 id="disabling-extensions"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#disabling-extensions"><span class="icon icon-link"></span></a>Disabling extensions</h2>
|
||||
<p>Disable all auto-discovered extensions:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --no-extensions</span></span></code></pre>
|
||||
<p>You can combine <code>--no-extensions</code> with <code>-e</code> to load only specific extensions:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --no-extensions</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> my-extension.go</span></span></code></pre>
|
||||
<h2 id="installing-from-git"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#installing-from-git"><span class="icon icon-link"></span></a>Installing from git</h2>
|
||||
<p>Install extensions from git repositories using <code>kit install</code>:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Install globally (to ~/.local/share/kit/git/)</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> https://github.com/user/my-kit-extension.git</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Install project-locally (to .kit/git/)</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -l</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> https://github.com/user/my-kit-extension.git</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Update an installed package</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -u</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> https://github.com/user/my-kit-extension.git</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Remove</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --uninstall</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> my-kit-extension</span></span></code></pre>
|
||||
<h2 id="extension-structure"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#extension-structure"><span class="icon icon-link"></span></a>Extension structure</h2>
|
||||
<h3 id="single-file-extensions"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#single-file-extensions"><span class="icon icon-link"></span></a>Single-file extensions</h3>
|
||||
<p>A single <code>.go</code> file with an <code>Init</code> function:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">//go:build ignore</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">package</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> main</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "</span><span style="color:#6F42C1;--shiki-dark:#B392F0">kit/ext</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Init</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">api</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">API</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // register handlers, tools, commands, etc.</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
|
||||
<p>The <code>//go:build ignore</code> directive prevents the Go toolchain from trying to compile the file as part of a normal build.</p>
|
||||
<h3 id="subdirectory-extensions"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#subdirectory-extensions"><span class="icon icon-link"></span></a>Subdirectory extensions</h3>
|
||||
<p>For more complex extensions, create a directory with a <code>main.go</code> entry point:</p>
|
||||
<pre><code>.kit/extensions/my-extension/
|
||||
├── main.go # Must contain Init(api ext.API)
|
||||
├── helpers.go # Additional source files
|
||||
└── config.go
|
||||
</code></pre>
|
||||
<h3 id="package-level-state"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#package-level-state"><span class="icon icon-link"></span></a>Package-level state</h3>
|
||||
<p>Yaegi supports package-level variables captured in closures. This is the standard way to maintain state across event callbacks:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">package</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> main</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "</span><span style="color:#6F42C1;--shiki-dark:#B392F0">kit/ext</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">var</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> callCount </span><span style="color:#D73A49;--shiki-dark:#F97583">int</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Init</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">api</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">API</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> api.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">OnToolCall</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">_</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ToolCallEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">ctx</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Context</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> callCount</span><span style="color:#D73A49;--shiki-dark:#F97583">++</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> ctx.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SetFooter</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">HeaderFooterConfig</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Content: </span><span style="color:#6F42C1;--shiki-dark:#B392F0">ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">WidgetContent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Text: fmt.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Sprintf</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Tools called: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">%d</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, callCount),</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> })</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> })</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>`,headings:[{depth:2,text:"Auto-discovery",id:"auto-discovery"},{depth:2,text:"Explicit loading",id:"explicit-loading"},{depth:2,text:"Disabling extensions",id:"disabling-extensions"},{depth:2,text:"Installing from git",id:"installing-from-git"},{depth:2,text:"Extension structure",id:"extension-structure"},{depth:3,text:"Single-file extensions",id:"single-file-extensions"},{depth:3,text:"Subdirectory extensions",id:"subdirectory-extensions"},{depth:3,text:"Package-level state",id:"package-level-state"}],raw:`
|
||||
# Loading Extensions
|
||||
|
||||
## Auto-discovery
|
||||
|
||||
Kit automatically discovers and loads extensions from these paths, in order:
|
||||
|
||||
| Path | Scope |
|
||||
|------|-------|
|
||||
| \`~/.config/kit/extensions/*.go\` | Global single files |
|
||||
| \`~/.config/kit/extensions/*/main.go\` | Global subdirectory extensions |
|
||||
| \`.kit/extensions/*.go\` | Project-local single files |
|
||||
| \`.kit/extensions/*/main.go\` | Project-local subdirectory extensions |
|
||||
| \`~/.local/share/kit/git/\` | Global git-installed packages |
|
||||
| \`.kit/git/\` | Project-local git-installed packages |
|
||||
|
||||
## Explicit loading
|
||||
|
||||
Load extensions by path using the \`-e\` flag:
|
||||
|
||||
\`\`\`bash
|
||||
kit -e path/to/extension.go
|
||||
\`\`\`
|
||||
|
||||
Load multiple extensions:
|
||||
|
||||
\`\`\`bash
|
||||
kit -e ext1.go -e ext2.go
|
||||
\`\`\`
|
||||
|
||||
## Disabling extensions
|
||||
|
||||
Disable all auto-discovered extensions:
|
||||
|
||||
\`\`\`bash
|
||||
kit --no-extensions
|
||||
\`\`\`
|
||||
|
||||
You can combine \`--no-extensions\` with \`-e\` to load only specific extensions:
|
||||
|
||||
\`\`\`bash
|
||||
kit --no-extensions -e my-extension.go
|
||||
\`\`\`
|
||||
|
||||
## Installing from git
|
||||
|
||||
Install extensions from git repositories using \`kit install\`:
|
||||
|
||||
\`\`\`bash
|
||||
# Install globally (to ~/.local/share/kit/git/)
|
||||
kit install https://github.com/user/my-kit-extension.git
|
||||
|
||||
# Install project-locally (to .kit/git/)
|
||||
kit install -l https://github.com/user/my-kit-extension.git
|
||||
|
||||
# Update an installed package
|
||||
kit install -u https://github.com/user/my-kit-extension.git
|
||||
|
||||
# Remove
|
||||
kit install --uninstall my-kit-extension
|
||||
\`\`\`
|
||||
|
||||
## Extension structure
|
||||
|
||||
### Single-file extensions
|
||||
|
||||
A single \`.go\` file with an \`Init\` function:
|
||||
|
||||
\`\`\`go
|
||||
//go:build ignore
|
||||
|
||||
package main
|
||||
|
||||
import "kit/ext"
|
||||
|
||||
func Init(api ext.API) {
|
||||
// register handlers, tools, commands, etc.
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
The \`//go:build ignore\` directive prevents the Go toolchain from trying to compile the file as part of a normal build.
|
||||
|
||||
### Subdirectory extensions
|
||||
|
||||
For more complex extensions, create a directory with a \`main.go\` entry point:
|
||||
|
||||
\`\`\`
|
||||
.kit/extensions/my-extension/
|
||||
├── main.go # Must contain Init(api ext.API)
|
||||
├── helpers.go # Additional source files
|
||||
└── config.go
|
||||
\`\`\`
|
||||
|
||||
### Package-level state
|
||||
|
||||
Yaegi supports package-level variables captured in closures. This is the standard way to maintain state across event callbacks:
|
||||
|
||||
\`\`\`go
|
||||
package main
|
||||
|
||||
import "kit/ext"
|
||||
|
||||
var callCount int
|
||||
|
||||
func Init(api ext.API) {
|
||||
api.OnToolCall(func(_ ext.ToolCallEvent, ctx ext.Context) {
|
||||
callCount++
|
||||
ctx.SetFooter(ext.HeaderFooterConfig{
|
||||
Content: ext.WidgetContent{
|
||||
Text: fmt.Sprintf("Tools called: %d", callCount),
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
\`\`\`
|
||||
`};export{s as default};
|
||||
File diff suppressed because one or more lines are too long
@@ -1,56 +0,0 @@
|
||||
const e={frontmatter:{title:"Extension System",description:"Overview of Kit's Go-based extension system.",hidden:!1,toc:!0,draft:!1},html:`<h1 id="extension-system"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#extension-system"><span class="icon icon-link"></span></a>Extension System</h1>
|
||||
<p>Extensions are Go source files interpreted at runtime via <a href="https://github.com/traefik/yaegi">Yaegi</a>. They can add custom tools, slash commands, widgets, keyboard shortcuts, and intercept lifecycle events — all without recompiling Kit.</p>
|
||||
<h2 id="minimal-extension"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#minimal-extension"><span class="icon icon-link"></span></a>Minimal extension</h2>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">//go:build ignore</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">package</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> main</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "</span><span style="color:#6F42C1;--shiki-dark:#B392F0">kit/ext</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Init</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">api</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">API</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> api.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">OnSessionStart</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">_</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SessionStartEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">ctx</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Context</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> ctx.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SetFooter</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">HeaderFooterConfig</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Content: </span><span style="color:#6F42C1;--shiki-dark:#B392F0">ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">WidgetContent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{Text: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Custom Footer"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">},</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> })</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> })</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
|
||||
<p>Run it with:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> examples/extensions/minimal.go</span></span></code></pre>
|
||||
<h2 id="how-extensions-work"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#how-extensions-work"><span class="icon icon-link"></span></a>How extensions work</h2>
|
||||
<ol>
|
||||
<li>Kit discovers extension files from <a href="/extensions/loading">auto-discovery paths</a> or explicit <code>-e</code> flags</li>
|
||||
<li>Each <code>.go</code> file is loaded into a Yaegi interpreter with access to the <code>kit/ext</code> package</li>
|
||||
<li>Kit calls the <code>Init(api ext.API)</code> function in each extension</li>
|
||||
<li>The extension registers callbacks, tools, commands, and UI components via the <code>api</code> and <code>ctx</code> objects</li>
|
||||
</ol>
|
||||
<h2 id="key-concepts"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#key-concepts"><span class="icon icon-link"></span></a>Key concepts</h2>
|
||||
<h3 id="the-api-object"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#the-api-object"><span class="icon icon-link"></span></a>The <code>API</code> object</h3>
|
||||
<p>Passed to <code>Init()</code>, the <code>API</code> object is used to register lifecycle event handlers and static components:</p>
|
||||
<ul>
|
||||
<li><strong>Lifecycle handlers</strong> — <code>api.OnSessionStart(...)</code>, <code>api.OnToolCall(...)</code>, etc.</li>
|
||||
<li><strong>Tools</strong> — <code>api.RegisterTool(ext.ToolDef{...})</code></li>
|
||||
<li><strong>Commands</strong> — <code>api.RegisterCommand(ext.CommandDef{...})</code></li>
|
||||
<li><strong>Shortcuts</strong> — <code>api.RegisterShortcut(ext.ShortcutDef{...}, handler)</code></li>
|
||||
<li><strong>Tool renderers</strong> — <code>api.RegisterToolRenderer(ext.ToolRenderConfig{...})</code></li>
|
||||
<li><strong>Message renderers</strong> — <code>api.RegisterMessageRenderer(ext.MessageRendererConfig{...})</code></li>
|
||||
<li><strong>Options</strong> — <code>api.RegisterOption(ext.OptionDef{...})</code></li>
|
||||
</ul>
|
||||
<h3 id="the-context-object"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#the-context-object"><span class="icon icon-link"></span></a>The <code>Context</code> object</h3>
|
||||
<p>Passed to event handlers, the <code>Context</code> object provides runtime access to Kit's state and UI:</p>
|
||||
<ul>
|
||||
<li><strong>Output</strong> — <code>ctx.Print(...)</code>, <code>ctx.PrintInfo(...)</code>, <code>ctx.PrintError(...)</code></li>
|
||||
<li><strong>UI components</strong> — <code>ctx.SetWidget(...)</code>, <code>ctx.SetHeader(...)</code>, <code>ctx.SetFooter(...)</code>, <code>ctx.SetStatus(...)</code></li>
|
||||
<li><strong>Editor</strong> — <code>ctx.SetEditor(...)</code>, <code>ctx.ResetEditor()</code></li>
|
||||
<li><strong>Prompts</strong> — <code>ctx.PromptSelect(...)</code>, <code>ctx.PromptConfirm(...)</code>, <code>ctx.PromptInput(...)</code></li>
|
||||
<li><strong>Overlays</strong> — <code>ctx.ShowOverlay(...)</code></li>
|
||||
<li><strong>Messages</strong> — <code>ctx.SendMessage(...)</code>, <code>ctx.GetMessages()</code></li>
|
||||
<li><strong>Model</strong> — <code>ctx.SetModel(...)</code>, <code>ctx.GetAvailableModels()</code></li>
|
||||
<li><strong>Tools</strong> — <code>ctx.GetAllTools()</code>, <code>ctx.SetActiveTools(...)</code></li>
|
||||
<li><strong>Context stats</strong> — <code>ctx.GetContextStats()</code></li>
|
||||
<li><strong>Session data</strong> — <code>ctx.AppendEntry(...)</code>, <code>ctx.GetEntries(...)</code> (append-only, in conversation tree)</li>
|
||||
<li><strong>Session state</strong> — <code>ctx.SetState(...)</code>, <code>ctx.GetState(...)</code>, <code>ctx.DeleteState(...)</code>, <code>ctx.ListState()</code> (last-write-wins, sidecar file)</li>
|
||||
<li><strong>Subagents</strong> — <code>ctx.SpawnSubagent(...)</code></li>
|
||||
<li><strong>LLM completion</strong> — <code>ctx.Complete(...)</code></li>
|
||||
<li><strong>Custom events</strong> — <code>ctx.EmitCustomEvent(...)</code></li>
|
||||
</ul>
|
||||
<p>See <a href="/extensions/capabilities">Capabilities</a> for full details on each component type, and <a href="/extensions/testing">Testing</a> for writing tests for your extensions.</p>`,headings:[{depth:2,text:"Minimal extension",id:"minimal-extension"},{depth:2,text:"How extensions work",id:"how-extensions-work"},{depth:2,text:"Key concepts",id:"key-concepts"},{depth:3,text:"The API object",id:"the-api-object"},{depth:3,text:"The Context object",id:"the-context-object"}],raw:'\n# Extension System\n\nExtensions are Go source files interpreted at runtime via [Yaegi](https://github.com/traefik/yaegi). They can add custom tools, slash commands, widgets, keyboard shortcuts, and intercept lifecycle events — all without recompiling Kit.\n\n## Minimal extension\n\n```go\n//go:build ignore\n\npackage main\n\nimport "kit/ext"\n\nfunc Init(api ext.API) {\n api.OnSessionStart(func(_ ext.SessionStartEvent, ctx ext.Context) {\n ctx.SetFooter(ext.HeaderFooterConfig{\n Content: ext.WidgetContent{Text: "Custom Footer"},\n })\n })\n}\n```\n\nRun it with:\n\n```bash\nkit -e examples/extensions/minimal.go\n```\n\n## How extensions work\n\n1. Kit discovers extension files from [auto-discovery paths](/extensions/loading) or explicit `-e` flags\n2. Each `.go` file is loaded into a Yaegi interpreter with access to the `kit/ext` package\n3. Kit calls the `Init(api ext.API)` function in each extension\n4. The extension registers callbacks, tools, commands, and UI components via the `api` and `ctx` objects\n\n## Key concepts\n\n### The `API` object\n\nPassed to `Init()`, the `API` object is used to register lifecycle event handlers and static components:\n\n- **Lifecycle handlers** — `api.OnSessionStart(...)`, `api.OnToolCall(...)`, etc.\n- **Tools** — `api.RegisterTool(ext.ToolDef{...})`\n- **Commands** — `api.RegisterCommand(ext.CommandDef{...})`\n- **Shortcuts** — `api.RegisterShortcut(ext.ShortcutDef{...}, handler)`\n- **Tool renderers** — `api.RegisterToolRenderer(ext.ToolRenderConfig{...})`\n- **Message renderers** — `api.RegisterMessageRenderer(ext.MessageRendererConfig{...})`\n- **Options** — `api.RegisterOption(ext.OptionDef{...})`\n\n### The `Context` object\n\nPassed to event handlers, the `Context` object provides runtime access to Kit\'s state and UI:\n\n- **Output** — `ctx.Print(...)`, `ctx.PrintInfo(...)`, `ctx.PrintError(...)`\n- **UI components** — `ctx.SetWidget(...)`, `ctx.SetHeader(...)`, `ctx.SetFooter(...)`, `ctx.SetStatus(...)`\n- **Editor** — `ctx.SetEditor(...)`, `ctx.ResetEditor()`\n- **Prompts** — `ctx.PromptSelect(...)`, `ctx.PromptConfirm(...)`, `ctx.PromptInput(...)`\n- **Overlays** — `ctx.ShowOverlay(...)`\n- **Messages** — `ctx.SendMessage(...)`, `ctx.GetMessages()`\n- **Model** — `ctx.SetModel(...)`, `ctx.GetAvailableModels()`\n- **Tools** — `ctx.GetAllTools()`, `ctx.SetActiveTools(...)`\n- **Context stats** — `ctx.GetContextStats()`\n- **Session data** — `ctx.AppendEntry(...)`, `ctx.GetEntries(...)` (append-only, in conversation tree)\n- **Session state** — `ctx.SetState(...)`, `ctx.GetState(...)`, `ctx.DeleteState(...)`, `ctx.ListState()` (last-write-wins, sidecar file)\n- **Subagents** — `ctx.SpawnSubagent(...)`\n- **LLM completion** — `ctx.Complete(...)`\n- **Custom events** — `ctx.EmitCustomEvent(...)`\n\nSee [Capabilities](/extensions/capabilities) for full details on each component type, and [Testing](/extensions/testing) for writing tests for your extensions.\n'};export{e as default};
|
||||
@@ -1,797 +0,0 @@
|
||||
const s={frontmatter:{title:"Go SDK",description:"Embed Kit in your Go applications.",hidden:!1,toc:!0,draft:!1},html:`<h1 id="go-sdk"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#go-sdk"><span class="icon icon-link"></span></a>Go SDK</h1>
|
||||
<p>The <code>pkg/kit</code> package lets you embed Kit as a library in your Go applications.</p>
|
||||
<h2 id="installation"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#installation"><span class="icon icon-link"></span></a>Installation</h2>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">go</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> get</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> github.com/mark3labs/kit/pkg/kit</span></span></code></pre>
|
||||
<h2 id="basic-usage"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#basic-usage"><span class="icon icon-link"></span></a>Basic usage</h2>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">package</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> main</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF"> "</span><span style="color:#6F42C1;--shiki-dark:#B392F0">context</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF"> "</span><span style="color:#6F42C1;--shiki-dark:#B392F0">log</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> kit </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span><span style="color:#6F42C1;--shiki-dark:#B392F0">github.com/mark3labs/kit/pkg/kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> main</span><span style="color:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> ctx </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Background</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Create Kit instance with default configuration</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> host, err </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">nil</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583"> if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> err </span><span style="color:#D73A49;--shiki-dark:#F97583">!=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> nil</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> log.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Fatal</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(err)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> }</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583"> defer</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Close</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Send a prompt</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> response, err </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Prompt</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"What is 2+2?"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583"> if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> err </span><span style="color:#D73A49;--shiki-dark:#F97583">!=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> nil</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> log.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Fatal</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(err)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> }</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0"> println</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(response)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
|
||||
<h2 id="functional-options-newagent"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#functional-options-newagent"><span class="icon icon-link"></span></a>Functional options (<code>NewAgent</code>)</h2>
|
||||
<p>For simple programmatic setups, <code>kit.NewAgent</code> offers an ergonomic
|
||||
functional-options front door over <code>kit.New</code>. Streaming is <strong>enabled by
|
||||
default</strong>; pass <code>kit.WithStreaming(false)</code> to opt out.</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host, err </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">NewAgent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">WithModel</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"anthropic/claude-sonnet-4-5-20250929"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">),</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">WithSystemPrompt</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"You are a helpful assistant."</span><span style="color:#24292E;--shiki-dark:#E1E4E8">),</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">WithMaxTokens</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">8192</span><span style="color:#24292E;--shiki-dark:#E1E4E8">),</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">WithThinkingLevel</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"medium"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">),</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Ephemeral</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="color:#6A737D;--shiki-dark:#6A737D">// in-memory session, no persistence</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> err </span><span style="color:#D73A49;--shiki-dark:#F97583">!=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> nil</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> log.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Fatal</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(err)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">defer</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Close</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span></code></pre>
|
||||
<p>Available options:</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Option</th>
|
||||
<th>Sets</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>WithModel(string)</code></td>
|
||||
<td><code>Options.Model</code> (provider/model format)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>WithSystemPrompt(string)</code></td>
|
||||
<td><code>Options.SystemPrompt</code> (inline text or file path)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>WithStreaming(bool)</code></td>
|
||||
<td><code>Options.Streaming</code> (default <code>true</code> under <code>NewAgent</code>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>WithMaxTokens(int)</code></td>
|
||||
<td><code>Options.MaxTokens</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>WithThinkingLevel(string)</code></td>
|
||||
<td><code>Options.ThinkingLevel</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>WithTools(...Tool)</code></td>
|
||||
<td><code>Options.Tools</code> (replaces the default set)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>WithExtraTools(...Tool)</code></td>
|
||||
<td><code>Options.ExtraTools</code> (adds alongside defaults)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>WithProviderAPIKey(string)</code></td>
|
||||
<td><code>Options.ProviderAPIKey</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>WithProviderURL(string)</code></td>
|
||||
<td><code>Options.ProviderURL</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>WithConfigFile(string)</code></td>
|
||||
<td><code>Options.ConfigFile</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>WithDebug()</code></td>
|
||||
<td><code>Options.Debug = true</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Ephemeral()</code></td>
|
||||
<td><code>Options.NoSession = true</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Options are applied in order, so later options override earlier ones. <code>Option</code>
|
||||
is a plain <code>func(*Options)</code>, so you can define your own. For advanced
|
||||
configuration not covered by the helpers (custom MCP config, in-process MCP
|
||||
servers, session backends, MCP task tuning) construct an <code>Options</code> value
|
||||
explicitly and call <code>kit.New</code>.</p>
|
||||
<h3 id="when-to-use-which"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#when-to-use-which"><span class="icon icon-link"></span></a>When to use which</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Constructor</th>
|
||||
<th>Use when</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>kit.NewAgent(ctx, ...Option)</code></td>
|
||||
<td>Quick programmatic setups; you only need the common fields. Streaming defaults on.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>kit.New(ctx, *Options)</code></td>
|
||||
<td>You need fields without a <code>With*</code> helper (<code>MCPConfig</code>, <code>InProcessMCPServers</code>, <code>SessionManager</code>, MCP task tuning, etc.), or you already hold an <code>Options</code> value.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2 id="per-instance-config-isolation"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#per-instance-config-isolation"><span class="icon icon-link"></span></a>Per-instance config isolation</h2>
|
||||
<p>Each <code>kit.New</code> / <code>kit.NewAgent</code> call owns an <strong>isolated configuration store</strong>,
|
||||
so constructing multiple Kit instances in the same process is safe: setting the
|
||||
model, thinking level, or generation parameters on one never affects another,
|
||||
and runtime mutators (<code>SetModel</code>, <code>SetThinkingLevel</code>) only touch the owning
|
||||
instance. This makes subagent spawning and multi-Kit embedding race-free with
|
||||
no external synchronization required.</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">a, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">NewAgent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">WithThinkingLevel</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"low"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">))</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">b, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">NewAgent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">WithThinkingLevel</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"high"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">))</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">a.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SetThinkingLevel</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"medium"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// a.GetThinkingLevel() == "medium"; b.GetThinkingLevel() is still "high"</span></span></code></pre>
|
||||
<h2 id="multi-turn-conversations"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#multi-turn-conversations"><span class="icon icon-link"></span></a>Multi-turn conversations</h2>
|
||||
<p>Conversations retain context automatically across calls:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Prompt</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"My name is Alice"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">response, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Prompt</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"What's my name?"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// response: "Your name is Alice"</span></span></code></pre>
|
||||
<h2 id="additional-prompt-methods"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#additional-prompt-methods"><span class="icon icon-link"></span></a>Additional prompt methods</h2>
|
||||
<p>The SDK provides several prompt variants:</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Method</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>Prompt(ctx, message)</code></td>
|
||||
<td>Simple prompt, returns response string</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>PromptWithOptions(ctx, message, opts)</code></td>
|
||||
<td>With per-call options</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>PromptResult(ctx, message)</code></td>
|
||||
<td>Returns full <code>TurnResult</code> with usage stats</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>PromptResultWithFiles(ctx, message, files)</code></td>
|
||||
<td>Multimodal with file attachments</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Steer(ctx, instruction)</code></td>
|
||||
<td>System-level steering without user message</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>FollowUp(ctx, text)</code></td>
|
||||
<td>Continue without new user input</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2 id="custom-tools"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#custom-tools"><span class="icon icon-link"></span></a>Custom tools</h2>
|
||||
<p>Create custom tools with <code>kit.NewTool</code>. The JSON schema is auto-generated from the input struct — no external dependencies required:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">type</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> WeatherInput</span><span style="color:#D73A49;--shiki-dark:#F97583"> struct</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> City </span><span style="color:#D73A49;--shiki-dark:#F97583">string</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> \`json:"city" description:"City name"\`</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">weatherTool </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">NewTool</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"get_weather"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Get current weather for a city"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583"> func</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">ctx</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> context</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Context</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">input</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> WeatherInput</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) (</span><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ToolOutput</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#D73A49;--shiki-dark:#F97583">error</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583"> return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">TextResult</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"72°F, sunny in "</span><span style="color:#D73A49;--shiki-dark:#F97583"> +</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> input.City), </span><span style="color:#005CC5;--shiki-dark:#79B8FF">nil</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#D73A49;--shiki-dark:#F97583">&</span><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Options</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> ExtraTools: []</span><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Tool</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{weatherTool},</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span></code></pre>
|
||||
<p>Struct tags control the schema:</p>
|
||||
<ul>
|
||||
<li><code>json:"name"</code> — parameter name</li>
|
||||
<li><code>description:"..."</code> — description shown to the LLM</li>
|
||||
<li><code>enum:"a,b,c"</code> — restrict valid values</li>
|
||||
<li><code>omitempty</code> — marks the parameter as optional</li>
|
||||
</ul>
|
||||
<p>Return values:</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Helper</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>kit.TextResult(s)</code></td>
|
||||
<td>Successful text result</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>kit.ErrorResult(s)</code></td>
|
||||
<td>Error result (LLM sees it as a tool error)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>kit.ImageResult(s, data, mediaType)</code></td>
|
||||
<td>Image result with binary data (e.g. <code>"image/png"</code>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>kit.MediaResult(s, data, mediaType)</code></td>
|
||||
<td>Non-image media result (e.g. <code>"audio/mpeg"</code>)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Binary data (images, audio, etc.) in <code>ToolOutput.Data</code> is automatically forwarded to the LLM when <code>MediaType</code> is set. For advanced use, return a <code>kit.ToolOutput</code> struct directly with <code>Data</code>, <code>MediaType</code>, and <code>Metadata</code> fields.</p>
|
||||
<p>Use <code>kit.NewParallelTool</code> for tools that are safe to run concurrently. Use <code>kit.ToolCallIDFromContext(ctx)</code> to retrieve the LLM-assigned call ID for logging or tracing.</p>
|
||||
<h2 id="generation--provider-overrides"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#generation--provider-overrides"><span class="icon icon-link"></span></a>Generation & provider overrides</h2>
|
||||
<p>SDK consumers can configure generation parameters and provider endpoints
|
||||
entirely in-code via <code>Options</code>, without touching <code>.kit.yml</code> or <code>viper.Set()</code>:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#D73A49;--shiki-dark:#F97583">&</span><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Options</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Model: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"anthropic/claude-sonnet-4-5-20250929"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> MaxTokens: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">16384</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#6A737D;--shiki-dark:#6A737D">// 0 = auto-resolve (env → config → per-model → floor)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> ThinkingLevel: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"high"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#6A737D;--shiki-dark:#6A737D">// "off" | "none" | "minimal" | "low" | "medium" | "high"</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Temperature: </span><span style="color:#6F42C1;--shiki-dark:#B392F0">ptrFloat32</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">0.2</span><span style="color:#24292E;--shiki-dark:#E1E4E8">), </span><span style="color:#6A737D;--shiki-dark:#6A737D">// nil = provider/per-model default</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> ProviderAPIKey: os.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Getenv</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"MY_SECRET"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">), </span><span style="color:#6A737D;--shiki-dark:#6A737D">// overrides pre-existing viper state</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> ProviderURL: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"https://proxy.internal/v1"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ptrFloat32</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">v</span><span style="color:#D73A49;--shiki-dark:#F97583"> float32</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">*float32</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="color:#D73A49;--shiki-dark:#F97583">return</span><span style="color:#D73A49;--shiki-dark:#F97583"> &</span><span style="color:#24292E;--shiki-dark:#E1E4E8">v }</span></span></code></pre>
|
||||
<p>See <a href="/sdk/options#generation-parameters">Options</a> for the full field reference,
|
||||
including <code>TopP</code>, <code>TopK</code>, <code>FrequencyPenalty</code>, <code>PresencePenalty</code>, and <code>TLSSkipVerify</code>.</p>
|
||||
<h2 id="event-system"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#event-system"><span class="icon icon-link"></span></a>Event system</h2>
|
||||
<p>Subscribe to events for monitoring:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">unsubscribe </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">OnToolCall</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">event</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ToolCallEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> fmt.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Println</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Tool called:"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, event.Name)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">defer</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> unsubscribe</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">OnToolResult</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">event</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ToolResultEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> fmt.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Println</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Tool result:"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, event.Name)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">OnMessageUpdate</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">event</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">MessageUpdateEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> fmt.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Print</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(event.Chunk)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span></code></pre>
|
||||
<h2 id="model-management"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#model-management"><span class="icon icon-link"></span></a>Model management</h2>
|
||||
<p>Switch models at runtime:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SetModel</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"openai/gpt-4o"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">info </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">GetModelInfo</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">models </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">GetAvailableModels</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span></code></pre>
|
||||
<h2 id="dynamic-mcp-servers"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#dynamic-mcp-servers"><span class="icon icon-link"></span></a>Dynamic MCP servers</h2>
|
||||
<p>Add and remove MCP servers at runtime:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">n, err </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AddMCPServer</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"github"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">MCPServerConfig</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Command: []</span><span style="color:#D73A49;--shiki-dark:#F97583">string</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"npx"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"-y"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"@modelcontextprotocol/server-github"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">},</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">fmt.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Printf</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Loaded </span><span style="color:#005CC5;--shiki-dark:#79B8FF">%d</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> tools</span><span style="color:#005CC5;--shiki-dark:#79B8FF">\\n</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, n)</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">err </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">RemoveMCPServer</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"github"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">servers </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ListMCPServers</span><span style="color:#24292E;--shiki-dark:#E1E4E8">() </span><span style="color:#6A737D;--shiki-dark:#6A737D">// []kit.MCPServerStatus</span></span></code></pre>
|
||||
<h3 id="in-process-mcp-servers"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#in-process-mcp-servers"><span class="icon icon-link"></span></a>In-process MCP servers</h3>
|
||||
<p>Register mcp-go servers running in the same process — zero subprocess overhead:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF"> "</span><span style="color:#6F42C1;--shiki-dark:#B392F0">github.com/mark3labs/mcp-go/mcp</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF"> "</span><span style="color:#6F42C1;--shiki-dark:#B392F0">github.com/mark3labs/mcp-go/server</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">mcpSrv </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> server.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">NewMCPServer</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"my-tools"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"1.0.0"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> server.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">WithToolCapabilities</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">true</span><span style="color:#24292E;--shiki-dark:#E1E4E8">),</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">mcpSrv.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AddTool</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(mcp.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">NewTool</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"search_docs"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> mcp.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">WithDescription</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Search documentation"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">),</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> mcp.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">WithString</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"query"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, mcp.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Required</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()),</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">), searchHandler)</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// At init time</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#D73A49;--shiki-dark:#F97583">&</span><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Options</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> InProcessMCPServers: </span><span style="color:#D73A49;--shiki-dark:#F97583">map</span><span style="color:#24292E;--shiki-dark:#E1E4E8">[</span><span style="color:#D73A49;--shiki-dark:#F97583">string</span><span style="color:#24292E;--shiki-dark:#E1E4E8">]</span><span style="color:#D73A49;--shiki-dark:#F97583">*</span><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">MCPServer</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF"> "docs"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: mcpSrv,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Or at runtime</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">n, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AddInProcessMCPServer</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"docs"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, mcpSrv)</span></span></code></pre>
|
||||
<h2 id="runtime-skills-and-context-files"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#runtime-skills-and-context-files"><span class="icon icon-link"></span></a>Runtime skills and context files</h2>
|
||||
<p>Kit auto-discovers skills and <code>AGENTS.md</code>-style context files during <code>New()</code>,
|
||||
but multi-tenant hosts (chatbots, web services, per-user agents) often need
|
||||
to swap these <strong>after</strong> construction. The runtime mutators below recompose
|
||||
the system prompt and apply it to the agent so the next turn picks up the
|
||||
updated instructions — no restart, no file shuffling.</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Add a programmatic skill — no file on disk required.</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AddSkill</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#D73A49;--shiki-dark:#F97583">&</span><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Skill</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Name: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"polite-french"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Description: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Respond in French and always greet the user."</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Content: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Always reply in French. Open every response with 'Bonjour'."</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Or load one from disk.</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">LoadAndAddSkill</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"/var/skills/refund-policy.md"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Project context (AGENTS.md equivalents): inline content from a DB...</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AddContextFileContent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> fmt.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Sprintf</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"session://</span><span style="color:#005CC5;--shiki-dark:#79B8FF">%s</span><span style="color:#032F62;--shiki-dark:#9ECBFF">/AGENTS.md"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, userID),</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> rulesFromDB,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// ...or load from disk.</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">LoadAndAddContextFile</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"/etc/agents/tenant-acme.md"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Remove individually when a session ends.</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">RemoveSkill</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"polite-french"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">RemoveContextFile</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(fmt.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Sprintf</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"session://</span><span style="color:#005CC5;--shiki-dark:#79B8FF">%s</span><span style="color:#032F62;--shiki-dark:#9ECBFF">/AGENTS.md"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, userID))</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Or replace the whole set in one call.</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SetSkills</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(activeSkillsForUser)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SetContextFiles</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(activeContextForUser)</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Inspect current state (snapshot copies — safe to mutate).</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">skills </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">GetSkills</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">ctxFiles </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">GetContextFiles</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span></code></pre>
|
||||
<p>Key points:</p>
|
||||
<ul>
|
||||
<li><strong>Auto-refresh.</strong> Every <code>Add*</code> / <code>Remove*</code> / <code>Set*</code> call recomposes the system
|
||||
prompt against the captured base prompt (preserving per-model overrides and
|
||||
<code>--system-prompt</code> resolution) and pushes the result onto the agent. Call
|
||||
<code>host.RefreshSystemPrompt()</code> only if you mutate state through a different
|
||||
path and need to force a re-render.</li>
|
||||
<li><strong>Dedup keys.</strong> Skills dedupe by <code>Name</code>; context files dedupe by <code>Path</code>.
|
||||
Re-adding the same key replaces the entry instead of appending a duplicate.</li>
|
||||
<li><strong>Path is opaque.</strong> <code>ContextFile.Path</code> does not have to point at a real file
|
||||
— it's only used for dedup and for the <code>Instructions from: <Path></code> header
|
||||
injected into the prompt. URIs like <code>session://user-123/AGENTS.md</code> work fine.</li>
|
||||
<li><strong>Thread safety.</strong> All readers and mutators are safe to call concurrently
|
||||
from multiple goroutines; the underlying state is guarded by an internal
|
||||
<code>RWMutex</code>.</li>
|
||||
<li><strong>Init-time options still apply.</strong> <code>Options.Skills</code>, <code>Options.SkillsDir</code>,
|
||||
<code>Options.NoSkills</code>, and <code>Options.NoContextFiles</code> continue to control the
|
||||
startup set; the runtime API mutates from whatever state <code>New()</code> produced.
|
||||
See <a href="/sdk/options#skills--configuration">SDK options</a>.</li>
|
||||
</ul>
|
||||
<h2 id="mcp-prompts-and-resources"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#mcp-prompts-and-resources"><span class="icon icon-link"></span></a>MCP prompts and resources</h2>
|
||||
<p>Query prompts and resources exposed by connected MCP servers:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// List and expand prompts</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">prompts </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ListMCPPrompts</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">result, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">GetMCPPrompt</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"server"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"prompt-name"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#D73A49;--shiki-dark:#F97583">map</span><span style="color:#24292E;--shiki-dark:#E1E4E8">[</span><span style="color:#D73A49;--shiki-dark:#F97583">string</span><span style="color:#24292E;--shiki-dark:#E1E4E8">]</span><span style="color:#D73A49;--shiki-dark:#F97583">string</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"key"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"value"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// List and read resources</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">resources </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ListMCPResources</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">content, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ReadMCPResource</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"server"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"file:///path"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span></code></pre>
|
||||
<h2 id="mcp-tasks-long-running-tools"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#mcp-tasks-long-running-tools"><span class="icon icon-link"></span></a>MCP tasks (long-running tools)</h2>
|
||||
<p>Kit advertises <a href="https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks">MCP task support</a>
|
||||
during <code>initialize</code>, so cooperating servers can return a <code>taskId</code> immediately
|
||||
and let Kit poll <code>tasks/get</code> / <code>tasks/result</code> until the operation completes.
|
||||
This avoids HTTP/SSE proxy timeouts on long tools and gives you clean
|
||||
cancellation via context.</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#D73A49;--shiki-dark:#F97583">&</span><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Options</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> MCPTaskMode: </span><span style="color:#D73A49;--shiki-dark:#F97583">map</span><span style="color:#24292E;--shiki-dark:#E1E4E8">[</span><span style="color:#D73A49;--shiki-dark:#F97583">string</span><span style="color:#24292E;--shiki-dark:#E1E4E8">]</span><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">MCPTaskMode</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF"> "build-server"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: kit.MCPTaskModeAlways,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> MCPTaskProgress: </span><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">p</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">MCPTaskProgress</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> log.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Printf</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span><span style="color:#005CC5;--shiki-dark:#79B8FF">%s</span><span style="color:#032F62;--shiki-dark:#9ECBFF">: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">%s</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, p.TaskID, p.Status)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Inspect / cancel in-flight tasks</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">tasks, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ListMCPTasks</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"build-server"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">_, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">CancelMCPTask</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"build-server"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, tasks[</span><span style="color:#005CC5;--shiki-dark:#79B8FF">0</span><span style="color:#24292E;--shiki-dark:#E1E4E8">].TaskID)</span></span></code></pre>
|
||||
<p>Defaults to <code>MCPTaskModeAuto</code> per server, so any existing MCP server keeps
|
||||
its previous synchronous behaviour. See <a href="/sdk/options#mcp-tasks">SDK options → MCP Tasks</a>
|
||||
for the full surface.</p>
|
||||
<h2 id="context-and-compaction"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#context-and-compaction"><span class="icon icon-link"></span></a>Context and compaction</h2>
|
||||
<p>Monitor and manage context usage:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">tokens </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">EstimateContextTokens</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">stats </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">GetContextStats</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ShouldCompact</span><span style="color:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> result, err </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Compact</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">nil</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">""</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
|
||||
<h2 id="in-process-subagents"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#in-process-subagents"><span class="icon icon-link"></span></a>In-process subagents</h2>
|
||||
<p>Spawn child Kit instances without subprocess overhead:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">result, err </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Subagent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SubagentConfig</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Prompt: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Analyze the test files"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Model: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"anthropic/claude-haiku-3-5-20241022"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> NoSession: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">true</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Timeout: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">2</span><span style="color:#D73A49;--shiki-dark:#F97583"> *</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> time.Minute,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span></code></pre>
|
||||
<p>See <a href="/sdk/options">Options</a>, <a href="/sdk/callbacks">Callbacks</a>, and <a href="/sdk/sessions">Sessions</a> for more details.</p>`,headings:[{depth:2,text:"Installation",id:"installation"},{depth:2,text:"Basic usage",id:"basic-usage"},{depth:2,text:"Functional options (NewAgent)",id:"functional-options-newagent"},{depth:3,text:"When to use which",id:"when-to-use-which"},{depth:2,text:"Per-instance config isolation",id:"per-instance-config-isolation"},{depth:2,text:"Multi-turn conversations",id:"multi-turn-conversations"},{depth:2,text:"Additional prompt methods",id:"additional-prompt-methods"},{depth:2,text:"Custom tools",id:"custom-tools"},{depth:2,text:"Generation & provider overrides",id:"generation--provider-overrides"},{depth:2,text:"Event system",id:"event-system"},{depth:2,text:"Model management",id:"model-management"},{depth:2,text:"Dynamic MCP servers",id:"dynamic-mcp-servers"},{depth:3,text:"In-process MCP servers",id:"in-process-mcp-servers"},{depth:2,text:"Runtime skills and context files",id:"runtime-skills-and-context-files"},{depth:2,text:"MCP prompts and resources",id:"mcp-prompts-and-resources"},{depth:2,text:"MCP tasks (long-running tools)",id:"mcp-tasks-long-running-tools"},{depth:2,text:"Context and compaction",id:"context-and-compaction"},{depth:2,text:"In-process subagents",id:"in-process-subagents"}],raw:`
|
||||
# Go SDK
|
||||
|
||||
The \`pkg/kit\` package lets you embed Kit as a library in your Go applications.
|
||||
|
||||
## Installation
|
||||
|
||||
\`\`\`bash
|
||||
go get github.com/mark3labs/kit/pkg/kit
|
||||
\`\`\`
|
||||
|
||||
## Basic usage
|
||||
|
||||
\`\`\`go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
kit "github.com/mark3labs/kit/pkg/kit"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
|
||||
// Create Kit instance with default configuration
|
||||
host, err := kit.New(ctx, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer host.Close()
|
||||
|
||||
// Send a prompt
|
||||
response, err := host.Prompt(ctx, "What is 2+2?")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
println(response)
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## Functional options (\`NewAgent\`)
|
||||
|
||||
For simple programmatic setups, \`kit.NewAgent\` offers an ergonomic
|
||||
functional-options front door over \`kit.New\`. Streaming is **enabled by
|
||||
default**; pass \`kit.WithStreaming(false)\` to opt out.
|
||||
|
||||
\`\`\`go
|
||||
host, err := kit.NewAgent(ctx,
|
||||
kit.WithModel("anthropic/claude-sonnet-4-5-20250929"),
|
||||
kit.WithSystemPrompt("You are a helpful assistant."),
|
||||
kit.WithMaxTokens(8192),
|
||||
kit.WithThinkingLevel("medium"),
|
||||
kit.Ephemeral(), // in-memory session, no persistence
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer host.Close()
|
||||
\`\`\`
|
||||
|
||||
Available options:
|
||||
|
||||
| Option | Sets |
|
||||
|--------|------|
|
||||
| \`WithModel(string)\` | \`Options.Model\` (provider/model format) |
|
||||
| \`WithSystemPrompt(string)\` | \`Options.SystemPrompt\` (inline text or file path) |
|
||||
| \`WithStreaming(bool)\` | \`Options.Streaming\` (default \`true\` under \`NewAgent\`) |
|
||||
| \`WithMaxTokens(int)\` | \`Options.MaxTokens\` |
|
||||
| \`WithThinkingLevel(string)\` | \`Options.ThinkingLevel\` |
|
||||
| \`WithTools(...Tool)\` | \`Options.Tools\` (replaces the default set) |
|
||||
| \`WithExtraTools(...Tool)\` | \`Options.ExtraTools\` (adds alongside defaults) |
|
||||
| \`WithProviderAPIKey(string)\` | \`Options.ProviderAPIKey\` |
|
||||
| \`WithProviderURL(string)\` | \`Options.ProviderURL\` |
|
||||
| \`WithConfigFile(string)\` | \`Options.ConfigFile\` |
|
||||
| \`WithDebug()\` | \`Options.Debug = true\` |
|
||||
| \`Ephemeral()\` | \`Options.NoSession = true\` |
|
||||
|
||||
Options are applied in order, so later options override earlier ones. \`Option\`
|
||||
is a plain \`func(*Options)\`, so you can define your own. For advanced
|
||||
configuration not covered by the helpers (custom MCP config, in-process MCP
|
||||
servers, session backends, MCP task tuning) construct an \`Options\` value
|
||||
explicitly and call \`kit.New\`.
|
||||
|
||||
### When to use which
|
||||
|
||||
| Constructor | Use when |
|
||||
|-------------|----------|
|
||||
| \`kit.NewAgent(ctx, ...Option)\` | Quick programmatic setups; you only need the common fields. Streaming defaults on. |
|
||||
| \`kit.New(ctx, *Options)\` | You need fields without a \`With*\` helper (\`MCPConfig\`, \`InProcessMCPServers\`, \`SessionManager\`, MCP task tuning, etc.), or you already hold an \`Options\` value. |
|
||||
|
||||
## Per-instance config isolation
|
||||
|
||||
Each \`kit.New\` / \`kit.NewAgent\` call owns an **isolated configuration store**,
|
||||
so constructing multiple Kit instances in the same process is safe: setting the
|
||||
model, thinking level, or generation parameters on one never affects another,
|
||||
and runtime mutators (\`SetModel\`, \`SetThinkingLevel\`) only touch the owning
|
||||
instance. This makes subagent spawning and multi-Kit embedding race-free with
|
||||
no external synchronization required.
|
||||
|
||||
\`\`\`go
|
||||
a, _ := kit.NewAgent(ctx, kit.WithThinkingLevel("low"))
|
||||
b, _ := kit.NewAgent(ctx, kit.WithThinkingLevel("high"))
|
||||
|
||||
a.SetThinkingLevel(ctx, "medium")
|
||||
// a.GetThinkingLevel() == "medium"; b.GetThinkingLevel() is still "high"
|
||||
\`\`\`
|
||||
|
||||
## Multi-turn conversations
|
||||
|
||||
Conversations retain context automatically across calls:
|
||||
|
||||
\`\`\`go
|
||||
host.Prompt(ctx, "My name is Alice")
|
||||
response, _ := host.Prompt(ctx, "What's my name?")
|
||||
// response: "Your name is Alice"
|
||||
\`\`\`
|
||||
|
||||
## Additional prompt methods
|
||||
|
||||
The SDK provides several prompt variants:
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| \`Prompt(ctx, message)\` | Simple prompt, returns response string |
|
||||
| \`PromptWithOptions(ctx, message, opts)\` | With per-call options |
|
||||
| \`PromptResult(ctx, message)\` | Returns full \`TurnResult\` with usage stats |
|
||||
| \`PromptResultWithFiles(ctx, message, files)\` | Multimodal with file attachments |
|
||||
| \`Steer(ctx, instruction)\` | System-level steering without user message |
|
||||
| \`FollowUp(ctx, text)\` | Continue without new user input |
|
||||
|
||||
## Custom tools
|
||||
|
||||
Create custom tools with \`kit.NewTool\`. The JSON schema is auto-generated from the input struct — no external dependencies required:
|
||||
|
||||
\`\`\`go
|
||||
type WeatherInput struct {
|
||||
City string \`json:"city" description:"City name"\`
|
||||
}
|
||||
|
||||
weatherTool := kit.NewTool("get_weather", "Get current weather for a city",
|
||||
func(ctx context.Context, input WeatherInput) (kit.ToolOutput, error) {
|
||||
return kit.TextResult("72°F, sunny in " + input.City), nil
|
||||
},
|
||||
)
|
||||
|
||||
host, _ := kit.New(ctx, &kit.Options{
|
||||
ExtraTools: []kit.Tool{weatherTool},
|
||||
})
|
||||
\`\`\`
|
||||
|
||||
Struct tags control the schema:
|
||||
|
||||
- \`json:"name"\` — parameter name
|
||||
- \`description:"..."\` — description shown to the LLM
|
||||
- \`enum:"a,b,c"\` — restrict valid values
|
||||
- \`omitempty\` — marks the parameter as optional
|
||||
|
||||
Return values:
|
||||
|
||||
| Helper | Description |
|
||||
|--------|-------------|
|
||||
| \`kit.TextResult(s)\` | Successful text result |
|
||||
| \`kit.ErrorResult(s)\` | Error result (LLM sees it as a tool error) |
|
||||
| \`kit.ImageResult(s, data, mediaType)\` | Image result with binary data (e.g. \`"image/png"\`) |
|
||||
| \`kit.MediaResult(s, data, mediaType)\` | Non-image media result (e.g. \`"audio/mpeg"\`) |
|
||||
|
||||
Binary data (images, audio, etc.) in \`ToolOutput.Data\` is automatically forwarded to the LLM when \`MediaType\` is set. For advanced use, return a \`kit.ToolOutput\` struct directly with \`Data\`, \`MediaType\`, and \`Metadata\` fields.
|
||||
|
||||
Use \`kit.NewParallelTool\` for tools that are safe to run concurrently. Use \`kit.ToolCallIDFromContext(ctx)\` to retrieve the LLM-assigned call ID for logging or tracing.
|
||||
|
||||
## Generation & provider overrides
|
||||
|
||||
SDK consumers can configure generation parameters and provider endpoints
|
||||
entirely in-code via \`Options\`, without touching \`.kit.yml\` or \`viper.Set()\`:
|
||||
|
||||
\`\`\`go
|
||||
host, _ := kit.New(ctx, &kit.Options{
|
||||
Model: "anthropic/claude-sonnet-4-5-20250929",
|
||||
MaxTokens: 16384, // 0 = auto-resolve (env → config → per-model → floor)
|
||||
ThinkingLevel: "high", // "off" | "none" | "minimal" | "low" | "medium" | "high"
|
||||
Temperature: ptrFloat32(0.2), // nil = provider/per-model default
|
||||
ProviderAPIKey: os.Getenv("MY_SECRET"), // overrides pre-existing viper state
|
||||
ProviderURL: "https://proxy.internal/v1",
|
||||
})
|
||||
|
||||
func ptrFloat32(v float32) *float32 { return &v }
|
||||
\`\`\`
|
||||
|
||||
See [Options](/sdk/options#generation-parameters) for the full field reference,
|
||||
including \`TopP\`, \`TopK\`, \`FrequencyPenalty\`, \`PresencePenalty\`, and \`TLSSkipVerify\`.
|
||||
|
||||
## Event system
|
||||
|
||||
Subscribe to events for monitoring:
|
||||
|
||||
\`\`\`go
|
||||
unsubscribe := host.OnToolCall(func(event kit.ToolCallEvent) {
|
||||
fmt.Println("Tool called:", event.Name)
|
||||
})
|
||||
defer unsubscribe()
|
||||
|
||||
host.OnToolResult(func(event kit.ToolResultEvent) {
|
||||
fmt.Println("Tool result:", event.Name)
|
||||
})
|
||||
|
||||
host.OnMessageUpdate(func(event kit.MessageUpdateEvent) {
|
||||
fmt.Print(event.Chunk)
|
||||
})
|
||||
\`\`\`
|
||||
|
||||
## Model management
|
||||
|
||||
Switch models at runtime:
|
||||
|
||||
\`\`\`go
|
||||
host.SetModel(ctx, "openai/gpt-4o")
|
||||
info := host.GetModelInfo()
|
||||
models := host.GetAvailableModels()
|
||||
\`\`\`
|
||||
|
||||
## Dynamic MCP servers
|
||||
|
||||
Add and remove MCP servers at runtime:
|
||||
|
||||
\`\`\`go
|
||||
n, err := host.AddMCPServer(ctx, "github", kit.MCPServerConfig{
|
||||
Command: []string{"npx", "-y", "@modelcontextprotocol/server-github"},
|
||||
})
|
||||
fmt.Printf("Loaded %d tools\\n", n)
|
||||
|
||||
err = host.RemoveMCPServer("github")
|
||||
servers := host.ListMCPServers() // []kit.MCPServerStatus
|
||||
\`\`\`
|
||||
|
||||
### In-process MCP servers
|
||||
|
||||
Register mcp-go servers running in the same process — zero subprocess overhead:
|
||||
|
||||
\`\`\`go
|
||||
import (
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
)
|
||||
|
||||
mcpSrv := server.NewMCPServer("my-tools", "1.0.0",
|
||||
server.WithToolCapabilities(true),
|
||||
)
|
||||
mcpSrv.AddTool(mcp.NewTool("search_docs",
|
||||
mcp.WithDescription("Search documentation"),
|
||||
mcp.WithString("query", mcp.Required()),
|
||||
), searchHandler)
|
||||
|
||||
// At init time
|
||||
host, _ := kit.New(ctx, &kit.Options{
|
||||
InProcessMCPServers: map[string]*kit.MCPServer{
|
||||
"docs": mcpSrv,
|
||||
},
|
||||
})
|
||||
|
||||
// Or at runtime
|
||||
n, _ := host.AddInProcessMCPServer(ctx, "docs", mcpSrv)
|
||||
\`\`\`
|
||||
|
||||
## Runtime skills and context files
|
||||
|
||||
Kit auto-discovers skills and \`AGENTS.md\`-style context files during \`New()\`,
|
||||
but multi-tenant hosts (chatbots, web services, per-user agents) often need
|
||||
to swap these **after** construction. The runtime mutators below recompose
|
||||
the system prompt and apply it to the agent so the next turn picks up the
|
||||
updated instructions — no restart, no file shuffling.
|
||||
|
||||
\`\`\`go
|
||||
// Add a programmatic skill — no file on disk required.
|
||||
host.AddSkill(&kit.Skill{
|
||||
Name: "polite-french",
|
||||
Description: "Respond in French and always greet the user.",
|
||||
Content: "Always reply in French. Open every response with 'Bonjour'.",
|
||||
})
|
||||
|
||||
// Or load one from disk.
|
||||
host.LoadAndAddSkill("/var/skills/refund-policy.md")
|
||||
|
||||
// Project context (AGENTS.md equivalents): inline content from a DB...
|
||||
host.AddContextFileContent(
|
||||
fmt.Sprintf("session://%s/AGENTS.md", userID),
|
||||
rulesFromDB,
|
||||
)
|
||||
// ...or load from disk.
|
||||
host.LoadAndAddContextFile("/etc/agents/tenant-acme.md")
|
||||
|
||||
// Remove individually when a session ends.
|
||||
host.RemoveSkill("polite-french")
|
||||
host.RemoveContextFile(fmt.Sprintf("session://%s/AGENTS.md", userID))
|
||||
|
||||
// Or replace the whole set in one call.
|
||||
host.SetSkills(activeSkillsForUser)
|
||||
host.SetContextFiles(activeContextForUser)
|
||||
|
||||
// Inspect current state (snapshot copies — safe to mutate).
|
||||
skills := host.GetSkills()
|
||||
ctxFiles := host.GetContextFiles()
|
||||
\`\`\`
|
||||
|
||||
Key points:
|
||||
|
||||
- **Auto-refresh.** Every \`Add*\` / \`Remove*\` / \`Set*\` call recomposes the system
|
||||
prompt against the captured base prompt (preserving per-model overrides and
|
||||
\`--system-prompt\` resolution) and pushes the result onto the agent. Call
|
||||
\`host.RefreshSystemPrompt()\` only if you mutate state through a different
|
||||
path and need to force a re-render.
|
||||
- **Dedup keys.** Skills dedupe by \`Name\`; context files dedupe by \`Path\`.
|
||||
Re-adding the same key replaces the entry instead of appending a duplicate.
|
||||
- **Path is opaque.** \`ContextFile.Path\` does not have to point at a real file
|
||||
— it's only used for dedup and for the \`Instructions from: <Path>\` header
|
||||
injected into the prompt. URIs like \`session://user-123/AGENTS.md\` work fine.
|
||||
- **Thread safety.** All readers and mutators are safe to call concurrently
|
||||
from multiple goroutines; the underlying state is guarded by an internal
|
||||
\`RWMutex\`.
|
||||
- **Init-time options still apply.** \`Options.Skills\`, \`Options.SkillsDir\`,
|
||||
\`Options.NoSkills\`, and \`Options.NoContextFiles\` continue to control the
|
||||
startup set; the runtime API mutates from whatever state \`New()\` produced.
|
||||
See [SDK options](/sdk/options#skills--configuration).
|
||||
|
||||
## MCP prompts and resources
|
||||
|
||||
Query prompts and resources exposed by connected MCP servers:
|
||||
|
||||
\`\`\`go
|
||||
// List and expand prompts
|
||||
prompts := host.ListMCPPrompts()
|
||||
result, _ := host.GetMCPPrompt(ctx, "server", "prompt-name", map[string]string{"key": "value"})
|
||||
|
||||
// List and read resources
|
||||
resources := host.ListMCPResources()
|
||||
content, _ := host.ReadMCPResource(ctx, "server", "file:///path")
|
||||
\`\`\`
|
||||
|
||||
## MCP tasks (long-running tools)
|
||||
|
||||
Kit advertises [MCP task support](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks)
|
||||
during \`initialize\`, so cooperating servers can return a \`taskId\` immediately
|
||||
and let Kit poll \`tasks/get\` / \`tasks/result\` until the operation completes.
|
||||
This avoids HTTP/SSE proxy timeouts on long tools and gives you clean
|
||||
cancellation via context.
|
||||
|
||||
\`\`\`go
|
||||
host, _ := kit.New(ctx, &kit.Options{
|
||||
MCPTaskMode: map[string]kit.MCPTaskMode{
|
||||
"build-server": kit.MCPTaskModeAlways,
|
||||
},
|
||||
MCPTaskProgress: func(p kit.MCPTaskProgress) {
|
||||
log.Printf("%s: %s", p.TaskID, p.Status)
|
||||
},
|
||||
})
|
||||
|
||||
// Inspect / cancel in-flight tasks
|
||||
tasks, _ := host.ListMCPTasks(ctx, "build-server")
|
||||
_, _ = host.CancelMCPTask(ctx, "build-server", tasks[0].TaskID)
|
||||
\`\`\`
|
||||
|
||||
Defaults to \`MCPTaskModeAuto\` per server, so any existing MCP server keeps
|
||||
its previous synchronous behaviour. See [SDK options → MCP Tasks](/sdk/options#mcp-tasks)
|
||||
for the full surface.
|
||||
|
||||
## Context and compaction
|
||||
|
||||
Monitor and manage context usage:
|
||||
|
||||
\`\`\`go
|
||||
tokens := host.EstimateContextTokens()
|
||||
stats := host.GetContextStats()
|
||||
|
||||
if host.ShouldCompact() {
|
||||
result, err := host.Compact(ctx, nil, "")
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## In-process subagents
|
||||
|
||||
Spawn child Kit instances without subprocess overhead:
|
||||
|
||||
\`\`\`go
|
||||
result, err := host.Subagent(ctx, kit.SubagentConfig{
|
||||
Prompt: "Analyze the test files",
|
||||
Model: "anthropic/claude-haiku-3-5-20241022",
|
||||
NoSession: true,
|
||||
Timeout: 2 * time.Minute,
|
||||
})
|
||||
\`\`\`
|
||||
|
||||
See [Options](/sdk/options), [Callbacks](/sdk/callbacks), and [Sessions](/sdk/sessions) for more details.
|
||||
`};export{s as default};
|
||||
@@ -1,406 +0,0 @@
|
||||
const s={frontmatter:{title:"Providers",description:"Supported LLM providers and model configuration.",hidden:!1,toc:!0,draft:!1},html:`<h1 id="providers"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#providers"><span class="icon icon-link"></span></a>Providers</h1>
|
||||
<p>Kit supports a wide range of LLM providers through a unified <code>provider/model</code> string format.</p>
|
||||
<h2 id="supported-providers"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#supported-providers"><span class="icon icon-link"></span></a>Supported providers</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Provider</th>
|
||||
<th>Prefix</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>Anthropic</strong></td>
|
||||
<td><code>anthropic/</code></td>
|
||||
<td>Claude models (native, prompt caching, OAuth)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>OpenAI</strong></td>
|
||||
<td><code>openai/</code></td>
|
||||
<td>GPT models</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>GitHub Copilot</strong></td>
|
||||
<td><code>copilot/</code></td>
|
||||
<td>Copilot models through GitHub device login (experimental)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Google</strong></td>
|
||||
<td><code>google/</code> or <code>gemini/</code></td>
|
||||
<td>Gemini models</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Ollama</strong></td>
|
||||
<td><code>ollama/</code></td>
|
||||
<td>Local models</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Azure OpenAI</strong></td>
|
||||
<td><code>azure/</code></td>
|
||||
<td>Azure-hosted OpenAI</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>AWS Bedrock</strong></td>
|
||||
<td><code>bedrock/</code></td>
|
||||
<td>Bedrock models</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Google Vertex</strong></td>
|
||||
<td><code>google-vertex-anthropic/</code></td>
|
||||
<td>Claude on Vertex AI</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>OpenRouter</strong></td>
|
||||
<td><code>openrouter/</code></td>
|
||||
<td>Multi-provider router</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Vercel AI</strong></td>
|
||||
<td><code>vercel/</code></td>
|
||||
<td>Vercel AI SDK models</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Custom</strong></td>
|
||||
<td><code>custom/</code></td>
|
||||
<td>Any OpenAI-compatible endpoint</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Auto-routed</strong></td>
|
||||
<td>any</td>
|
||||
<td>Any provider from the models.dev database</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2 id="model-string-format"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#model-string-format"><span class="icon icon-link"></span></a>Model string format</h2>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">provider/model</span><span style="color:#6A737D;--shiki-dark:#6A737D"> # Standard format</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">anthropic/claude-sonnet-latest</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">openai/gpt-4o</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">copilot/gpt-5.5</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">ollama/llama3</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">google/gemini-2.5-flash</span></span></code></pre>
|
||||
<h2 id="model-aliases"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#model-aliases"><span class="icon icon-link"></span></a>Model aliases</h2>
|
||||
<p>Kit provides aliases for commonly used models:</p>
|
||||
<h3 id="anthropic-claude"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#anthropic-claude"><span class="icon icon-link"></span></a>Anthropic Claude</h3>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">claude-opus-latest</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> claude-opus-4-6</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">claude-sonnet-latest</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> claude-sonnet-4-6</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">claude-haiku-latest</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> claude-haiku-4-5</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">claude-4-opus-latest</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> claude-opus-4-6</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">claude-4-sonnet-latest</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> claude-sonnet-4-6</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">claude-4-haiku-latest</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> claude-haiku-4-5</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">claude-3-7-sonnet-latest</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> claude-3-7-sonnet-20250219</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">claude-3-5-sonnet-latest</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> claude-3-5-sonnet-20241022</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">claude-3-5-haiku-latest</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> claude-3-5-haiku-20241022</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">claude-3-opus-latest</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> claude-3-opus-20240229</span></span></code></pre>
|
||||
<h3 id="openai-gpt"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#openai-gpt"><span class="icon icon-link"></span></a>OpenAI GPT</h3>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">o1-latest</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> o1</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">o3-latest</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> o3</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">o4-latest</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> o4-mini</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">gpt-5-latest</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> gpt-5.4</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">gpt-5-chat-latest</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> gpt-5.4</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">gpt-4-latest</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> gpt-4o</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">gpt-4</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> gpt-4o</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">gpt-3.5-latest</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> gpt-3.5-turbo</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">gpt-3.5</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> gpt-3.5-turbo</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">codex-latest</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> codex-mini-latest</span></span></code></pre>
|
||||
<h3 id="google-gemini"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#google-gemini"><span class="icon icon-link"></span></a>Google Gemini</h3>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">gemini-pro-latest</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> gemini-2.5-pro</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">gemini-flash-latest</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> gemini-2.5-flash</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">gemini-flash</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> gemini-2.5-flash</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">gemini-pro</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> →</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> gemini-2.5-pro</span></span></code></pre>
|
||||
<h2 id="specifying-a-model"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#specifying-a-model"><span class="icon icon-link"></span></a>Specifying a model</h2>
|
||||
<p>Via CLI flag:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --model</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> openai/gpt-4o</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -m</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> ollama/llama3</span></span></code></pre>
|
||||
<p>Via config file:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">model</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">anthropic/claude-sonnet-latest</span></span></code></pre>
|
||||
<p>Via environment variable:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> KIT_MODEL</span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"google/gemini-2.0-flash-exp"</span></span></code></pre>
|
||||
<h2 id="authentication"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#authentication"><span class="icon icon-link"></span></a>Authentication</h2>
|
||||
<h3 id="api-keys"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#api-keys"><span class="icon icon-link"></span></a>API keys</h3>
|
||||
<p>Set the appropriate environment variable for your provider:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ANTHROPIC_API_KEY</span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"sk-..."</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> OPENAI_API_KEY</span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"sk-..."</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> GOOGLE_API_KEY</span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"..."</span></span></code></pre>
|
||||
<p>Or pass it directly:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --provider-api-key</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "sk-..."</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --model</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> openai/gpt-4o</span></span></code></pre>
|
||||
<h3 id="oauth"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#oauth"><span class="icon icon-link"></span></a>OAuth</h3>
|
||||
<p>For providers that support OAuth:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> auth</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> login</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> anthropic</span><span style="color:#6A737D;--shiki-dark:#6A737D"> # Anthropic OAuth</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> auth</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> login</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> openai</span><span style="color:#6A737D;--shiki-dark:#6A737D"> # ChatGPT/Codex OAuth</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> auth</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> login</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> copilot</span><span style="color:#6A737D;--shiki-dark:#6A737D"> # GitHub Copilot device login (experimental)</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> auth</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> status</span><span style="color:#6A737D;--shiki-dark:#6A737D"> # Check authentication status</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> auth</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> logout</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> copilot</span><span style="color:#6A737D;--shiki-dark:#6A737D"> # Remove credentials</span></span></code></pre>
|
||||
<p>The experimental <code>copilot/</code> provider requires an active GitHub Copilot subscription
|
||||
and uses GitHub device login; no OpenAI account or OpenAI API key is required.</p>
|
||||
<h3 id="custom-provider-url"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#custom-provider-url"><span class="icon icon-link"></span></a>Custom provider URL</h3>
|
||||
<p>For self-hosted or proxy endpoints:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --provider-url</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "https://my-proxy.example.com/v1"</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --model</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> openai/gpt-4o</span></span></code></pre>
|
||||
<p>When <code>--provider-url</code> is set with an explicit <code>--model</code>, Kit routes through the
|
||||
<code>custom</code> (OpenAI-compatible) wire and strips any provider prefix from the model
|
||||
name. So <code>openai/gpt-4o</code>, <code>google/gemma-4-12b</code>, and bare <code>gpt-4o</code> all resolve
|
||||
to the same endpoint — Kit treats <code>--provider-url</code> as authoritative about <em>where</em>
|
||||
to send the request, and the model string as just the upstream model id.</p>
|
||||
<p>This avoids name collisions when a local server (LM Studio, Ollama, vLLM, ...)
|
||||
happens to expose a model whose name matches a known cloud provider.</p>
|
||||
<p>When <code>--provider-url</code> is provided without <code>--model</code>, Kit automatically defaults to <code>custom/custom</code>:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --provider-url</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "http://localhost:8080/v1"</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "Hello"</span></span></code></pre>
|
||||
<p>The <code>custom/custom</code> model has zero cost, 262K context window, and supports reasoning. It routes through the <code>openaicompat</code> provider and accepts any OpenAI-compatible API endpoint.</p>
|
||||
<p>Optionally set <code>CUSTOM_API_KEY</code> environment variable or use <code>--provider-api-key</code> for endpoints requiring authentication.</p>
|
||||
<h2 id="auto-routed-providers"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#auto-routed-providers"><span class="icon icon-link"></span></a>Auto-routed providers</h2>
|
||||
<p>Any provider in the <a href="https://models.dev">models.dev</a> database can be used with the
|
||||
standard <code>provider/model</code> format, even without a dedicated native integration. Kit
|
||||
auto-routes the request through the matching <strong>wire protocol</strong> — the actual API
|
||||
shape the provider speaks — rather than requiring a per-provider code path:</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Wire protocol</th>
|
||||
<th>npm package (models.dev)</th>
|
||||
<th>Transport used</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>OpenAI (Responses API)</td>
|
||||
<td><code>@ai-sdk/openai</code></td>
|
||||
<td>OpenAI</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenAI (chat completions)</td>
|
||||
<td><code>@ai-sdk/openai-compatible</code></td>
|
||||
<td>OpenAI-compatible</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Anthropic</td>
|
||||
<td><code>@ai-sdk/anthropic</code></td>
|
||||
<td>Anthropic</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Google Gemini</td>
|
||||
<td><code>@ai-sdk/google</code></td>
|
||||
<td>Google</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>The provider's <code>api</code> URL from the database is used as the base URL. A provider
|
||||
whose npm package isn't recognized but that has an <code>api</code> URL falls back to the
|
||||
OpenAI-compatible wire.</p>
|
||||
<p>Because routing follows the wire protocol, aggregator/proxy providers work across
|
||||
<strong>all</strong> of their models — including ones they re-flavor onto a different protocol
|
||||
via a per-model override. For example, an aggregator that proxies Claude, GPT,
|
||||
<em>and</em> Gemini routes them to the Anthropic, OpenAI, and Google transports
|
||||
respectively:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --model</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> opencode/claude-haiku-4-5</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "Hello"</span><span style="color:#6A737D;--shiki-dark:#6A737D"> # → Anthropic wire</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --model</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> opencode/gpt-5</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "Hello"</span><span style="color:#6A737D;--shiki-dark:#6A737D"> # → OpenAI wire</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --model</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> opencode/gemini-3.5-flash</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "Hello"</span><span style="color:#6A737D;--shiki-dark:#6A737D"> # → Google wire</span></span></code></pre>
|
||||
<p>Provide the provider's API key the same way as any other — via its environment
|
||||
variable (e.g. <code>OPENCODE_API_KEY</code>) or <code>--provider-api-key</code>.</p>
|
||||
<h2 id="model-database"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#model-database"><span class="icon icon-link"></span></a>Model database</h2>
|
||||
<p>Kit ships with a local model database that maps provider names to API configurations. You can manage it with:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> models</span><span style="color:#6A737D;--shiki-dark:#6A737D"> # List available models</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> models</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> openai</span><span style="color:#6A737D;--shiki-dark:#6A737D"> # Filter by provider</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> models</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --all</span><span style="color:#6A737D;--shiki-dark:#6A737D"> # Show all providers</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> update-models</span><span style="color:#6A737D;--shiki-dark:#6A737D"> # Update from models.dev</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> update-models</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> embedded</span><span style="color:#6A737D;--shiki-dark:#6A737D"> # Reset to bundled database</span></span></code></pre>`,headings:[{depth:2,text:"Supported providers",id:"supported-providers"},{depth:2,text:"Model string format",id:"model-string-format"},{depth:2,text:"Model aliases",id:"model-aliases"},{depth:3,text:"Anthropic Claude",id:"anthropic-claude"},{depth:3,text:"OpenAI GPT",id:"openai-gpt"},{depth:3,text:"Google Gemini",id:"google-gemini"},{depth:2,text:"Specifying a model",id:"specifying-a-model"},{depth:2,text:"Authentication",id:"authentication"},{depth:3,text:"API keys",id:"api-keys"},{depth:3,text:"OAuth",id:"oauth"},{depth:3,text:"Custom provider URL",id:"custom-provider-url"},{depth:2,text:"Auto-routed providers",id:"auto-routed-providers"},{depth:2,text:"Model database",id:"model-database"}],raw:`
|
||||
# Providers
|
||||
|
||||
Kit supports a wide range of LLM providers through a unified \`provider/model\` string format.
|
||||
|
||||
## Supported providers
|
||||
|
||||
| Provider | Prefix | Description |
|
||||
|----------|--------|-------------|
|
||||
| **Anthropic** | \`anthropic/\` | Claude models (native, prompt caching, OAuth) |
|
||||
| **OpenAI** | \`openai/\` | GPT models |
|
||||
| **GitHub Copilot** | \`copilot/\` | Copilot models through GitHub device login (experimental) |
|
||||
| **Google** | \`google/\` or \`gemini/\` | Gemini models |
|
||||
| **Ollama** | \`ollama/\` | Local models |
|
||||
| **Azure OpenAI** | \`azure/\` | Azure-hosted OpenAI |
|
||||
| **AWS Bedrock** | \`bedrock/\` | Bedrock models |
|
||||
| **Google Vertex** | \`google-vertex-anthropic/\` | Claude on Vertex AI |
|
||||
| **OpenRouter** | \`openrouter/\` | Multi-provider router |
|
||||
| **Vercel AI** | \`vercel/\` | Vercel AI SDK models |
|
||||
| **Custom** | \`custom/\` | Any OpenAI-compatible endpoint |
|
||||
| **Auto-routed** | any | Any provider from the models.dev database |
|
||||
|
||||
## Model string format
|
||||
|
||||
\`\`\`bash
|
||||
provider/model # Standard format
|
||||
anthropic/claude-sonnet-latest
|
||||
openai/gpt-4o
|
||||
copilot/gpt-5.5
|
||||
ollama/llama3
|
||||
google/gemini-2.5-flash
|
||||
\`\`\`
|
||||
|
||||
## Model aliases
|
||||
|
||||
Kit provides aliases for commonly used models:
|
||||
|
||||
### Anthropic Claude
|
||||
|
||||
\`\`\`bash
|
||||
claude-opus-latest → claude-opus-4-6
|
||||
claude-sonnet-latest → claude-sonnet-4-6
|
||||
claude-haiku-latest → claude-haiku-4-5
|
||||
claude-4-opus-latest → claude-opus-4-6
|
||||
claude-4-sonnet-latest → claude-sonnet-4-6
|
||||
claude-4-haiku-latest → claude-haiku-4-5
|
||||
claude-3-7-sonnet-latest → claude-3-7-sonnet-20250219
|
||||
claude-3-5-sonnet-latest → claude-3-5-sonnet-20241022
|
||||
claude-3-5-haiku-latest → claude-3-5-haiku-20241022
|
||||
claude-3-opus-latest → claude-3-opus-20240229
|
||||
\`\`\`
|
||||
|
||||
### OpenAI GPT
|
||||
|
||||
\`\`\`bash
|
||||
o1-latest → o1
|
||||
o3-latest → o3
|
||||
o4-latest → o4-mini
|
||||
gpt-5-latest → gpt-5.4
|
||||
gpt-5-chat-latest → gpt-5.4
|
||||
gpt-4-latest → gpt-4o
|
||||
gpt-4 → gpt-4o
|
||||
gpt-3.5-latest → gpt-3.5-turbo
|
||||
gpt-3.5 → gpt-3.5-turbo
|
||||
codex-latest → codex-mini-latest
|
||||
\`\`\`
|
||||
|
||||
### Google Gemini
|
||||
|
||||
\`\`\`bash
|
||||
gemini-pro-latest → gemini-2.5-pro
|
||||
gemini-flash-latest → gemini-2.5-flash
|
||||
gemini-flash → gemini-2.5-flash
|
||||
gemini-pro → gemini-2.5-pro
|
||||
\`\`\`
|
||||
|
||||
## Specifying a model
|
||||
|
||||
Via CLI flag:
|
||||
|
||||
\`\`\`bash
|
||||
kit --model openai/gpt-4o
|
||||
kit -m ollama/llama3
|
||||
\`\`\`
|
||||
|
||||
Via config file:
|
||||
|
||||
\`\`\`yaml
|
||||
model: anthropic/claude-sonnet-latest
|
||||
\`\`\`
|
||||
|
||||
Via environment variable:
|
||||
|
||||
\`\`\`bash
|
||||
export KIT_MODEL="google/gemini-2.0-flash-exp"
|
||||
\`\`\`
|
||||
|
||||
## Authentication
|
||||
|
||||
### API keys
|
||||
|
||||
Set the appropriate environment variable for your provider:
|
||||
|
||||
\`\`\`bash
|
||||
export ANTHROPIC_API_KEY="sk-..."
|
||||
export OPENAI_API_KEY="sk-..."
|
||||
export GOOGLE_API_KEY="..."
|
||||
\`\`\`
|
||||
|
||||
Or pass it directly:
|
||||
|
||||
\`\`\`bash
|
||||
kit --provider-api-key "sk-..." --model openai/gpt-4o
|
||||
\`\`\`
|
||||
|
||||
### OAuth
|
||||
|
||||
For providers that support OAuth:
|
||||
|
||||
\`\`\`bash
|
||||
kit auth login anthropic # Anthropic OAuth
|
||||
kit auth login openai # ChatGPT/Codex OAuth
|
||||
kit auth login copilot # GitHub Copilot device login (experimental)
|
||||
kit auth status # Check authentication status
|
||||
kit auth logout copilot # Remove credentials
|
||||
\`\`\`
|
||||
|
||||
The experimental \`copilot/\` provider requires an active GitHub Copilot subscription
|
||||
and uses GitHub device login; no OpenAI account or OpenAI API key is required.
|
||||
|
||||
### Custom provider URL
|
||||
|
||||
For self-hosted or proxy endpoints:
|
||||
|
||||
\`\`\`bash
|
||||
kit --provider-url "https://my-proxy.example.com/v1" --model openai/gpt-4o
|
||||
\`\`\`
|
||||
|
||||
When \`--provider-url\` is set with an explicit \`--model\`, Kit routes through the
|
||||
\`custom\` (OpenAI-compatible) wire and strips any provider prefix from the model
|
||||
name. So \`openai/gpt-4o\`, \`google/gemma-4-12b\`, and bare \`gpt-4o\` all resolve
|
||||
to the same endpoint — Kit treats \`--provider-url\` as authoritative about *where*
|
||||
to send the request, and the model string as just the upstream model id.
|
||||
|
||||
This avoids name collisions when a local server (LM Studio, Ollama, vLLM, ...)
|
||||
happens to expose a model whose name matches a known cloud provider.
|
||||
|
||||
When \`--provider-url\` is provided without \`--model\`, Kit automatically defaults to \`custom/custom\`:
|
||||
|
||||
\`\`\`bash
|
||||
kit --provider-url "http://localhost:8080/v1" "Hello"
|
||||
\`\`\`
|
||||
|
||||
The \`custom/custom\` model has zero cost, 262K context window, and supports reasoning. It routes through the \`openaicompat\` provider and accepts any OpenAI-compatible API endpoint.
|
||||
|
||||
Optionally set \`CUSTOM_API_KEY\` environment variable or use \`--provider-api-key\` for endpoints requiring authentication.
|
||||
|
||||
## Auto-routed providers
|
||||
|
||||
Any provider in the [models.dev](https://models.dev) database can be used with the
|
||||
standard \`provider/model\` format, even without a dedicated native integration. Kit
|
||||
auto-routes the request through the matching **wire protocol** — the actual API
|
||||
shape the provider speaks — rather than requiring a per-provider code path:
|
||||
|
||||
| Wire protocol | npm package (models.dev) | Transport used |
|
||||
|---------------|--------------------------|----------------|
|
||||
| OpenAI (Responses API) | \`@ai-sdk/openai\` | OpenAI |
|
||||
| OpenAI (chat completions) | \`@ai-sdk/openai-compatible\` | OpenAI-compatible |
|
||||
| Anthropic | \`@ai-sdk/anthropic\` | Anthropic |
|
||||
| Google Gemini | \`@ai-sdk/google\` | Google |
|
||||
|
||||
The provider's \`api\` URL from the database is used as the base URL. A provider
|
||||
whose npm package isn't recognized but that has an \`api\` URL falls back to the
|
||||
OpenAI-compatible wire.
|
||||
|
||||
Because routing follows the wire protocol, aggregator/proxy providers work across
|
||||
**all** of their models — including ones they re-flavor onto a different protocol
|
||||
via a per-model override. For example, an aggregator that proxies Claude, GPT,
|
||||
*and* Gemini routes them to the Anthropic, OpenAI, and Google transports
|
||||
respectively:
|
||||
|
||||
\`\`\`bash
|
||||
kit --model opencode/claude-haiku-4-5 "Hello" # → Anthropic wire
|
||||
kit --model opencode/gpt-5 "Hello" # → OpenAI wire
|
||||
kit --model opencode/gemini-3.5-flash "Hello" # → Google wire
|
||||
\`\`\`
|
||||
|
||||
Provide the provider's API key the same way as any other — via its environment
|
||||
variable (e.g. \`OPENCODE_API_KEY\`) or \`--provider-api-key\`.
|
||||
|
||||
## Model database
|
||||
|
||||
Kit ships with a local model database that maps provider names to API configurations. You can manage it with:
|
||||
|
||||
\`\`\`bash
|
||||
kit models # List available models
|
||||
kit models openai # Filter by provider
|
||||
kit models --all # Show all providers
|
||||
kit update-models # Update from models.dev
|
||||
kit update-models embedded # Reset to bundled database
|
||||
\`\`\`
|
||||
`};export{s as default};
|
||||
@@ -1,125 +0,0 @@
|
||||
const s={frontmatter:{title:"Quick Start",description:"Get up and running with Kit in minutes.",hidden:!1,toc:!0,draft:!1},html:`<h1 id="quick-start"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#quick-start"><span class="icon icon-link"></span></a>Quick Start</h1>
|
||||
<h2 id="basic-usage"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#basic-usage"><span class="icon icon-link"></span></a>Basic usage</h2>
|
||||
<p>Start an interactive session:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span></span></code></pre>
|
||||
<p>Run a one-off prompt:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "List files in src/"</span></span></code></pre>
|
||||
<p>Attach files as context using the <code>@</code> prefix:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> @main.go</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> @test.go</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "Review these files"</span></span></code></pre>
|
||||
<p>Binary files (images, audio, PDFs) are automatically detected via MIME type and sent as multimodal attachments. You can also reference MCP resources:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> @mcp:myserver:file:///data/report.csv</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "Summarize this data"</span></span></code></pre>
|
||||
<p>Use a specific model:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --model</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> anthropic/claude-sonnet-latest</span></span></code></pre>
|
||||
<h2 id="non-interactive-mode"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#non-interactive-mode"><span class="icon icon-link"></span></a>Non-interactive mode</h2>
|
||||
<p>Kit can run as a non-interactive tool for scripting and automation.</p>
|
||||
<p>Get JSON output:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "Explain main.go"</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --json</span></span></code></pre>
|
||||
<p>Quiet mode (final response only, no TUI):</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "Run tests"</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --quiet</span></span></code></pre>
|
||||
<p>Ephemeral mode (no session file created):</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "Quick question"</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --no-session</span></span></code></pre>
|
||||
<h2 id="resuming-sessions"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#resuming-sessions"><span class="icon icon-link"></span></a>Resuming sessions</h2>
|
||||
<p>Continue the most recent session for the current directory:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --continue</span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># or</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -c</span></span></code></pre>
|
||||
<p>Pick from previous sessions interactively:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --resume</span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># or</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -r</span></span></code></pre>
|
||||
<h2 id="acp-server-mode"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#acp-server-mode"><span class="icon icon-link"></span></a>ACP server mode</h2>
|
||||
<p>Kit can run as an <a href="https://agentclientprotocol.com">ACP (Agent Client Protocol)</a> agent server, enabling ACP-compatible clients (such as <a href="https://github.com/sst/opencode">OpenCode</a>) to drive Kit as a remote coding agent over stdio:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Start Kit as an ACP server (JSON-RPC 2.0 on stdin/stdout)</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> acp</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># With debug logging to stderr</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> acp</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --debug</span></span></code></pre>
|
||||
<p>The ACP server exposes Kit's full capabilities — LLM execution, tool calls (bash, read, write, edit, grep, etc.), and session persistence — over the standard ACP protocol.</p>`,headings:[{depth:2,text:"Basic usage",id:"basic-usage"},{depth:2,text:"Non-interactive mode",id:"non-interactive-mode"},{depth:2,text:"Resuming sessions",id:"resuming-sessions"},{depth:2,text:"ACP server mode",id:"acp-server-mode"}],raw:`
|
||||
# Quick Start
|
||||
|
||||
## Basic usage
|
||||
|
||||
Start an interactive session:
|
||||
|
||||
\`\`\`bash
|
||||
kit
|
||||
\`\`\`
|
||||
|
||||
Run a one-off prompt:
|
||||
|
||||
\`\`\`bash
|
||||
kit "List files in src/"
|
||||
\`\`\`
|
||||
|
||||
Attach files as context using the \`@\` prefix:
|
||||
|
||||
\`\`\`bash
|
||||
kit @main.go @test.go "Review these files"
|
||||
\`\`\`
|
||||
|
||||
Binary files (images, audio, PDFs) are automatically detected via MIME type and sent as multimodal attachments. You can also reference MCP resources:
|
||||
|
||||
\`\`\`bash
|
||||
kit @mcp:myserver:file:///data/report.csv "Summarize this data"
|
||||
\`\`\`
|
||||
|
||||
Use a specific model:
|
||||
|
||||
\`\`\`bash
|
||||
kit --model anthropic/claude-sonnet-latest
|
||||
\`\`\`
|
||||
|
||||
## Non-interactive mode
|
||||
|
||||
Kit can run as a non-interactive tool for scripting and automation.
|
||||
|
||||
Get JSON output:
|
||||
|
||||
\`\`\`bash
|
||||
kit "Explain main.go" --json
|
||||
\`\`\`
|
||||
|
||||
Quiet mode (final response only, no TUI):
|
||||
|
||||
\`\`\`bash
|
||||
kit "Run tests" --quiet
|
||||
\`\`\`
|
||||
|
||||
Ephemeral mode (no session file created):
|
||||
|
||||
\`\`\`bash
|
||||
kit "Quick question" --no-session
|
||||
\`\`\`
|
||||
|
||||
## Resuming sessions
|
||||
|
||||
Continue the most recent session for the current directory:
|
||||
|
||||
\`\`\`bash
|
||||
kit --continue
|
||||
# or
|
||||
kit -c
|
||||
\`\`\`
|
||||
|
||||
Pick from previous sessions interactively:
|
||||
|
||||
\`\`\`bash
|
||||
kit --resume
|
||||
# or
|
||||
kit -r
|
||||
\`\`\`
|
||||
|
||||
## ACP server mode
|
||||
|
||||
Kit can run as an [ACP (Agent Client Protocol)](https://agentclientprotocol.com) agent server, enabling ACP-compatible clients (such as [OpenCode](https://github.com/sst/opencode)) to drive Kit as a remote coding agent over stdio:
|
||||
|
||||
\`\`\`bash
|
||||
# Start Kit as an ACP server (JSON-RPC 2.0 on stdin/stdout)
|
||||
kit acp
|
||||
|
||||
# With debug logging to stderr
|
||||
kit acp --debug
|
||||
\`\`\`
|
||||
|
||||
The ACP server exposes Kit's full capabilities — LLM execution, tool calls (bash, read, write, edit, grep, etc.), and session persistence — over the standard ACP protocol.
|
||||
`};export{s as default};
|
||||
@@ -1,223 +0,0 @@
|
||||
const e={frontmatter:{title:"Session Management",description:"How Kit persists and manages conversation sessions.",hidden:!1,toc:!0,draft:!1},html:`<h1 id="session-management"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#session-management"><span class="icon icon-link"></span></a>Session Management</h1>
|
||||
<p>Kit uses a tree-based session model that supports branching and forking conversations.</p>
|
||||
<h2 id="session-storage"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#session-storage"><span class="icon icon-link"></span></a>Session storage</h2>
|
||||
<p>Sessions are stored as JSONL (JSON Lines) files:</p>
|
||||
<pre><code>~/.kit/sessions/<cwd-path>/<timestamp>_<id>.jsonl
|
||||
</code></pre>
|
||||
<p>Path separators in the working directory are replaced with <code>--</code>. For example, <code>/home/user/project</code> becomes <code>home--user--project</code>.</p>
|
||||
<p>Each line in the session file is a JSON entry representing a message, tool call, model change, or extension data. The tree structure allows branching from any message to explore alternate paths.</p>
|
||||
<h2 id="compaction"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#compaction"><span class="icon icon-link"></span></a>Compaction</h2>
|
||||
<p>When conversations grow long, Kit can compact them to free up context window space. The compaction system:</p>
|
||||
<ul>
|
||||
<li><strong>Non-destructive</strong>: Old messages remain on disk for history; only the LLM context is summarized</li>
|
||||
<li><strong>File tracking</strong>: Tracks which files were read and modified across compactions</li>
|
||||
<li><strong>Split-turn handling</strong>: Can summarize large single turns by splitting them</li>
|
||||
<li><strong>Tool result truncation</strong>: Caps tool output during serialization to stay within token budgets</li>
|
||||
</ul>
|
||||
<p>Use <code>/compact [focus]</code> to manually compact, or enable <code>--auto-compact</code> to compact automatically near the context limit.</p>
|
||||
<h2 id="auto-cleanup"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#auto-cleanup"><span class="icon icon-link"></span></a>Auto-cleanup</h2>
|
||||
<p>Kit automatically cleans up empty sessions on shutdown and when using <code>/resume</code>. A session is considered empty if it has no messages beyond the initial system prompt. This prevents cluttering your sessions directory with unused files.</p>
|
||||
<p>To start fresh without creating a session file at all, use ephemeral mode:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --no-session</span></span></code></pre>
|
||||
<h2 id="resuming-sessions"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#resuming-sessions"><span class="icon icon-link"></span></a>Resuming sessions</h2>
|
||||
<h3 id="continue-most-recent"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#continue-most-recent"><span class="icon icon-link"></span></a>Continue most recent</h3>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --continue</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -c</span></span></code></pre>
|
||||
<h3 id="interactive-picker"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#interactive-picker"><span class="icon icon-link"></span></a>Interactive picker</h3>
|
||||
<p>Choose from previous sessions interactively:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --resume</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -r</span></span></code></pre>
|
||||
<p>The session picker supports search, scope/filter toggles (all sessions vs. current directory), and session deletion. You can also open it during a session with the <code>/resume</code> slash command.</p>
|
||||
<h3 id="open-a-specific-session"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#open-a-specific-session"><span class="icon icon-link"></span></a>Open a specific session</h3>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --session</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> path/to/session.jsonl</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -s</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> path/to/session.jsonl</span></span></code></pre>
|
||||
<h2 id="session-commands"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#session-commands"><span class="icon icon-link"></span></a>Session commands</h2>
|
||||
<p>These slash commands are available during an interactive session:</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Command</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>/name [name]</code></td>
|
||||
<td>Set or display the session's display name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/session</code></td>
|
||||
<td>Show session info (path, ID, message count)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/resume</code></td>
|
||||
<td>Open the session picker to switch sessions</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/export [path]</code></td>
|
||||
<td>Export session as JSONL (auto-generates path if omitted)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/import <path></code></td>
|
||||
<td>Import and switch to a session from a JSONL file</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/share</code></td>
|
||||
<td>Upload session to GitHub Gist and get a shareable viewer URL</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/tree</code></td>
|
||||
<td>Navigate the session tree</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/fork</code></td>
|
||||
<td>Fork to new session from an earlier message (creates new session file)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/new</code></td>
|
||||
<td>Start a new session (creates new session file)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2 id="ephemeral-mode"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#ephemeral-mode"><span class="icon icon-link"></span></a>Ephemeral mode</h2>
|
||||
<p>Run without creating a session file:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> --no-session</span></span></code></pre>
|
||||
<p>This is useful for one-off prompts, scripting, and subagent patterns where persistence isn't needed.</p>
|
||||
<h2 id="sharing-sessions"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#sharing-sessions"><span class="icon icon-link"></span></a>Sharing sessions</h2>
|
||||
<p>The <code>/share</code> command uploads your session JSONL to GitHub Gist (via the <code>gh</code> CLI) and prints a shareable viewer URL:</p>
|
||||
<pre><code>/share
|
||||
</code></pre>
|
||||
<p>The shared session includes:</p>
|
||||
<ul>
|
||||
<li>The <strong>system prompt</strong> that was active during the conversation</li>
|
||||
<li>The <strong>model</strong> used (e.g., <code>anthropic/claude-sonnet-4-5</code>)</li>
|
||||
</ul>
|
||||
<p>The viewer displays this information in a collapsible "System Prompt" section at the top of the session, with the model shown as a badge in the header.</p>
|
||||
<p>The viewer is available at <code>https://go-kit.dev/session/#GIST_ID</code> and supports all message types including text, reasoning blocks, tool calls, images, and model changes.</p>
|
||||
<p>You can also load any JSONL session via URL parameter: <code>https://go-kit.dev/session/?url=https://example.com/session.jsonl</code></p>
|
||||
<h2 id="preferences-persistence"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#preferences-persistence"><span class="icon icon-link"></span></a>Preferences persistence</h2>
|
||||
<p>Kit automatically saves your preferences across sessions to <code>~/.config/kit/preferences.yml</code>:</p>
|
||||
<ul>
|
||||
<li><strong>Theme</strong> — Set via <code>/theme <name></code></li>
|
||||
<li><strong>Model</strong> — Set via <code>/model <name></code> or the model selector</li>
|
||||
<li><strong>Thinking level</strong> — Set via <code>/thinking <level></code> or Shift+Tab cycling</li>
|
||||
</ul>
|
||||
<p>These preferences are restored on next launch. Precedence: CLI flag > config file > saved preference > default.</p>`,headings:[{depth:2,text:"Session storage",id:"session-storage"},{depth:2,text:"Compaction",id:"compaction"},{depth:2,text:"Auto-cleanup",id:"auto-cleanup"},{depth:2,text:"Resuming sessions",id:"resuming-sessions"},{depth:3,text:"Continue most recent",id:"continue-most-recent"},{depth:3,text:"Interactive picker",id:"interactive-picker"},{depth:3,text:"Open a specific session",id:"open-a-specific-session"},{depth:2,text:"Session commands",id:"session-commands"},{depth:2,text:"Ephemeral mode",id:"ephemeral-mode"},{depth:2,text:"Sharing sessions",id:"sharing-sessions"},{depth:2,text:"Preferences persistence",id:"preferences-persistence"}],raw:`
|
||||
# Session Management
|
||||
|
||||
Kit uses a tree-based session model that supports branching and forking conversations.
|
||||
|
||||
## Session storage
|
||||
|
||||
Sessions are stored as JSONL (JSON Lines) files:
|
||||
|
||||
\`\`\`
|
||||
~/.kit/sessions/<cwd-path>/<timestamp>_<id>.jsonl
|
||||
\`\`\`
|
||||
|
||||
Path separators in the working directory are replaced with \`--\`. For example, \`/home/user/project\` becomes \`home--user--project\`.
|
||||
|
||||
Each line in the session file is a JSON entry representing a message, tool call, model change, or extension data. The tree structure allows branching from any message to explore alternate paths.
|
||||
|
||||
## Compaction
|
||||
|
||||
When conversations grow long, Kit can compact them to free up context window space. The compaction system:
|
||||
|
||||
- **Non-destructive**: Old messages remain on disk for history; only the LLM context is summarized
|
||||
- **File tracking**: Tracks which files were read and modified across compactions
|
||||
- **Split-turn handling**: Can summarize large single turns by splitting them
|
||||
- **Tool result truncation**: Caps tool output during serialization to stay within token budgets
|
||||
|
||||
Use \`/compact [focus]\` to manually compact, or enable \`--auto-compact\` to compact automatically near the context limit.
|
||||
|
||||
## Auto-cleanup
|
||||
|
||||
Kit automatically cleans up empty sessions on shutdown and when using \`/resume\`. A session is considered empty if it has no messages beyond the initial system prompt. This prevents cluttering your sessions directory with unused files.
|
||||
|
||||
To start fresh without creating a session file at all, use ephemeral mode:
|
||||
|
||||
\`\`\`bash
|
||||
kit --no-session
|
||||
\`\`\`
|
||||
|
||||
## Resuming sessions
|
||||
|
||||
### Continue most recent
|
||||
|
||||
\`\`\`bash
|
||||
kit --continue
|
||||
kit -c
|
||||
\`\`\`
|
||||
|
||||
### Interactive picker
|
||||
|
||||
Choose from previous sessions interactively:
|
||||
|
||||
\`\`\`bash
|
||||
kit --resume
|
||||
kit -r
|
||||
\`\`\`
|
||||
|
||||
The session picker supports search, scope/filter toggles (all sessions vs. current directory), and session deletion. You can also open it during a session with the \`/resume\` slash command.
|
||||
|
||||
### Open a specific session
|
||||
|
||||
\`\`\`bash
|
||||
kit --session path/to/session.jsonl
|
||||
kit -s path/to/session.jsonl
|
||||
\`\`\`
|
||||
|
||||
## Session commands
|
||||
|
||||
These slash commands are available during an interactive session:
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| \`/name [name]\` | Set or display the session's display name |
|
||||
| \`/session\` | Show session info (path, ID, message count) |
|
||||
| \`/resume\` | Open the session picker to switch sessions |
|
||||
| \`/export [path]\` | Export session as JSONL (auto-generates path if omitted) |
|
||||
| \`/import <path>\` | Import and switch to a session from a JSONL file |
|
||||
| \`/share\` | Upload session to GitHub Gist and get a shareable viewer URL |
|
||||
| \`/tree\` | Navigate the session tree |
|
||||
| \`/fork\` | Fork to new session from an earlier message (creates new session file) |
|
||||
| \`/new\` | Start a new session (creates new session file) |
|
||||
|
||||
## Ephemeral mode
|
||||
|
||||
Run without creating a session file:
|
||||
|
||||
\`\`\`bash
|
||||
kit --no-session
|
||||
\`\`\`
|
||||
|
||||
This is useful for one-off prompts, scripting, and subagent patterns where persistence isn't needed.
|
||||
|
||||
## Sharing sessions
|
||||
|
||||
The \`/share\` command uploads your session JSONL to GitHub Gist (via the \`gh\` CLI) and prints a shareable viewer URL:
|
||||
|
||||
\`\`\`
|
||||
/share
|
||||
\`\`\`
|
||||
|
||||
The shared session includes:
|
||||
- The **system prompt** that was active during the conversation
|
||||
- The **model** used (e.g., \`anthropic/claude-sonnet-4-5\`)
|
||||
|
||||
The viewer displays this information in a collapsible "System Prompt" section at the top of the session, with the model shown as a badge in the header.
|
||||
|
||||
The viewer is available at \`https://go-kit.dev/session/#GIST_ID\` and supports all message types including text, reasoning blocks, tool calls, images, and model changes.
|
||||
|
||||
You can also load any JSONL session via URL parameter: \`https://go-kit.dev/session/?url=https://example.com/session.jsonl\`
|
||||
|
||||
## Preferences persistence
|
||||
|
||||
Kit automatically saves your preferences across sessions to \`~/.config/kit/preferences.yml\`:
|
||||
|
||||
- **Theme** — Set via \`/theme <name>\`
|
||||
- **Model** — Set via \`/model <name>\` or the model selector
|
||||
- **Thinking level** — Set via \`/thinking <level>\` or Shift+Tab cycling
|
||||
|
||||
These preferences are restored on next launch. Precedence: CLI flag > config file > saved preference > default.
|
||||
`};export{e as default};
|
||||
@@ -1,163 +0,0 @@
|
||||
const s={frontmatter:{title:"SDK Sessions",description:"Session management in the Kit Go SDK.",hidden:!1,toc:!0,draft:!1},html:`<h1 id="sdk-sessions"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#sdk-sessions"><span class="icon icon-link"></span></a>SDK Sessions</h1>
|
||||
<h2 id="automatic-persistence"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#automatic-persistence"><span class="icon icon-link"></span></a>Automatic persistence</h2>
|
||||
<p>By default, Kit automatically persists sessions to JSONL files. Multi-turn conversations retain context across calls:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Prompt</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"My name is Alice"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">response, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Prompt</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"What's my name?"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// response: "Your name is Alice"</span></span></code></pre>
|
||||
<h2 id="accessing-session-info"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#accessing-session-info"><span class="icon icon-link"></span></a>Accessing session info</h2>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Get the current session file path</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">path </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">GetSessionPath</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Get the session ID</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">id </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">GetSessionID</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Get the current model string</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">model </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">GetModelString</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span></code></pre>
|
||||
<h2 id="configuring-sessions-via-options"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#configuring-sessions-via-options"><span class="icon icon-link"></span></a>Configuring sessions via Options</h2>
|
||||
<p>Session behavior is configured at initialization:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Open a specific session file</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#D73A49;--shiki-dark:#F97583">&</span><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Options</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> SessionPath: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"./my-session.jsonl"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Resume the most recent session for the current directory</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#D73A49;--shiki-dark:#F97583">&</span><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Options</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Continue: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">true</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Ephemeral mode (no file persistence)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#D73A49;--shiki-dark:#F97583">&</span><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Options</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> NoSession: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">true</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Custom session directory</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#D73A49;--shiki-dark:#F97583">&</span><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Options</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> SessionDir: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"/custom/sessions/"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span></code></pre>
|
||||
<h2 id="clearing-history"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#clearing-history"><span class="icon icon-link"></span></a>Clearing history</h2>
|
||||
<p>Clear the in-memory conversation history (does not delete the session file):</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ClearSession</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span></code></pre>
|
||||
<h2 id="tree-based-sessions"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#tree-based-sessions"><span class="icon icon-link"></span></a>Tree-based sessions</h2>
|
||||
<p>Kit's session model is tree-based, supporting branching. You can branch from any entry to explore alternate conversation paths:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Access the tree session manager</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">ts </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">GetTreeSession</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Branch from a specific entry</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">err </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Branch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"entry-id-123"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span></code></pre>
|
||||
<h2 id="listing-and-managing-sessions"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#listing-and-managing-sessions"><span class="icon icon-link"></span></a>Listing and managing sessions</h2>
|
||||
<p>Package-level functions for session discovery:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// List sessions for a specific directory</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">sessions </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ListSessions</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"/home/user/project"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// List all sessions across all directories</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">all </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ListAllSessions</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Delete a session file</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">DeleteSession</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"/path/to/session.jsonl"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span></code></pre>
|
||||
<h2 id="custom-session-manager"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#custom-session-manager"><span class="icon icon-link"></span></a>Custom session manager</h2>
|
||||
<p>For advanced use cases (databases, cloud storage, multi-user apps), implement the <code>SessionManager</code> interface to replace the default JSONL file backend:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> kit.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#D73A49;--shiki-dark:#F97583">&</span><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Options</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> SessionManager: myCustomSession,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span></code></pre>
|
||||
<p>The interface requires methods for message storage, branching, compaction, extension data, and lifecycle management. See the <a href="https://github.com/mark3labs/kit">SDK skill reference</a> for the complete interface definition.</p>
|
||||
<p>When using a custom <code>SessionManager</code>, the <code>SessionPath</code>, <code>Continue</code>, and <code>NoSession</code> options are ignored — your manager handles its own storage and session selection.</p>`,headings:[{depth:2,text:"Automatic persistence",id:"automatic-persistence"},{depth:2,text:"Accessing session info",id:"accessing-session-info"},{depth:2,text:"Configuring sessions via Options",id:"configuring-sessions-via-options"},{depth:2,text:"Clearing history",id:"clearing-history"},{depth:2,text:"Tree-based sessions",id:"tree-based-sessions"},{depth:2,text:"Listing and managing sessions",id:"listing-and-managing-sessions"},{depth:2,text:"Custom session manager",id:"custom-session-manager"}],raw:`
|
||||
# SDK Sessions
|
||||
|
||||
## Automatic persistence
|
||||
|
||||
By default, Kit automatically persists sessions to JSONL files. Multi-turn conversations retain context across calls:
|
||||
|
||||
\`\`\`go
|
||||
host.Prompt(ctx, "My name is Alice")
|
||||
response, _ := host.Prompt(ctx, "What's my name?")
|
||||
// response: "Your name is Alice"
|
||||
\`\`\`
|
||||
|
||||
## Accessing session info
|
||||
|
||||
\`\`\`go
|
||||
// Get the current session file path
|
||||
path := host.GetSessionPath()
|
||||
|
||||
// Get the session ID
|
||||
id := host.GetSessionID()
|
||||
|
||||
// Get the current model string
|
||||
model := host.GetModelString()
|
||||
\`\`\`
|
||||
|
||||
## Configuring sessions via Options
|
||||
|
||||
Session behavior is configured at initialization:
|
||||
|
||||
\`\`\`go
|
||||
// Open a specific session file
|
||||
host, _ := kit.New(ctx, &kit.Options{
|
||||
SessionPath: "./my-session.jsonl",
|
||||
})
|
||||
|
||||
// Resume the most recent session for the current directory
|
||||
host, _ := kit.New(ctx, &kit.Options{
|
||||
Continue: true,
|
||||
})
|
||||
|
||||
// Ephemeral mode (no file persistence)
|
||||
host, _ := kit.New(ctx, &kit.Options{
|
||||
NoSession: true,
|
||||
})
|
||||
|
||||
// Custom session directory
|
||||
host, _ := kit.New(ctx, &kit.Options{
|
||||
SessionDir: "/custom/sessions/",
|
||||
})
|
||||
\`\`\`
|
||||
|
||||
## Clearing history
|
||||
|
||||
Clear the in-memory conversation history (does not delete the session file):
|
||||
|
||||
\`\`\`go
|
||||
host.ClearSession()
|
||||
\`\`\`
|
||||
|
||||
## Tree-based sessions
|
||||
|
||||
Kit's session model is tree-based, supporting branching. You can branch from any entry to explore alternate conversation paths:
|
||||
|
||||
\`\`\`go
|
||||
// Access the tree session manager
|
||||
ts := host.GetTreeSession()
|
||||
|
||||
// Branch from a specific entry
|
||||
err := host.Branch("entry-id-123")
|
||||
\`\`\`
|
||||
|
||||
## Listing and managing sessions
|
||||
|
||||
Package-level functions for session discovery:
|
||||
|
||||
\`\`\`go
|
||||
// List sessions for a specific directory
|
||||
sessions := kit.ListSessions("/home/user/project")
|
||||
|
||||
// List all sessions across all directories
|
||||
all := kit.ListAllSessions()
|
||||
|
||||
// Delete a session file
|
||||
kit.DeleteSession("/path/to/session.jsonl")
|
||||
\`\`\`
|
||||
|
||||
## Custom session manager
|
||||
|
||||
For advanced use cases (databases, cloud storage, multi-user apps), implement the \`SessionManager\` interface to replace the default JSONL file backend:
|
||||
|
||||
\`\`\`go
|
||||
host, _ := kit.New(ctx, &kit.Options{
|
||||
SessionManager: myCustomSession,
|
||||
})
|
||||
\`\`\`
|
||||
|
||||
The interface requires methods for message storage, branching, compaction, extension data, and lifecycle management. See the [SDK skill reference](https://github.com/mark3labs/kit) for the complete interface definition.
|
||||
|
||||
When using a custom \`SessionManager\`, the \`SessionPath\`, \`Continue\`, and \`NoSession\` options are ignored — your manager handles its own storage and session selection.
|
||||
`};export{s as default};
|
||||
@@ -1,315 +0,0 @@
|
||||
const s={frontmatter:{title:"Subagents",description:"Multi-agent orchestration with Kit subagents.",hidden:!1,toc:!0,draft:!1},html:`<h1 id="subagents"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#subagents"><span class="icon icon-link"></span></a>Subagents</h1>
|
||||
<p>Kit supports multi-agent orchestration through both subprocess spawning and in-process subagents.</p>
|
||||
<h2 id="subprocess-pattern"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#subprocess-pattern"><span class="icon icon-link"></span></a>Subprocess pattern</h2>
|
||||
<p>Spawn Kit as a subprocess for isolated agent execution:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "Analyze codebase"</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> \\</span></span>
|
||||
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF"> --json</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> \\</span></span>
|
||||
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF"> --no-session</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> \\</span></span>
|
||||
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF"> --no-extensions</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> \\</span></span>
|
||||
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF"> --quiet</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> \\</span></span>
|
||||
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF"> --model</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> anthropic/claude-haiku-latest</span></span></code></pre>
|
||||
<p>Key flags for subprocess usage:</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Flag</th>
|
||||
<th>Purpose</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>--quiet</code></td>
|
||||
<td>Stdout only, no TUI</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--no-session</code></td>
|
||||
<td>Ephemeral, no persistence</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--no-extensions</code></td>
|
||||
<td>Prevent recursive extension loading</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--json</code></td>
|
||||
<td>Machine-readable output</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--system-prompt</code></td>
|
||||
<td>Custom system prompt (string or file path)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Positional arguments are the prompt. <code>@file</code> arguments attach file content as context.</p>
|
||||
<h2 id="built-in-subagent-tool"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#built-in-subagent-tool"><span class="icon icon-link"></span></a>Built-in subagent tool</h2>
|
||||
<p>Kit includes a built-in <code>subagent</code> tool that the LLM can use to delegate tasks to independent child agents:</p>
|
||||
<pre><code>subagent(
|
||||
task: "Analyze the test files and summarize coverage",
|
||||
model: "anthropic/claude-haiku-latest", // optional
|
||||
system_prompt: "You are a test analysis expert.", // optional
|
||||
timeout_seconds: 300 // optional, max 1800
|
||||
)
|
||||
</code></pre>
|
||||
<p>Subagents run as separate in-process Kit instances with full tool access (except spawning further subagents, to prevent infinite recursion). They can run in parallel.</p>
|
||||
<h2 id="extension-subagents"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#extension-subagents"><span class="icon icon-link"></span></a>Extension subagents</h2>
|
||||
<p>Extensions can spawn subagents programmatically:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">result </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ctx.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SpawnSubagent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SubagentConfig</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Task: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Review this code for security issues"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Model: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"anthropic/claude-sonnet-latest"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> SystemPrompt: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"You are a security auditor."</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span></code></pre>
|
||||
<h3 id="monitoring-subagents-from-extensions"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#monitoring-subagents-from-extensions"><span class="icon icon-link"></span></a>Monitoring subagents from extensions</h3>
|
||||
<p>When the LLM (not the extension itself) spawns a subagent using the <code>subagent</code> tool, extensions can monitor its activity in real-time using three lifecycle event handlers:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Track active subagents and display their output</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">var</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> subagentWidgets </span><span style="color:#D73A49;--shiki-dark:#F97583">map</span><span style="color:#24292E;--shiki-dark:#E1E4E8">[</span><span style="color:#D73A49;--shiki-dark:#F97583">string</span><span style="color:#24292E;--shiki-dark:#E1E4E8">]</span><span style="color:#D73A49;--shiki-dark:#F97583">*</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SubagentWidget</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Init</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">api</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">API</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Subagent started by the main agent</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> api.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">OnSubagentStart</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">e</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SubagentStartEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">ctx</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Context</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // e.ToolCallID — unique ID for this subagent invocation</span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // e.Task — the task/prompt sent to the subagent</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> widget </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> NewWidget</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(e.ToolCallID, e.Task)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> subagentWidgets[e.ToolCallID] </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> widget</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> ctx.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SetWidget</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(widget.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Config</span><span style="color:#24292E;--shiki-dark:#E1E4E8">())</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> })</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Real-time streaming from subagent</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> api.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">OnSubagentChunk</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">e</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SubagentChunkEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">ctx</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Context</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // e.ToolCallID — matches the start event</span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // e.ChunkType — "text", "tool_call", "tool_execution_start", "tool_result"</span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // e.Content — text content</span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // e.ToolName — tool name (for tool chunks)</span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // e.IsError — true if tool result failed</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> widget </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> subagentWidgets[e.ToolCallID]</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583"> if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> widget </span><span style="color:#D73A49;--shiki-dark:#F97583">!=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> nil</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> widget.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AddOutput</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(e)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> ctx.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SetWidget</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(widget.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Config</span><span style="color:#24292E;--shiki-dark:#E1E4E8">())</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> }</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> })</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Subagent completed</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> api.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">OnSubagentEnd</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">e</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SubagentEndEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">ctx</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Context</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // e.Response — final response from subagent</span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // e.ErrorMsg — error message if subagent failed</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> widget </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> subagentWidgets[e.ToolCallID]</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583"> if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> widget </span><span style="color:#D73A49;--shiki-dark:#F97583">!=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> nil</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> widget.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">MarkComplete</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(e.Response, e.ErrorMsg)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> ctx.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SetWidget</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(widget.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Config</span><span style="color:#24292E;--shiki-dark:#E1E4E8">())</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0"> delete</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(subagentWidgets, e.ToolCallID)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> }</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> })</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
|
||||
<p><strong>Event structs:</strong></p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">type</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> SubagentStartEvent</span><span style="color:#D73A49;--shiki-dark:#F97583"> struct</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> ToolCallID </span><span style="color:#D73A49;--shiki-dark:#F97583">string</span><span style="color:#6A737D;--shiki-dark:#6A737D"> // Unique ID for this subagent invocation</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Task </span><span style="color:#D73A49;--shiki-dark:#F97583">string</span><span style="color:#6A737D;--shiki-dark:#6A737D"> // The task/prompt sent to subagent</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">type</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> SubagentChunkEvent</span><span style="color:#D73A49;--shiki-dark:#F97583"> struct</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> ToolCallID </span><span style="color:#D73A49;--shiki-dark:#F97583">string</span><span style="color:#6A737D;--shiki-dark:#6A737D"> // Matches SubagentStartEvent.ToolCallID</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Task </span><span style="color:#D73A49;--shiki-dark:#F97583">string</span><span style="color:#6A737D;--shiki-dark:#6A737D"> // Task description</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> ChunkType </span><span style="color:#D73A49;--shiki-dark:#F97583">string</span><span style="color:#6A737D;--shiki-dark:#6A737D"> // "text", "tool_call", "tool_execution_start", "tool_result"</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Content </span><span style="color:#D73A49;--shiki-dark:#F97583">string</span><span style="color:#6A737D;--shiki-dark:#6A737D"> // For text chunks</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> ToolName </span><span style="color:#D73A49;--shiki-dark:#F97583">string</span><span style="color:#6A737D;--shiki-dark:#6A737D"> // For tool-related chunks</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> IsError </span><span style="color:#D73A49;--shiki-dark:#F97583">bool</span><span style="color:#6A737D;--shiki-dark:#6A737D"> // For tool_result chunks</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">type</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> SubagentEndEvent</span><span style="color:#D73A49;--shiki-dark:#F97583"> struct</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> ToolCallID </span><span style="color:#D73A49;--shiki-dark:#F97583">string</span><span style="color:#6A737D;--shiki-dark:#6A737D"> // Matches start event</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Task </span><span style="color:#D73A49;--shiki-dark:#F97583">string</span><span style="color:#6A737D;--shiki-dark:#6A737D"> // Task description</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Response </span><span style="color:#D73A49;--shiki-dark:#F97583">string</span><span style="color:#6A737D;--shiki-dark:#6A737D"> // Final response from subagent</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> ErrorMsg </span><span style="color:#D73A49;--shiki-dark:#F97583">string</span><span style="color:#6A737D;--shiki-dark:#6A737D"> // Error message if failed</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
|
||||
<p>This enables building monitoring widgets that display real-time activity from all subagents spawned by the main agent.</p>
|
||||
<h2 id="go-sdk-subagents"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#go-sdk-subagents"><span class="icon icon-link"></span></a>Go SDK subagents</h2>
|
||||
<p>The SDK provides in-process subagent spawning:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">result, err </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Subagent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ctx, </span><span style="color:#6F42C1;--shiki-dark:#B392F0">kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SubagentConfig</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Task: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Summarize the changes in this PR"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Model: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"anthropic/claude-haiku-latest"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> SystemPrompt: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"You are a code reviewer."</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Timeout: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">5</span><span style="color:#D73A49;--shiki-dark:#F97583"> *</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> time.Minute,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span></code></pre>
|
||||
<h3 id="real-time-subagent-events"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#real-time-subagent-events"><span class="icon icon-link"></span></a>Real-time subagent events</h3>
|
||||
<p>Use <code>SubscribeSubagent</code> to receive real-time events from LLM-initiated subagents (i.e., when the model uses the <code>subagent</code> tool). Register inside an <code>OnToolCall</code> handler using the tool call ID:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">OnToolCall</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">e</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ToolCallEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583"> if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> e.ToolName </span><span style="color:#D73A49;--shiki-dark:#F97583">==</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "subagent"</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> host.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SubscribeSubagent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(e.ToolCallID, </span><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">event</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Event</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583"> switch</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ev </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> event.(</span><span style="color:#D73A49;--shiki-dark:#F97583">type</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583"> case</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">MessageUpdateEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> fmt.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Print</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(ev.Chunk) </span><span style="color:#6A737D;--shiki-dark:#6A737D">// streaming text from child</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583"> case</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ToolCallEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> fmt.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Printf</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Child calling: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">%s\\n</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, ev.ToolName)</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583"> case</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> kit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ToolResultEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> fmt.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Printf</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Child result: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">%s\\n</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, ev.ToolName)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> }</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> })</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> }</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span></code></pre>
|
||||
<p>The listener receives the same event types as <code>Subscribe()</code> (<code>ToolCallEvent</code>, <code>MessageUpdateEvent</code>, <code>ReasoningDeltaEvent</code>, etc.) but scoped to the child agent's activity. Listeners are cleaned up automatically when the subagent completes.</p>
|
||||
<p>If no listeners are registered for a tool call, no event dispatching overhead is incurred.</p>`,headings:[{depth:2,text:"Subprocess pattern",id:"subprocess-pattern"},{depth:2,text:"Built-in subagent tool",id:"built-in-subagent-tool"},{depth:2,text:"Extension subagents",id:"extension-subagents"},{depth:3,text:"Monitoring subagents from extensions",id:"monitoring-subagents-from-extensions"},{depth:2,text:"Go SDK subagents",id:"go-sdk-subagents"},{depth:3,text:"Real-time subagent events",id:"real-time-subagent-events"}],raw:`
|
||||
# Subagents
|
||||
|
||||
Kit supports multi-agent orchestration through both subprocess spawning and in-process subagents.
|
||||
|
||||
## Subprocess pattern
|
||||
|
||||
Spawn Kit as a subprocess for isolated agent execution:
|
||||
|
||||
\`\`\`bash
|
||||
kit "Analyze codebase" \\
|
||||
--json \\
|
||||
--no-session \\
|
||||
--no-extensions \\
|
||||
--quiet \\
|
||||
--model anthropic/claude-haiku-latest
|
||||
\`\`\`
|
||||
|
||||
Key flags for subprocess usage:
|
||||
|
||||
| Flag | Purpose |
|
||||
|------|---------|
|
||||
| \`--quiet\` | Stdout only, no TUI |
|
||||
| \`--no-session\` | Ephemeral, no persistence |
|
||||
| \`--no-extensions\` | Prevent recursive extension loading |
|
||||
| \`--json\` | Machine-readable output |
|
||||
| \`--system-prompt\` | Custom system prompt (string or file path) |
|
||||
|
||||
Positional arguments are the prompt. \`@file\` arguments attach file content as context.
|
||||
|
||||
## Built-in subagent tool
|
||||
|
||||
Kit includes a built-in \`subagent\` tool that the LLM can use to delegate tasks to independent child agents:
|
||||
|
||||
\`\`\`
|
||||
subagent(
|
||||
task: "Analyze the test files and summarize coverage",
|
||||
model: "anthropic/claude-haiku-latest", // optional
|
||||
system_prompt: "You are a test analysis expert.", // optional
|
||||
timeout_seconds: 300 // optional, max 1800
|
||||
)
|
||||
\`\`\`
|
||||
|
||||
Subagents run as separate in-process Kit instances with full tool access (except spawning further subagents, to prevent infinite recursion). They can run in parallel.
|
||||
|
||||
## Extension subagents
|
||||
|
||||
Extensions can spawn subagents programmatically:
|
||||
|
||||
\`\`\`go
|
||||
result := ctx.SpawnSubagent(ext.SubagentConfig{
|
||||
Task: "Review this code for security issues",
|
||||
Model: "anthropic/claude-sonnet-latest",
|
||||
SystemPrompt: "You are a security auditor.",
|
||||
})
|
||||
\`\`\`
|
||||
|
||||
### Monitoring subagents from extensions
|
||||
|
||||
When the LLM (not the extension itself) spawns a subagent using the \`subagent\` tool, extensions can monitor its activity in real-time using three lifecycle event handlers:
|
||||
|
||||
\`\`\`go
|
||||
// Track active subagents and display their output
|
||||
var subagentWidgets map[string]*SubagentWidget
|
||||
|
||||
func Init(api ext.API) {
|
||||
// Subagent started by the main agent
|
||||
api.OnSubagentStart(func(e ext.SubagentStartEvent, ctx ext.Context) {
|
||||
// e.ToolCallID — unique ID for this subagent invocation
|
||||
// e.Task — the task/prompt sent to the subagent
|
||||
widget := NewWidget(e.ToolCallID, e.Task)
|
||||
subagentWidgets[e.ToolCallID] = widget
|
||||
ctx.SetWidget(widget.Config())
|
||||
})
|
||||
|
||||
// Real-time streaming from subagent
|
||||
api.OnSubagentChunk(func(e ext.SubagentChunkEvent, ctx ext.Context) {
|
||||
// e.ToolCallID — matches the start event
|
||||
// e.ChunkType — "text", "tool_call", "tool_execution_start", "tool_result"
|
||||
// e.Content — text content
|
||||
// e.ToolName — tool name (for tool chunks)
|
||||
// e.IsError — true if tool result failed
|
||||
widget := subagentWidgets[e.ToolCallID]
|
||||
if widget != nil {
|
||||
widget.AddOutput(e)
|
||||
ctx.SetWidget(widget.Config())
|
||||
}
|
||||
})
|
||||
|
||||
// Subagent completed
|
||||
api.OnSubagentEnd(func(e ext.SubagentEndEvent, ctx ext.Context) {
|
||||
// e.Response — final response from subagent
|
||||
// e.ErrorMsg — error message if subagent failed
|
||||
widget := subagentWidgets[e.ToolCallID]
|
||||
if widget != nil {
|
||||
widget.MarkComplete(e.Response, e.ErrorMsg)
|
||||
ctx.SetWidget(widget.Config())
|
||||
delete(subagentWidgets, e.ToolCallID)
|
||||
}
|
||||
})
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
**Event structs:**
|
||||
|
||||
\`\`\`go
|
||||
type SubagentStartEvent struct {
|
||||
ToolCallID string // Unique ID for this subagent invocation
|
||||
Task string // The task/prompt sent to subagent
|
||||
}
|
||||
|
||||
type SubagentChunkEvent struct {
|
||||
ToolCallID string // Matches SubagentStartEvent.ToolCallID
|
||||
Task string // Task description
|
||||
ChunkType string // "text", "tool_call", "tool_execution_start", "tool_result"
|
||||
Content string // For text chunks
|
||||
ToolName string // For tool-related chunks
|
||||
IsError bool // For tool_result chunks
|
||||
}
|
||||
|
||||
type SubagentEndEvent struct {
|
||||
ToolCallID string // Matches start event
|
||||
Task string // Task description
|
||||
Response string // Final response from subagent
|
||||
ErrorMsg string // Error message if failed
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
This enables building monitoring widgets that display real-time activity from all subagents spawned by the main agent.
|
||||
|
||||
## Go SDK subagents
|
||||
|
||||
The SDK provides in-process subagent spawning:
|
||||
|
||||
\`\`\`go
|
||||
result, err := host.Subagent(ctx, kit.SubagentConfig{
|
||||
Task: "Summarize the changes in this PR",
|
||||
Model: "anthropic/claude-haiku-latest",
|
||||
SystemPrompt: "You are a code reviewer.",
|
||||
Timeout: 5 * time.Minute,
|
||||
})
|
||||
\`\`\`
|
||||
|
||||
### Real-time subagent events
|
||||
|
||||
Use \`SubscribeSubagent\` to receive real-time events from LLM-initiated subagents (i.e., when the model uses the \`subagent\` tool). Register inside an \`OnToolCall\` handler using the tool call ID:
|
||||
|
||||
\`\`\`go
|
||||
host.OnToolCall(func(e kit.ToolCallEvent) {
|
||||
if e.ToolName == "subagent" {
|
||||
host.SubscribeSubagent(e.ToolCallID, func(event kit.Event) {
|
||||
switch ev := event.(type) {
|
||||
case kit.MessageUpdateEvent:
|
||||
fmt.Print(ev.Chunk) // streaming text from child
|
||||
case kit.ToolCallEvent:
|
||||
fmt.Printf("Child calling: %s\\n", ev.ToolName)
|
||||
case kit.ToolResultEvent:
|
||||
fmt.Printf("Child result: %s\\n", ev.ToolName)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
\`\`\`
|
||||
|
||||
The listener receives the same event types as \`Subscribe()\` (\`ToolCallEvent\`, \`MessageUpdateEvent\`, \`ReasoningDeltaEvent\`, etc.) but scoped to the child agent's activity. Listeners are cleaned up automatically when the subagent completes.
|
||||
|
||||
If no listeners are registered for a tool call, no event dispatching overhead is incurred.
|
||||
`};export{s as default};
|
||||
@@ -1,882 +0,0 @@
|
||||
const s={frontmatter:{title:"Testing Extensions",description:"Write unit tests for your Kit extensions using the test package.",hidden:!1,toc:!0,draft:!1},html:`<h1 id="testing-extensions"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#testing-extensions"><span class="icon icon-link"></span></a>Testing Extensions</h1>
|
||||
<p>Kit provides a testing package (<code>github.com/mark3labs/kit/pkg/extensions/test</code>) that enables you to write unit tests for your extensions. Tests run outside the Yaegi interpreter but load your extension code into an isolated interpreter instance, allowing you to verify behavior without running the full Kit TUI.</p>
|
||||
<h2 id="overview"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#overview"><span class="icon icon-link"></span></a>Overview</h2>
|
||||
<p>Extension tests allow you to:</p>
|
||||
<ul>
|
||||
<li>Test event handlers without running the interactive TUI</li>
|
||||
<li>Verify tool/command registration</li>
|
||||
<li>Assert that context methods (Print, SetWidget, etc.) are called correctly</li>
|
||||
<li>Test blocking and non-blocking event handling</li>
|
||||
<li>Simulate user input and tool calls</li>
|
||||
<li>Verify widget, header, footer, and status bar updates</li>
|
||||
</ul>
|
||||
<h2 id="installation"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#installation"><span class="icon icon-link"></span></a>Installation</h2>
|
||||
<p>The test package is part of the Kit codebase. Import it in your extension tests:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF"> "</span><span style="color:#6F42C1;--shiki-dark:#B392F0">testing</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF"> "</span><span style="color:#6F42C1;--shiki-dark:#B392F0">github.com/mark3labs/kit/pkg/extensions/test</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF"> "</span><span style="color:#6F42C1;--shiki-dark:#B392F0">github.com/mark3labs/kit/internal/extensions</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span></code></pre>
|
||||
<h2 id="basic-usage"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#basic-usage"><span class="icon icon-link"></span></a>Basic Usage</h2>
|
||||
<h3 id="testing-an-extension-file"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#testing-an-extension-file"><span class="icon icon-link"></span></a>Testing an Extension File</h3>
|
||||
<p>Create a test file alongside your extension (e.g., <code>my-ext_test.go</code>):</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">package</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> main</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF"> "</span><span style="color:#6F42C1;--shiki-dark:#B392F0">testing</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF"> "</span><span style="color:#6F42C1;--shiki-dark:#B392F0">github.com/mark3labs/kit/pkg/extensions/test</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF"> "</span><span style="color:#6F42C1;--shiki-dark:#B392F0">github.com/mark3labs/kit/internal/extensions</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> TestMyExtension</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">t</span><span style="color:#D73A49;--shiki-dark:#F97583"> *</span><span style="color:#6F42C1;--shiki-dark:#B392F0">testing</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">T</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Create a test harness</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Load your extension</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">LoadFile</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"my-ext.go"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Emit events and check results</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> result, err </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Emit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">extensions</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ToolCallEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> ToolName: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"my_tool"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Input: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">\`{"key": "value"}\`</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> })</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583"> if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> err </span><span style="color:#D73A49;--shiki-dark:#F97583">!=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> nil</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> t.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Fatalf</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"unexpected error: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">%v</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, err)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> }</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Use assertion helpers</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AssertNotBlocked</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t, result)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AssertPrinted</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t, harness, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"expected output"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
|
||||
<h3 id="testing-inline-extension-code"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#testing-inline-extension-code"><span class="icon icon-link"></span></a>Testing Inline Extension Code</h3>
|
||||
<p>For quick tests or edge cases, you can load extension source directly:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> TestToolBlocking</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">t</span><span style="color:#D73A49;--shiki-dark:#F97583"> *</span><span style="color:#6F42C1;--shiki-dark:#B392F0">testing</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">T</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> src </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> \`package main</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF">import "kit/ext"</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF">func Init(api ext.API) {</span></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF"> api.OnToolCall(func(tc ext.ToolCallEvent, ctx ext.Context) *ext.ToolCallResult {</span></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF"> if tc.ToolName == "dangerous" {</span></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF"> return &ext.ToolCallResult{Block: true, Reason: "not allowed"}</span></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF"> }</span></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF"> return nil</span></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF"> })</span></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF">}</span></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF">\`</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">LoadString</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(src, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"test-ext.go"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Test the tool is blocked</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> result, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Emit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">extensions</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ToolCallEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> ToolName: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"dangerous"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Input: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"{}"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> })</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AssertBlocked</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t, result, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"not allowed"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
|
||||
<h2 id="common-testing-patterns"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#common-testing-patterns"><span class="icon icon-link"></span></a>Common Testing Patterns</h2>
|
||||
<h3 id="testing-handler-registration"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#testing-handler-registration"><span class="icon icon-link"></span></a>Testing Handler Registration</h3>
|
||||
<p>Verify your extension registers the expected handlers:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> TestHandlers</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">t</span><span style="color:#D73A49;--shiki-dark:#F97583"> *</span><span style="color:#6F42C1;--shiki-dark:#B392F0">testing</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">T</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">LoadFile</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"my-ext.go"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AssertHasHandlers</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t, harness, extensions.ToolCall)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AssertHasHandlers</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t, harness, extensions.SessionStart)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AssertNoHandlers</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t, harness, extensions.AgentEnd) </span><span style="color:#6A737D;--shiki-dark:#6A737D">// Verify no unexpected handlers</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
|
||||
<h3 id="testing-tool-registration"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#testing-tool-registration"><span class="icon icon-link"></span></a>Testing Tool Registration</h3>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> TestTools</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">t</span><span style="color:#D73A49;--shiki-dark:#F97583"> *</span><span style="color:#6F42C1;--shiki-dark:#B392F0">testing</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">T</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">LoadFile</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"my-ext.go"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Verify a specific tool is registered</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AssertToolRegistered</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t, harness, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"my_tool"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Or inspect all tools</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> tools </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">RegisteredTools</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583"> for</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> _, tool </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#D73A49;--shiki-dark:#F97583"> range</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> tools {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> t.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Logf</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Tool: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">%s</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> - </span><span style="color:#005CC5;--shiki-dark:#79B8FF">%s</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, tool.Name, tool.Description)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> }</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
|
||||
<h3 id="testing-commands"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#testing-commands"><span class="icon icon-link"></span></a>Testing Commands</h3>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> TestCommands</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">t</span><span style="color:#D73A49;--shiki-dark:#F97583"> *</span><span style="color:#6F42C1;--shiki-dark:#B392F0">testing</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">T</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">LoadFile</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"my-ext.go"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AssertCommandRegistered</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t, harness, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"mycommand"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
|
||||
<h3 id="testing-widgets"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#testing-widgets"><span class="icon icon-link"></span></a>Testing Widgets</h3>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> TestWidgets</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">t</span><span style="color:#D73A49;--shiki-dark:#F97583"> *</span><span style="color:#6F42C1;--shiki-dark:#B392F0">testing</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">T</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">LoadFile</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"my-ext.go"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Trigger event that creates the widget</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> _, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Emit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">extensions</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SessionStartEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{SessionID: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"test"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Verify widget was set</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AssertWidgetSet</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t, harness, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"my-widget"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AssertWidgetText</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t, harness, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"my-widget"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Expected Text"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AssertWidgetTextContains</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t, harness, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"my-widget"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"partial"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Check widget properties directly</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> widget, ok </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Context</span><span style="color:#24292E;--shiki-dark:#E1E4E8">().</span><span style="color:#6F42C1;--shiki-dark:#B392F0">GetWidget</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"my-widget"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583"> if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ok {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> t.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Logf</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Border color: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">%s</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, widget.Style.BorderColor)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> }</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
|
||||
<h3 id="testing-input-handling"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#testing-input-handling"><span class="icon icon-link"></span></a>Testing Input Handling</h3>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> TestInput</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">t</span><span style="color:#D73A49;--shiki-dark:#F97583"> *</span><span style="color:#6F42C1;--shiki-dark:#B392F0">testing</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">T</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">LoadFile</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"my-ext.go"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> result, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Emit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">extensions</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">InputEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Text: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"!mycommand"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Source: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"cli"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> })</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AssertInputHandled</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t, result, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"handled"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
|
||||
<h3 id="testing-headers-and-footers"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#testing-headers-and-footers"><span class="icon icon-link"></span></a>Testing Headers and Footers</h3>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> TestHeaderFooter</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">t</span><span style="color:#D73A49;--shiki-dark:#F97583"> *</span><span style="color:#6F42C1;--shiki-dark:#B392F0">testing</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">T</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">LoadFile</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"my-ext.go"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> _, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Emit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">extensions</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SessionStartEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{SessionID: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"test"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AssertHeaderSet</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t, harness)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AssertFooterSet</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t, harness)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Inspect content</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> header </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Context</span><span style="color:#24292E;--shiki-dark:#E1E4E8">().</span><span style="color:#6F42C1;--shiki-dark:#B392F0">GetHeader</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583"> if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> header </span><span style="color:#D73A49;--shiki-dark:#F97583">!=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> nil</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> t.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Logf</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Header text: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">%s</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, header.Content.Text)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> }</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
|
||||
<h3 id="testing-status-bar"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#testing-status-bar"><span class="icon icon-link"></span></a>Testing Status Bar</h3>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> TestStatus</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">t</span><span style="color:#D73A49;--shiki-dark:#F97583"> *</span><span style="color:#6F42C1;--shiki-dark:#B392F0">testing</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">T</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">LoadFile</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"my-ext.go"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> _, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Emit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">extensions</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AgentEndEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{})</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AssertStatusSet</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t, harness, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"myext:status"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AssertStatusText</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t, harness, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"myext:status"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Ready"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
|
||||
<h3 id="testing-print-output"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#testing-print-output"><span class="icon icon-link"></span></a>Testing Print Output</h3>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> TestOutput</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">t</span><span style="color:#D73A49;--shiki-dark:#F97583"> *</span><span style="color:#6F42C1;--shiki-dark:#B392F0">testing</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">T</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">LoadFile</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"my-ext.go"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> _, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Emit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">extensions</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ToolCallEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{ToolName: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"test"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Exact match</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AssertPrinted</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t, harness, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"exact output"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Partial match</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AssertPrintedContains</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t, harness, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"partial"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Styled output</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AssertPrintInfo</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t, harness, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"info message"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AssertPrintError</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t, harness, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"error message"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
|
||||
<h3 id="testing-with-prompts"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#testing-with-prompts"><span class="icon icon-link"></span></a>Testing with Prompts</h3>
|
||||
<p>Configure mock prompt results for testing interactive behavior:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> TestWithPrompts</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">t</span><span style="color:#D73A49;--shiki-dark:#F97583"> *</span><span style="color:#6F42C1;--shiki-dark:#B392F0">testing</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">T</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">LoadFile</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"my-ext.go"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Configure what prompts should return</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Context</span><span style="color:#24292E;--shiki-dark:#E1E4E8">().</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SetPromptSelectResult</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">extensions</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">PromptSelectResult</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Value: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"option1"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Index: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">0</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Cancelled: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">false</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> })</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Context</span><span style="color:#24292E;--shiki-dark:#E1E4E8">().</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SetPromptConfirmResult</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">extensions</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">PromptConfirmResult</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Value: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">true</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Cancelled: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">false</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> })</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Now when your extension calls ctx.PromptSelect(), it gets this result</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> _, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Emit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">extensions</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SessionStartEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{SessionID: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"test"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
|
||||
<h3 id="testing-complete-session-flow"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#testing-complete-session-flow"><span class="icon icon-link"></span></a>Testing Complete Session Flow</h3>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> TestFullSession</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">t</span><span style="color:#D73A49;--shiki-dark:#F97583"> *</span><span style="color:#6F42C1;--shiki-dark:#B392F0">testing</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">T</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">LoadFile</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"my-ext.go"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Simulate a complete session</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> _, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Emit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">extensions</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SessionStartEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{SessionID: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"test"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> _, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Emit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">extensions</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">BeforeAgentStartEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{})</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> _, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Emit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">extensions</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AgentStartEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{})</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Multiple tool calls</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> tools </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> []</span><span style="color:#D73A49;--shiki-dark:#F97583">string</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Read"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Grep"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Bash"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583"> for</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> _, tool </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#D73A49;--shiki-dark:#F97583"> range</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> tools {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> _, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Emit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">extensions</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ToolCallEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{ToolName: tool})</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> _, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Emit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">extensions</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ToolResultEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{ToolName: tool})</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> }</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> _, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Emit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">extensions</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AgentEndEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{})</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> _, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Emit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">extensions</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SessionShutdownEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{})</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> </span></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"> // Verify final state</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AssertWidgetTextContains</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t, harness, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"status"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Complete"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
|
||||
<h2 id="available-assertions"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#available-assertions"><span class="icon icon-link"></span></a>Available Assertions</h2>
|
||||
<p>The test package provides these assertion helpers:</p>
|
||||
<h3 id="event-results"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#event-results"><span class="icon icon-link"></span></a>Event Results</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Function</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>AssertNotBlocked(t, result)</code></td>
|
||||
<td>Verify tool was not blocked</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AssertBlocked(t, result, reason)</code></td>
|
||||
<td>Verify tool was blocked with reason</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AssertInputHandled(t, result, action)</code></td>
|
||||
<td>Verify input was handled</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AssertInputTransformed(t, result, text)</code></td>
|
||||
<td>Verify input was transformed</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 id="context-interactions"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#context-interactions"><span class="icon icon-link"></span></a>Context Interactions</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Function</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>AssertPrinted(t, harness, text)</code></td>
|
||||
<td>Verify exact print output</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AssertPrintedContains(t, harness, substring)</code></td>
|
||||
<td>Verify partial print output</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AssertPrintInfo(t, harness, text)</code></td>
|
||||
<td>Verify PrintInfo was called</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AssertPrintError(t, harness, text)</code></td>
|
||||
<td>Verify PrintError was called</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AssertWidgetSet(t, harness, id)</code></td>
|
||||
<td>Verify widget was set</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AssertWidgetNotSet(t, harness, id)</code></td>
|
||||
<td>Verify widget was not set</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AssertWidgetText(t, harness, id, text)</code></td>
|
||||
<td>Verify widget content</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AssertWidgetTextContains(t, harness, id, substring)</code></td>
|
||||
<td>Verify widget contains text</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AssertHeaderSet(t, harness)</code></td>
|
||||
<td>Verify header was set</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AssertFooterSet(t, harness)</code></td>
|
||||
<td>Verify footer was set</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AssertStatusSet(t, harness, key)</code></td>
|
||||
<td>Verify status was set</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AssertStatusText(t, harness, key, text)</code></td>
|
||||
<td>Verify status text</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 id="registration"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#registration"><span class="icon icon-link"></span></a>Registration</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Function</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>AssertToolRegistered(t, harness, name)</code></td>
|
||||
<td>Verify tool registration</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AssertCommandRegistered(t, harness, name)</code></td>
|
||||
<td>Verify command registration</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AssertHasHandlers(t, harness, eventType)</code></td>
|
||||
<td>Verify handlers exist</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AssertNoHandlers(t, harness, eventType)</code></td>
|
||||
<td>Verify no handlers</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 id="messaging"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#messaging"><span class="icon icon-link"></span></a>Messaging</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Function</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>AssertMessageSent(t, harness, text)</code></td>
|
||||
<td>Verify SendMessage was called</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AssertCancelAndSend(t, harness, text)</code></td>
|
||||
<td>Verify CancelAndSend was called</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2 id="helper-functions"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#helper-functions"><span class="icon icon-link"></span></a>Helper Functions</h2>
|
||||
<p>For custom assertions, extract result details:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">result, _ </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Emit</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">extensions</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ToolCallEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span><span style="color:#D73A49;--shiki-dark:#F97583">...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">tcr </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">GetToolCallResult</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(result)</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> tcr </span><span style="color:#D73A49;--shiki-dark:#F97583">!=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> nil</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> t.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Logf</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Block: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">%v</span><span style="color:#032F62;--shiki-dark:#9ECBFF">, Reason: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">%s</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, tcr.Block, tcr.Reason)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">ir </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">GetInputResult</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(result)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">trr </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">GetToolResultResult</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(result)</span></span></code></pre>
|
||||
<h2 id="advanced-usage"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#advanced-usage"><span class="icon icon-link"></span></a>Advanced Usage</h2>
|
||||
<h3 id="accessing-the-mock-context"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#accessing-the-mock-context"><span class="icon icon-link"></span></a>Accessing the Mock Context</h3>
|
||||
<p>For custom verification:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">ctx </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> harness.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Context</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Get all recorded prints</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">prints </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ctx.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">GetPrints</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Check options</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">value </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ctx.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">GetOption</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"my-option"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Verify widget properties</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">widget, ok </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ctx.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">GetWidget</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"my-widget"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ok </span><span style="color:#D73A49;--shiki-dark:#F97583">&&</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> widget.Style.BorderColor </span><span style="color:#D73A49;--shiki-dark:#F97583">==</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "#ff0000"</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> t.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Log</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Widget has red border"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Check status entries</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">status, ok </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ctx.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">GetStatus</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"myext:status"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span></code></pre>
|
||||
<h3 id="testing-multiple-extensions"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#testing-multiple-extensions"><span class="icon icon-link"></span></a>Testing Multiple Extensions</h3>
|
||||
<p>Each harness is isolated:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">harness1 </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">harness1.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">LoadFile</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"ext1.go"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">harness2 </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> test.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">New</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(t)</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">harness2.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">LoadFile</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"ext2.go"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Events to one don't affect the other</span></span></code></pre>
|
||||
<h3 id="running-tests"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#running-tests"><span class="icon icon-link"></span></a>Running Tests</h3>
|
||||
<p>Run all tests in your extension directory:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> examples/extensions</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">go</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> test</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -v</span></span></code></pre>
|
||||
<p>Run with race detector:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">go</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> test</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -race</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -v</span></span></code></pre>
|
||||
<p>Run a specific test:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">go</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> test</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -v</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -run</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> TestMyExtension</span></span></code></pre>
|
||||
<h2 id="best-practices"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#best-practices"><span class="icon icon-link"></span></a>Best Practices</h2>
|
||||
<ol>
|
||||
<li><strong>Test one behavior per test</strong> — Keep tests focused and readable</li>
|
||||
<li><strong>Use inline source for edge cases</strong> — <code>LoadString()</code> is great for testing specific scenarios</li>
|
||||
<li><strong>Use <code>LoadFile()</code> for integration tests</strong> — Tests the actual extension file</li>
|
||||
<li><strong>Assert on context calls</strong> — Verify your extension interacts with the context correctly</li>
|
||||
<li><strong>Test both positive and negative cases</strong> — Verify tools are blocked AND allowed appropriately</li>
|
||||
<li><strong>Test all event handlers</strong> — Make sure all registered handlers work correctly</li>
|
||||
<li><strong>Use descriptive test names</strong> — <code>TestExtension_BlocksDangerousTools</code> is clearer than <code>Test1</code></li>
|
||||
</ol>
|
||||
<h2 id="limitations"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#limitations"><span class="icon icon-link"></span></a>Limitations</h2>
|
||||
<p>The test harness has these intentional limitations:</p>
|
||||
<ul>
|
||||
<li><strong>No TUI rendering</strong> — Widgets are recorded but not rendered visually</li>
|
||||
<li><strong>Prompts return configured values</strong> — Pre-configure prompt results in tests</li>
|
||||
<li><strong>Subagents don't spawn real processes</strong> — <code>SpawnSubagent()</code> returns nil/empty results</li>
|
||||
<li><strong>LLM completions are mocked</strong> — <code>Complete()</code> returns empty responses</li>
|
||||
<li><strong>Some context methods are no-ops</strong> — <code>Exit()</code>, <code>SetActiveTools()</code>, etc. don't have side effects</li>
|
||||
</ul>
|
||||
<p>These limitations focus testing on extension logic rather than the full Kit runtime.</p>
|
||||
<h2 id="complete-example"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#complete-example"><span class="icon icon-link"></span></a>Complete Example</h2>
|
||||
<p>See <code>examples/extensions/tool-logger_test.go</code> for a complete example with 14 tests covering:</p>
|
||||
<ul>
|
||||
<li>Handler registration</li>
|
||||
<li>Tool call and result handling</li>
|
||||
<li>Session lifecycle events</li>
|
||||
<li>Input commands (<code>!time</code>, <code>!status</code>)</li>
|
||||
<li>Unknown command handling</li>
|
||||
<li>Concurrent operations (race condition check)</li>
|
||||
<li>Real file logging verification</li>
|
||||
</ul>`,headings:[{depth:2,text:"Overview",id:"overview"},{depth:2,text:"Installation",id:"installation"},{depth:2,text:"Basic Usage",id:"basic-usage"},{depth:3,text:"Testing an Extension File",id:"testing-an-extension-file"},{depth:3,text:"Testing Inline Extension Code",id:"testing-inline-extension-code"},{depth:2,text:"Common Testing Patterns",id:"common-testing-patterns"},{depth:3,text:"Testing Handler Registration",id:"testing-handler-registration"},{depth:3,text:"Testing Tool Registration",id:"testing-tool-registration"},{depth:3,text:"Testing Commands",id:"testing-commands"},{depth:3,text:"Testing Widgets",id:"testing-widgets"},{depth:3,text:"Testing Input Handling",id:"testing-input-handling"},{depth:3,text:"Testing Headers and Footers",id:"testing-headers-and-footers"},{depth:3,text:"Testing Status Bar",id:"testing-status-bar"},{depth:3,text:"Testing Print Output",id:"testing-print-output"},{depth:3,text:"Testing with Prompts",id:"testing-with-prompts"},{depth:3,text:"Testing Complete Session Flow",id:"testing-complete-session-flow"},{depth:2,text:"Available Assertions",id:"available-assertions"},{depth:3,text:"Event Results",id:"event-results"},{depth:3,text:"Context Interactions",id:"context-interactions"},{depth:3,text:"Registration",id:"registration"},{depth:3,text:"Messaging",id:"messaging"},{depth:2,text:"Helper Functions",id:"helper-functions"},{depth:2,text:"Advanced Usage",id:"advanced-usage"},{depth:3,text:"Accessing the Mock Context",id:"accessing-the-mock-context"},{depth:3,text:"Testing Multiple Extensions",id:"testing-multiple-extensions"},{depth:3,text:"Running Tests",id:"running-tests"},{depth:2,text:"Best Practices",id:"best-practices"},{depth:2,text:"Limitations",id:"limitations"},{depth:2,text:"Complete Example",id:"complete-example"}],raw:`
|
||||
# Testing Extensions
|
||||
|
||||
Kit provides a testing package (\`github.com/mark3labs/kit/pkg/extensions/test\`) that enables you to write unit tests for your extensions. Tests run outside the Yaegi interpreter but load your extension code into an isolated interpreter instance, allowing you to verify behavior without running the full Kit TUI.
|
||||
|
||||
## Overview
|
||||
|
||||
Extension tests allow you to:
|
||||
|
||||
- Test event handlers without running the interactive TUI
|
||||
- Verify tool/command registration
|
||||
- Assert that context methods (Print, SetWidget, etc.) are called correctly
|
||||
- Test blocking and non-blocking event handling
|
||||
- Simulate user input and tool calls
|
||||
- Verify widget, header, footer, and status bar updates
|
||||
|
||||
## Installation
|
||||
|
||||
The test package is part of the Kit codebase. Import it in your extension tests:
|
||||
|
||||
\`\`\`go
|
||||
import (
|
||||
"testing"
|
||||
"github.com/mark3labs/kit/pkg/extensions/test"
|
||||
"github.com/mark3labs/kit/internal/extensions"
|
||||
)
|
||||
\`\`\`
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Testing an Extension File
|
||||
|
||||
Create a test file alongside your extension (e.g., \`my-ext_test.go\`):
|
||||
|
||||
\`\`\`go
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"github.com/mark3labs/kit/pkg/extensions/test"
|
||||
"github.com/mark3labs/kit/internal/extensions"
|
||||
)
|
||||
|
||||
func TestMyExtension(t *testing.T) {
|
||||
// Create a test harness
|
||||
harness := test.New(t)
|
||||
|
||||
// Load your extension
|
||||
harness.LoadFile("my-ext.go")
|
||||
|
||||
// Emit events and check results
|
||||
result, err := harness.Emit(extensions.ToolCallEvent{
|
||||
ToolName: "my_tool",
|
||||
Input: \`{"key": "value"}\`,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// Use assertion helpers
|
||||
test.AssertNotBlocked(t, result)
|
||||
test.AssertPrinted(t, harness, "expected output")
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
### Testing Inline Extension Code
|
||||
|
||||
For quick tests or edge cases, you can load extension source directly:
|
||||
|
||||
\`\`\`go
|
||||
func TestToolBlocking(t *testing.T) {
|
||||
src := \`package main
|
||||
|
||||
import "kit/ext"
|
||||
|
||||
func Init(api ext.API) {
|
||||
api.OnToolCall(func(tc ext.ToolCallEvent, ctx ext.Context) *ext.ToolCallResult {
|
||||
if tc.ToolName == "dangerous" {
|
||||
return &ext.ToolCallResult{Block: true, Reason: "not allowed"}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
\`
|
||||
harness := test.New(t)
|
||||
harness.LoadString(src, "test-ext.go")
|
||||
|
||||
// Test the tool is blocked
|
||||
result, _ := harness.Emit(extensions.ToolCallEvent{
|
||||
ToolName: "dangerous",
|
||||
Input: "{}",
|
||||
})
|
||||
|
||||
test.AssertBlocked(t, result, "not allowed")
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## Common Testing Patterns
|
||||
|
||||
### Testing Handler Registration
|
||||
|
||||
Verify your extension registers the expected handlers:
|
||||
|
||||
\`\`\`go
|
||||
func TestHandlers(t *testing.T) {
|
||||
harness := test.New(t)
|
||||
harness.LoadFile("my-ext.go")
|
||||
|
||||
test.AssertHasHandlers(t, harness, extensions.ToolCall)
|
||||
test.AssertHasHandlers(t, harness, extensions.SessionStart)
|
||||
test.AssertNoHandlers(t, harness, extensions.AgentEnd) // Verify no unexpected handlers
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
### Testing Tool Registration
|
||||
|
||||
\`\`\`go
|
||||
func TestTools(t *testing.T) {
|
||||
harness := test.New(t)
|
||||
harness.LoadFile("my-ext.go")
|
||||
|
||||
// Verify a specific tool is registered
|
||||
test.AssertToolRegistered(t, harness, "my_tool")
|
||||
|
||||
// Or inspect all tools
|
||||
tools := harness.RegisteredTools()
|
||||
for _, tool := range tools {
|
||||
t.Logf("Tool: %s - %s", tool.Name, tool.Description)
|
||||
}
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
### Testing Commands
|
||||
|
||||
\`\`\`go
|
||||
func TestCommands(t *testing.T) {
|
||||
harness := test.New(t)
|
||||
harness.LoadFile("my-ext.go")
|
||||
|
||||
test.AssertCommandRegistered(t, harness, "mycommand")
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
### Testing Widgets
|
||||
|
||||
\`\`\`go
|
||||
func TestWidgets(t *testing.T) {
|
||||
harness := test.New(t)
|
||||
harness.LoadFile("my-ext.go")
|
||||
|
||||
// Trigger event that creates the widget
|
||||
_, _ = harness.Emit(extensions.SessionStartEvent{SessionID: "test"})
|
||||
|
||||
// Verify widget was set
|
||||
test.AssertWidgetSet(t, harness, "my-widget")
|
||||
test.AssertWidgetText(t, harness, "my-widget", "Expected Text")
|
||||
test.AssertWidgetTextContains(t, harness, "my-widget", "partial")
|
||||
|
||||
// Check widget properties directly
|
||||
widget, ok := harness.Context().GetWidget("my-widget")
|
||||
if ok {
|
||||
t.Logf("Border color: %s", widget.Style.BorderColor)
|
||||
}
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
### Testing Input Handling
|
||||
|
||||
\`\`\`go
|
||||
func TestInput(t *testing.T) {
|
||||
harness := test.New(t)
|
||||
harness.LoadFile("my-ext.go")
|
||||
|
||||
result, _ := harness.Emit(extensions.InputEvent{
|
||||
Text: "!mycommand",
|
||||
Source: "cli",
|
||||
})
|
||||
|
||||
test.AssertInputHandled(t, result, "handled")
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
### Testing Headers and Footers
|
||||
|
||||
\`\`\`go
|
||||
func TestHeaderFooter(t *testing.T) {
|
||||
harness := test.New(t)
|
||||
harness.LoadFile("my-ext.go")
|
||||
|
||||
_, _ = harness.Emit(extensions.SessionStartEvent{SessionID: "test"})
|
||||
|
||||
test.AssertHeaderSet(t, harness)
|
||||
test.AssertFooterSet(t, harness)
|
||||
|
||||
// Inspect content
|
||||
header := harness.Context().GetHeader()
|
||||
if header != nil {
|
||||
t.Logf("Header text: %s", header.Content.Text)
|
||||
}
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
### Testing Status Bar
|
||||
|
||||
\`\`\`go
|
||||
func TestStatus(t *testing.T) {
|
||||
harness := test.New(t)
|
||||
harness.LoadFile("my-ext.go")
|
||||
|
||||
_, _ = harness.Emit(extensions.AgentEndEvent{})
|
||||
|
||||
test.AssertStatusSet(t, harness, "myext:status")
|
||||
test.AssertStatusText(t, harness, "myext:status", "Ready")
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
### Testing Print Output
|
||||
|
||||
\`\`\`go
|
||||
func TestOutput(t *testing.T) {
|
||||
harness := test.New(t)
|
||||
harness.LoadFile("my-ext.go")
|
||||
|
||||
_, _ = harness.Emit(extensions.ToolCallEvent{ToolName: "test"})
|
||||
|
||||
// Exact match
|
||||
test.AssertPrinted(t, harness, "exact output")
|
||||
|
||||
// Partial match
|
||||
test.AssertPrintedContains(t, harness, "partial")
|
||||
|
||||
// Styled output
|
||||
test.AssertPrintInfo(t, harness, "info message")
|
||||
test.AssertPrintError(t, harness, "error message")
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
### Testing with Prompts
|
||||
|
||||
Configure mock prompt results for testing interactive behavior:
|
||||
|
||||
\`\`\`go
|
||||
func TestWithPrompts(t *testing.T) {
|
||||
harness := test.New(t)
|
||||
harness.LoadFile("my-ext.go")
|
||||
|
||||
// Configure what prompts should return
|
||||
harness.Context().SetPromptSelectResult(extensions.PromptSelectResult{
|
||||
Value: "option1",
|
||||
Index: 0,
|
||||
Cancelled: false,
|
||||
})
|
||||
|
||||
harness.Context().SetPromptConfirmResult(extensions.PromptConfirmResult{
|
||||
Value: true,
|
||||
Cancelled: false,
|
||||
})
|
||||
|
||||
// Now when your extension calls ctx.PromptSelect(), it gets this result
|
||||
_, _ = harness.Emit(extensions.SessionStartEvent{SessionID: "test"})
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
### Testing Complete Session Flow
|
||||
|
||||
\`\`\`go
|
||||
func TestFullSession(t *testing.T) {
|
||||
harness := test.New(t)
|
||||
harness.LoadFile("my-ext.go")
|
||||
|
||||
// Simulate a complete session
|
||||
_, _ = harness.Emit(extensions.SessionStartEvent{SessionID: "test"})
|
||||
_, _ = harness.Emit(extensions.BeforeAgentStartEvent{})
|
||||
_, _ = harness.Emit(extensions.AgentStartEvent{})
|
||||
|
||||
// Multiple tool calls
|
||||
tools := []string{"Read", "Grep", "Bash"}
|
||||
for _, tool := range tools {
|
||||
_, _ = harness.Emit(extensions.ToolCallEvent{ToolName: tool})
|
||||
_, _ = harness.Emit(extensions.ToolResultEvent{ToolName: tool})
|
||||
}
|
||||
|
||||
_, _ = harness.Emit(extensions.AgentEndEvent{})
|
||||
_, _ = harness.Emit(extensions.SessionShutdownEvent{})
|
||||
|
||||
// Verify final state
|
||||
test.AssertWidgetTextContains(t, harness, "status", "Complete")
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## Available Assertions
|
||||
|
||||
The test package provides these assertion helpers:
|
||||
|
||||
### Event Results
|
||||
|
||||
| Function | Description |
|
||||
|----------|-------------|
|
||||
| \`AssertNotBlocked(t, result)\` | Verify tool was not blocked |
|
||||
| \`AssertBlocked(t, result, reason)\` | Verify tool was blocked with reason |
|
||||
| \`AssertInputHandled(t, result, action)\` | Verify input was handled |
|
||||
| \`AssertInputTransformed(t, result, text)\` | Verify input was transformed |
|
||||
|
||||
### Context Interactions
|
||||
|
||||
| Function | Description |
|
||||
|----------|-------------|
|
||||
| \`AssertPrinted(t, harness, text)\` | Verify exact print output |
|
||||
| \`AssertPrintedContains(t, harness, substring)\` | Verify partial print output |
|
||||
| \`AssertPrintInfo(t, harness, text)\` | Verify PrintInfo was called |
|
||||
| \`AssertPrintError(t, harness, text)\` | Verify PrintError was called |
|
||||
| \`AssertWidgetSet(t, harness, id)\` | Verify widget was set |
|
||||
| \`AssertWidgetNotSet(t, harness, id)\` | Verify widget was not set |
|
||||
| \`AssertWidgetText(t, harness, id, text)\` | Verify widget content |
|
||||
| \`AssertWidgetTextContains(t, harness, id, substring)\` | Verify widget contains text |
|
||||
| \`AssertHeaderSet(t, harness)\` | Verify header was set |
|
||||
| \`AssertFooterSet(t, harness)\` | Verify footer was set |
|
||||
| \`AssertStatusSet(t, harness, key)\` | Verify status was set |
|
||||
| \`AssertStatusText(t, harness, key, text)\` | Verify status text |
|
||||
|
||||
### Registration
|
||||
|
||||
| Function | Description |
|
||||
|----------|-------------|
|
||||
| \`AssertToolRegistered(t, harness, name)\` | Verify tool registration |
|
||||
| \`AssertCommandRegistered(t, harness, name)\` | Verify command registration |
|
||||
| \`AssertHasHandlers(t, harness, eventType)\` | Verify handlers exist |
|
||||
| \`AssertNoHandlers(t, harness, eventType)\` | Verify no handlers |
|
||||
|
||||
### Messaging
|
||||
|
||||
| Function | Description |
|
||||
|----------|-------------|
|
||||
| \`AssertMessageSent(t, harness, text)\` | Verify SendMessage was called |
|
||||
| \`AssertCancelAndSend(t, harness, text)\` | Verify CancelAndSend was called |
|
||||
|
||||
## Helper Functions
|
||||
|
||||
For custom assertions, extract result details:
|
||||
|
||||
\`\`\`go
|
||||
result, _ := harness.Emit(extensions.ToolCallEvent{...})
|
||||
tcr := test.GetToolCallResult(result)
|
||||
if tcr != nil {
|
||||
t.Logf("Block: %v, Reason: %s", tcr.Block, tcr.Reason)
|
||||
}
|
||||
|
||||
ir := test.GetInputResult(result)
|
||||
trr := test.GetToolResultResult(result)
|
||||
\`\`\`
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Accessing the Mock Context
|
||||
|
||||
For custom verification:
|
||||
|
||||
\`\`\`go
|
||||
ctx := harness.Context()
|
||||
|
||||
// Get all recorded prints
|
||||
prints := ctx.GetPrints()
|
||||
|
||||
// Check options
|
||||
value := ctx.GetOption("my-option")
|
||||
|
||||
// Verify widget properties
|
||||
widget, ok := ctx.GetWidget("my-widget")
|
||||
if ok && widget.Style.BorderColor == "#ff0000" {
|
||||
t.Log("Widget has red border")
|
||||
}
|
||||
|
||||
// Check status entries
|
||||
status, ok := ctx.GetStatus("myext:status")
|
||||
\`\`\`
|
||||
|
||||
### Testing Multiple Extensions
|
||||
|
||||
Each harness is isolated:
|
||||
|
||||
\`\`\`go
|
||||
harness1 := test.New(t)
|
||||
harness1.LoadFile("ext1.go")
|
||||
|
||||
harness2 := test.New(t)
|
||||
harness2.LoadFile("ext2.go")
|
||||
|
||||
// Events to one don't affect the other
|
||||
\`\`\`
|
||||
|
||||
### Running Tests
|
||||
|
||||
Run all tests in your extension directory:
|
||||
|
||||
\`\`\`bash
|
||||
cd examples/extensions
|
||||
go test -v
|
||||
\`\`\`
|
||||
|
||||
Run with race detector:
|
||||
|
||||
\`\`\`bash
|
||||
go test -race -v
|
||||
\`\`\`
|
||||
|
||||
Run a specific test:
|
||||
|
||||
\`\`\`bash
|
||||
go test -v -run TestMyExtension
|
||||
\`\`\`
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Test one behavior per test** — Keep tests focused and readable
|
||||
2. **Use inline source for edge cases** — \`LoadString()\` is great for testing specific scenarios
|
||||
3. **Use \`LoadFile()\` for integration tests** — Tests the actual extension file
|
||||
4. **Assert on context calls** — Verify your extension interacts with the context correctly
|
||||
5. **Test both positive and negative cases** — Verify tools are blocked AND allowed appropriately
|
||||
6. **Test all event handlers** — Make sure all registered handlers work correctly
|
||||
7. **Use descriptive test names** — \`TestExtension_BlocksDangerousTools\` is clearer than \`Test1\`
|
||||
|
||||
## Limitations
|
||||
|
||||
The test harness has these intentional limitations:
|
||||
|
||||
- **No TUI rendering** — Widgets are recorded but not rendered visually
|
||||
- **Prompts return configured values** — Pre-configure prompt results in tests
|
||||
- **Subagents don't spawn real processes** — \`SpawnSubagent()\` returns nil/empty results
|
||||
- **LLM completions are mocked** — \`Complete()\` returns empty responses
|
||||
- **Some context methods are no-ops** — \`Exit()\`, \`SetActiveTools()\`, etc. don't have side effects
|
||||
|
||||
These limitations focus testing on extension logic rather than the full Kit runtime.
|
||||
|
||||
## Complete Example
|
||||
|
||||
See \`examples/extensions/tool-logger_test.go\` for a complete example with 14 tests covering:
|
||||
|
||||
- Handler registration
|
||||
- Tool call and result handling
|
||||
- Session lifecycle events
|
||||
- Input commands (\`!time\`, \`!status\`)
|
||||
- Unknown command handling
|
||||
- Concurrent operations (race condition check)
|
||||
- Real file logging verification
|
||||
`};export{s as default};
|
||||
@@ -1,130 +0,0 @@
|
||||
const s={frontmatter:{title:"Testing with tmux",description:"Test Kit's TUI non-interactively using tmux.",hidden:!1,toc:!0,draft:!1},html:`<h1 id="testing-with-tmux"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#testing-with-tmux"><span class="icon icon-link"></span></a>Testing with tmux</h1>
|
||||
<p>Kit's interactive TUI can be tested non-interactively using tmux. This is useful for automated testing, CI pipelines, and extension development.</p>
|
||||
<h2 id="basic-pattern"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#basic-pattern"><span class="icon icon-link"></span></a>Basic pattern</h2>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Start Kit in a detached tmux session</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">tmux</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> new-session</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -s</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> kittest</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -x</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> 120</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -y</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> 40</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> \\</span></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF"> "output/kit -e ext.go --no-session 2>kit_stderr.log"</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Wait for startup</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">sleep</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> 3</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Capture the current screen</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">tmux</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> capture-pane</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -t</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> kittest</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -p</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Send input</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">tmux</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> send-keys</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -t</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> kittest</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> '/command'</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> Enter</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Wait for response</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">sleep</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> 2</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Capture updated screen</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">tmux</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> capture-pane</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -t</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> kittest</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -p</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Cleanup</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">tmux</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> kill-session</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -t</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> kittest</span></span></code></pre>
|
||||
<h2 id="testing-extensions"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#testing-extensions"><span class="icon icon-link"></span></a>Testing extensions</h2>
|
||||
<p>When testing extensions, the pattern is:</p>
|
||||
<ol>
|
||||
<li>Build Kit with your changes</li>
|
||||
<li>Start Kit in tmux with the extension loaded</li>
|
||||
<li>Send slash commands or prompts</li>
|
||||
<li>Capture and verify the screen output</li>
|
||||
<li>Check stderr logs for errors</li>
|
||||
</ol>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Build first</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">go</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> build</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -o</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> output/kit</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> ./cmd/kit</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Start with extension</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">tmux</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> new-session</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -s</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> kittest</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -x</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> 120</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -y</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> 40</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> \\</span></span>
|
||||
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF"> "output/kit -e examples/extensions/widget-status.go --no-session 2>kit_stderr.log"</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">sleep</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> 3</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Verify widget appears in screen</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">tmux</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> capture-pane</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -t</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> kittest</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> grep</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "Status"</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Send a slash command</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">tmux</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> send-keys</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -t</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> kittest</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> '/stats'</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> Enter</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">sleep</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> 1</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">tmux</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> capture-pane</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -t</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> kittest</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -p</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Cleanup</span></span>
|
||||
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">tmux</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> kill-session</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> -t</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> kittest</span></span></code></pre>
|
||||
<h2 id="tips"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#tips"><span class="icon icon-link"></span></a>Tips</h2>
|
||||
<ul>
|
||||
<li>Use <code>-x</code> and <code>-y</code> to set consistent terminal dimensions</li>
|
||||
<li>Redirect stderr to a log file (<code>2>kit.log</code>) for debugging</li>
|
||||
<li>Use <code>--no-session</code> to avoid creating session files during tests</li>
|
||||
<li>Add sufficient <code>sleep</code> between commands for the TUI to render</li>
|
||||
<li>Use <code>grep</code> on captured pane output to verify specific content</li>
|
||||
</ul>`,headings:[{depth:2,text:"Basic pattern",id:"basic-pattern"},{depth:2,text:"Testing extensions",id:"testing-extensions"},{depth:2,text:"Tips",id:"tips"}],raw:`
|
||||
# Testing with tmux
|
||||
|
||||
Kit's interactive TUI can be tested non-interactively using tmux. This is useful for automated testing, CI pipelines, and extension development.
|
||||
|
||||
## Basic pattern
|
||||
|
||||
\`\`\`bash
|
||||
# Start Kit in a detached tmux session
|
||||
tmux new-session -d -s kittest -x 120 -y 40 \\
|
||||
"output/kit -e ext.go --no-session 2>kit_stderr.log"
|
||||
|
||||
# Wait for startup
|
||||
sleep 3
|
||||
|
||||
# Capture the current screen
|
||||
tmux capture-pane -t kittest -p
|
||||
|
||||
# Send input
|
||||
tmux send-keys -t kittest '/command' Enter
|
||||
|
||||
# Wait for response
|
||||
sleep 2
|
||||
|
||||
# Capture updated screen
|
||||
tmux capture-pane -t kittest -p
|
||||
|
||||
# Cleanup
|
||||
tmux kill-session -t kittest
|
||||
\`\`\`
|
||||
|
||||
## Testing extensions
|
||||
|
||||
When testing extensions, the pattern is:
|
||||
|
||||
1. Build Kit with your changes
|
||||
2. Start Kit in tmux with the extension loaded
|
||||
3. Send slash commands or prompts
|
||||
4. Capture and verify the screen output
|
||||
5. Check stderr logs for errors
|
||||
|
||||
\`\`\`bash
|
||||
# Build first
|
||||
go build -o output/kit ./cmd/kit
|
||||
|
||||
# Start with extension
|
||||
tmux new-session -d -s kittest -x 120 -y 40 \\
|
||||
"output/kit -e examples/extensions/widget-status.go --no-session 2>kit_stderr.log"
|
||||
|
||||
sleep 3
|
||||
|
||||
# Verify widget appears in screen
|
||||
tmux capture-pane -t kittest -p | grep "Status"
|
||||
|
||||
# Send a slash command
|
||||
tmux send-keys -t kittest '/stats' Enter
|
||||
sleep 1
|
||||
tmux capture-pane -t kittest -p
|
||||
|
||||
# Cleanup
|
||||
tmux kill-session -t kittest
|
||||
\`\`\`
|
||||
|
||||
## Tips
|
||||
|
||||
- Use \`-x\` and \`-y\` to set consistent terminal dimensions
|
||||
- Redirect stderr to a log file (\`2>kit.log\`) for debugging
|
||||
- Use \`--no-session\` to avoid creating session files during tests
|
||||
- Add sufficient \`sleep\` between commands for the TUI to render
|
||||
- Use \`grep\` on captured pane output to verify specific content
|
||||
`};export{s as default};
|
||||
@@ -1 +0,0 @@
|
||||
const e={};export{e as default};
|
||||
@@ -1,685 +0,0 @@
|
||||
const s={frontmatter:{title:"Themes",description:"Customize Kit's appearance with built-in themes, custom theme files, and the extension theme API.",hidden:!1,toc:!0,draft:!1},html:`<h1 id="themes"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#themes"><span class="icon icon-link"></span></a>Themes</h1>
|
||||
<p>Kit ships with 22 built-in color themes and supports custom themes via YAML/JSON files or the extension API. Themes control all UI colors: input borders, popups, system messages, markdown rendering, syntax highlighting, and diff displays.</p>
|
||||
<h2 id="quick-start"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#quick-start"><span class="icon icon-link"></span></a>Quick start</h2>
|
||||
<p>Switch themes at runtime with the <code>/theme</code> command:</p>
|
||||
<pre><code>/theme dracula
|
||||
/theme catppuccin
|
||||
/theme kitt
|
||||
</code></pre>
|
||||
<p>Run <code>/theme</code> with no arguments to list all available themes.</p>
|
||||
<p><strong>Theme selections are automatically saved</strong> to <code>~/.config/kit/preferences.yml</code> and restored on next launch. You don't need to add anything to your config file — just <code>/theme <name></code> and it sticks. This persistence also applies to <strong>model</strong> and <strong>thinking level</strong> selections.</p>
|
||||
<h2 id="built-in-themes"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#built-in-themes"><span class="icon icon-link"></span></a>Built-in themes</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Theme</th>
|
||||
<th>Style</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>kitt</code></td>
|
||||
<td>KITT-inspired reds and ambers (default)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>catppuccin</code></td>
|
||||
<td>Soothing pastels (Mocha/Latte)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>dracula</code></td>
|
||||
<td>Purple and cyan dark theme</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>tokyonight</code></td>
|
||||
<td>Cool blues with warm accents</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>nord</code></td>
|
||||
<td>Arctic, north-bluish palette</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>gruvbox</code></td>
|
||||
<td>Retro groove colors</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>monokai</code></td>
|
||||
<td>Classic syntax theme</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>solarized</code></td>
|
||||
<td>Precision colors for machines and people</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>github</code></td>
|
||||
<td>GitHub's light and dark palettes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>one-dark</code></td>
|
||||
<td>Atom One Dark</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>rose-pine</code></td>
|
||||
<td>Soho vibes with muted tones</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>ayu</code></td>
|
||||
<td>Simple with bright colors</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>material</code></td>
|
||||
<td>Material Design palette</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>everforest</code></td>
|
||||
<td>Green-focused comfortable theme</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>kanagawa</code></td>
|
||||
<td>Dark theme inspired by Katsushika Hokusai</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>amoled</code></td>
|
||||
<td>Pure black background, vivid accents</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>synthwave</code></td>
|
||||
<td>Retro neon glows</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>vesper</code></td>
|
||||
<td>Warm minimalist dark theme</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>flexoki</code></td>
|
||||
<td>Inky reading palette</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>matrix</code></td>
|
||||
<td>Green-on-black terminal aesthetic</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>vercel</code></td>
|
||||
<td>Clean monochrome with blue accents</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>zenburn</code></td>
|
||||
<td>Low-contrast, warm dark theme</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>All themes support both light and dark terminal modes via adaptive colors.</p>
|
||||
<h2 id="custom-theme-files"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#custom-theme-files"><span class="icon icon-link"></span></a>Custom theme files</h2>
|
||||
<p>Create a <code>.yml</code>, <code>.yaml</code>, or <code>.json</code> file with color definitions. Kit discovers themes from two directories:</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Location</th>
|
||||
<th>Scope</th>
|
||||
<th>Precedence</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>~/.config/kit/themes/</code></td>
|
||||
<td>User (global)</td>
|
||||
<td>Overrides built-ins</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>.kit/themes/</code></td>
|
||||
<td>Project-local</td>
|
||||
<td>Overrides user and built-ins</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 id="theme-file-format"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#theme-file-format"><span class="icon icon-link"></span></a>Theme file format</h3>
|
||||
<p>A theme file defines adaptive color pairs with <code>light</code> and <code>dark</code> hex values. Any field left empty inherits from the default KITT theme.</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># ~/.config/kit/themes/my-theme.yml</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Core semantic colors</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">primary</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#8839ef"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#cba6f7"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">secondary</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#04a5e5"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#89dceb"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">success</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#40a02b"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#a6e3a1"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">warning</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#df8e1d"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#f9e2af"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">error</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#d20f39"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#f38ba8"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">info</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#1e66f5"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#89b4fa"</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Text and chrome</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">text</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#4c4f69"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#cdd6f4"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">muted</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#6c6f85"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#a6adc8"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">very-muted</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#9ca0b0"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#6c7086"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">background</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#eff1f5"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#1e1e2e"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">border</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#acb0be"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#585b70"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">muted-border</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#ccd0da"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#313244"</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Semantic roles</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">system</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#179299"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#94e2d5"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">tool</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#fe640b"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#fab387"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">accent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#ea76cb"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#f5c2e7"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">highlight</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#e6e9ef"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#181825"</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Diff backgrounds</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">diff-insert-bg</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#d5f0d5"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#1a3a2a"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">diff-delete-bg</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#f5d5d5"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#3a1a2a"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">diff-equal-bg</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#eceef3"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#232336"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">diff-missing-bg</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#e4e6eb"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#1a1a2e"</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Code block backgrounds</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">code-bg</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#eceef3"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#232336"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">gutter-bg</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#e4e6eb"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#1a1a2e"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">write-bg</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#d5f0d5"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#1a3a2a"</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Markdown and syntax highlighting</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">markdown</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> heading</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#1e66f5"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#89b4fa"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> link</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#1e66f5"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#89b4fa"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> keyword</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#8839ef"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#cba6f7"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> string</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#40a02b"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#a6e3a1"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> number</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#fe640b"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#fab387"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> comment</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#9ca0b0"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#6c7086"</span></span></code></pre>
|
||||
<h3 id="partial-themes"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#partial-themes"><span class="icon icon-link"></span></a>Partial themes</h3>
|
||||
<p>You only need to define the colors you want to change. Unspecified fields fall back to the default theme:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D"># Just override the primary and accent colors</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">primary</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#FF00FF"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">accent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#00FFFF"</span></span></code></pre>
|
||||
<h3 id="distributing-themes"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#distributing-themes"><span class="icon icon-link"></span></a>Distributing themes</h3>
|
||||
<ul>
|
||||
<li><strong>Personal</strong>: Drop a file in <code>~/.config/kit/themes/</code></li>
|
||||
<li><strong>Team/project</strong>: Drop a file in <code>.kit/themes/</code> and commit it to version control</li>
|
||||
<li><strong>Override built-in</strong>: Name your file the same as a built-in (e.g., <code>dracula.yml</code>) and it takes precedence</li>
|
||||
</ul>
|
||||
<h2 id="config-file-theme"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#config-file-theme"><span class="icon icon-link"></span></a>Config file theme</h2>
|
||||
<p>You can also set theme colors directly in <code>.kit.yml</code>:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">theme</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> primary</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> light</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#8839ef"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#cba6f7"</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> error</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
|
||||
<span class="line"><span style="color:#22863A;--shiki-dark:#85E89D"> dark</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#FF0000"</span></span></code></pre>
|
||||
<p>Or reference an external theme file:</p>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#22863A;--shiki-dark:#85E89D">theme</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"./themes/my-custom-theme.yml"</span></span></code></pre>
|
||||
<h2 id="extension-theme-api"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#extension-theme-api"><span class="icon icon-link"></span></a>Extension theme API</h2>
|
||||
<p>Extensions can register and switch themes programmatically at runtime.</p>
|
||||
<h3 id="registering-a-theme"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#registering-a-theme"><span class="icon icon-link"></span></a>Registering a theme</h3>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">api.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">OnSessionStart</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#D73A49;--shiki-dark:#F97583">func</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">_</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SessionStartEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">ctx</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Context</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> ctx.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">RegisterTheme</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"neon"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#6F42C1;--shiki-dark:#B392F0">ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ThemeColorConfig</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Primary: </span><span style="color:#6F42C1;--shiki-dark:#B392F0">ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ThemeColor</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{Light: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#CC00FF"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, Dark: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#FF00FF"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">},</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Secondary: </span><span style="color:#6F42C1;--shiki-dark:#B392F0">ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ThemeColor</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{Light: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#0088CC"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, Dark: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#00FFFF"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">},</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Success: </span><span style="color:#6F42C1;--shiki-dark:#B392F0">ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ThemeColor</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{Light: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#00CC44"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, Dark: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#00FF66"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">},</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Warning: </span><span style="color:#6F42C1;--shiki-dark:#B392F0">ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ThemeColor</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{Light: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#CCAA00"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, Dark: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#FFFF00"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">},</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Error: </span><span style="color:#6F42C1;--shiki-dark:#B392F0">ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ThemeColor</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{Light: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#CC0033"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, Dark: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#FF0055"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">},</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Info: </span><span style="color:#6F42C1;--shiki-dark:#B392F0">ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ThemeColor</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{Light: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#0088CC"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, Dark: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#00CCFF"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">},</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Text: </span><span style="color:#6F42C1;--shiki-dark:#B392F0">ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ThemeColor</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{Light: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#111111"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, Dark: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#F0F0F0"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">},</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> Background: </span><span style="color:#6F42C1;--shiki-dark:#B392F0">ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ThemeColor</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{Light: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#F0F0F0"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, Dark: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#0A0A14"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">},</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> MdKeyword: </span><span style="color:#6F42C1;--shiki-dark:#B392F0">ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ThemeColor</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{Light: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#CC00FF"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, Dark: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#FF00FF"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">},</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> MdString: </span><span style="color:#6F42C1;--shiki-dark:#B392F0">ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ThemeColor</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{Light: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#00CC44"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, Dark: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#00FF66"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">},</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> MdComment: </span><span style="color:#6F42C1;--shiki-dark:#B392F0">ext</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ThemeColor</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{Light: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#888888"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, Dark: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"#555555"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">},</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8"> })</span></span>
|
||||
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span></code></pre>
|
||||
<h3 id="switching-themes"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#switching-themes"><span class="icon icon-link"></span></a>Switching themes</h3>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">err </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ctx.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">SetTheme</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"dracula"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span></code></pre>
|
||||
<h3 id="listing-available-themes"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#listing-available-themes"><span class="icon icon-link"></span></a>Listing available themes</h3>
|
||||
<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">names </span><span style="color:#D73A49;--shiki-dark:#F97583">:=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ctx.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ListThemes</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span></code></pre>
|
||||
<h3 id="themecolorconfig-fields"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#themecolorconfig-fields"><span class="icon icon-link"></span></a>ThemeColorConfig fields</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>Primary</code></td>
|
||||
<td>Main brand/accent color</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Secondary</code></td>
|
||||
<td>Secondary accent</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Success</code></td>
|
||||
<td>Success states</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Warning</code></td>
|
||||
<td>Warning states</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Error</code></td>
|
||||
<td>Error/critical states</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Info</code></td>
|
||||
<td>Informational states</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Text</code></td>
|
||||
<td>Primary text</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Muted</code></td>
|
||||
<td>Dimmed text</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>VeryMuted</code></td>
|
||||
<td>Very dimmed text</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Background</code></td>
|
||||
<td>Base background</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Border</code></td>
|
||||
<td>Panel borders</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>MutedBorder</code></td>
|
||||
<td>Subtle dividers</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>System</code></td>
|
||||
<td>System messages</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Tool</code></td>
|
||||
<td>Tool-related elements</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Accent</code></td>
|
||||
<td>Secondary highlight</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Highlight</code></td>
|
||||
<td>Highlighted regions</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>MdHeading</code></td>
|
||||
<td>Markdown headings</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>MdLink</code></td>
|
||||
<td>Markdown links</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>MdKeyword</code></td>
|
||||
<td>Syntax: keywords</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>MdString</code></td>
|
||||
<td>Syntax: strings</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>MdNumber</code></td>
|
||||
<td>Syntax: numbers</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>MdComment</code></td>
|
||||
<td>Syntax: comments</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Each field is an <code>ext.ThemeColor</code> with <code>Light</code> and <code>Dark</code> hex strings. Empty fields inherit from the default theme.</p>
|
||||
<h2 id="precedence-order"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#precedence-order"><span class="icon icon-link"></span></a>Precedence order</h2>
|
||||
<p>When multiple sources define the same theme name, later sources win:</p>
|
||||
<ol>
|
||||
<li>Built-in presets (lowest)</li>
|
||||
<li>User themes (<code>~/.config/kit/themes/</code>)</li>
|
||||
<li>Project-local themes (<code>.kit/themes/</code>)</li>
|
||||
<li>Extension-registered themes (highest)</li>
|
||||
</ol>
|
||||
<h3 id="startup-theme-resolution"><a class="heading-anchor" aria-hidden="" tabindex="-1" href="#startup-theme-resolution"><span class="icon icon-link"></span></a>Startup theme resolution</h3>
|
||||
<p>At startup, Kit determines which theme to apply:</p>
|
||||
<ol>
|
||||
<li><strong><code>.kit.yml</code> <code>theme:</code> key</strong> — explicit config always wins (highest priority)</li>
|
||||
<li><strong><code>~/.config/kit/preferences.yml</code></strong> — persisted <code>/theme</code> selection</li>
|
||||
<li><strong>Default <code>kitt</code> theme</strong> — fallback</li>
|
||||
</ol>
|
||||
<p>The preferences file is updated automatically whenever you use <code>/theme</code> or <code>ctx.SetTheme()</code>. It is separate from <code>.kit.yml</code> so it never clobbers your config comments or formatting.</p>
|
||||
<p>Theme changes via <code>/theme</code> or <code>ctx.SetTheme()</code> take effect immediately on all UI elements, including previously rendered messages.</p>`,headings:[{depth:2,text:"Quick start",id:"quick-start"},{depth:2,text:"Built-in themes",id:"built-in-themes"},{depth:2,text:"Custom theme files",id:"custom-theme-files"},{depth:3,text:"Theme file format",id:"theme-file-format"},{depth:3,text:"Partial themes",id:"partial-themes"},{depth:3,text:"Distributing themes",id:"distributing-themes"},{depth:2,text:"Config file theme",id:"config-file-theme"},{depth:2,text:"Extension theme API",id:"extension-theme-api"},{depth:3,text:"Registering a theme",id:"registering-a-theme"},{depth:3,text:"Switching themes",id:"switching-themes"},{depth:3,text:"Listing available themes",id:"listing-available-themes"},{depth:3,text:"ThemeColorConfig fields",id:"themecolorconfig-fields"},{depth:2,text:"Precedence order",id:"precedence-order"},{depth:3,text:"Startup theme resolution",id:"startup-theme-resolution"}],raw:`
|
||||
# Themes
|
||||
|
||||
Kit ships with 22 built-in color themes and supports custom themes via YAML/JSON files or the extension API. Themes control all UI colors: input borders, popups, system messages, markdown rendering, syntax highlighting, and diff displays.
|
||||
|
||||
## Quick start
|
||||
|
||||
Switch themes at runtime with the \`/theme\` command:
|
||||
|
||||
\`\`\`
|
||||
/theme dracula
|
||||
/theme catppuccin
|
||||
/theme kitt
|
||||
\`\`\`
|
||||
|
||||
Run \`/theme\` with no arguments to list all available themes.
|
||||
|
||||
**Theme selections are automatically saved** to \`~/.config/kit/preferences.yml\` and restored on next launch. You don't need to add anything to your config file — just \`/theme <name>\` and it sticks. This persistence also applies to **model** and **thinking level** selections.
|
||||
|
||||
## Built-in themes
|
||||
|
||||
| Theme | Style |
|
||||
|-------|-------|
|
||||
| \`kitt\` | KITT-inspired reds and ambers (default) |
|
||||
| \`catppuccin\` | Soothing pastels (Mocha/Latte) |
|
||||
| \`dracula\` | Purple and cyan dark theme |
|
||||
| \`tokyonight\` | Cool blues with warm accents |
|
||||
| \`nord\` | Arctic, north-bluish palette |
|
||||
| \`gruvbox\` | Retro groove colors |
|
||||
| \`monokai\` | Classic syntax theme |
|
||||
| \`solarized\` | Precision colors for machines and people |
|
||||
| \`github\` | GitHub's light and dark palettes |
|
||||
| \`one-dark\` | Atom One Dark |
|
||||
| \`rose-pine\` | Soho vibes with muted tones |
|
||||
| \`ayu\` | Simple with bright colors |
|
||||
| \`material\` | Material Design palette |
|
||||
| \`everforest\` | Green-focused comfortable theme |
|
||||
| \`kanagawa\` | Dark theme inspired by Katsushika Hokusai |
|
||||
| \`amoled\` | Pure black background, vivid accents |
|
||||
| \`synthwave\` | Retro neon glows |
|
||||
| \`vesper\` | Warm minimalist dark theme |
|
||||
| \`flexoki\` | Inky reading palette |
|
||||
| \`matrix\` | Green-on-black terminal aesthetic |
|
||||
| \`vercel\` | Clean monochrome with blue accents |
|
||||
| \`zenburn\` | Low-contrast, warm dark theme |
|
||||
|
||||
All themes support both light and dark terminal modes via adaptive colors.
|
||||
|
||||
## Custom theme files
|
||||
|
||||
Create a \`.yml\`, \`.yaml\`, or \`.json\` file with color definitions. Kit discovers themes from two directories:
|
||||
|
||||
| Location | Scope | Precedence |
|
||||
|----------|-------|------------|
|
||||
| \`~/.config/kit/themes/\` | User (global) | Overrides built-ins |
|
||||
| \`.kit/themes/\` | Project-local | Overrides user and built-ins |
|
||||
|
||||
### Theme file format
|
||||
|
||||
A theme file defines adaptive color pairs with \`light\` and \`dark\` hex values. Any field left empty inherits from the default KITT theme.
|
||||
|
||||
\`\`\`yaml
|
||||
# ~/.config/kit/themes/my-theme.yml
|
||||
|
||||
# Core semantic colors
|
||||
primary:
|
||||
light: "#8839ef"
|
||||
dark: "#cba6f7"
|
||||
secondary:
|
||||
light: "#04a5e5"
|
||||
dark: "#89dceb"
|
||||
success:
|
||||
light: "#40a02b"
|
||||
dark: "#a6e3a1"
|
||||
warning:
|
||||
light: "#df8e1d"
|
||||
dark: "#f9e2af"
|
||||
error:
|
||||
light: "#d20f39"
|
||||
dark: "#f38ba8"
|
||||
info:
|
||||
light: "#1e66f5"
|
||||
dark: "#89b4fa"
|
||||
|
||||
# Text and chrome
|
||||
text:
|
||||
light: "#4c4f69"
|
||||
dark: "#cdd6f4"
|
||||
muted:
|
||||
light: "#6c6f85"
|
||||
dark: "#a6adc8"
|
||||
very-muted:
|
||||
light: "#9ca0b0"
|
||||
dark: "#6c7086"
|
||||
background:
|
||||
light: "#eff1f5"
|
||||
dark: "#1e1e2e"
|
||||
border:
|
||||
light: "#acb0be"
|
||||
dark: "#585b70"
|
||||
muted-border:
|
||||
light: "#ccd0da"
|
||||
dark: "#313244"
|
||||
|
||||
# Semantic roles
|
||||
system:
|
||||
light: "#179299"
|
||||
dark: "#94e2d5"
|
||||
tool:
|
||||
light: "#fe640b"
|
||||
dark: "#fab387"
|
||||
accent:
|
||||
light: "#ea76cb"
|
||||
dark: "#f5c2e7"
|
||||
highlight:
|
||||
light: "#e6e9ef"
|
||||
dark: "#181825"
|
||||
|
||||
# Diff backgrounds
|
||||
diff-insert-bg:
|
||||
light: "#d5f0d5"
|
||||
dark: "#1a3a2a"
|
||||
diff-delete-bg:
|
||||
light: "#f5d5d5"
|
||||
dark: "#3a1a2a"
|
||||
diff-equal-bg:
|
||||
light: "#eceef3"
|
||||
dark: "#232336"
|
||||
diff-missing-bg:
|
||||
light: "#e4e6eb"
|
||||
dark: "#1a1a2e"
|
||||
|
||||
# Code block backgrounds
|
||||
code-bg:
|
||||
light: "#eceef3"
|
||||
dark: "#232336"
|
||||
gutter-bg:
|
||||
light: "#e4e6eb"
|
||||
dark: "#1a1a2e"
|
||||
write-bg:
|
||||
light: "#d5f0d5"
|
||||
dark: "#1a3a2a"
|
||||
|
||||
# Markdown and syntax highlighting
|
||||
markdown:
|
||||
heading:
|
||||
light: "#1e66f5"
|
||||
dark: "#89b4fa"
|
||||
link:
|
||||
light: "#1e66f5"
|
||||
dark: "#89b4fa"
|
||||
keyword:
|
||||
light: "#8839ef"
|
||||
dark: "#cba6f7"
|
||||
string:
|
||||
light: "#40a02b"
|
||||
dark: "#a6e3a1"
|
||||
number:
|
||||
light: "#fe640b"
|
||||
dark: "#fab387"
|
||||
comment:
|
||||
light: "#9ca0b0"
|
||||
dark: "#6c7086"
|
||||
\`\`\`
|
||||
|
||||
### Partial themes
|
||||
|
||||
You only need to define the colors you want to change. Unspecified fields fall back to the default theme:
|
||||
|
||||
\`\`\`yaml
|
||||
# Just override the primary and accent colors
|
||||
primary:
|
||||
dark: "#FF00FF"
|
||||
accent:
|
||||
dark: "#00FFFF"
|
||||
\`\`\`
|
||||
|
||||
### Distributing themes
|
||||
|
||||
- **Personal**: Drop a file in \`~/.config/kit/themes/\`
|
||||
- **Team/project**: Drop a file in \`.kit/themes/\` and commit it to version control
|
||||
- **Override built-in**: Name your file the same as a built-in (e.g., \`dracula.yml\`) and it takes precedence
|
||||
|
||||
## Config file theme
|
||||
|
||||
You can also set theme colors directly in \`.kit.yml\`:
|
||||
|
||||
\`\`\`yaml
|
||||
theme:
|
||||
primary:
|
||||
light: "#8839ef"
|
||||
dark: "#cba6f7"
|
||||
error:
|
||||
dark: "#FF0000"
|
||||
\`\`\`
|
||||
|
||||
Or reference an external theme file:
|
||||
|
||||
\`\`\`yaml
|
||||
theme: "./themes/my-custom-theme.yml"
|
||||
\`\`\`
|
||||
|
||||
## Extension theme API
|
||||
|
||||
Extensions can register and switch themes programmatically at runtime.
|
||||
|
||||
### Registering a theme
|
||||
|
||||
\`\`\`go
|
||||
api.OnSessionStart(func(_ ext.SessionStartEvent, ctx ext.Context) {
|
||||
ctx.RegisterTheme("neon", ext.ThemeColorConfig{
|
||||
Primary: ext.ThemeColor{Light: "#CC00FF", Dark: "#FF00FF"},
|
||||
Secondary: ext.ThemeColor{Light: "#0088CC", Dark: "#00FFFF"},
|
||||
Success: ext.ThemeColor{Light: "#00CC44", Dark: "#00FF66"},
|
||||
Warning: ext.ThemeColor{Light: "#CCAA00", Dark: "#FFFF00"},
|
||||
Error: ext.ThemeColor{Light: "#CC0033", Dark: "#FF0055"},
|
||||
Info: ext.ThemeColor{Light: "#0088CC", Dark: "#00CCFF"},
|
||||
Text: ext.ThemeColor{Light: "#111111", Dark: "#F0F0F0"},
|
||||
Background: ext.ThemeColor{Light: "#F0F0F0", Dark: "#0A0A14"},
|
||||
MdKeyword: ext.ThemeColor{Light: "#CC00FF", Dark: "#FF00FF"},
|
||||
MdString: ext.ThemeColor{Light: "#00CC44", Dark: "#00FF66"},
|
||||
MdComment: ext.ThemeColor{Light: "#888888", Dark: "#555555"},
|
||||
})
|
||||
})
|
||||
\`\`\`
|
||||
|
||||
### Switching themes
|
||||
|
||||
\`\`\`go
|
||||
err := ctx.SetTheme("dracula")
|
||||
\`\`\`
|
||||
|
||||
### Listing available themes
|
||||
|
||||
\`\`\`go
|
||||
names := ctx.ListThemes()
|
||||
\`\`\`
|
||||
|
||||
### ThemeColorConfig fields
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| \`Primary\` | Main brand/accent color |
|
||||
| \`Secondary\` | Secondary accent |
|
||||
| \`Success\` | Success states |
|
||||
| \`Warning\` | Warning states |
|
||||
| \`Error\` | Error/critical states |
|
||||
| \`Info\` | Informational states |
|
||||
| \`Text\` | Primary text |
|
||||
| \`Muted\` | Dimmed text |
|
||||
| \`VeryMuted\` | Very dimmed text |
|
||||
| \`Background\` | Base background |
|
||||
| \`Border\` | Panel borders |
|
||||
| \`MutedBorder\` | Subtle dividers |
|
||||
| \`System\` | System messages |
|
||||
| \`Tool\` | Tool-related elements |
|
||||
| \`Accent\` | Secondary highlight |
|
||||
| \`Highlight\` | Highlighted regions |
|
||||
| \`MdHeading\` | Markdown headings |
|
||||
| \`MdLink\` | Markdown links |
|
||||
| \`MdKeyword\` | Syntax: keywords |
|
||||
| \`MdString\` | Syntax: strings |
|
||||
| \`MdNumber\` | Syntax: numbers |
|
||||
| \`MdComment\` | Syntax: comments |
|
||||
|
||||
Each field is an \`ext.ThemeColor\` with \`Light\` and \`Dark\` hex strings. Empty fields inherit from the default theme.
|
||||
|
||||
## Precedence order
|
||||
|
||||
When multiple sources define the same theme name, later sources win:
|
||||
|
||||
1. Built-in presets (lowest)
|
||||
2. User themes (\`~/.config/kit/themes/\`)
|
||||
3. Project-local themes (\`.kit/themes/\`)
|
||||
4. Extension-registered themes (highest)
|
||||
|
||||
### Startup theme resolution
|
||||
|
||||
At startup, Kit determines which theme to apply:
|
||||
|
||||
1. **\`.kit.yml\` \`theme:\` key** — explicit config always wins (highest priority)
|
||||
2. **\`~/.config/kit/preferences.yml\`** — persisted \`/theme\` selection
|
||||
3. **Default \`kitt\` theme** — fallback
|
||||
|
||||
The preferences file is updated automatically whenever you use \`/theme\` or \`ctx.SetTheme()\`. It is separate from \`.kit.yml\` so it never clobbers your config comments or formatting.
|
||||
|
||||
Theme changes via \`/theme\` or \`ctx.SetTheme()\` take effect immediately on all UI elements, including previously rendered messages.
|
||||
`};export{s as default};
|
||||
@@ -0,0 +1,95 @@
|
||||
{
|
||||
"$schema": "https://btca.dev/btca.schema.json",
|
||||
"resources": [
|
||||
{
|
||||
"type": "git",
|
||||
"name": "bubbletea",
|
||||
"url": "https://github.com/charmbracelet/bubbletea",
|
||||
"branch": "main"
|
||||
},
|
||||
{
|
||||
"type": "git",
|
||||
"name": "lipgloss",
|
||||
"url": "https://github.com/charmbracelet/lipgloss",
|
||||
"branch": "main"
|
||||
},
|
||||
{
|
||||
"type": "git",
|
||||
"name": "bubbles",
|
||||
"url": "https://github.com/charmbracelet/bubbles",
|
||||
"branch": "main"
|
||||
},
|
||||
{
|
||||
"type": "git",
|
||||
"name": "glamour",
|
||||
"url": "https://github.com/charmbracelet/glamour",
|
||||
"branch": "v2-exp"
|
||||
},
|
||||
{
|
||||
"type": "git",
|
||||
"name": "fantasy",
|
||||
"url": "https://github.com/charmbracelet/fantasy",
|
||||
"branch": "main"
|
||||
},
|
||||
{
|
||||
"type": "git",
|
||||
"name": "catwalk",
|
||||
"url": "https://github.com/charmbracelet/catwalk",
|
||||
"branch": "main"
|
||||
},
|
||||
{
|
||||
"type": "git",
|
||||
"name": "crush",
|
||||
"url": "https://github.com/charmbracelet/crush",
|
||||
"branch": "main"
|
||||
},
|
||||
{
|
||||
"type": "git",
|
||||
"name": "pi",
|
||||
"url": "https://github.com/badlogic/pi-mono",
|
||||
"branch": "main",
|
||||
"searchPaths": [
|
||||
"packages/coding-agent",
|
||||
"packages/tui"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "git",
|
||||
"name": "iteratr",
|
||||
"url": "https://github.com/mark3labs/iteratr",
|
||||
"branch": "master"
|
||||
},
|
||||
{
|
||||
"type": "git",
|
||||
"name": "yaegi",
|
||||
"url": "https://github.com/traefik/yaegi",
|
||||
"branch": "master"
|
||||
},
|
||||
{
|
||||
"type": "git",
|
||||
"name": "acp-go-sdk",
|
||||
"url": "https://github.com/coder/acp-go-sdk",
|
||||
"branch": "main"
|
||||
},
|
||||
{
|
||||
"type": "git",
|
||||
"name": "opencode",
|
||||
"url": "https://github.com/anomalyco/opencode",
|
||||
"branch": "dev"
|
||||
},
|
||||
{
|
||||
"type": "git",
|
||||
"name": "herald",
|
||||
"url": "https://github.com/indaco/herald",
|
||||
"branch": "main"
|
||||
},
|
||||
{
|
||||
"type": "git",
|
||||
"name": "herald-md",
|
||||
"url": "https://github.com/indaco/herald-md",
|
||||
"branch": "main"
|
||||
}
|
||||
],
|
||||
"model": "claude-haiku-4-5",
|
||||
"provider": "opencode"
|
||||
}
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/charmbracelet/log"
|
||||
acp "github.com/coder/acp-go-sdk"
|
||||
|
||||
"github.com/mark3labs/kit/internal/acpserver"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var acpCmd = &cobra.Command{
|
||||
Use: "acp",
|
||||
Short: "Start Kit as an ACP agent server",
|
||||
Long: `Start Kit as an ACP (Agent Client Protocol) agent server.
|
||||
|
||||
Communicates over stdio (stdin/stdout) using JSON-RPC 2.0 with
|
||||
newline-delimited JSON, compatible with OpenCode and other ACP clients.
|
||||
|
||||
The server exposes Kit's LLM execution, tool system, and session
|
||||
management via the Agent Client Protocol. Sessions are persisted
|
||||
to Kit's standard JSONL session files.`,
|
||||
RunE: runACP,
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(acpCmd)
|
||||
}
|
||||
|
||||
func runACP(cmd *cobra.Command, _ []string) error {
|
||||
// Create the ACP agent implementation.
|
||||
agent := acpserver.NewAgent()
|
||||
defer agent.Close()
|
||||
|
||||
// Create the stdio connection. The SDK reads JSON-RPC from stdin and
|
||||
// writes responses to stdout. We wrap stdin with a normalizer that
|
||||
// fills in optional fields the SDK's generated validation requires
|
||||
// (e.g. mcpServers) so clients that omit them still work.
|
||||
conn := acp.NewAgentSideConnection(agent, os.Stdout, newACPNormalizer(os.Stdin))
|
||||
|
||||
// Wire the connection back to the agent so it can send session updates.
|
||||
agent.SetAgentConnection(conn)
|
||||
|
||||
// Enable debug logging to stderr if requested.
|
||||
if debugMode {
|
||||
conn.SetLogger(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
|
||||
Level: slog.LevelDebug,
|
||||
})))
|
||||
// Also set charmbracelet/log level for acpserver package logging
|
||||
log.SetLevel(log.DebugLevel)
|
||||
}
|
||||
|
||||
// Wait for either the client to disconnect or a signal.
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
select {
|
||||
case <-conn.Done():
|
||||
fmt.Fprintln(os.Stderr, "kit: ACP client disconnected")
|
||||
case sig := <-sigCh:
|
||||
fmt.Fprintf(os.Stderr, "kit: received %s, shutting down\n", sig)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// acpNormalizer wraps an io.Reader carrying newline-delimited JSON-RPC and
|
||||
// patches incoming messages so that fields the SDK validates as required —
|
||||
// but that some clients (e.g. Zed) omit — are defaulted. This avoids
|
||||
// InvalidParams errors without forking the SDK.
|
||||
type acpNormalizer struct {
|
||||
scanner *bufio.Scanner
|
||||
buf bytes.Buffer // leftover bytes from the last normalized line
|
||||
}
|
||||
|
||||
func newACPNormalizer(r io.Reader) *acpNormalizer {
|
||||
const maxMsg = 10 * 1024 * 1024 // 10 MB, matches SDK buffer
|
||||
s := bufio.NewScanner(r)
|
||||
s.Buffer(make([]byte, 0, 1024*1024), maxMsg)
|
||||
return &acpNormalizer{scanner: s}
|
||||
}
|
||||
|
||||
// Read satisfies io.Reader. It feeds one normalized JSON line (plus newline)
|
||||
// per underlying scan, buffering across short caller reads.
|
||||
func (n *acpNormalizer) Read(p []byte) (int, error) {
|
||||
// Drain any leftover bytes from the previous line first.
|
||||
if n.buf.Len() > 0 {
|
||||
return n.buf.Read(p)
|
||||
}
|
||||
|
||||
if !n.scanner.Scan() {
|
||||
if err := n.scanner.Err(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
line := n.scanner.Bytes()
|
||||
normalized := normalizeACPLine(line)
|
||||
n.buf.Write(normalized)
|
||||
n.buf.WriteByte('\n')
|
||||
return n.buf.Read(p)
|
||||
}
|
||||
|
||||
// normalizeACPLine ensures session/new and session/load params contain an
|
||||
// mcpServers array. Returns the original line unchanged for all other methods.
|
||||
func normalizeACPLine(line []byte) []byte {
|
||||
// Quick check: if it already contains mcpServers, nothing to do.
|
||||
if bytes.Contains(line, []byte(`"mcpServers"`)) {
|
||||
return line
|
||||
}
|
||||
|
||||
// Only bother parsing if the method could be session/new or session/load.
|
||||
if !bytes.Contains(line, []byte(`"session/new"`)) &&
|
||||
!bytes.Contains(line, []byte(`"session/load"`)) {
|
||||
return line
|
||||
}
|
||||
|
||||
var msg struct {
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
ID json.RawMessage `json:"id,omitempty"`
|
||||
Method string `json:"method"`
|
||||
Params json.RawMessage `json:"params,omitempty"`
|
||||
}
|
||||
if err := json.Unmarshal(line, &msg); err != nil {
|
||||
return line
|
||||
}
|
||||
if msg.Method != "session/new" && msg.Method != "session/load" {
|
||||
return line
|
||||
}
|
||||
|
||||
// Patch params to include mcpServers: [].
|
||||
var params map[string]json.RawMessage
|
||||
if err := json.Unmarshal(msg.Params, ¶ms); err != nil {
|
||||
return line
|
||||
}
|
||||
if _, ok := params["mcpServers"]; ok {
|
||||
return line
|
||||
}
|
||||
params["mcpServers"] = json.RawMessage(`[]`)
|
||||
|
||||
patched, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
return line
|
||||
}
|
||||
msg.Params = patched
|
||||
|
||||
out, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return line
|
||||
}
|
||||
return out
|
||||
}
|
||||
+577
@@ -0,0 +1,577 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"charm.land/huh/v2"
|
||||
"github.com/mark3labs/kit/internal/auth"
|
||||
kit "github.com/mark3labs/kit/pkg/kit"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// authCmd represents the auth command for managing AI provider authentication.
|
||||
// This command provides subcommands for login, logout, and status checking
|
||||
// of authentication credentials for various AI providers, with OAuth support
|
||||
// for providers like Anthropic and OpenAI.
|
||||
var authCmd = &cobra.Command{
|
||||
Use: "auth",
|
||||
Short: "Manage authentication credentials for AI providers",
|
||||
Long: `Manage authentication credentials for AI providers.
|
||||
|
||||
This command allows you to securely authenticate and manage credentials for various AI providers
|
||||
using OAuth flows. Stored credentials take precedence over environment variables.
|
||||
|
||||
Available providers:
|
||||
- anthropic: Anthropic Claude API (OAuth)
|
||||
- openai: OpenAI API (OAuth and API key)
|
||||
|
||||
Examples:
|
||||
kit auth login anthropic
|
||||
kit auth login openai
|
||||
kit auth logout anthropic
|
||||
kit auth status`,
|
||||
}
|
||||
|
||||
// authLoginCmd represents the login subcommand for authenticating with AI providers.
|
||||
// It handles OAuth flow for supported providers, opening a browser for authentication
|
||||
// and securely storing the resulting credentials for future use.
|
||||
var authLoginCmd = &cobra.Command{
|
||||
Use: "login [provider]",
|
||||
Short: "Authenticate with an AI provider using OAuth",
|
||||
Long: `Authenticate with an AI provider using OAuth flow.
|
||||
|
||||
This will open your browser to complete the OAuth authentication process.
|
||||
Your credentials will be securely stored and will take precedence over
|
||||
environment variables when making API calls.
|
||||
|
||||
Available providers:
|
||||
- anthropic: Anthropic Claude API (OAuth)
|
||||
- openai: OpenAI ChatGPT Plus/Pro (Codex OAuth)
|
||||
|
||||
Example:
|
||||
kit auth login anthropic
|
||||
kit auth login openai`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runAuthLogin,
|
||||
}
|
||||
|
||||
// authLogoutCmd represents the logout subcommand for removing stored authentication credentials.
|
||||
// This command removes stored API keys or OAuth tokens for specified providers,
|
||||
// requiring the user to authenticate again or use environment variables.
|
||||
var authLogoutCmd = &cobra.Command{
|
||||
Use: "logout [provider]",
|
||||
Short: "Remove stored authentication credentials for a provider",
|
||||
Long: `Remove stored authentication credentials for an AI provider.
|
||||
|
||||
This will delete the stored API key or OAuth credentials for the specified provider.
|
||||
You will need to use environment variables or command-line flags for authentication after logout.
|
||||
|
||||
Available providers:
|
||||
- anthropic: Anthropic Claude API
|
||||
- openai: OpenAI API
|
||||
|
||||
Example:
|
||||
kit auth logout anthropic
|
||||
kit auth logout openai`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runAuthLogout,
|
||||
}
|
||||
|
||||
// authStatusCmd represents the status subcommand for checking authentication status.
|
||||
// It displays which providers have stored credentials, their types (OAuth vs API key),
|
||||
// creation dates, and expiration status without revealing the actual credentials.
|
||||
var authStatusCmd = &cobra.Command{
|
||||
Use: "status",
|
||||
Short: "Show authentication status for all providers",
|
||||
Long: `Show the current authentication status for all supported AI providers.
|
||||
|
||||
This command displays which providers have stored credentials and when they were created.
|
||||
It does not display the actual API keys for security reasons.
|
||||
|
||||
Example:
|
||||
kit auth status`,
|
||||
RunE: runAuthStatus,
|
||||
}
|
||||
|
||||
func init() {
|
||||
authCmd.AddCommand(authLoginCmd)
|
||||
authCmd.AddCommand(authLogoutCmd)
|
||||
authCmd.AddCommand(authStatusCmd)
|
||||
}
|
||||
|
||||
func runAuthLogin(cmd *cobra.Command, args []string) error {
|
||||
provider := strings.ToLower(args[0])
|
||||
|
||||
switch provider {
|
||||
case "anthropic":
|
||||
return loginAnthropic()
|
||||
case "openai":
|
||||
return loginOpenAI()
|
||||
default:
|
||||
return fmt.Errorf("unsupported provider: %s. Available providers: anthropic, openai", provider)
|
||||
}
|
||||
}
|
||||
|
||||
func runAuthLogout(cmd *cobra.Command, args []string) error {
|
||||
provider := strings.ToLower(args[0])
|
||||
|
||||
switch provider {
|
||||
case "anthropic":
|
||||
return logoutAnthropic()
|
||||
case "openai":
|
||||
return logoutOpenAI()
|
||||
default:
|
||||
return fmt.Errorf("unsupported provider: %s. Available providers: anthropic, openai", provider)
|
||||
}
|
||||
}
|
||||
|
||||
func runAuthStatus(cmd *cobra.Command, args []string) error {
|
||||
cm, err := kit.NewCredentialManager()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize credential manager: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("Authentication Status")
|
||||
fmt.Println("====================")
|
||||
fmt.Printf("Credentials file: %s\n\n", cm.GetCredentialsPath())
|
||||
|
||||
// Check Anthropic credentials
|
||||
fmt.Print("Anthropic Claude: ")
|
||||
if hasAnthropicCreds, err := cm.HasAnthropicCredentials(); err != nil {
|
||||
fmt.Printf("Error checking credentials: %v\n", err)
|
||||
} else if hasAnthropicCreds {
|
||||
if creds, err := cm.GetAnthropicCredentials(); err != nil {
|
||||
fmt.Printf("Error reading credentials: %v\n", err)
|
||||
} else {
|
||||
authType := "API Key"
|
||||
status := "✓ Authenticated"
|
||||
|
||||
if creds.Type == "oauth" {
|
||||
authType = "OAuth"
|
||||
if creds.IsExpired() {
|
||||
status = "⚠️ Token expired (will refresh automatically)"
|
||||
} else if creds.NeedsRefresh() {
|
||||
status = "⚠️ Token expires soon (will refresh automatically)"
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("%s (%s, stored %s)\n", status, authType, creds.CreatedAt.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
} else {
|
||||
fmt.Println("✗ Not authenticated")
|
||||
// Check if environment variable is set
|
||||
if os.Getenv("ANTHROPIC_API_KEY") != "" {
|
||||
fmt.Println(" (ANTHROPIC_API_KEY environment variable is set)")
|
||||
}
|
||||
}
|
||||
|
||||
// Check OpenAI credentials
|
||||
fmt.Print("\nOpenAI: ")
|
||||
if hasOpenAICreds, err := cm.HasOpenAICredentials(); err != nil {
|
||||
fmt.Printf("Error checking credentials: %v\n", err)
|
||||
} else if hasOpenAICreds {
|
||||
if creds, err := cm.GetOpenAICredentials(); err != nil {
|
||||
fmt.Printf("Error reading credentials: %v\n", err)
|
||||
} else {
|
||||
authType := "API Key"
|
||||
status := "✓ Authenticated"
|
||||
|
||||
if creds.Type == "oauth" {
|
||||
authType = "OAuth (ChatGPT/Codex)"
|
||||
if creds.IsExpired() {
|
||||
status = "⚠️ Token expired (will refresh automatically)"
|
||||
} else if creds.NeedsRefresh() {
|
||||
status = "⚠️ Token expires soon (will refresh automatically)"
|
||||
}
|
||||
}
|
||||
|
||||
accountInfo := ""
|
||||
if creds.Type == "oauth" && creds.AccountID != "" {
|
||||
accountInfo = fmt.Sprintf(" [%s]", creds.AccountID)
|
||||
}
|
||||
|
||||
fmt.Printf("%s (%s%s, stored %s)\n", status, authType, accountInfo, creds.CreatedAt.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
} else {
|
||||
fmt.Println("✗ Not authenticated")
|
||||
// Check if environment variable is set
|
||||
if os.Getenv("OPENAI_API_KEY") != "" {
|
||||
fmt.Println(" (OPENAI_API_KEY environment variable is set)")
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("\nTo authenticate with a provider:")
|
||||
fmt.Println(" kit auth login anthropic")
|
||||
fmt.Println(" kit auth login openai")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func loginAnthropic() error {
|
||||
cm, err := kit.NewCredentialManager()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize credential manager: %w", err)
|
||||
}
|
||||
|
||||
// Check if already authenticated
|
||||
if hasAuth, err := cm.HasAnthropicCredentials(); err == nil && hasAuth {
|
||||
var reauth bool
|
||||
err := huh.NewConfirm().
|
||||
Title("You are already authenticated with Anthropic").
|
||||
Description("Do you want to re-authenticate?").
|
||||
Affirmative("Yes").
|
||||
Negative("No").
|
||||
Value(&reauth).
|
||||
Run()
|
||||
if err != nil || !reauth {
|
||||
fmt.Println("Authentication cancelled.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Create OAuth client
|
||||
client := auth.NewOAuthClient()
|
||||
|
||||
// Generate authorization URL
|
||||
fmt.Println("🔐 Starting OAuth authentication with Anthropic...")
|
||||
authData, err := client.GetAuthorizationURL()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate authorization URL: %w", err)
|
||||
}
|
||||
|
||||
// Display URL and try to open browser
|
||||
fmt.Println("\n📱 Opening your browser for authentication...")
|
||||
fmt.Println("If the browser doesn't open automatically, please visit this URL:")
|
||||
fmt.Printf("\n%s\n\n", authData.URL)
|
||||
|
||||
// Try to open browser
|
||||
auth.TryOpenBrowser(authData.URL)
|
||||
|
||||
// Wait for user to complete OAuth flow
|
||||
fmt.Println("After authorizing the application, you'll receive an authorization code.")
|
||||
|
||||
var code string
|
||||
err = huh.NewInput().
|
||||
Title("Authorization code").
|
||||
Description("Paste the code from your browser").
|
||||
Value(&code).
|
||||
Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read authorization code: %w", err)
|
||||
}
|
||||
code = strings.TrimSpace(code)
|
||||
|
||||
if code == "" {
|
||||
return fmt.Errorf("authorization code cannot be empty")
|
||||
}
|
||||
|
||||
// Exchange code for tokens
|
||||
fmt.Println("\n🔄 Exchanging authorization code for access token...")
|
||||
creds, err := client.ExchangeCode(code, authData.Verifier)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to exchange authorization code: %w", err)
|
||||
}
|
||||
|
||||
// Store the credentials
|
||||
if err := cm.SetOAuthCredentials(creds); err != nil {
|
||||
return fmt.Errorf("failed to store credentials: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("✅ Successfully authenticated with Anthropic!")
|
||||
fmt.Printf("📁 Credentials stored in: %s\n", cm.GetCredentialsPath())
|
||||
fmt.Println("\n🎉 Your OAuth credentials will now be used for Anthropic API calls.")
|
||||
fmt.Println("💡 You can check your authentication status with: kit auth status")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func logoutAnthropic() error {
|
||||
cm, err := kit.NewCredentialManager()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize credential manager: %w", err)
|
||||
}
|
||||
|
||||
// Check if authenticated
|
||||
hasAuth, err := cm.HasAnthropicCredentials()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check authentication status: %w", err)
|
||||
}
|
||||
|
||||
if !hasAuth {
|
||||
fmt.Println("You are not currently authenticated with Anthropic.")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Confirm logout
|
||||
var confirm bool
|
||||
err = huh.NewConfirm().
|
||||
Title("Remove Anthropic credentials").
|
||||
Description("Are you sure you want to remove your stored credentials?").
|
||||
Affirmative("Yes").
|
||||
Negative("No").
|
||||
Value(&confirm).
|
||||
Run()
|
||||
if err != nil || !confirm {
|
||||
fmt.Println("Logout cancelled.")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove credentials
|
||||
if err := cm.RemoveAnthropicCredentials(); err != nil {
|
||||
return fmt.Errorf("failed to remove credentials: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("✓ Successfully logged out from Anthropic!")
|
||||
fmt.Println("You will need to use environment variables or command-line flags for authentication.")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func loginOpenAI() error {
|
||||
cm, err := kit.NewCredentialManager()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize credential manager: %w", err)
|
||||
}
|
||||
|
||||
// Check if already authenticated
|
||||
if hasAuth, err := cm.HasOpenAICredentials(); err == nil && hasAuth {
|
||||
var reauth bool
|
||||
err := huh.NewConfirm().
|
||||
Title("You are already authenticated with OpenAI (ChatGPT/Codex)").
|
||||
Description("Do you want to re-authenticate?").
|
||||
Affirmative("Yes").
|
||||
Negative("No").
|
||||
Value(&reauth).
|
||||
Run()
|
||||
if err != nil || !reauth {
|
||||
fmt.Println("Authentication cancelled.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Create OAuth client
|
||||
client := auth.NewOpenAIOAuthClient()
|
||||
|
||||
// Generate authorization URL
|
||||
fmt.Println("🔐 Starting OAuth authentication with OpenAI (ChatGPT/Codex)...")
|
||||
fmt.Println("This will open your browser to authenticate with your ChatGPT account.")
|
||||
fmt.Println()
|
||||
|
||||
authData, err := client.GetAuthorizationURL()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate authorization URL: %w", err)
|
||||
}
|
||||
|
||||
// Start local callback server
|
||||
callbackServer, err := startOpenAICallbackServer(authData.State)
|
||||
if err != nil {
|
||||
fmt.Printf("⚠️ Could not start local callback server: %v\n", err)
|
||||
fmt.Println("Falling back to manual code entry.")
|
||||
}
|
||||
if callbackServer != nil {
|
||||
defer callbackServer.Close()
|
||||
}
|
||||
|
||||
// Display URL and try to open browser
|
||||
fmt.Println("📱 Opening your browser for authentication...")
|
||||
fmt.Println("If the browser doesn't open automatically, please visit this URL:")
|
||||
fmt.Printf("\n%s\n\n", authData.URL)
|
||||
|
||||
// Try to open browser
|
||||
auth.TryOpenBrowser(authData.URL)
|
||||
|
||||
// Wait for callback or manual input
|
||||
var code string
|
||||
if callbackServer != nil {
|
||||
fmt.Println("Waiting for browser authentication...")
|
||||
select {
|
||||
case callbackCode := <-callbackServer.CodeChan:
|
||||
if callbackCode != "" {
|
||||
code = callbackCode
|
||||
fmt.Println("✓ Received authorization code from browser callback.")
|
||||
}
|
||||
case <-time.After(2 * time.Minute):
|
||||
fmt.Println("\n⏱️ Timeout waiting for browser callback.")
|
||||
callbackServer.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// If no code from callback, prompt for manual entry
|
||||
if code == "" {
|
||||
fmt.Println("\nAfter authorizing, paste the callback URL or authorization code below.")
|
||||
fmt.Println("(The callback URL will look like: http://localhost:1455/auth/callback?code=...&state=...)")
|
||||
fmt.Println()
|
||||
|
||||
var input string
|
||||
err = huh.NewInput().
|
||||
Title("Callback URL or Code").
|
||||
Description("Paste the full callback URL or just the authorization code").
|
||||
Value(&input).
|
||||
Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read input: %w", err)
|
||||
}
|
||||
input = strings.TrimSpace(input)
|
||||
|
||||
if input == "" {
|
||||
return fmt.Errorf("authorization code cannot be empty")
|
||||
}
|
||||
|
||||
// Parse the input (could be full URL or just code)
|
||||
parsedCode, parsedState := auth.ParseOpenAIAuthorizationInput(input)
|
||||
if parsedCode == "" {
|
||||
return fmt.Errorf("could not extract authorization code from input")
|
||||
}
|
||||
|
||||
// Validate state if provided
|
||||
if parsedState != "" && parsedState != authData.State {
|
||||
return fmt.Errorf("state mismatch - possible security issue")
|
||||
}
|
||||
code = parsedCode
|
||||
}
|
||||
|
||||
// Exchange code for tokens
|
||||
fmt.Println("\n🔄 Exchanging authorization code for access token...")
|
||||
creds, err := client.ExchangeCode(code, authData.Verifier)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to exchange authorization code: %w", err)
|
||||
}
|
||||
|
||||
// Store the credentials
|
||||
if err := cm.SetOpenAIOAuthCredentials(creds); err != nil {
|
||||
return fmt.Errorf("failed to store credentials: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("✅ Successfully authenticated with OpenAI (ChatGPT/Codex)!")
|
||||
fmt.Printf("📁 Credentials stored in: %s\n", cm.GetCredentialsPath())
|
||||
fmt.Printf("👤 Account ID: %s\n", creds.AccountID)
|
||||
fmt.Println("\n🎉 Your OAuth credentials will now be used for OpenAI API calls.")
|
||||
fmt.Println("💡 You can check your authentication status with: kit auth status")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// callbackServer holds the HTTP server and channel for receiving the OAuth callback
|
||||
type callbackServer struct {
|
||||
Server *http.Server
|
||||
CodeChan chan string
|
||||
State string
|
||||
}
|
||||
|
||||
// Close shuts down the callback server
|
||||
func (cs *callbackServer) Close() {
|
||||
if cs.Server != nil {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
_ = cs.Server.Shutdown(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// startOpenAICallbackServer starts a local HTTP server to receive the OAuth callback
|
||||
func startOpenAICallbackServer(expectedState string) (*callbackServer, error) {
|
||||
codeChan := make(chan string, 1)
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := &http.Server{
|
||||
Addr: "127.0.0.1:1455",
|
||||
Handler: mux,
|
||||
}
|
||||
|
||||
mux.HandleFunc("/auth/callback", func(w http.ResponseWriter, r *http.Request) {
|
||||
// Check state
|
||||
state := r.URL.Query().Get("state")
|
||||
if state != expectedState {
|
||||
http.Error(w, "State mismatch", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
code := r.URL.Query().Get("code")
|
||||
if code == "" {
|
||||
http.Error(w, "Missing authorization code", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Send code to channel
|
||||
select {
|
||||
case codeChan <- code:
|
||||
default:
|
||||
}
|
||||
|
||||
// Return success page
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = fmt.Fprintf(w, `<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Authentication Successful</title></head>
|
||||
<body style="font-family: sans-serif; text-align: center; padding: 50px;">
|
||||
<h1>✓ Authentication Successful</h1>
|
||||
<p>You can close this window and return to the terminal.</p>
|
||||
</body>
|
||||
</html>`)
|
||||
})
|
||||
|
||||
// Try to start server
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:1455")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("port 1455 not available: %w", err)
|
||||
}
|
||||
_ = listener.Close()
|
||||
|
||||
go func() {
|
||||
_ = server.ListenAndServe()
|
||||
}()
|
||||
|
||||
return &callbackServer{
|
||||
Server: server,
|
||||
CodeChan: codeChan,
|
||||
State: expectedState,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func logoutOpenAI() error {
|
||||
cm, err := kit.NewCredentialManager()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize credential manager: %w", err)
|
||||
}
|
||||
|
||||
// Check if authenticated
|
||||
hasAuth, err := cm.HasOpenAICredentials()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check authentication status: %w", err)
|
||||
}
|
||||
|
||||
if !hasAuth {
|
||||
fmt.Println("You are not currently authenticated with OpenAI.")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Confirm logout
|
||||
var confirm bool
|
||||
err = huh.NewConfirm().
|
||||
Title("Remove OpenAI credentials").
|
||||
Description("Are you sure you want to remove your stored credentials?").
|
||||
Affirmative("Yes").
|
||||
Negative("No").
|
||||
Value(&confirm).
|
||||
Run()
|
||||
if err != nil || !confirm {
|
||||
fmt.Println("Logout cancelled.")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove credentials
|
||||
if err := cm.RemoveOpenAICredentials(); err != nil {
|
||||
return fmt.Errorf("failed to remove credentials: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("✓ Successfully logged out from OpenAI!")
|
||||
fmt.Println("You will need to use environment variables or command-line flags for authentication.")
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/mark3labs/kit/internal/extensions"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var extensionsCmd = &cobra.Command{
|
||||
Use: "extensions",
|
||||
Short: "Manage KIT extensions",
|
||||
Long: "Commands for listing, validating, and scaffolding KIT extensions",
|
||||
}
|
||||
|
||||
var extensionsListCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List discovered extensions and their handlers",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
loaded, err := extensions.LoadExtensions(viper.GetStringSlice("extension"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading extensions: %w", err)
|
||||
}
|
||||
|
||||
if len(loaded) == 0 {
|
||||
fmt.Println("No extensions found.")
|
||||
fmt.Println()
|
||||
fmt.Println("Extension search paths:")
|
||||
fmt.Println(" ~/.config/kit/extensions/*.go (global)")
|
||||
fmt.Println(" .kit/extensions/*.go (project)")
|
||||
fmt.Println()
|
||||
fmt.Println("Run 'kit extensions init' to create an example extension.")
|
||||
return nil
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
_, _ = fmt.Fprintln(w, "EXTENSION\tEVENT\tHANDLERS\tTOOLS\tCOMMANDS")
|
||||
|
||||
for _, ext := range loaded {
|
||||
totalHandlers := 0
|
||||
for _, handlers := range ext.Handlers {
|
||||
totalHandlers += len(handlers)
|
||||
}
|
||||
first := true
|
||||
for event, handlers := range ext.Handlers {
|
||||
if first {
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\t%d\t%d\t%d\n",
|
||||
ext.Path, event, len(handlers), len(ext.Tools), len(ext.Commands))
|
||||
first = false
|
||||
} else {
|
||||
_, _ = fmt.Fprintf(w, "\t%s\t%d\t\t\n",
|
||||
event, len(handlers))
|
||||
}
|
||||
}
|
||||
if first {
|
||||
// Extension loaded but registered no handlers
|
||||
_, _ = fmt.Fprintf(w, "%s\t(none)\t0\t%d\t%d\n",
|
||||
ext.Path, len(ext.Tools), len(ext.Commands))
|
||||
}
|
||||
}
|
||||
|
||||
return w.Flush()
|
||||
},
|
||||
}
|
||||
|
||||
var extensionsValidateCmd = &cobra.Command{
|
||||
Use: "validate",
|
||||
Short: "Validate all extension files can be loaded",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
loaded, err := extensions.LoadExtensions(viper.GetStringSlice("extension"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("validation failed: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Loaded %d extension(s) successfully\n", len(loaded))
|
||||
for _, ext := range loaded {
|
||||
total := 0
|
||||
for _, h := range ext.Handlers {
|
||||
total += len(h)
|
||||
}
|
||||
fmt.Printf(" %s (%d handlers, %d tools, %d commands)\n",
|
||||
ext.Path, total, len(ext.Tools), len(ext.Commands))
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var extensionsInitCmd = &cobra.Command{
|
||||
Use: "init",
|
||||
Short: "Generate an example extension file",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
dir := ".kit/extensions"
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return fmt.Errorf("creating extensions directory: %w", err)
|
||||
}
|
||||
|
||||
example := `package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"kit/ext"
|
||||
)
|
||||
|
||||
// Init is called when the extension is loaded. Register handlers here.
|
||||
func Init(api ext.API) {
|
||||
// ── Event handlers ────────────────────────────────────────────────
|
||||
|
||||
// Log every tool call to a file.
|
||||
api.OnToolCall(func(tc ext.ToolCallEvent, ctx ext.Context) *ext.ToolCallResult {
|
||||
f, err := os.OpenFile("/tmp/kit-tool-log.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err == nil {
|
||||
defer f.Close()
|
||||
fmt.Fprintf(f, "[%s] tool=%s\n", time.Now().Format(time.RFC3339), tc.ToolName)
|
||||
}
|
||||
return nil // don't block
|
||||
})
|
||||
|
||||
// Block dangerous bash commands.
|
||||
api.OnToolCall(func(tc ext.ToolCallEvent, ctx ext.Context) *ext.ToolCallResult {
|
||||
if tc.ToolName == "bash" && strings.Contains(tc.Input, "rm -rf /") {
|
||||
return &ext.ToolCallResult{Block: true, Reason: "Blocked: dangerous command"}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// Handle custom ! commands via OnInput. Use ctx.Print* instead of
|
||||
// fmt.Println — BubbleTea captures stdout in interactive mode.
|
||||
api.OnInput(func(ie ext.InputEvent, ctx ext.Context) *ext.InputResult {
|
||||
switch ie.Text {
|
||||
case "!time":
|
||||
ctx.PrintInfo("Current time: " + time.Now().Format(time.RFC3339))
|
||||
return &ext.InputResult{Action: "handled"}
|
||||
|
||||
case "!status":
|
||||
ctx.PrintBlock(ext.PrintBlockOpts{
|
||||
Text: "Session active\nModel: " + ctx.Model + "\nCWD: " + ctx.CWD,
|
||||
BorderColor: "#a6e3a1",
|
||||
Subtitle: "my-extension",
|
||||
})
|
||||
return &ext.InputResult{Action: "handled"}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// ── Slash commands ────────────────────────────────────────────────
|
||||
// Slash commands appear in /help and the autocomplete popup.
|
||||
// They are invoked as /name <args> in the interactive TUI.
|
||||
|
||||
api.RegisterCommand(ext.CommandDef{
|
||||
Name: "echo",
|
||||
Description: "Echo back the provided text",
|
||||
Execute: func(args string, ctx ext.Context) (string, error) {
|
||||
if args == "" {
|
||||
return "Usage: /echo <text>", nil
|
||||
}
|
||||
return args, nil
|
||||
},
|
||||
})
|
||||
|
||||
// ── Background work with SendMessage ─────────────────────────────
|
||||
// ctx.SendMessage injects a message into the conversation and
|
||||
// triggers a new agent turn. Safe to call from goroutines.
|
||||
|
||||
api.RegisterCommand(ext.CommandDef{
|
||||
Name: "run",
|
||||
Description: "Run a shell command in the background and feed the result to the agent",
|
||||
Execute: func(args string, ctx ext.Context) (string, error) {
|
||||
if args == "" {
|
||||
return "Usage: /run <command>", nil
|
||||
}
|
||||
go func() {
|
||||
out, err := exec.Command("sh", "-c", args).CombinedOutput()
|
||||
if err != nil {
|
||||
ctx.SendMessage(fmt.Sprintf("Background command %q failed: %v\n%s", args, err, out))
|
||||
return
|
||||
}
|
||||
ctx.SendMessage(fmt.Sprintf("Background command %q finished:\n%s", args, out))
|
||||
}()
|
||||
return fmt.Sprintf("Running %q in background...", args), nil
|
||||
},
|
||||
})
|
||||
|
||||
// ── Custom tools ──────────────────────────────────────────────────
|
||||
// Custom tools are added to the agent's tool list and can be
|
||||
// called by the LLM. Parameters is a JSON Schema string.
|
||||
|
||||
api.RegisterTool(ext.ToolDef{
|
||||
Name: "current_time",
|
||||
Description: "Get the current date and time",
|
||||
Parameters: ` + "`" + `{"type":"object","properties":{}}` + "`" + `,
|
||||
Execute: func(input string) (string, error) {
|
||||
return time.Now().Format(time.RFC3339), nil
|
||||
},
|
||||
})
|
||||
|
||||
api.RegisterTool(ext.ToolDef{
|
||||
Name: "env_lookup",
|
||||
Description: "Look up the value of an environment variable",
|
||||
Parameters: ` + "`" + `{"type":"object","properties":{"name":{"type":"string","description":"Environment variable name"}},"required":["name"]}` + "`" + `,
|
||||
Execute: func(input string) (string, error) {
|
||||
var params struct {
|
||||
Name string ` + "`" + `json:"name"` + "`" + `
|
||||
}
|
||||
if err := json.Unmarshal([]byte(input), ¶ms); err != nil {
|
||||
return "", fmt.Errorf("invalid parameters: %w", err)
|
||||
}
|
||||
val, ok := os.LookupEnv(params.Name)
|
||||
if !ok {
|
||||
return fmt.Sprintf("Environment variable %q is not set", params.Name), nil
|
||||
}
|
||||
return val, nil
|
||||
},
|
||||
})
|
||||
}
|
||||
`
|
||||
|
||||
path := dir + "/example.go"
|
||||
if err := os.WriteFile(path, []byte(example), 0644); err != nil {
|
||||
return fmt.Errorf("writing example: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Created %s with example extension\n", path)
|
||||
fmt.Println()
|
||||
fmt.Println("The extension will be auto-loaded on the next kit run.")
|
||||
fmt.Println("Use --no-extensions to disable all extensions.")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(extensionsCmd)
|
||||
extensionsCmd.AddCommand(extensionsListCmd)
|
||||
extensionsCmd.AddCommand(extensionsValidateCmd)
|
||||
extensionsCmd.AddCommand(extensionsInitCmd)
|
||||
}
|
||||
+225
@@ -0,0 +1,225 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
|
||||
"github.com/charmbracelet/log"
|
||||
"github.com/mark3labs/kit/internal/extensions"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
installLocalFlag bool
|
||||
installUpdateFlag bool
|
||||
installUninstallFlag bool
|
||||
installAllFlag bool
|
||||
)
|
||||
|
||||
var installCmd = &cobra.Command{
|
||||
Use: "install <git-url>",
|
||||
Short: "Install extensions from git repositories",
|
||||
Long: `Install extensions from git repositories.
|
||||
|
||||
The install command downloads and installs Kit extensions from git repositories.
|
||||
Extensions are stored in the global extensions directory by default, or in the
|
||||
project's .kit/git/ directory when using the --local flag.
|
||||
|
||||
When a repo contains multiple extensions, an interactive multi-select is shown
|
||||
so you can choose which to install. Use --all to skip selection and install everything.
|
||||
|
||||
Supported URL formats:
|
||||
- github.com/user/repo (shorthand, defaults to HTTPS)
|
||||
- git:github.com/user/repo
|
||||
- https://github.com/user/repo
|
||||
- ssh://git@github.com/user/repo
|
||||
- git@github.com:user/repo
|
||||
|
||||
You can pin to a specific version, tag, or commit using @:
|
||||
- github.com/user/repo@v1.0.0
|
||||
- github.com/user/repo@main
|
||||
- github.com/user/repo@abc1234
|
||||
|
||||
Examples:
|
||||
kit install github.com/user/my-extension
|
||||
kit install github.com/user/my-extension@v1.0.0
|
||||
kit install github.com/user/my-extension --local
|
||||
kit install github.com/user/collection --all`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runInstall,
|
||||
}
|
||||
|
||||
func init() {
|
||||
installCmd.Flags().BoolVarP(&installLocalFlag, "local", "l", false, "Install to project-local .kit/git/ directory")
|
||||
installCmd.Flags().BoolVarP(&installUpdateFlag, "update", "u", false, "Update an already-installed package")
|
||||
installCmd.Flags().BoolVar(&installUninstallFlag, "uninstall", false, "Remove an installed package")
|
||||
installCmd.Flags().BoolVar(&installAllFlag, "all", false, "Install all extensions without prompting")
|
||||
|
||||
rootCmd.AddCommand(installCmd)
|
||||
}
|
||||
|
||||
func runInstall(cmd *cobra.Command, args []string) error {
|
||||
sourceStr := args[0]
|
||||
|
||||
// Check that git is available
|
||||
if _, err := exec.LookPath("git"); err != nil {
|
||||
return fmt.Errorf("git is not installed or not in PATH")
|
||||
}
|
||||
|
||||
// Parse the source
|
||||
source, err := extensions.ParseGitSource(sourceStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid source: %w", err)
|
||||
}
|
||||
|
||||
// Determine scope
|
||||
scope := extensions.ScopeGlobal
|
||||
if installLocalFlag {
|
||||
scope = extensions.ScopeProject
|
||||
}
|
||||
|
||||
installer := extensions.NewInstaller(".")
|
||||
|
||||
// Handle uninstall
|
||||
if installUninstallFlag {
|
||||
return runUninstall(installer, source, scope)
|
||||
}
|
||||
|
||||
// Handle update
|
||||
if installUpdateFlag {
|
||||
return runUpdate(installer, source, scope)
|
||||
}
|
||||
|
||||
// Handle install
|
||||
return runInstallPackage(installer, source, scope)
|
||||
}
|
||||
|
||||
func runInstallPackage(installer *extensions.Installer, source *extensions.GitSource, scope extensions.InstallScope) error {
|
||||
// Check if already installed
|
||||
existingScope, installed := installer.IsInstalled(source)
|
||||
if installed {
|
||||
return fmt.Errorf("extension already installed (scope: %s). Use --update to update or --uninstall to remove", existingScope)
|
||||
}
|
||||
|
||||
// Preview extensions to decide if we need multi-select
|
||||
previews, tempDir, err := installer.PreviewExtensions(source)
|
||||
if err != nil {
|
||||
return fmt.Errorf("previewing extensions: %w", err)
|
||||
}
|
||||
defer extensions.CleanupTempDir(tempDir)
|
||||
|
||||
if len(previews) == 0 {
|
||||
return fmt.Errorf("no extensions found in %s", source.String())
|
||||
}
|
||||
|
||||
scopeStr := "globally"
|
||||
if scope == extensions.ScopeProject {
|
||||
scopeStr = "locally in .kit/git/"
|
||||
}
|
||||
|
||||
// Single extension or --all flag: install everything directly
|
||||
if len(previews) == 1 || installAllFlag {
|
||||
if err := installer.Install(source, scope); err != nil {
|
||||
return fmt.Errorf("install failed: %w", err)
|
||||
}
|
||||
|
||||
if source.Pinned {
|
||||
fmt.Printf("Installed %s at %s %s\n", source.String(), source.Ref, scopeStr)
|
||||
} else {
|
||||
fmt.Printf("Installed %d extension(s) from %s %s\n", len(previews), source.String(), scopeStr)
|
||||
}
|
||||
|
||||
log.Info("extension installed", "source", source.String(), "scope", scope)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Multiple extensions: show interactive selection
|
||||
includePaths, err := multiSelectForInstall(previews)
|
||||
if err != nil {
|
||||
if err.Error() == "selection cancelled" || err.Error() == "no extensions selected" {
|
||||
fmt.Println("Install cancelled.")
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("selection failed: %w", err)
|
||||
}
|
||||
|
||||
if err := installer.InstallWithInclude(source, scope, includePaths); err != nil {
|
||||
return fmt.Errorf("install failed: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Installed %d extension(s) from %s %s\n", len(includePaths), source.String(), scopeStr)
|
||||
for _, path := range includePaths {
|
||||
fmt.Printf(" - %s\n", path)
|
||||
}
|
||||
|
||||
log.Info("extension installed", "source", source.String(), "scope", scope, "selected", len(includePaths))
|
||||
return nil
|
||||
}
|
||||
|
||||
func runUpdate(installer *extensions.Installer, source *extensions.GitSource, scope extensions.InstallScope) error {
|
||||
// Find the installed package
|
||||
existingScope, installed := installer.IsInstalled(source)
|
||||
if !installed {
|
||||
// Try to find with wildcard (no version)
|
||||
entry, foundScope, err := extensions.FindInManifest(source.Identity())
|
||||
if err != nil || entry == nil {
|
||||
return fmt.Errorf("extension not installed: %s", source.Identity())
|
||||
}
|
||||
// Parse the found entry's source
|
||||
foundSource, err := extensions.ParseGitSource(entry.Source)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse installed source: %w", err)
|
||||
}
|
||||
existingScope = foundScope
|
||||
source = foundSource
|
||||
}
|
||||
|
||||
// Override scope if specified
|
||||
if installLocalFlag && scope != existingScope {
|
||||
return fmt.Errorf("extension installed in %s scope, cannot update with --local flag", existingScope)
|
||||
}
|
||||
scope = existingScope
|
||||
|
||||
// Check if pinned
|
||||
if source.Pinned {
|
||||
fmt.Printf("Skipping %s (pinned at %s)\n", source.Identity(), source.Ref)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update
|
||||
if err := installer.Update(source, scope); err != nil {
|
||||
return fmt.Errorf("update failed: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Updated %s\n", source.Identity())
|
||||
log.Info("extension updated", "source", source.Identity(), "scope", scope)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runUninstall(installer *extensions.Installer, source *extensions.GitSource, scope extensions.InstallScope) error {
|
||||
// Find where it's installed (ignore scope flag for uninstall - remove from wherever it exists)
|
||||
existingScope, installed := installer.IsInstalled(source)
|
||||
if !installed {
|
||||
// Try to find in manifests
|
||||
entry, foundScope, err := extensions.FindInManifest(source.Identity())
|
||||
if err != nil || entry == nil {
|
||||
return fmt.Errorf("extension not installed: %s", source.Identity())
|
||||
}
|
||||
existingScope = foundScope
|
||||
// Parse the found entry's source
|
||||
foundSource, err := extensions.ParseGitSource(entry.Source)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse installed source: %w", err)
|
||||
}
|
||||
source = foundSource
|
||||
}
|
||||
|
||||
// Uninstall from the scope where it's installed
|
||||
if err := installer.Uninstall(source, existingScope); err != nil {
|
||||
return fmt.Errorf("uninstall failed: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Uninstalled %s from %s scope\n", source.Identity(), existingScope)
|
||||
log.Info("extension uninstalled", "source", source.Identity(), "scope", existingScope)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"charm.land/huh/v2"
|
||||
"github.com/charmbracelet/log"
|
||||
"github.com/mark3labs/kit/internal/extensions"
|
||||
)
|
||||
|
||||
// multiSelectForInstall runs a multi-select prompt for extension selection.
|
||||
// Returns the selected extension paths, or an error if cancelled.
|
||||
func multiSelectForInstall(previews []extensions.ExtensionPreview) ([]string, error) {
|
||||
if len(previews) == 0 {
|
||||
return nil, fmt.Errorf("no extensions to select")
|
||||
}
|
||||
|
||||
// Non-interactive: select all
|
||||
if !isInteractive() {
|
||||
log.Info("Non-interactive mode, selecting all extensions")
|
||||
paths := make([]string, len(previews))
|
||||
for i, p := range previews {
|
||||
paths[i] = p.Path
|
||||
}
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
// Single extension: just return it
|
||||
if len(previews) == 1 {
|
||||
return []string{previews[0].Path}, nil
|
||||
}
|
||||
|
||||
// Build options for huh MultiSelect
|
||||
options := make([]huh.Option[string], len(previews))
|
||||
for i, p := range previews {
|
||||
label := fmt.Sprintf("%s %s", p.Name, p.Path)
|
||||
options[i] = huh.NewOption(label, p.Path).Selected(true)
|
||||
}
|
||||
|
||||
var selected []string
|
||||
|
||||
form := huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewMultiSelect[string]().
|
||||
Title("Select extensions to install").
|
||||
Options(options...).
|
||||
Value(&selected),
|
||||
),
|
||||
)
|
||||
|
||||
if err := form.Run(); err != nil {
|
||||
return nil, fmt.Errorf("selection cancelled")
|
||||
}
|
||||
|
||||
if len(selected) == 0 {
|
||||
return nil, fmt.Errorf("no extensions selected")
|
||||
}
|
||||
|
||||
return selected, nil
|
||||
}
|
||||
|
||||
// isInteractive checks if the terminal is interactive.
|
||||
func isInteractive() bool {
|
||||
fi, err := os.Stdout.Stat()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return (fi.Mode() & os.ModeCharDevice) != 0
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/charmbracelet/fang"
|
||||
"github.com/mark3labs/kit/cmd"
|
||||
)
|
||||
|
||||
var version = "dev"
|
||||
|
||||
func main() {
|
||||
for _, arg := range os.Args[1:] {
|
||||
if arg == "--version" || arg == "-v" {
|
||||
fmt.Printf("kit version %s\n", version)
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
rootCmd := cmd.GetRootCommand(version)
|
||||
if err := fang.Execute(context.Background(), rootCmd); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/mark3labs/kit/internal/models"
|
||||
kit "github.com/mark3labs/kit/pkg/kit"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var modelsAllFlag bool
|
||||
|
||||
var modelsCmd = &cobra.Command{
|
||||
Use: "models [provider]",
|
||||
Short: "List available models from the model database",
|
||||
Long: `List models known to kit from the models.dev database.
|
||||
|
||||
By default, shows only providers that kit can use (native fantasy
|
||||
providers plus openai-compatible auto-routed providers). Use --all
|
||||
to show every provider in the database.
|
||||
|
||||
When a provider name is given, shows only that provider's models.
|
||||
|
||||
Note: models not listed here can still be used — the database is
|
||||
advisory. Run 'kit update-models' to refresh.
|
||||
|
||||
Examples:
|
||||
kit models
|
||||
kit models --all
|
||||
kit models anthropic
|
||||
kit models deepseek`,
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: runModels,
|
||||
}
|
||||
|
||||
func init() {
|
||||
modelsCmd.Flags().BoolVar(&modelsAllFlag, "all", false, "show all providers in the database, not just fantasy-compatible ones")
|
||||
rootCmd.AddCommand(modelsCmd)
|
||||
}
|
||||
|
||||
func runModels(_ *cobra.Command, args []string) error {
|
||||
if len(args) == 1 {
|
||||
return printProvider(args[0])
|
||||
}
|
||||
|
||||
return printAllProviders(modelsAllFlag)
|
||||
}
|
||||
|
||||
func printAllProviders(showAll bool) error {
|
||||
// Reload the registry to pick up any custom models from config
|
||||
models.ReloadGlobalRegistry()
|
||||
|
||||
var providerIDs []string
|
||||
if showAll {
|
||||
providerIDs = kit.GetSupportedProviders()
|
||||
} else {
|
||||
providerIDs = kit.GetLLMProviders()
|
||||
}
|
||||
sort.Strings(providerIDs)
|
||||
|
||||
// Filter to providers that have models
|
||||
var withModels []string
|
||||
for _, id := range providerIDs {
|
||||
m, _ := kit.GetModelsForProvider(id)
|
||||
if len(m) > 0 {
|
||||
withModels = append(withModels, id)
|
||||
}
|
||||
}
|
||||
|
||||
if len(withModels) == 0 {
|
||||
fmt.Println("No models in database. Run 'kit update-models' to fetch.")
|
||||
return nil
|
||||
}
|
||||
|
||||
for i, id := range withModels {
|
||||
m, _ := kit.GetModelsForProvider(id)
|
||||
modelIDs := sortedModelIDs(m)
|
||||
|
||||
isLast := i == len(withModels)-1
|
||||
branch := "├── "
|
||||
if isLast {
|
||||
branch = "└── "
|
||||
}
|
||||
fmt.Printf("%s%s\n", branch, id)
|
||||
|
||||
childPrefix := "│ "
|
||||
if isLast {
|
||||
childPrefix = " "
|
||||
}
|
||||
|
||||
for j, modelID := range modelIDs {
|
||||
modelBranch := "├── "
|
||||
if j == len(modelIDs)-1 {
|
||||
modelBranch = "└── "
|
||||
}
|
||||
fmt.Printf("%s%s%s\n", childPrefix, modelBranch, modelID)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func printProvider(provider string) error {
|
||||
// Reload the registry to pick up any custom models from config
|
||||
models.ReloadGlobalRegistry()
|
||||
|
||||
m, err := kit.GetModelsForProvider(provider)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unknown provider %q. Run 'kit models' to see all providers", provider)
|
||||
}
|
||||
|
||||
if len(m) == 0 {
|
||||
fmt.Printf("No models listed for %s.\n", provider)
|
||||
return nil
|
||||
}
|
||||
|
||||
modelIDs := sortedModelIDs(m)
|
||||
for _, id := range modelIDs {
|
||||
fmt.Println(id)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func sortedModelIDs(m map[string]kit.ModelInfo) []string {
|
||||
ids := make([]string, 0, len(m))
|
||||
for id := range m {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
sort.Strings(ids)
|
||||
return ids
|
||||
}
|
||||
+1897
File diff suppressed because it is too large
Load Diff
+137
@@ -0,0 +1,137 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/mark3labs/kit/internal/app"
|
||||
"github.com/mark3labs/kit/internal/config"
|
||||
"github.com/mark3labs/kit/internal/ui"
|
||||
kit "github.com/mark3labs/kit/pkg/kit"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// CollectAgentMetadata extracts model display info and tool/server name lists
|
||||
// from the Kit instance, used to populate app.Options and UI setup.
|
||||
// It also returns the number of MCP tools and extension tools separately.
|
||||
func CollectAgentMetadata(k *kit.Kit, mcpConfig *config.Config) (provider, modelName string, serverNames, toolNames []string, mcpToolCount, extensionToolCount int) {
|
||||
modelString := viper.GetString("model")
|
||||
provider, modelName, _ = kit.ParseModelString(modelString)
|
||||
if modelName == "" {
|
||||
modelName = "Unknown"
|
||||
}
|
||||
|
||||
for name := range mcpConfig.MCPServers {
|
||||
serverNames = append(serverNames, name)
|
||||
}
|
||||
|
||||
toolNames = k.GetToolNames()
|
||||
mcpToolCount = k.GetMCPToolCount()
|
||||
extensionToolCount = k.GetExtensionToolCount()
|
||||
|
||||
return provider, modelName, serverNames, toolNames, mcpToolCount, extensionToolCount
|
||||
}
|
||||
|
||||
// BuildAppOptions constructs the app.Options struct from the current state.
|
||||
func BuildAppOptions(mcpConfig *config.Config, modelName string, serverNames, toolNames []string) app.Options {
|
||||
return app.Options{
|
||||
MCPConfig: mcpConfig,
|
||||
ModelName: modelName,
|
||||
ServerNames: serverNames,
|
||||
ToolNames: toolNames,
|
||||
StreamingEnabled: viper.GetBool("stream"),
|
||||
Quiet: quietFlag,
|
||||
Debug: viper.GetBool("debug"),
|
||||
}
|
||||
}
|
||||
|
||||
// DisplayDebugConfig builds and displays the debug configuration map through
|
||||
// the CLI for non-interactive mode.
|
||||
func DisplayDebugConfig(cli *ui.CLI, k *kit.Kit, mcpConfig *config.Config, provider string) {
|
||||
if quietFlag || cli == nil || !viper.GetBool("debug") {
|
||||
return
|
||||
}
|
||||
|
||||
debugConfig := map[string]any{
|
||||
"model": viper.GetString("model"),
|
||||
"max-steps": viper.GetInt("max-steps"),
|
||||
"max-tokens": viper.GetInt("max-tokens"),
|
||||
"temperature": viper.GetFloat64("temperature"),
|
||||
"top-p": viper.GetFloat64("top-p"),
|
||||
"top-k": viper.GetInt("top-k"),
|
||||
"provider-url": viper.GetString("provider-url"),
|
||||
"system-prompt": viper.GetString("system-prompt"),
|
||||
}
|
||||
|
||||
if viper.GetBool("tls-skip-verify") {
|
||||
debugConfig["tls-skip-verify"] = true
|
||||
}
|
||||
|
||||
if provider == "ollama" {
|
||||
debugConfig["num-gpu-layers"] = viper.GetInt("num-gpu-layers")
|
||||
debugConfig["main-gpu"] = viper.GetInt("main-gpu")
|
||||
}
|
||||
|
||||
if stopSeqs := viper.GetStringSlice("stop-sequences"); len(stopSeqs) > 0 {
|
||||
debugConfig["stop-sequences"] = stopSeqs
|
||||
}
|
||||
|
||||
if viper.GetString("provider-api-key") != "" {
|
||||
debugConfig["provider-api-key"] = "[SET]"
|
||||
}
|
||||
|
||||
// MCP server info
|
||||
if len(mcpConfig.MCPServers) > 0 {
|
||||
mcpServers := make(map[string]any)
|
||||
loadedServerSet := make(map[string]bool)
|
||||
for _, name := range k.GetLoadedServerNames() {
|
||||
loadedServerSet[name] = true
|
||||
}
|
||||
|
||||
for name, server := range mcpConfig.MCPServers {
|
||||
serverInfo := map[string]any{
|
||||
"type": server.Type,
|
||||
"status": "failed",
|
||||
}
|
||||
if loadedServerSet[name] {
|
||||
serverInfo["status"] = "loaded"
|
||||
}
|
||||
if len(server.Command) > 0 {
|
||||
serverInfo["command"] = server.Command
|
||||
}
|
||||
if len(server.Environment) > 0 {
|
||||
maskedEnv := make(map[string]string)
|
||||
for k, v := range server.Environment {
|
||||
if strings.Contains(strings.ToLower(k), "token") ||
|
||||
strings.Contains(strings.ToLower(k), "key") ||
|
||||
strings.Contains(strings.ToLower(k), "secret") {
|
||||
maskedEnv[k] = "[MASKED]"
|
||||
} else {
|
||||
maskedEnv[k] = v
|
||||
}
|
||||
}
|
||||
serverInfo["environment"] = maskedEnv
|
||||
}
|
||||
if server.URL != "" {
|
||||
serverInfo["url"] = server.URL
|
||||
}
|
||||
mcpServers[name] = serverInfo
|
||||
}
|
||||
debugConfig["mcpServers"] = mcpServers
|
||||
}
|
||||
|
||||
cli.DisplayDebugConfig(debugConfig)
|
||||
}
|
||||
|
||||
// SetupCLIForNonInteractive creates the CLI display layer for non-interactive
|
||||
// mode (--prompt). Returns nil when quiet mode is active.
|
||||
func SetupCLIForNonInteractive(k *kit.Kit) (*ui.CLI, error) {
|
||||
agentAdapter := &kitUIAdapter{kit: k}
|
||||
return ui.SetupCLI(&ui.CLISetupOptions{
|
||||
Agent: agentAdapter,
|
||||
ModelString: viper.GetString("model"),
|
||||
Debug: viper.GetBool("debug"),
|
||||
Quiet: quietFlag,
|
||||
ShowDebug: false,
|
||||
ProviderAPIKey: viper.GetString("provider-api-key"),
|
||||
})
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user