Merge branch 'main' into install-script-update

This commit is contained in:
Marian Alexandru Alecu
2026-05-06 12:14:58 +03:00
committed by GitHub
2291 changed files with 90216 additions and 100718 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": patch
---
Improve the JetBrains model picker with sections, favorites, and smart filtering.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": patch
---
Add a JetBrains tool window action for starting new sessions and let recent sessions reopen in the side panel without duplicate views.
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": patch
---
Scale JetBrains chat transcript fonts from the editor font settings.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": patch
---
Keep the JetBrains model picker at the same row position when models are favorited or unfavorited.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": patch
---
Show JetBrains session connection status inline above the prompt, cap expanded connection details, and keep empty sessions in the shared chat scroll area.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": patch
---
Show detailed JetBrains session connection and configuration issues in the connection panel, and make retry recover app-side problems more reliably.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": patch
---
Match VS Code-style reasoning and tool run rendering in JetBrains chat.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": patch
---
Improve the JetBrains mode picker with descriptions, deprecated badges, and native IntelliJ selection behavior.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": patch
---
Show a scroll-to-bottom button when JetBrains chat sessions are scrolled away from the latest message.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": patch
---
Support rebinding the JetBrains prompt Send action in the IntelliJ keymap.
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": patch
---
Control JetBrains chat transcript spacing from the session layout.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": patch
---
Support stopping active JetBrains sessions from the prompt action button.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": patch
---
Size the JetBrains model picker from its rendered model rows instead of fixed popup dimensions.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": patch
---
Support reasoning effort selection and resetting JetBrains chat model overrides to the default model.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": patch
---
Align checked and unchecked reasoning picker rows in the JetBrains plugin.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": patch
---
Keep the JetBrains session connection status above the prompt without shifting the chat layout.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": patch
---
Refine JetBrains session loading UI and settings actions.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": patch
---
Preserve JetBrains chat scroll position when reviewing earlier session output.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": patch
---
Avoid flickering transient loading and connection error states in JetBrains sessions.
+5
View File
@@ -7,3 +7,8 @@ end_of_line = lf
indent_style = space
indent_size = 2
max_line_length = 80
[*.{kt,kts}]
indent_size = 4
tab_width = 4
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
+7 -2
View File
@@ -1,5 +1,10 @@
name: "Setup Bun"
description: "Setup Bun with caching and install dependencies"
inputs:
install-flags:
description: "Additional flags to pass to 'bun install'"
required: false
default: ""
runs:
using: "composite"
steps:
@@ -46,8 +51,8 @@ runs:
# e.g. ./patches/ for standard-openapi
# https://github.com/oven-sh/bun/issues/28147
if [ "$RUNNER_OS" = "Windows" ]; then
bun install --linker hoisted
bun install --linker hoisted ${{ inputs.install-flags }}
else
bun install
bun install ${{ inputs.install-flags }}
fi
shell: bash
@@ -39,6 +39,15 @@ runs:
- name: Clear checkout auth
run: |
git config --local --unset-all http.https://github.com/.extraheader || true
# kilocode_change start
keys="$(git config --local --name-only --get-regexp '^includeIf\.gitdir:' || true)"
for key in $keys; do
path="$(git config --local --get "$key" || true)"
case "$path" in
*git-credentials-*.config) git config --local --unset-all "$key" || true ;;
esac
done
# kilocode_change end
shell: bash
- name: Configure git remote
+2 -2
View File
@@ -13,8 +13,8 @@ Some description of HOW you achieved it. Perhaps give a high level description o
## Screenshots
| before | after |
| ------ | ----- |
| | |
|---|---|
| | |
## How to Test
+2 -1
View File
@@ -14,8 +14,9 @@ jobs:
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6 # kilocode_change
with:
persist-credentials: false # kilocode_change
fetch-depth: 0
- name: Setup Bun
@@ -0,0 +1,24 @@
name: Check markdown table padding
on:
pull_request:
paths:
- "**/*.md"
- "script/check-md-table-padding.ts"
- ".github/workflows/check-md-table-padding.yml"
workflow_dispatch:
jobs:
check:
name: Check markdown table padding
if: github.repository == 'Kilo-Org/kilocode'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6 # kilocode_change
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- uses: oven-sh/setup-bun@v2
- name: Run check
run: bun run script/check-md-table-padding.ts
@@ -1,11 +1,18 @@
name: Check opencode annotations
name: Check shared upstream annotations
on:
pull_request:
paths:
- ".github/**"
- "github/**"
- "sdks/vscode/**"
- "packages/extensions/**"
- "packages/opencode/**"
- "script/check-opencode-annotations.ts"
- ".github/workflows/check-opencode-annotations.yml"
- "packages/script/**"
- "packages/shared/**"
- "packages/storybook/**"
- "packages/ui/**"
- "script/**"
workflow_dispatch:
jobs:
@@ -14,14 +21,14 @@ jobs:
if: github.repository == 'Kilo-Org/kilocode'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6 # kilocode_change
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
fetch-depth: 0
- uses: oven-sh/setup-bun@v2
- name: Check kilocode_change annotations in shared opencode files
- name: Check kilocode_change annotations in shared upstream files
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: |
@@ -30,3 +37,8 @@ jobs:
else
echo "No PR base SHA available (workflow_dispatch without PR context) — skipping."
fi
# kilocode_change start
- name: Check workflow allowlist
run: bun run script/check-workflows.ts
# kilocode_change end
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
contents: read
issues: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6 # kilocode_change
- uses: oven-sh/setup-bun@v2
with:
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
OWNER: ${{ github.repository_owner }}
# kilocode_change end
- uses: actions/checkout@v4
- uses: actions/checkout@v6 # kilocode_change
- uses: ./.github/actions/setup-bun
@@ -13,7 +13,6 @@ permissions:
jobs:
close-non-compliant:
if: github.repository == 'Kilo-Org/kilocode' && false # kilocode_change - disabled: not needed in kilocode repo
runs-on: ubuntu-latest
steps:
- name: Close non-compliant issues and PRs after 2 hours
@@ -8,9 +8,6 @@ on:
jobs:
daily-recap:
# kilocode_change start - disabled for kilo-cli fork (OpenCode Discord integration)
if: false
# kilocode_change end
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: read
@@ -24,7 +21,7 @@ jobs:
- uses: ./.github/actions/setup-bun
- name: Install opencode
run: curl -fsSL https://opencode.ai/install | bash
run: curl -fsSL https://kilo.ai/install | bash
- name: Generate daily issues recap
id: recap
@@ -46,7 +43,7 @@ jobs:
# Get today's date range
TODAY=$(date -u +%Y-%m-%d)
opencode run -m kilo/anthropic/claude-sonnet-4.5 "Generate a daily issues recap for the OpenCode repository.
opencode run -m opencode/claude-sonnet-4-5 "Generate a daily issues recap for the Kilo repository.
TODAY'S DATE: ${TODAY}
@@ -8,9 +8,6 @@ on:
jobs:
pr-recap:
# kilocode_change start - disabled for kilo-cli fork (OpenCode Discord integration)
if: false
# kilocode_change end
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: read
@@ -24,7 +21,7 @@ jobs:
- uses: ./.github/actions/setup-bun
- name: Install opencode
run: curl -fsSL https://opencode.ai/install | bash
run: curl -fsSL https://kilo.ai/install | bash
- name: Generate daily PR recap
id: recap
@@ -45,7 +42,7 @@ jobs:
run: |
TODAY=$(date -u +%Y-%m-%d)
opencode run -m kilo/anthropic/claude-sonnet-4.5 "Generate a daily PR activity recap for the OpenCode repository.
opencode run -m opencode/claude-sonnet-4-5 "Generate a daily PR activity recap for the Kilo repository.
TODAY'S DATE: ${TODAY}
@@ -5,20 +5,7 @@ on:
types: [opened]
jobs:
# kilocode_change start
check-author:
if: github.repository == 'Kilo-Org/kilocode' && false # kilocode_change - disabled: not needed in kilocode repo
uses: ./.github/workflows/check-org-member.yml
with:
username: ${{ github.event.pull_request.user.login }}
secrets:
kilo-maintainer-app-id: ${{ secrets.KILO_MAINTAINER_APP_ID }}
kilo-maintainer-app-secret: ${{ secrets.KILO_MAINTAINER_APP_SECRET }}
check-duplicates:
needs: check-author
if: github.repository == 'Kilo-Org/kilocode' && false # kilocode_change - disabled: not needed in kilocode repo
# kilocode_change end
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: read
@@ -48,10 +35,9 @@ jobs:
if: steps.team-check.outputs.is_team != 'true'
run: bun install
# kilocode_change start
- name: Setup Kilo
uses: ./.github/actions/setup-kilo
# kilocode_change end
- name: Install opencode
if: steps.team-check.outputs.is_team != 'true'
run: curl -fsSL https://kilo.ai/install | bash
- name: Build prompt
if: steps.team-check.outputs.is_team != 'true'
@@ -73,10 +59,7 @@ jobs:
- name: Check for duplicate PRs
if: steps.team-check.outputs.is_team != 'true'
env:
# kilocode_change start
KILO_API_KEY: ${{ secrets.KILO_API_KEY }}
KILO_ORG_ID: ${{ secrets.KILO_ORG_ID }}
# kilocode_change end
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
@@ -89,8 +72,7 @@ jobs:
fi
add-contributor-label:
if: github.repository == 'Kilo-Org/kilocode' && false # kilocode_change - disabled: not needed in kilocode repo
runs-on: blacksmith-2vcpu-ubuntu-2404 # kilocode_change
runs-on: ubuntu-latest
permissions:
pull-requests: write
issues: write
@@ -5,22 +5,10 @@ on:
types: [opened, edited, synchronize]
jobs:
# kilocode_change start
check-author:
if: github.repository == 'Kilo-Org/kilocode' && false # kilocode_change - disabled: not needed in kilocode repo
uses: ./.github/workflows/check-org-member.yml
with:
username: ${{ github.event.pull_request.user.login }}
secrets:
kilo-maintainer-app-id: ${{ secrets.KILO_MAINTAINER_APP_ID }}
kilo-maintainer-app-secret: ${{ secrets.KILO_MAINTAINER_APP_SECRET }}
check-standards:
needs: check-author
if: github.repository == 'Kilo-Org/kilocode' && false # kilocode_change - disabled: not needed in kilocode repo
# kilocode_change end
runs-on: blacksmith-4vcpu-ubuntu-2404
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Check PR standards
@@ -28,6 +16,30 @@ jobs:
with:
script: |
const pr = context.payload.pull_request;
const login = pr.user.login;
// Skip PRs older than Feb 18, 2026 at 6PM EST (Feb 19, 2026 00:00 UTC)
const cutoff = new Date('2026-02-19T00:00:00Z');
const prCreated = new Date(pr.created_at);
if (prCreated < cutoff) {
console.log(`Skipping: PR #${pr.number} was created before cutoff (${prCreated.toISOString()})`);
return;
}
// Check if author is a team member or bot
if (login === 'opencode-agent[bot]') return;
const { data: file } = await github.rest.repos.getContent({
owner: context.repo.owner,
repo: context.repo.repo,
path: '.github/TEAM_MEMBERS',
ref: 'dev'
});
const members = Buffer.from(file.content, 'base64').toString().split('\n').map(l => l.trim()).filter(Boolean);
if (members.includes(login)) {
console.log(`Skipping: ${login} is a team member`);
return;
}
const title = pr.title;
async function addLabel(label) {
@@ -59,10 +71,10 @@ jobs:
repo: context.repo.repo,
issue_number: pr.number
});
const existing = comments.find(c => c.body.includes(markerText));
if (existing) return;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
@@ -88,9 +100,9 @@ jobs:
- \`refactor:\` or \`refactor(scope):\` code refactoring
- \`test:\` or \`test(scope):\` adding or updating tests
Where \`scope\` is the package name (e.g., \`app\`, \`desktop\`, \`kilo\`).
Where \`scope\` is the package name (e.g., \`app\`, \`desktop\`, \`opencode\`).
See [CONTRIBUTING.md](../blob/main/CONTRIBUTING.md#pr-titles) for details.`);
See [CONTRIBUTING.md](../blob/dev/CONTRIBUTING.md#pr-titles) for details.`);
return;
}
@@ -133,7 +145,7 @@ jobs:
1. Open an issue describing the bug/feature (if one doesn't exist)
2. Add \`Fixes #<number>\` or \`Closes #<number>\` to this PR description
See [CONTRIBUTING.md](../blob/main/CONTRIBUTING.md#issue-first-policy) for details.`);
See [CONTRIBUTING.md](../blob/dev/CONTRIBUTING.md#issue-first-policy) for details.`);
return;
}
@@ -141,7 +153,6 @@ jobs:
console.log('PR meets all standards');
check-compliance:
if: github.repository == 'Kilo-Org/kilocode' && false # kilocode_change - disabled: not needed in kilocode repo
runs-on: ubuntu-latest
permissions:
contents: read
@@ -49,13 +49,13 @@ jobs:
- name: Check PR guidelines compliance
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
KILO_PERMISSION: '{ "bash": { "*": "deny", "gh*": "allow", "gh pr review*": "deny" } }'
PR_TITLE: ${{ steps.pr-details.outputs.title }}
run: |
PR_BODY=$(jq -r .body pr_data.json)
opencode run -m anthropic/claude-opus-4-5 "A new pull request has been created: '${PR_TITLE}'
opencode run -m opencode/gpt-5.5 --variant medium "A new pull request has been created: '${PR_TITLE}'
<pr-number>
${{ steps.pr-number.outputs.number }}
@@ -25,7 +25,6 @@ concurrency:
jobs:
build:
if: false
name: storybook build
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6 # kilocode_change
- name: Setup Bun
uses: ./.github/actions/setup-bun
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
if: github.repository == 'Kilo-Org/kilocode'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6 # kilocode_change
- name: Link Checker
uses: Kilo-Org/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411
+2 -2
View File
@@ -13,7 +13,7 @@ jobs:
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6 # kilocode_change
with:
fetch-depth: 1
@@ -128,7 +128,7 @@ jobs:
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6 # kilocode_change
with:
fetch-depth: 1
+5 -1
View File
@@ -14,7 +14,11 @@ jobs:
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6 # kilocode_change
# kilocode_change start
with:
persist-credentials: false
# kilocode_change end
- name: Setup Bun
uses: ./.github/actions/setup-bun
-19
View File
@@ -40,8 +40,6 @@ jobs:
SYSTEMS="x86_64-linux aarch64-linux x86_64-darwin aarch64-darwin"
PACKAGES="kilo"
# TODO: move 'desktop' to PACKAGES when #11755 is fixed
OPTIONAL_PACKAGES="desktop"
echo ""
echo "=== Evaluating packages for all systems ==="
@@ -61,23 +59,6 @@ jobs:
done
done
echo ""
echo "=== Evaluating optional packages ==="
for system in $SYSTEMS; do
echo ""
echo "--- $system ---"
for pkg in $OPTIONAL_PACKAGES; do
printf " %s: " "$pkg"
if output=$(nix eval ".#packages.$system.$pkg.drvPath" --raw 2>&1); then
echo "✓"
else
echo "✗"
echo "::warning::Evaluation failed for packages.$system.$pkg"
echo "$output"
fi
done
done
echo ""
echo "=== Evaluating devShells for all systems ==="
for system in $SYSTEMS; do
+3 -3
View File
@@ -73,7 +73,7 @@ jobs:
echo "Computed hash for ${SYSTEM}: $HASH"
- name: Upload hash
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7 # kilocode_change
with:
name: hash-${{ matrix.system }}
path: hash.txt
@@ -86,7 +86,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6 # kilocode_change
with:
persist-credentials: false
fetch-depth: 0
@@ -106,7 +106,7 @@ jobs:
git pull --rebase --autostash origin "$GITHUB_REF_NAME"
- name: Download hash artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8 # kilocode_change
with:
path: hashes
pattern: hash-*
+54 -136
View File
@@ -22,11 +22,13 @@ on:
description: "Override version (optional)"
required: false
type: string
# kilocode_change start
pre_release:
description: "Publish as pre-release (VS Code marketplace + npm rc channel)"
required: false
type: boolean
default: false
default: true
# kilocode_change end
concurrency: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.version || inputs.bump }}
@@ -40,7 +42,7 @@ jobs:
runs-on: blacksmith-4vcpu-ubuntu-2404
if: github.repository == 'Kilo-Org/kilocode'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6 # kilocode_change
with:
fetch-depth: 0
@@ -76,7 +78,7 @@ jobs:
runs-on: blacksmith-4vcpu-ubuntu-2404
if: github.repository == 'Kilo-Org/kilocode'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6 # kilocode_change
with:
fetch-tags: true
@@ -93,10 +95,25 @@ jobs:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
- uses: actions/upload-artifact@v4
# kilocode_change start - pack dist tree into a single zstd-compressed tar before upload.
# download-artifact@v4's streaming unzip is CPU-bound and processes entries one by one;
# collapsing ~480 files into one tar cuts build-vscode download from >30m to seconds.
# zstd compresses the wasms (~4x) and leaves the already-compact binaries alone, for
# ~1 GB artifact. upload-artifact@v7's `archive: false` skips the zip wrapper entirely
# so the tarball travels as-is (download-artifact@v8 auto-detects via Content-Type).
# With `archive: false` the `name:` input is ignored; the artifact is named after the
# file (kilo-cli.tar.zst). zstd + GNU tar are preinstalled on both github-hosted and
# blacksmith ubuntu-2404. See actions/upload-artifact#36, actions/toolkit#1533.
- name: Pack CLI dist into tar.zst
run: |
find packages/opencode/dist -name '*.map' -delete
tar --zstd -cf /tmp/kilo-cli.tar.zst -C packages/opencode/dist .
- uses: actions/upload-artifact@v7
with:
name: kilo-cli
path: packages/opencode/dist
path: /tmp/kilo-cli.tar.zst
archive: false
# kilocode_change end
outputs:
version: ${{ needs.version.outputs.version }}
@@ -106,11 +123,11 @@ jobs:
runs-on: blacksmith-4vcpu-ubuntu-2404
if: github.repository == 'Kilo-Org/kilocode'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6 # kilocode_change
- uses: ./.github/actions/setup-bun
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6 # kilocode_change
with:
node-version: "24"
registry-url: "https://registry.npmjs.org"
@@ -118,11 +135,18 @@ jobs:
- name: Install @vscode/vsce
run: bun install -g @vscode/vsce
- uses: actions/download-artifact@v4
# kilocode_change start - download into /tmp and extract the tar.zst packed by build-cli
- uses: actions/download-artifact@v8
with:
name: kilo-cli
path: packages/opencode/dist
name: kilo-cli.tar.zst
path: /tmp
skip-decompress: true
- name: Unpack CLI dist
run: |
mkdir -p packages/opencode/dist
tar --zstd -xf /tmp/kilo-cli.tar.zst -C packages/opencode/dist
# kilocode_change end
- name: Build VSIX packages
run: bun script/build.ts
working-directory: ./packages/kilo-vscode
@@ -132,127 +156,11 @@ jobs:
KILO_PRE_RELEASE: ${{ inputs.pre_release }}
GH_REPO: ${{ github.repository }}
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v7 # kilocode_change
with:
name: kilo-vscode
path: packages/kilo-vscode/out
# build-tauri:
# needs:
# - build-cli
# - version
# continue-on-error: false
# strategy:
# fail-fast: false
# matrix:
# settings:
# - host: macos-latest
# target: x86_64-apple-darwin
# - host: macos-latest
# target: aarch64-apple-darwin
# - host: blacksmith-4vcpu-windows-2025
# target: x86_64-pc-windows-msvc
# - host: blacksmith-4vcpu-ubuntu-2404
# target: x86_64-unknown-linux-gnu
# - host: blacksmith-8vcpu-ubuntu-2404-arm
# target: aarch64-unknown-linux-gnu
# runs-on: ${{ matrix.settings.host }}
# steps:
# - uses: actions/checkout@v3
# with:
# fetch-tags: true
# - uses: apple-actions/import-codesign-certs@v2
# if: ${{ runner.os == 'macOS' }}
# with:
# keychain: build
# p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }}
# p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
# - name: Verify Certificate
# if: ${{ runner.os == 'macOS' }}
# run: |
# CERT_INFO=$(security find-identity -v -p codesigning build.keychain | grep "Developer ID Application")
# CERT_ID=$(echo "$CERT_INFO" | awk -F'"' '{print $2}')
# echo "CERT_ID=$CERT_ID" >> $GITHUB_ENV
# echo "Certificate imported."
# - name: Setup Apple API Key
# if: ${{ runner.os == 'macOS' }}
# run: |
# echo "${{ secrets.APPLE_API_KEY_PATH }}" > $RUNNER_TEMP/apple-api-key.p8
# - uses: ./.github/actions/setup-bun
# - name: Cache apt packages
# if: contains(matrix.settings.host, 'ubuntu')
# uses: actions/cache@v4
# with:
# path: /var/cache/apt/archives
# key: ${{ runner.os }}-${{ matrix.settings.target }}-apt-${{ hashFiles('.github/workflows/publish.yml') }}
# restore-keys: |
# ${{ runner.os }}-${{ matrix.settings.target }}-apt-
# - name: install dependencies (ubuntu only)
# if: contains(matrix.settings.host, 'ubuntu')
# run: |
# sudo apt-get update
# sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
# - name: install Rust stable
# uses: dtolnay/rust-toolchain@stable
# with:
# targets: ${{ matrix.settings.target }}
# - uses: Swatinem/rust-cache@v2
# with:
# workspaces: packages/desktop/src-tauri
# shared-key: ${{ matrix.settings.target }}
# - name: Prepare
# run: |
# cd packages/desktop
# bun ./scripts/prepare.ts
# env:
# KILO_VERSION: ${{ needs.version.outputs.version }}
# GITHUB_TOKEN: ${{ steps.committer.outputs.token }}
# RUST_TARGET: ${{ matrix.settings.target }}
# GH_TOKEN: ${{ github.token }}
# GITHUB_RUN_ID: ${{ github.run_id }}
# # Fixes AppImage build issues, can be removed when https://github.com/tauri-apps/tauri/pull/12491 is released
# - name: Install tauri-cli from portable appimage branch
# if: contains(matrix.settings.host, 'ubuntu')
# run: |
# cargo install tauri-cli --git https://github.com/tauri-apps/tauri --branch feat/truly-portable-appimage --force
# echo "Installed tauri-cli version:"
# cargo tauri --version
# - name: Build and upload artifacts
# uses: tauri-apps/tauri-action@390cbe447412ced1303d35abe75287949e43437a
# timeout-minutes: 60
# with:
# projectPath: packages/desktop
# uploadWorkflowArtifacts: true
# tauriScript: ${{ (contains(matrix.settings.host, 'ubuntu') && 'cargo tauri') || '' }}
# args: --target ${{ matrix.settings.target }} --config ./src-tauri/tauri.prod.conf.json --verbose
# updaterJsonPreferNsis: true
# releaseId: ${{ needs.version.outputs.release }}
# tagName: ${{ needs.version.outputs.tag }}
# releaseDraft: true
# releaseAssetNamePattern: opencode-desktop-[platform]-[arch][ext]
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# TAURI_BUNDLER_NEW_APPIMAGE_FORMAT: true
# TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
# TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
# APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
# APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
# APPLE_SIGNING_IDENTITY: ${{ env.CERT_ID }}
# APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
# APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
# APPLE_API_KEY_PATH: ${{ runner.temp }}/apple-api-key.p8
# kilocode_change start
# Run smoke tests against CLI assets uploaded to the draft GitHub release
# before publishing the release and package artifacts.
@@ -273,10 +181,13 @@ jobs:
- build-cli
- build-vscode
- smoke-test
# - build-tauri
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6 # kilocode_change
# kilocode_change start
with:
persist-credentials: false
# kilocode_change end
- uses: ./.github/actions/setup-bun
@@ -293,7 +204,7 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6 # kilocode_change
with:
node-version: "24"
registry-url: "https://registry.npmjs.org"
@@ -310,18 +221,25 @@ jobs:
kilo-maintainer-app-secret: ${{ secrets.KILO_MAINTAINER_APP_SECRET }}
# kilocode_change end
- uses: actions/download-artifact@v4
# kilocode_change start - download into /tmp and extract the tar.zst packed by build-cli
- uses: actions/download-artifact@v8
with:
name: kilo-cli
path: packages/opencode/dist
name: kilo-cli.tar.zst
path: /tmp
skip-decompress: true
- uses: actions/download-artifact@v4
- name: Unpack CLI dist
run: |
mkdir -p packages/opencode/dist
tar --zstd -xf /tmp/kilo-cli.tar.zst -C packages/opencode/dist
# kilocode_change end
- uses: actions/download-artifact@v8
with:
name: kilo-vscode
path: packages/kilo-vscode/out
- name: Cache apt packages (AUR)
uses: actions/cache@v4
uses: actions/cache@v5 # kilocode_change
with:
path: /var/cache/apt/archives
key: ${{ runner.os }}-apt-aur-${{ hashFiles('.github/workflows/publish.yml') }}
+2 -2
View File
@@ -47,7 +47,7 @@ jobs:
KILO_ORG_ID: ${{ secrets.KILO_ORG_ID }}
steps:
- name: Checkout kilo-bench
uses: actions/checkout@v4
uses: actions/checkout@v6 # kilocode_change
with:
repository: Kilo-Org/kilo-bench
token: ${{ secrets.BENCH_GITHUB_TOKEN }}
@@ -122,7 +122,7 @@ jobs:
- name: Upload results
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7 # kilocode_change
with:
name: smoke-test-results
path: |
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
if: github.repository == 'Kilo-Org/kilocode'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6 # kilocode_change
- uses: oven-sh/setup-bun@v2
+1 -1
View File
@@ -27,7 +27,7 @@ jobs:
shell: bash
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6 # kilocode_change
with:
token: ${{ secrets.GITHUB_TOKEN }}
+6 -87
View File
@@ -28,21 +28,21 @@ jobs:
matrix:
settings:
- name: linux
host: blacksmith-8vcpu-ubuntu-2404
host: blacksmith-4vcpu-ubuntu-2404 # kilocode_change
- name: windows
host: blacksmith-8vcpu-windows-2025
host: blacksmith-4vcpu-windows-2025 # kilocode_change
runs-on: ${{ matrix.settings.host }}
defaults:
run:
shell: bash
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6 # kilocode_change
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6 # kilocode_change
with:
node-version: "24"
@@ -55,7 +55,7 @@ jobs:
git config --global user.name "kilo-maintainer[bot]"
- name: Cache Turbo
uses: actions/cache@v4
uses: actions/cache@v5 # kilocode_change
with:
path: node_modules/.cache/turbo
key: turbo-${{ runner.os }}-${{ hashFiles('turbo.json', '**/package.json') }}-${{ github.sha }}
@@ -80,7 +80,7 @@ jobs:
- name: Upload unit artifacts
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7 # kilocode_change
with:
name: unit-${{ matrix.settings.name }}-${{ github.run_attempt }}
include-hidden-files: true
@@ -88,87 +88,6 @@ jobs:
retention-days: 7
path: packages/*/.artifacts/unit/junit.xml
e2e:
# kilocode_change - disabled: packages/app is not actively maintained
if: false
name: e2e (${{ matrix.settings.name }})
strategy:
fail-fast: false
matrix:
settings:
- name: linux
host: blacksmith-4vcpu-ubuntu-2404
- name: windows
host: blacksmith-4vcpu-windows-2025
runs-on: ${{ matrix.settings.host }}
env:
PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/.playwright-browsers
defaults:
run:
shell: bash
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "24"
- name: Setup Bun
uses: ./.github/actions/setup-bun
- name: Read Playwright version
id: playwright-version
run: |
version=$(node -e 'console.log(require("./package.json").workspaces.catalog["@playwright/test"])')
echo "version=$version" >> "$GITHUB_OUTPUT"
- name: Cache Playwright browsers
id: playwright-cache
uses: actions/cache@v4
with:
path: ${{ github.workspace }}/.playwright-browsers
key: ${{ runner.os }}-${{ runner.arch }}-playwright-${{ steps.playwright-version.outputs.version }}-chromium
- name: Install Playwright system dependencies
if: runner.os == 'Linux'
working-directory: packages/app
run: bunx playwright install-deps chromium
- name: Install Playwright browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
working-directory: packages/app
run: bunx playwright install chromium
- name: Run app e2e tests
run: bun --cwd packages/app test:e2e:local
env:
CI: true
# kilocode_change start
KILO_API_KEY: ${{ secrets.KILO_API_KEY }}
KILO_ORG_ID: ${{ secrets.KILO_ORG_ID }}
KILO_DISABLE_SHARE: "true"
KILO_DISABLE_SESSION_INGEST: "true"
KILO_E2E_REQUIRE_PAID: "true"
# kilocode_change end
PLAYWRIGHT_JUNIT_OUTPUT: e2e/junit-${{ matrix.settings.name }}.xml
timeout-minutes: 30
- name: Upload Playwright artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-${{ matrix.settings.name }}-${{ github.run_attempt }}
if-no-files-found: ignore
retention-days: 7
path: |
packages/app/e2e/junit-*.xml
packages/app/e2e/test-results
packages/app/e2e/playwright-report
required:
name: test (linux)
runs-on: blacksmith-4vcpu-ubuntu-2404
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6 # kilocode_change
with:
fetch-depth: 1
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6 # kilocode_change
- name: Setup Bun
uses: ./.github/actions/setup-bun
+13 -13
View File
@@ -12,7 +12,7 @@ jobs:
matched: ${{ steps.filter.outputs.matched }}
is_fork: ${{ steps.fork-check.outputs.is_fork }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6 # kilocode_change
- uses: Kilo-Org/paths-filter@master
id: filter
with:
@@ -47,7 +47,7 @@ jobs:
steps:
- name: Checkout (internal)
if: needs.check-paths.outputs.is_fork != 'true'
uses: actions/checkout@v4
uses: actions/checkout@v6 # kilocode_change
with:
lfs: true
token: ${{ secrets.BOT_PAT }}
@@ -55,7 +55,7 @@ jobs:
- name: Checkout (fork)
if: needs.check-paths.outputs.is_fork == 'true'
uses: actions/checkout@v4
uses: actions/checkout@v6 # kilocode_change
with:
lfs: true
@@ -77,7 +77,7 @@ jobs:
bun-version: latest
- name: Cache Bun modules
uses: actions/cache@v4
uses: actions/cache@v5 # kilocode_change
with:
path: ~/.bun/install/cache
key: bun-${{ hashFiles('bun.lock') }}
@@ -87,7 +87,7 @@ jobs:
- name: Cache Playwright browsers
id: playwright-cache
uses: actions/cache@v4
uses: actions/cache@v5 # kilocode_change
with:
path: ~/.cache/ms-playwright
key: playwright-${{ hashFiles('packages/kilo-ui/package.json') }}
@@ -103,7 +103,7 @@ jobs:
- name: Cache Storybook build
id: storybook-cache
uses: actions/cache@v4
uses: actions/cache@v5 # kilocode_change
with:
path: packages/kilo-ui/storybook-static
key: storybook-${{ hashFiles('packages/kilo-ui/src/**', 'packages/kilo-ui/.storybook/**', 'packages/ui/src/**', 'packages/kilo-ui/package.json') }}
@@ -195,7 +195,7 @@ jobs:
- name: Upload test results on failure
if: failure()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7 # kilocode_change
with:
name: visual-regression-results
path: packages/kilo-ui/test-results/
@@ -211,7 +211,7 @@ jobs:
steps:
- name: Checkout (internal)
if: needs.check-paths.outputs.is_fork != 'true'
uses: actions/checkout@v4
uses: actions/checkout@v6 # kilocode_change
with:
lfs: true
token: ${{ secrets.BOT_PAT }}
@@ -219,7 +219,7 @@ jobs:
- name: Checkout (fork)
if: needs.check-paths.outputs.is_fork == 'true'
uses: actions/checkout@v4
uses: actions/checkout@v6 # kilocode_change
with:
lfs: true
@@ -241,7 +241,7 @@ jobs:
bun-version: latest
- name: Cache Bun modules
uses: actions/cache@v4
uses: actions/cache@v5 # kilocode_change
with:
path: ~/.bun/install/cache
key: bun-${{ hashFiles('bun.lock') }}
@@ -251,7 +251,7 @@ jobs:
- name: Cache Playwright browsers
id: playwright-cache-vscode
uses: actions/cache@v4
uses: actions/cache@v5 # kilocode_change
with:
path: ~/.cache/ms-playwright
key: playwright-vscode-${{ hashFiles('packages/kilo-vscode/package.json') }}
@@ -267,7 +267,7 @@ jobs:
- name: Cache Storybook build
id: storybook-cache-vscode
uses: actions/cache@v4
uses: actions/cache@v5 # kilocode_change
with:
path: packages/kilo-vscode/storybook-static
key: storybook-vscode-${{ hashFiles('packages/kilo-vscode/webview-ui/src/**', 'packages/kilo-vscode/.storybook/**', 'packages/kilo-ui/src/**', 'packages/ui/src/**', 'packages/kilo-vscode/package.json') }}
@@ -386,7 +386,7 @@ jobs:
- name: Upload test results on failure
if: failure()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7 # kilocode_change
with:
name: visual-regression-vscode-results
path: packages/kilo-vscode/test-results/
@@ -1,13 +1,13 @@
on:
schedule:
- cron: "7,37 * * * *" # every 30 min at :07 and :37
- cron: "7,37 * * * *" # every 30 min at :07 and :37
workflow_dispatch:
concurrency:
group: opencode-release-watch
cancel-in-progress: false
permissions:
permissions:
contents: read
jobs:
@@ -66,4 +66,3 @@ jobs:
with:
path: .cache/last-seen
key: last-seen-${{ steps.fetch.outputs.tag }}
+263
View File
@@ -0,0 +1,263 @@
---
description: Resolve upstream merge conflicts
---
Resolve the manual part of an upstream merge.
Arguments: `$ARGUMENTS`
Use the first argument as the upstream version, for example `v1.1.50` or
`1.1.50`. If no argument is provided, infer the version from the current branch
name, `upstream-merge-report-<version>.md`, or the newest relevant report file.
## Workflow
### 1. Inspect the current merge state
- `git status --short`
- `git diff --name-only --diff-filter=U`
- `upstream-merge-report-<version>.md` when present
- `.worktrees/opencode-merge/auto-merge` for the automated merge snapshot when present
### 2. Read every conflicted file end-to-end before planning
Use `script/upstream/find-conflict-markers.sh <file>` to jump to each region,
then read enough surrounding lines to understand the code — not just the
conflict hunk. Specifically check:
- is this a plain 3-way on a single expression, or a structural refactor?
- does upstream rename/move something that invalidates a HEAD-only declaration?
- does a `kilocode_change` marker in HEAD encode a bug fix, a feature, or a
defensive check?
- is the conflicted block referenced by *non-conflicted* code elsewhere in the
same file (imports, signatures, call sites) that will break if we drop it?
When HEAD includes a non-obvious Kilo-specific wrapper (e.g. a helper in
`packages/opencode/src/kilocode/`), find out why it exists before deciding to
keep or bypass it:
```bash
git log --all --oneline -S "<symbol>" -- packages/opencode/src/kilocode/
git log --all --oneline -- <kilo-file>
```
Look at the commit message and any PR reference. "We wrote our own because of
PR #NNNN" is a real constraint; "we wrote our own because of a typo" is not.
### 3. Write a plan in chat and get approval
For every conflicted file (and any adjacent file the resolution forces you to
touch — see §6) include:
- expected resolution kind: `hybrid`, `take-ours`, `take-theirs`, `regenerated`,
`removed`, `renamed`, or `other`
- risk level: `low`, `medium`, or `high`
- one-sentence rationale (what Kilo behaviour is preserved, what upstream
feature is adopted, what is dropped)
- verification commands you expect to run (targeted tests, typecheck)
Group files by risk level. Ask the user which batch to start with. You can
resolve an entire `low` batch in one pass if the user approves the batch, but
resolve `medium` and `high` files one at a time.
**Do not resolve a file until the user has approved that file's (or batch's)
strategy.**
### 4. Before every edit, explain reasoning before showing the diff
The user needs to review intent, not just the raw change. For each file, in
order:
1. Show the conflict's surrounding context (10–30 lines around each conflict
region, in chat).
2. Explain what each of the three sides (HEAD, merge-base, upstream) is doing.
3. State which Kilo behaviour must survive and why (reference PR numbers /
`kilocode_change` comments when possible).
4. State the resolution and why it is better than the alternatives.
5. Then apply the edit. The tool will display the diff — the user only has to
verify the diff matches the reasoning.
Do not lead with the diff. A diff without reasoning forces the user to
reverse-engineer the decision.
### 5. Apply resolution rules
Reference worktrees when present:
- `.worktrees/opencode-merge/opencode` — pristine upstream tree
- `.worktrees/opencode-merge/kilo-main` — Kilo base snapshot
- `.worktrees/opencode-merge/auto-merge` — automated merge snapshot (original
conflict reference)
Apply in order:
- prefer upstream code and architecture whenever compatible with Kilo behaviour
- preserve Kilo-specific behaviour marked with `kilocode_change`
- keep `kilocode_change` markers around Kilo-specific code in shared opencode
files
- when upstream refactors a region that HEAD had annotated with
`kilocode_change`, **check whether the marker encodes a bug fix or a feature
delta**. Bug fixes (missing `await`, defensive null-check, error capture)
usually need to be re-applied on top of the upstream refactor. Example from
v1.14.30: `Workspace.isSyncing` was missing an `await` — upstream's Effect
refactor reintroduced the same bug, so we had to port the fix into the new
`Effect.gen` block.
- when a `take-theirs` drops a line that was the target of a Kilo pre-filter,
the upstream line may be actively wrong for Kilo — e.g. an inner `continue`
filter whose condition collides with an outer filter Kilo added. Re-read the
surrounding 20 lines before committing to `take-theirs`.
- if Kilo-specific code must be refactored to fit new upstream architecture,
explain the refactor in the final summary
- if upstream moved the relevant logic to another file, port the Kilo behaviour
there and list both paths in the final summary. Verify the new file already
carries the Kilo-renamed symbols (e.g. `x-kilo-directory`) by diffing against
pristine upstream.
- if upstream deleted a file, analyse whether the Kilo behaviour should be
ported elsewhere or removed rather than restoring the deleted file
- if tests fail only because upstream intentionally removed behaviour, remove
or update the obsolete tests rather than adding the old file back
- do not modify unrelated files
When removing code that existed in one side of a conflict, prefer
**commenting it out with `kilocode_change` markers** over deletion when the
surrounding structure (an `if`, a loop) still makes sense. That keeps the
intent visible to the next merger. Example:
```ts
} else if (input?.scope !== "project" && !Flag.KILO_EXPERIMENTAL_WORKSPACES) {
// kilocode_change start - directory filtering handled by KiloSession.filters above
// if (input?.directory) {
// conditions.push(eq(SessionTable.directory, input.directory))
// }
// kilocode_change end
}
```
Use `TODO:` not `NOTE:` for follow-ups. `TODO` is searchable and implies an
owner will act on it; `NOTE` reads as permanent commentary.
### 6. Look for adjacent files the conflict forces you to touch
Upstream restructures sometimes split one file into several (e.g. `permission.ts`
→ `groups/permission.ts` + `handlers/permission.ts`). Only the *renamed* file
shows up in `git diff --diff-filter=U`; the new sibling may need a Kilo feature
ported in too. After resolving the flagged file, check:
- files that import from the resolved file — do they compile?
- files at paths implied by new imports (e.g. `../middleware/*`, `./handlers/*`)
- `kilocode_change` comments in the *auto-merge* snapshot that didn't end up in
the working tree because the hosting file was renamed
Add any such files to the plan as `hybrid` or `take-ours` with the same
approval flow.
### 7. Verify each resolution before moving on
- confirm `script/upstream/find-conflict-markers.sh <file>` prints nothing
- read the final file region (the new shape after edit) and sanity-check imports
- for apparently-unused symbols upstream introduced, `grep` the file and the
rest of the package before deleting — they may be called from non-conflicted
code elsewhere. Example: `isTheme` in `theme.tsx` looked unused at the
resolution site but was called twice further down.
- run the smallest relevant check (single `bun test` file, or `bun run
typecheck` in the touched package)
- summarise the exact resolution, tradeoff, and verification result in chat
- ask the user to approve the resolved file before staging it or resolving the
next one (for `medium` / `high`; `low` batches can be staged together)
### 8. Run the full checks once everything is resolved
- `git diff --name-only --diff-filter=U` returns empty
- `bun run typecheck` from `packages/opencode/` (targeted) and from repo root
(catches non-conflicted call-site breakage)
- relevant targeted tests. Tests that hang or time out in an unrelated part of
the graph may be pre-existing — note them, don't block the merge on them
- `bun run script/check-opencode-annotations.ts` if `packages/opencode/` shared
files changed. Note that this tool compares against the merge base via `HEAD`
and will be silent until the merge commit lands
- other CI guards that touched files imply (knip for `kilo-vscode/`,
`check-kilocode-change`, source-links, visual regression)
### 9. Commit with the standard message
Per `script/upstream/README.md`:
```bash
git commit -m "resolve merge conflicts"
```
The default `git merge` auto-message (`Merge branch '…' into …`) is also fine,
but `resolve merge conflicts` is the convention for these PRs.
### 10. Resync version strings in a separate commit
Upstream stamps its own version into shared files — notably
`packages/extensions/zed/extension.toml` (version field + 5 Kilo-Org download
URLs), and any `package.json` that upstream bumped in the same release window.
After the merge this leaves parts of the tree pointing at upstream's version
(e.g. `1.14.30`), whose release tag does not exist on Kilo's pipeline, so the
Zed download URLs silently 404.
Fix this in a dedicated commit *after* `resolve merge conflicts`:
```bash
bun run script/sync-versions.ts # uses root package.json version
# or, to target an explicit version:
bun run script/sync-versions.ts 7.2.41
git add -A
git commit -m "chore: resync versions after upstream merge"
```
The script rewrites every top-level `"version"` in `package.json` files
(excluding `node_modules`, hidden dirs, and `packages/kilo-jetbrains/` which
tracks its own cadence), plus the Zed extension toml. It is idempotent — rerun
it any time to rebase the version back onto Kilo main (useful during
long-running upstream merges where `main` releases in the meantime).
Keeping this in its own commit makes reviewers' job easier: the merge commit
only contains behavioural resolutions, and the version resync is a trivial
diff they can skim in one glance.
### 11. Write the PR body
Structure the description so reviewers can skim:
- **Non-trivial merge decisions**: a short section per file (or group of
related files) that required more than a mechanical `take-ours`/`take-theirs`.
Focus on *what Kilo behaviour survived* and *what upstream features were
adopted*. Link to Kilo PRs when a `kilocode_change` encodes a specific fix.
- **Notable auto-merged changes**: new columns, new helper files, renamed
middleware — anything reviewers should eyeball even though git didn't flag
it.
- **What to test**: explicit, scenario-level test steps for each non-trivial
change. Don't list tests; list *user-visible behaviour* so a tester who
doesn't read the diff can exercise it.
- **CI guards to watch**: typecheck, knip, annotation check, visual regression.
- **Follow-ups**: any `TODO:` you left in code, as a bullet list with links.
## User-approval checkpoints
Every manual merge decision requires explicit user approval **before applying**
and **again after verification**. Be especially cautious when a decision is
destructive, changes auth, billing, data deletion, public API compatibility,
config schema behaviour, migrations, provider routing, or security posture.
## Common pitfalls
- Auto-merged code can reference declarations that still live inside conflict
blocks.
- Related sibling files can need edits even when they are not listed as
unmerged — especially after upstream structural splits.
- `renamed` should be used only when behaviour moves to a different file.
- Function signatures can drift across conflict boundaries (args added, return
types widened). Grep for every call site before finalising.
- Full-repo typecheck is the catch-all for non-conflicted call-site breakage.
- Upstream can reintroduce bugs a Kilo `kilocode_change` had already fixed —
during big refactors check every `kilocode_change` the refactor touched.
- "Take-theirs" on an inner conditional is often wrong when Kilo added an outer
pre-filter whose whole point was to widen what makes it to the inner block.
- Apparently-unused upstream-added declarations may be called from
non-conflicted code elsewhere. Grep before deleting.
- Stricter DOM lib types (upstream TS upgrade) can surface latent casting
issues around `WebSocket.send`, `Headers`, etc. — prefer narrowing the Kilo
type over adding `any` casts.
+8 -8
View File
@@ -5,16 +5,16 @@
"packages": {
"": {
"dependencies": {
"@kilocode/plugin": "7.2.21"
"@kilocode/plugin": "7.2.31"
}
},
"node_modules/@kilocode/plugin": {
"version": "7.2.21",
"resolved": "https://registry.npmjs.org/@kilocode/plugin/-/plugin-7.2.21.tgz",
"integrity": "sha512-PUo1Vc7OmOHZAxOdrFsez01e+JAX7jhyGw0caBOuXwAWuT39tGISjrUQoeW5k2/VqkZv3V8fPyvcuto0iuNeDA==",
"version": "7.2.31",
"resolved": "https://registry.npmjs.org/@kilocode/plugin/-/plugin-7.2.31.tgz",
"integrity": "sha512-KmKTTIly7hRlJdXKhqZ/j/brvTPh0z0UTjWSjJWq5fqf4pATgYGn7G0g3ZjILnN7MUkkZXuljgqExTEeQJHGkQ==",
"license": "MIT",
"dependencies": {
"@kilocode/sdk": "7.2.21",
"@kilocode/sdk": "7.2.31",
"effect": "4.0.0-beta.48",
"zod": "4.1.8"
},
@@ -32,9 +32,9 @@
}
},
"node_modules/@kilocode/sdk": {
"version": "7.2.21",
"resolved": "https://registry.npmjs.org/@kilocode/sdk/-/sdk-7.2.21.tgz",
"integrity": "sha512-iqhUEAEF4ouqXdlm3CuVCCh07FhMpjVT/QfZqDFAis7PO0JTmR76sCAMtqSzv8nogt/CFuR/vgpK79d+xZcM2g==",
"version": "7.2.31",
"resolved": "https://registry.npmjs.org/@kilocode/sdk/-/sdk-7.2.31.tgz",
"integrity": "sha512-Sx05yv+3TIlc6M4Ze+YGgKCLoIg8B0WRE15JXDriVncT+wz7M6+e+4mjNWkwfsuywdeOTYPfeFViqX8iO7ckKA==",
"license": "MIT",
"dependencies": {
"cross-spawn": "7.0.6"
@@ -0,0 +1,119 @@
---
name: kilocode-merge-minimizer
description: Use when changing shared upstream-owned files, editing or reviewing `kilocode_change` markers, or moving additive Kilo-specific behavior into Kilo-owned code to reduce future merge conflicts.
---
# Kilo Merge Minimizer
Use this skill whenever a normal development task touches shared upstream-owned code and includes Kilo-specific behavior, especially for marker cleanup, extraction work, or `kilocode_change` annotations.
Do not use this as the primary guide for upstream merge resolution. Upstream merges have their own instructions and should not duplicate that workflow here.
## Goal
Minimize Kilo's long-term diff against upstream OpenCode while preserving behavior.
Prefer this shape for Kilo-specific additions:
1. Shared upstream file contains only a minimal hook, import, call, registration, or config entry.
2. Kilo-specific behavior lives in Kilo-owned code.
3. Unavoidable shared-file changes have narrow `kilocode_change` markers.
4. The annotation checker passes.
For changes to existing upstream behavior, prefer the smallest in-place shared-file diff with narrow markers. Do not move changed upstream logic into Kilo-owned code just to avoid textual conflicts, because that can create harder semantic merge conflicts.
## Core Rules
- Use `script/check-opencode-annotations.ts` as the source of truth for current shared scopes and exempt paths.
- Use `script/upstream/fix-kilocode-markers.ts` for stale or broad markers, inspecting `--dry-run` output before applying changes.
- Treat upstream-owned files as shared unless the checker or repo ownership rules exempt them.
- Put Kilo-owned UI, CLI, runtime logic, and tests in Kilo-owned paths where practical.
- Avoid adding Kilo business logic directly to shared files.
- Keep shared-file edits as close as possible to upstream shape.
- Do not change shared files unless the change is required for Kilo functionality, fixes a Kilo bug, or is a minimal targeted upstream-quality fix.
- Do not create a large Kilo-only fork for a general upstream-quality improvement. Prefer a minimal targeted fix, or leave the broader change for upstream.
- Do not duplicate upstream logic unless there is a concrete reason. If duplication is unavoidable, isolate the Kilo delta and keep the upstream dependency obvious.
## Shared File Structure
- Do not refactor, rename, split files, or extract helpers in shared files just to improve readability or make Kilo extraction cleaner.
- Avoid structural changes that make upstream behavior harder to compare or hide semantic dependency on upstream code.
## Shared File Style
- Preserve upstream formatting and import style in shared files, even when it differs from Kilo style.
- Put Kilo-only imports on separate marked lines instead of reorganizing upstream imports.
## Decision Rules
Extract Kilo logic when:
- The change is an additive Kilo feature or integration, not a modification of existing upstream behavior.
- The shared-file change has meaningful Kilo-owned behavior, not just a tiny condition, import, registration, or field.
- The code has loops, branching, error handling, async workflows, storage access, network calls, UI rendering, or telemetry.
- The shared file can become a small orchestrator that calls Kilo helpers.
- The Kilo code is independent enough that extraction will not hide future upstream fixes or behavior changes.
Keep the change inline when:
- The Kilo delta is a single field, import, call, simple condition, or small registry entry.
- Extraction would reshape upstream code more than the Kilo change itself.
- The change modifies an upstream algorithm, ordering, heuristic, control flow, or bug fix.
- Extraction would duplicate upstream logic or hide semantic dependency on upstream behavior.
- The Kilo helper closes over upstream-local state. Keep closure-scoped helpers inline and contiguous in one narrow marker block.
- The shared file owns the only route table, enum, schema, switch, or registry where the hook must exist.
- The change restores upstream shape or removes a stale Kilo divergence.
Always preserve upstream behavior order unless the Kilo behavior change is intentional and tested.
## Marker Rules
- Mark only Kilo-specific diff lines in shared upstream files.
- Prefer inline markers for single-line changes: `const value = 42 // kilocode_change`.
- Use block markers only for adjacent Kilo-specific lines:
```ts
// kilocode_change start
registerKiloFeature(app)
// kilocode_change end
```
- Use the file's native comment style, including JSX block comments inside JSX and `#` comments for YAML, TOML, and shell.
- Do not add markers in checker-exempt Kilo-owned paths.
- Remove stale markers when upstream already contains the behavior or when touching Kilo-owned files that still have old markers.
- Use `// kilocode_change - new file` only for unavoidable new Kilo-specific files inside shared upstream paths.
## Tests
- Put Kilo-specific CLI/runtime tests in Kilo-owned test paths.
- Move tests out of shared upstream test paths when the behavior under test is Kilo-specific.
- Tests should cover the real failing path, not private or unstable APIs chosen only for convenience.
- Do not add skip gates for required regression coverage.
## Verification
After editing shared files or marker comments, run:
```bash
bun run script/check-opencode-annotations.ts
```
If the PR uses a non-default comparison base, pass the correct base ref:
```bash
bun run script/check-opencode-annotations.ts --base <base-ref>
```
For stale or broad markers in one shared file, inspect the dry run before applying:
```bash
bun run script/upstream/fix-kilocode-markers.ts <repo-relative-file> --dry-run
```
Before finishing, confirm:
- Shared files contain minimal integration points only.
- Kilo logic and tests live in Kilo-owned paths where practical.
- Markers are narrow.
- Stale markers are removed.
- The annotation checker passed, or the reason it could not run is reported.
+8 -8
View File
@@ -5,16 +5,16 @@
"packages": {
"": {
"dependencies": {
"@kilocode/plugin": "7.2.21"
"@kilocode/plugin": "7.2.31"
}
},
"node_modules/@kilocode/plugin": {
"version": "7.2.21",
"resolved": "https://registry.npmjs.org/@kilocode/plugin/-/plugin-7.2.21.tgz",
"integrity": "sha512-PUo1Vc7OmOHZAxOdrFsez01e+JAX7jhyGw0caBOuXwAWuT39tGISjrUQoeW5k2/VqkZv3V8fPyvcuto0iuNeDA==",
"version": "7.2.31",
"resolved": "https://registry.npmjs.org/@kilocode/plugin/-/plugin-7.2.31.tgz",
"integrity": "sha512-KmKTTIly7hRlJdXKhqZ/j/brvTPh0z0UTjWSjJWq5fqf4pATgYGn7G0g3ZjILnN7MUkkZXuljgqExTEeQJHGkQ==",
"license": "MIT",
"dependencies": {
"@kilocode/sdk": "7.2.21",
"@kilocode/sdk": "7.2.31",
"effect": "4.0.0-beta.48",
"zod": "4.1.8"
},
@@ -32,9 +32,9 @@
}
},
"node_modules/@kilocode/sdk": {
"version": "7.2.21",
"resolved": "https://registry.npmjs.org/@kilocode/sdk/-/sdk-7.2.21.tgz",
"integrity": "sha512-iqhUEAEF4ouqXdlm3CuVCCh07FhMpjVT/QfZqDFAis7PO0JTmR76sCAMtqSzv8nogt/CFuR/vgpK79d+xZcM2g==",
"version": "7.2.31",
"resolved": "https://registry.npmjs.org/@kilocode/sdk/-/sdk-7.2.31.tgz",
"integrity": "sha512-Sx05yv+3TIlc6M4Ze+YGgKCLoIg8B0WRE15JXDriVncT+wz7M6+e+4mjNWkwfsuywdeOTYPfeFViqX8iO7ckKA==",
"license": "MIT",
"dependencies": {
"cross-spawn": "7.0.6"
@@ -21,15 +21,15 @@ The test runner at `tests/visual-regression.spec.ts` is fully automatic — it f
Stories live in `packages/kilo-vscode/webview-ui/src/stories/`. Existing files and their scope:
| File | Components covered |
| --------------------------- | -------------------------------------------------------- |
| `agent-manager.stories.tsx` | FileTree, DiffPanel, FullScreenDiffView, WorktreeItem |
| `chat.stories.tsx` | ChatView, QuestionDock |
| `composite.stories.tsx` | AssistantMessage with tool cards, permissions, questions |
| `prompt-input.stories.tsx` | PromptInput (sidebar prompt bar) |
| `settings.stories.tsx` | Settings panel, ProvidersTab |
| `history.stories.tsx` | SessionList |
| `shared.stories.tsx` | ModelSelector and shared controls |
| File | Components covered |
|---|---|
| `agent-manager.stories.tsx` | FileTree, DiffPanel, FullScreenDiffView, WorktreeItem |
| `chat.stories.tsx` | ChatView, QuestionDock |
| `composite.stories.tsx` | AssistantMessage with tool cards, permissions, questions |
| `prompt-input.stories.tsx` | PromptInput (sidebar prompt bar) |
| `settings.stories.tsx` | Settings panel, ProvidersTab |
| `history.stories.tsx` | SessionList |
| `shared.stories.tsx` | ModelSelector and shared controls |
Add to an existing file if the component fits. Create a new file only for a genuinely new component area.
@@ -227,12 +227,12 @@ The title-slug is derived from the meta `title` (lowercased, slashes become hyph
Example mapping:
| Meta title | Export name | Snapshot path |
| --------------------- | -------------------- | ----------------------------------------------------------- |
| `"Chat"` | `ChatViewIdle` | `chat/chat-view-idle-chromium-linux.png` |
| Meta title | Export name | Snapshot path |
|---|---|---|
| `"Chat"` | `ChatViewIdle` | `chat/chat-view-idle-chromium-linux.png` |
| `"Composite/Webview"` | `GlobWithPermission` | `composite-webview/glob-with-permission-chromium-linux.png` |
| `"Prompt Input"` | `Default200` | `prompt-input/default-200-chromium-linux.png` |
| `"AgentManager"` | `WorktreeItemActive` | `agentmanager/worktree-item-active-chromium-linux.png` |
| `"Prompt Input"` | `Default200` | `prompt-input/default-200-chromium-linux.png` |
| `"AgentManager"` | `WorktreeItemActive` | `agentmanager/worktree-item-active-chromium-linux.png` |
# Reference: Playwright config
-899
View File
@@ -1,899 +0,0 @@
---
description: Translate content for a specified locale while preserving technical terms
mode: subagent
model: kilo/google/gemini-3.1-pro-preview
---
You are a professional translator and localization specialist.
Translate the user's content into the requested target locale (language + region, e.g. fr-FR, de-DE).
Requirements:
- Preserve meaning, intent, tone, and formatting (including Markdown/MDX structure).
- Preserve all technical terms and artifacts exactly: product/company names, API names, identifiers, code, commands/flags, file paths, URLs, versions, error messages, config keys/values, and anything inside inline code or code blocks.
- Also preserve every term listed in the Do-Not-Translate glossary below.
- Also apply locale-specific guidance from `.opencode/glossary/<locale>.md` when available (for example, `zh-cn.md`).
- Do not modify fenced code blocks.
- Output ONLY the translation (no commentary).
If the target locale is missing, ask the user to provide it.
If no locale-specific glossary exists, use the global glossary only.
---
# Locale-Specific Glossaries
When a locale glossary exists, use it to:
- Apply preferred wording for recurring UI/docs terms in that locale
- Preserve locale-specific do-not-translate terms and casing decisions
- Prefer natural phrasing over literal translation when the locale file calls it out
- If the repo uses a locale alias slug, apply that file too (for example, `pt-BR` maps to `br.md` in this repo)
Locale guidance does not override code/command preservation rules or the global Do-Not-Translate glossary below.
---
# Do-Not-Translate Terms (OpenCode Docs)
Generated from: `packages/web/src/content/docs/*.mdx` (default English docs)
Generated on: 2026-02-10
Use this as a translation QA checklist / glossary. Preserve listed terms exactly (spelling, casing, punctuation).
General rules (verbatim, even if not listed below):
- Anything inside inline code (single backticks) or fenced code blocks (triple backticks)
- MDX/JS code in docs: `import ... from "..."`, component tags, identifiers
- CLI commands, flags, config keys/values, file paths, URLs/domains, and env vars
## Proper nouns and product names
Additional (not reliably captured via link text):
```text
Astro
Bun
Chocolatey
Cursor
Docker
Git
GitHub Actions
GitLab CI
GNOME Terminal
Homebrew
Mise
Neovim
Node.js
npm
Obsidian
opencode
opencode-ai
Paru
pnpm
ripgrep
Scoop
SST
Starlight
Visual Studio Code
VS Code
VSCodium
Windsurf
Windows Terminal
Yarn
Zellij
Zed
anomalyco
```
Extracted from link labels in the English docs (review and prune as desired):
```text
@openspoon/subtask2
302.AI console
ACP progress report
Agent Client Protocol
Agent Skills
Agentic
AGENTS.md
AI SDK
Alacritty
Anthropic
Anthropic's Data Policies
Atom One
Avante.nvim
Ayu
Azure AI Foundry
Azure portal
Baseten
built-in GITHUB_TOKEN
Bun.$
Catppuccin
Cerebras console
ChatGPT Plus or Pro
Cloudflare dashboard
CodeCompanion.nvim
CodeNomad
Configuring Adapters: Environment Variables
Context7 MCP server
Cortecs console
Deep Infra dashboard
DeepSeek console
Duo Agent Platform
Everforest
Fireworks AI console
Firmware dashboard
Ghostty
GitLab CLI agents docs
GitLab docs
GitLab User Settings > Access Tokens
Granular Rules (Object Syntax)
Grep by Vercel
Groq console
Gruvbox
Helicone
Helicone documentation
Helicone Header Directory
Helicone's Model Directory
Hugging Face Inference Providers
Hugging Face settings
install WSL
IO.NET console
JetBrains IDE
Kanagawa
Kitty
MiniMax API Console
Models.dev
Moonshot AI console
Nebius Token Factory console
Nord
OAuth
Ollama integration docs
OpenAI's Data Policies
OpenChamber
OpenCode
OpenCode config
OpenCode Config
OpenCode TUI with the opencode theme
OpenCode Web - Active Session
OpenCode Web - New Session
OpenCode Web - See Servers
OpenCode Zen
OpenCode-Obsidian
OpenRouter dashboard
OpenWork
OVHcloud panel
Pro+ subscription
SAP BTP Cockpit
Scaleway Console IAM settings
Scaleway Generative APIs
SDK documentation
Sentry MCP server
shell API
Together AI console
Tokyonight
Unified Billing
Venice AI console
Vercel dashboard
WezTerm
Windows Subsystem for Linux (WSL)
WSL
WSL (Windows Subsystem for Linux)
WSL extension
xAI console
Z.AI API console
Zed
ZenMux dashboard
Zod
```
## Acronyms and initialisms
```text
ACP
AGENTS
AI
AI21
ANSI
API
AST
AWS
BTP
CD
CDN
CI
CLI
CMD
CORS
DEBUG
EKS
ERROR
FAQ
GLM
GNOME
GPT
HTML
HTTP
HTTPS
IAM
ID
IDE
INFO
IO
IP
IRSA
JS
JSON
JSONC
K2
LLM
LM
LSP
M2
MCP
MR
NET
NPM
NTLM
OIDC
OS
PAT
PATH
PHP
PR
PTY
README
RFC
RPC
SAP
SDK
SKILL
SSE
SSO
TS
TTY
TUI
UI
URL
US
UX
VCS
VPC
VPN
VS
WARN
WSL
X11
YAML
```
## Code identifiers used in prose (CamelCase, mixedCase)
```text
apiKey
AppleScript
AssistantMessage
baseURL
BurntSushi
ChatGPT
ClangFormat
CodeCompanion
CodeNomad
DeepSeek
DefaultV2
FileContent
FileDiff
FileNode
fineGrained
FormatterStatus
GitHub
GitLab
iTerm2
JavaScript
JetBrains
macOS
mDNS
MiniMax
NeuralNomadsAI
NickvanDyke
NoeFabris
OpenAI
OpenAPI
OpenChamber
OpenCode
OpenRouter
OpenTUI
OpenWork
ownUserPermissions
PowerShell
ProviderAuthAuthorization
ProviderAuthMethod
ProviderInitError
SessionStatus
TabItem
tokenType
ToolIDs
ToolList
TypeScript
typesUrl
UserMessage
VcsInfo
WebView2
WezTerm
xAI
ZenMux
```
## OpenCode CLI commands (as shown in docs)
```text
opencode
opencode [project]
opencode /path/to/project
opencode acp
opencode agent [command]
opencode agent create
opencode agent list
opencode attach [url]
opencode attach http://10.20.30.40:4096
opencode attach http://localhost:4096
opencode auth [command]
opencode auth list
opencode auth login
opencode auth logout
opencode auth ls
opencode export [sessionID]
opencode github [command]
opencode github install
opencode github run
opencode import <file>
opencode import https://opncd.ai/s/abc123
opencode import session.json
opencode mcp [command]
opencode mcp add
opencode mcp auth [name]
opencode mcp auth list
opencode mcp auth ls
opencode mcp auth my-oauth-server
opencode mcp auth sentry
opencode mcp debug <name>
opencode mcp debug my-oauth-server
opencode mcp list
opencode mcp logout [name]
opencode mcp logout my-oauth-server
opencode mcp ls
opencode models --refresh
opencode models [provider]
opencode models anthropic
opencode run [message..]
opencode run Explain the use of context in Go
opencode serve
opencode serve --cors http://localhost:5173 --cors https://app.example.com
opencode serve --hostname 0.0.0.0 --port 4096
opencode serve [--port <number>] [--hostname <string>] [--cors <origin>]
opencode session [command]
opencode session list
opencode session delete <sessionID>
opencode stats
opencode uninstall
opencode upgrade
opencode upgrade [target]
opencode upgrade v0.1.48
opencode web
opencode web --cors https://example.com
opencode web --hostname 0.0.0.0
opencode web --mdns
opencode web --mdns --mdns-domain myproject.local
opencode web --port 4096
opencode web --port 4096 --hostname 0.0.0.0
opencode.server.close()
```
## Slash commands and routes
```text
/agent
/auth/:id
/clear
/command
/config
/config/providers
/connect
/continue
/doc
/editor
/event
/experimental/tool?provider=<p>&model=<m>
/experimental/tool/ids
/export
/file?path=<path>
/file/content?path=<p>
/file/status
/find?pattern=<pat>
/find/file
/find/file?query=<q>
/find/symbol?query=<q>
/formatter
/global/event
/global/health
/help
/init
/instance/dispose
/log
/lsp
/mcp
/mnt/
/mnt/c/
/mnt/d/
/models
/oc
/opencode
/path
/project
/project/current
/provider
/provider/{id}/oauth/authorize
/provider/{id}/oauth/callback
/provider/auth
/q
/quit
/redo
/resume
/session
/session/:id
/session/:id/abort
/session/:id/children
/session/:id/command
/session/:id/diff
/session/:id/fork
/session/:id/init
/session/:id/message
/session/:id/message/:messageID
/session/:id/permissions/:permissionID
/session/:id/prompt_async
/session/:id/revert
/session/:id/share
/session/:id/shell
/session/:id/summarize
/session/:id/todo
/session/:id/unrevert
/session/status
/share
/summarize
/theme
/tui
/tui/append-prompt
/tui/clear-prompt
/tui/control/next
/tui/control/response
/tui/execute-command
/tui/open-help
/tui/open-models
/tui/open-sessions
/tui/open-themes
/tui/show-toast
/tui/submit-prompt
/undo
/Users/username
/Users/username/projects/*
/vcs
```
## CLI flags and short options
```text
--agent
--attach
--command
--continue
--cors
--cwd
--days
--dir
--dry-run
--event
--file
--force
--fork
--format
--help
--hostname
--hostname 0.0.0.0
--keep-config
--keep-data
--log-level
--max-count
--mdns
--mdns-domain
--method
--model
--models
--port
--print-logs
--project
--prompt
--refresh
--session
--share
--title
--token
--tools
--verbose
--version
--wait
-c
-d
-f
-h
-m
-n
-s
-v
```
## Environment variables
```text
AI_API_URL
AI_FLOW_CONTEXT
AI_FLOW_EVENT
AI_FLOW_INPUT
AICORE_DEPLOYMENT_ID
AICORE_RESOURCE_GROUP
AICORE_SERVICE_KEY
ANTHROPIC_API_KEY
AWS_ACCESS_KEY_ID
AWS_BEARER_TOKEN_BEDROCK
AWS_PROFILE
AWS_REGION
AWS_ROLE_ARN
AWS_SECRET_ACCESS_KEY
AWS_WEB_IDENTITY_TOKEN_FILE
AZURE_COGNITIVE_SERVICES_RESOURCE_NAME
AZURE_RESOURCE_NAME
CI_PROJECT_DIR
CI_SERVER_FQDN
CI_WORKLOAD_REF
CLOUDFLARE_ACCOUNT_ID
CLOUDFLARE_API_TOKEN
CLOUDFLARE_GATEWAY_ID
CONTEXT7_API_KEY
GITHUB_TOKEN
GITLAB_AI_GATEWAY_URL
GITLAB_HOST
GITLAB_INSTANCE_URL
GITLAB_OAUTH_CLIENT_ID
GITLAB_TOKEN
GITLAB_TOKEN_OPENCODE
GOOGLE_APPLICATION_CREDENTIALS
GOOGLE_CLOUD_PROJECT
HTTP_PROXY
HTTPS_PROXY
K2_
MY_API_KEY
MY_ENV_VAR
MY_MCP_CLIENT_ID
MY_MCP_CLIENT_SECRET
NO_PROXY
NODE_ENV
NODE_EXTRA_CA_CERTS
NPM_AUTH_TOKEN
OC_ALLOW_WAYLAND
KILO_API_KEY
KILO_AUTH_JSON
KILO_AUTO_SHARE
KILO_CLIENT
KILO_CONFIG
KILO_CONFIG_CONTENT
KILO_CONFIG_DIR
KILO_DISABLE_AUTOCOMPACT
KILO_DISABLE_AUTOUPDATE
KILO_DISABLE_CLAUDE_CODE
KILO_DISABLE_CLAUDE_CODE_PROMPT
KILO_DISABLE_CLAUDE_CODE_SKILLS
KILO_DISABLE_DEFAULT_PLUGINS
KILO_DISABLE_LSP_DOWNLOAD
KILO_DISABLE_MODELS_FETCH
KILO_DISABLE_PRUNE
KILO_DISABLE_TERMINAL_TITLE
KILO_ENABLE_EXA
KILO_ENABLE_EXPERIMENTAL_MODELS
KILO_EXPERIMENTAL
KILO_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS
KILO_EXPERIMENTAL_DISABLE_COPY_ON_SELECT
KILO_EXPERIMENTAL_DISABLE_FILEWATCHER
KILO_EXPERIMENTAL_EXA
KILO_EXPERIMENTAL_FILEWATCHER
KILO_EXPERIMENTAL_ICON_DISCOVERY
KILO_EXPERIMENTAL_LSP_TOOL
KILO_EXPERIMENTAL_LSP_TY
KILO_EXPERIMENTAL_MARKDOWN
KILO_EXPERIMENTAL_OUTPUT_TOKEN_MAX
KILO_EXPERIMENTAL_OXFMT
KILO_EXPERIMENTAL_PLAN_MODE
KILO_ENABLE_QUESTION_TOOL
KILO_FAKE_VCS
KILO_GIT_BASH_PATH
KILO_MODEL
KILO_MODELS_URL
KILO_PERMISSION
KILO_PORT
KILO_SERVER_PASSWORD
KILO_SERVER_USERNAME
PROJECT_ROOT
RESOURCE_NAME
RUST_LOG
VARIABLE_NAME
VERTEX_LOCATION
XDG_CONFIG_HOME
```
## Package/module identifiers
```text
../../../config.mjs
@astrojs/starlight/components
@opencode-ai/plugin
@opencode-ai/sdk
path
shescape
zod
@
@ai-sdk/anthropic
@ai-sdk/cerebras
@ai-sdk/google
@ai-sdk/openai
@ai-sdk/openai-compatible
@File#L37-42
@modelcontextprotocol/server-everything
@opencode
```
## GitHub owner/repo slugs referenced in docs
```text
24601/opencode-zellij-namer
angristan/opencode-wakatime
anomalyco/opencode
apps/opencode-agent
athal7/opencode-devcontainers
awesome-opencode/awesome-opencode
backnotprop/plannotator
ben-vargas/ai-sdk-provider-opencode-sdk
btriapitsyn/openchamber
BurntSushi/ripgrep
Cluster444/agentic
code-yeongyu/oh-my-opencode
darrenhinde/opencode-agents
different-ai/opencode-scheduler
different-ai/openwork
features/copilot
folke/tokyonight.nvim
franlol/opencode-md-table-formatter
ggml-org/llama.cpp
ghoulr/opencode-websearch-cited.git
H2Shami/opencode-helicone-session
hosenur/portal
jamesmurdza/daytona
jenslys/opencode-gemini-auth
JRedeker/opencode-morph-fast-apply
JRedeker/opencode-shell-strategy
kdcokenny/ocx
kdcokenny/opencode-background-agents
kdcokenny/opencode-notify
kdcokenny/opencode-workspace
kdcokenny/opencode-worktree
login/device
mohak34/opencode-notifier
morhetz/gruvbox
mtymek/opencode-obsidian
NeuralNomadsAI/CodeNomad
nick-vi/opencode-type-inject
NickvanDyke/opencode.nvim
NoeFabris/opencode-antigravity-auth
nordtheme/nord
numman-ali/opencode-openai-codex-auth
olimorris/codecompanion.nvim
panta82/opencode-notificator
rebelot/kanagawa.nvim
remorses/kimaki
sainnhe/everforest
shekohex/opencode-google-antigravity-auth
shekohex/opencode-pty.git
spoons-and-mirrors/subtask2
sudo-tee/opencode.nvim
supermemoryai/opencode-supermemory
Tarquinen/opencode-dynamic-context-pruning
Th3Whit3Wolf/one-nvim
upstash/context7
vtemian/micode
vtemian/octto
yetone/avante.nvim
zenobi-us/opencode-plugin-template
zenobi-us/opencode-skillful
```
## Paths, filenames, globs, and URLs
```text
./.opencode/themes/*.json
./<project-slug>/storage/
./config/#custom-directory
./global/storage/
.agents/skills/*/SKILL.md
.agents/skills/<name>/SKILL.md
.clang-format
.claude
.claude/skills
.claude/skills/*/SKILL.md
.claude/skills/<name>/SKILL.md
.env
.github/workflows/opencode.yml
.gitignore
.gitlab-ci.yml
.ignore
.NET SDK
.npmrc
.ocamlformat
.opencode
.opencode/
.opencode/agents/
.opencode/commands/
.opencode/commands/test.md
.opencode/modes/
.opencode/plans/*.md
.opencode/plugins/
.opencode/skills/<name>/SKILL.md
.opencode/skills/git-release/SKILL.md
.opencode/tools/
.well-known/opencode
{ type: "raw" \| "patch", content: string }
{file:path/to/file}
**/*.js
%USERPROFILE%/intelephense/license.txt
%USERPROFILE%\.cache\opencode
%USERPROFILE%\.config\opencode\opencode.jsonc
%USERPROFILE%\.config\opencode\plugins
%USERPROFILE%\.local\share\opencode
%USERPROFILE%\.local\share\opencode\log
<project-root>/.opencode/themes/*.json
<providerId>/<modelId>
<your-project>/.opencode/plugins/
~
~/...
~/.agents/skills/*/SKILL.md
~/.agents/skills/<name>/SKILL.md
~/.aws/credentials
~/.bashrc
~/.cache/opencode
~/.cache/opencode/node_modules/
~/.claude/CLAUDE.md
~/.claude/skills/
~/.claude/skills/*/SKILL.md
~/.claude/skills/<name>/SKILL.md
~/.config/opencode
~/.config/opencode/AGENTS.md
~/.config/opencode/agents/
~/.config/opencode/commands/
~/.config/opencode/modes/
~/.config/opencode/opencode.json
~/.config/opencode/opencode.jsonc
~/.config/opencode/plugins/
~/.config/opencode/skills/*/SKILL.md
~/.config/opencode/skills/<name>/SKILL.md
~/.config/opencode/themes/*.json
~/.config/opencode/tools/
~/.config/zed/settings.json
~/.local/share
~/.local/share/opencode/
~/.local/share/opencode/auth.json
~/.local/share/opencode/log/
~/.local/share/opencode/mcp-auth.json
~/.local/share/opencode/opencode.jsonc
~/.npmrc
~/.zshrc
~/code/
~/Library/Application Support
~/projects/*
~/projects/personal/
${config.github}/blob/dev/packages/sdk/js/src/gen/types.gen.ts
$HOME/intelephense/license.txt
$HOME/projects/*
$XDG_CONFIG_HOME/opencode/themes/*.json
agent/
agents/
build/
commands/
dist/
http://<wsl-ip>:4096
http://127.0.0.1:8080/callback
http://localhost:<port>
http://localhost:4096
http://localhost:4096/doc
https://app.example.com
https://AZURE_COGNITIVE_SERVICES_RESOURCE_NAME.cognitiveservices.azure.com/
https://opencode.ai/zen/v1/chat/completions
https://opencode.ai/zen/v1/messages
https://opencode.ai/zen/v1/models/gemini-3-flash
https://opencode.ai/zen/v1/models/gemini-3-pro
https://opencode.ai/zen/v1/responses
https://RESOURCE_NAME.openai.azure.com/
laravel/pint
log/
model: "anthropic/claude-sonnet-4-5"
modes/
node_modules/
openai/gpt-4.1
app.kilo.ai/config.json
opencode/<model-id>
opencode/gpt-5.1-codex
opencode/gpt-5.2-codex
opencode/kimi-k2
openrouter/google/gemini-2.5-flash
opncd.ai/s/<share-id>
packages/*/AGENTS.md
plugins/
project/
provider_id/model_id
provider/model
provider/model-id
rm -rf ~/.cache/opencode
skills/
skills/*/SKILL.md
src/**/*.ts
themes/
tools/
```
## Keybind strings
```text
alt+b
Alt+Ctrl+K
alt+d
alt+f
Cmd+Esc
Cmd+Option+K
Cmd+Shift+Esc
Cmd+Shift+G
Cmd+Shift+P
ctrl+a
ctrl+b
ctrl+d
ctrl+e
Ctrl+Esc
ctrl+f
ctrl+g
ctrl+k
Ctrl+Shift+Esc
Ctrl+Shift+P
ctrl+t
ctrl+u
ctrl+w
ctrl+x
DELETE
Shift+Enter
WIN+R
```
## Model ID strings referenced
```text
{env:KILO_MODEL}
anthropic/claude-3-5-sonnet-20241022
anthropic/claude-haiku-4-20250514
anthropic/claude-haiku-4-5
anthropic/claude-sonnet-4-20250514
anthropic/claude-sonnet-4-5
gitlab/duo-chat-haiku-4-5
lmstudio/google/gemma-3n-e4b
openai/gpt-4.1
openai/gpt-5
opencode/gpt-5.1-codex
opencode/gpt-5.2-codex
opencode/kimi-k2
openrouter/google/gemini-2.5-flash
```
+14
View File
@@ -0,0 +1,14 @@
---
description: translate English to other languages
model: opencode/claude-opus-4-7
---
run git diff and translate changed english doc and UI copy files to other international languages. Translate all languages in parallel to save time.
Requirements:
- Preserve meaning, intent, tone, and formatting (including Markdown/MDX structure).
- Preserve all technical terms and artifacts exactly: product/company names, API names, identifiers, code, commands/flags, file paths, URLs, versions, error messages, config keys/values, and anything inside inline code or code blocks.
- Also preserve every term listed in the Do-Not-Translate glossary below.
- Also apply locale-specific guidance from `.opencode/glossary/<locale>.md` when available (for example, `zh-cn.md`).
- Do not modify fenced code blocks.
+19 -10
View File
@@ -1,21 +1,30 @@
---
name: effect
description: Answer questions about the Effect framework
description: Work with Effect v4 / effect-smol TypeScript code in this repo
---
# Effect
This codebase uses Effect, a framework for writing typescript.
This codebase uses Effect for typed, composable TypeScript services, schemas, and workflows.
## How to Answer Effect Questions
## Source Of Truth
1. Clone the Effect repository: `https://github.com/Effect-TS/effect-smol` to
`.opencode/references/effect-smol` in this project NOT the skill folder.
2. Use the explore agent to search the codebase for answers about Effect patterns, APIs, and concepts
3. Provide responses based on the actual Effect source code and documentation
Use the current Effect v4 / effect-smol source, not memory or older Effect v2/v3 examples.
1. If `.opencode/references/effect-smol` is missing, clone `https://github.com/Effect-TS/effect-smol` there. Do this in the project, not in the skill folder.
2. Search `.opencode/references/effect-smol` for exact APIs, examples, tests, and naming patterns before answering or implementing Effect-specific code.
3. Also inspect existing repo code for local house style before introducing new patterns.
4. Prefer answers and implementations backed by specific source files or nearby repo examples.
## Guidelines
- Always use the explore agent with the cloned repository when answering Effect-related questions
- Reference specific files and patterns found in the Effect codebase
- Do not answer from memory - always verify against the source
- Prefer current Effect v4 APIs and project-local patterns over old blog posts, examples, or package-memory guesses.
- Use `Effect.gen(function* () { ... })` for multi-step workflows.
- Use `Effect.fn("Name")` or `Effect.fnUntraced(...)` for named effects when adding reusable service methods or important workflows.
- Prefer Effect `Schema` for API and domain data shapes. Use branded schemas for IDs and `Schema.TaggedErrorClass` for typed domain errors when modeling new error surfaces.
- Keep HTTP handlers thin: decode input, read request context, call services, and map transport errors. Put business rules in services.
- In Effect service code, prefer Effect-aware platform abstractions and dependencies over ad hoc promises where the surrounding code already does so.
- Keep layer composition explicit. Avoid broad hidden provisioning that makes missing dependencies hard to see.
- In tests, prefer the repo's existing Effect test helpers and live tests for filesystem, git, child process, locks, or timing behavior.
- Do not introduce `any`, non-null assertions, unchecked casts, or older Effect APIs just to satisfy types.
- Do not answer from memory. Verify against `.opencode/references/effect-smol` or nearby code first.
+2 -2
View File
@@ -3,8 +3,8 @@ import { tool } from "@kilocode/plugin"
const TEAM = {
desktop: ["adamdotdevin", "iamdavidhill", "Brendonovich", "nexxeln"],
zen: ["fwang", "MrMushrooooom"],
tui: ["thdxr", "kommander", "rekram1-node"],
core: ["thdxr", "rekram1-node", "jlongster"],
tui: ["kommander", "rekram1-node", "simonklee"],
core: ["kitlangton", "rekram1-node", "jlongster"],
docs: ["R44VC0RP"],
windows: ["Hona"],
} as const
+9
View File
@@ -1 +1,10 @@
packages/desktop/src/bindings.ts
# Markdown is excluded because prettier re-pads markdown table cells for column
# alignment, which creates large spurious diffs on any unrelated content change.
# See AGENTS.md "Markdown Tables" and script/check-md-table-padding.ts.
*.md
packages/opencode/src/provider/models-snapshot.ts
packages/opencode/src/provider/models-snapshot.js
packages/opencode/src/provider/models-snapshot.d.ts
+2 -1
View File
@@ -2,6 +2,7 @@
"recommendations": [
"dbaeumer.vscode-eslint",
"connor4312.esbuild-problem-matchers",
"ms-vscode.extension-test-runner"
"ms-vscode.extension-test-runner",
"mkhl.direnv"
]
}
+63 -19
View File
@@ -14,43 +14,63 @@ Kilo CLI is an open source AI coding agent that generates code from natural lang
- **Extension**: `bun run extension` (build + launch VS Code with the extension in dev mode). Pass `--no-build` to skip the build.
- **Typecheck**: `bun turbo typecheck` (uses `tsgo`, not `tsc`)
- **Test**: `bun test` from `packages/opencode/` (NOT from root -- root blocks tests)
- **Single test**: `bun test test/tool/tool.test.ts` from `packages/opencode/`
- **Single test**: `bun test ./test/tool/tool-define.test.ts` from `packages/opencode/`
- **CLI build artifact size check**: after `bun run script/build.ts --single --skip-install` in `packages/opencode/`, use `du -h dist/*/*/bin/kilo` (scoped package output lives under `dist/@kilocode/`)
- **SDK regen**: After changing server endpoints in `packages/opencode/src/server/`, run `./script/generate.ts` from root to regenerate `packages/sdk/js/`
- **Knip** (unused exports): `bun run knip` from `packages/kilo-vscode/`. CI runs this — all exported types/functions must be imported somewhere. Remove or unexport unused exports before pushing.
- **Source links**: After adding or changing URLs in `packages/kilo-vscode/`, `packages/kilo-vscode/webview-ui/`, or `packages/opencode/src/`, run `bun run script/extract-source-links.ts` from the repo root and commit the updated `packages/kilo-docs/source-links.md`. CI runs this check — the build fails if the file is stale.
- **kilocode_change check**: `bun run check-kilocode-change` from `packages/kilo-vscode/`. CI runs this — `kilocode_change` is a marker for upstream merge conflicts and must not appear in `packages/kilo-vscode/` or `packages/kilo-ui/` (these are entirely Kilo Code additions). Remove the markers before pushing.
- **opencode annotation check**: `bun run script/check-opencode-annotations.ts` from repo root. CI runs this on PRs touching `packages/opencode/` — every Kilo-specific change in shared opencode files must be annotated with `kilocode_change` markers. Exempt paths (no markers needed): `packages/opencode/src/kilocode/`, `packages/opencode/test/kilocode/`, and any path containing `kilocode` in the name.
- **workflow allowlist**: `bun run script/check-workflows.ts` from repo root. CI runs this as part of the annotations workflow — any `.yml` / `.yaml` file added to or removed from `.github/workflows/` must be reflected in the hardcoded list in `script/check-workflows.ts`. Prevents upstream-merged workflows from silently starting to run in our CI.
- **Backend/SDK programmatic testing**: see [TESTING.md](./TESTING.md) for spawning the local main-branch backend (`bun dev serve`) and driving it via `curl` — use this instead of `kilo serve` (prod binary) when testing backend fixes.
## Quality Checks
Before saying an implementation is ready, run the smallest relevant checks that can catch lint, typecheck, and test failures for the touched package. Do not rely on manual extension launch to discover build problems. Fix failures you introduced before the final response, or state exactly which check is still failing or could not be run.
| Area | Checks |
|---|---|
| Root / cross-package | `bun run lint`, `bun run typecheck` |
| CLI | From `packages/opencode/`: `bun run typecheck`, `bun test` or targeted `bun test ./path/to/file.test.ts` |
| VS Code extension | From `packages/kilo-vscode/`: `bun run typecheck`, `bun run lint`, `bun run test:unit` or `bun run test` |
| Extension build/package | From `packages/kilo-vscode/`: `bun run compile` or `bun run package` when touching build, packaging, SDK, or webview integration paths |
| CI-only guards | Run affected guards documented above, such as `bun run knip`, `bun run check-kilocode-change`, `bun run script/check-opencode-annotations.ts`, or source link extraction |
Never run root `bun test`; the root script prints `do not run tests from root` and exits with code 1. Use package-level tests instead.
## Products
All products are clients of the **CLI** (`packages/opencode/`), which contains the AI agent runtime, HTTP server, and session management. Each client spawns or connects to a `kilo serve` process and communicates via HTTP + SSE using `@kilocode/sdk`.
| Product | Package | Description |
| ---------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Kilo CLI | `packages/opencode/` | Core engine. TUI, `kilo run`, `kilo serve`, `kilo web`. Fork of upstream OpenCode. |
| Product | Package | Description |
|---|---|---|
| Kilo CLI | `packages/opencode/` | Core engine. TUI, `kilo run`, `kilo serve`. Fork of upstream OpenCode. |
| Kilo VS Code Extension | `packages/kilo-vscode/` | VS Code extension. Bundles the CLI binary, spawns `kilo serve` as a child process. Includes the **Agent Manager** — a multi-session orchestration panel with git worktree isolation. |
| OpenCode Desktop | `packages/desktop/` | Standalone Tauri native app. Bundles CLI as sidecar. Single-session UI. Unrelated to the VS Code extension. Not actively maintained — synced from upstream fork. |
| OpenCode Web | `packages/app/` | Shared SolidJS frontend used by both the desktop app and `kilo web` CLI command. Not actively maintained — synced from upstream fork. |
**Agent Manager** refers to a feature inside `packages/kilo-vscode/` (extension code in `src/agent-manager/`, webview in `webview-ui/agent-manager/`). It is not a standalone product. See the extension's `AGENTS.md` for details.
Extension-specific settings should live in the Kilo extension settings, not default VS Code settings, unless they are intentionally VS Code-wide.
## Package Instructions
- When a task primarily touches `packages/kilo-jetbrains/`, read `packages/kilo-jetbrains/AGENTS.md` before planning or editing.
- For JetBrains Kotlin/Swing UI work, also apply `packages/kilo-jetbrains/.kilo/skills/jetbrains-ui-style/SKILL.md`.
## Monorepo Structure
Turborepo + Bun workspaces. The packages you'll work with most:
| Package | Name | Purpose |
| -------------------------- | -------------------------- | ------------------------------------------------------------------------------------------ |
| `packages/opencode/` | `@kilocode/cli` | Core CLI -- agents, tools, sessions, server, TUI. This is where most work happens. |
| `packages/sdk/js/` | `@kilocode/sdk` | Auto-generated TypeScript SDK (client for the server API). Do not edit `src/gen/` by hand. |
| `packages/kilo-vscode/` | `kilo-code` | VS Code extension with sidebar chat + Agent Manager. See its own `AGENTS.md` for details. |
| `packages/kilo-gateway/` | `@kilocode/kilo-gateway` | Kilo auth, provider routing, API integration |
| `packages/kilo-telemetry/` | `@kilocode/kilo-telemetry` | PostHog analytics + OpenTelemetry |
| `packages/kilo-i18n/` | `@kilocode/kilo-i18n` | Internationalization / translations |
| `packages/kilo-ui/` | `@kilocode/kilo-ui` | SolidJS component library shared by the extension webview and `packages/app/` |
| `packages/app/` | `@opencode-ai/app` | Shared SolidJS web UI for desktop app and `kilo web` |
| `packages/desktop/` | `@opencode-ai/desktop` | Tauri desktop app shell |
| `packages/util/` | `@opencode-ai/util` | Shared utilities (error, path, retry, slug, etc.) |
| `packages/plugin/` | `@kilocode/plugin` | Plugin/tool interface definitions |
| Package | Name | Purpose |
|---|---|---|
| `packages/opencode/` | `@kilocode/cli` | Core CLI -- agents, tools, sessions, server, TUI. This is where most work happens. |
| `packages/sdk/js/` | `@kilocode/sdk` | Auto-generated TypeScript SDK (client for the server API). Do not edit `src/gen/` by hand. |
| `packages/kilo-vscode/` | `kilo-code` | VS Code extension with sidebar chat + Agent Manager. See its own `AGENTS.md` for details. |
| `packages/kilo-gateway/` | `@kilocode/kilo-gateway` | Kilo auth, provider routing, API integration |
| `packages/kilo-telemetry/` | `@kilocode/kilo-telemetry` | PostHog analytics + OpenTelemetry |
| `packages/kilo-i18n/` | `@kilocode/kilo-i18n` | Internationalization / translations |
| `packages/kilo-ui/` | `@kilocode/kilo-ui` | SolidJS component library shared by the extension webview and docs screenshot stories |
| `packages/util/` | `@opencode-ai/util` | Shared utilities (error, path, retry, slug, etc.) |
| `packages/plugin/` | `@kilocode/plugin` | Plugin/tool interface definitions |
## Style Guide
@@ -167,6 +187,26 @@ const bazFoo = 3
You MUST avoid using `mocks` as much as possible.
Tests MUST test actual implementation, do not duplicate logic into a test.
## Markdown Tables
Do not pad markdown table cells for column alignment. Use the compact form with single-space-padded content cells and a minimal separator row:
```
| Command | What it runs |
|---|---|
| `kilo serve` | The prod CLI on `$PATH`. |
```
Do **not** right-pad cells to line up columns:
```
| Command | What it runs |
| ----------------------------- | ------------------------ |
| `kilo serve` | The prod CLI on `$PATH`. |
```
Padding makes every content change rewrite the entire table, which blows up diffs on untouched rows. Markdown files are excluded from prettier (see `.prettierignore`) so running the formatter won't re-pad them, and `script/check-md-table-padding.ts` enforces the rule in CI. Run `bun run script/check-md-table-padding.ts --fix` to auto-rewrite padded tables.
## Commit Conventions
[Conventional Commits](https://www.conventionalcommits.org/) with scopes matching packages: `vscode`, `cli`, `agent-manager`, `sdk`, `ui`, `i18n`, `kilo-docs`, `gateway`, `telemetry`, `desktop`. Omit scope when spanning multiple packages.
@@ -216,6 +256,10 @@ We regularly merge upstream changes from opencode. To minimize merge conflicts a
The goal is to keep our diff from upstream as small as possible, making regular merges straightforward and reducing the risk of conflicts.
### Git conflict style
`bun install` sets `merge.conflictStyle=zdiff3` repo-locally via `script/setup-git.ts` (wired into `postinstall`). Conflicts include the common ancestor between `|||||||` and `=======`, which is what `script/upstream/` and `mergiraf` rely on for structural resolution and what makes manual resolution on shared opencode files tractable. If you've overridden it in your user config, the repo-local setting takes precedence — don't override it back.
### Kilocode Change Markers
To minimize merge conflicts when syncing with upstream, mark Kilo Code-specific changes in shared code with `kilocode_change` comments.
+81 -9
View File
@@ -16,7 +16,7 @@ The Kilo Community is [on Discord](https://kilo.ai/discord).
## Developing Kilo CLI
- **Requirements:** Bun 1.3.10+
- **Requirements:** Bun 1.3.13+
- Install dependencies and start the dev server from the repo root:
```bash
@@ -48,6 +48,40 @@ To run Kilo CLI in the root of the repo itself:
bun dev .
```
### Running Kilo CLI from any folder
`bin/kilodev` is a self-locating launcher that runs this checkout from wherever you invoke it. Running it with no arguments launches the TUI pointed at the caller's directory; any arguments are forwarded to the CLI unchanged.
One-shot install (recommended). From the repo root:
```bash
./bin/kilodev dev-setup
```
This detects your shell, shows exactly what it will add, asks for confirmation, writes an idempotent block to your rc file, and saves a timestamped backup of the original. Re-running is safe — it only rewrites when the snippet has changed.
Useful flags:
- `--yes` — skip the confirmation prompt (good for CI/containers).
- `--print` — just print the snippet, don't touch any file (pipe-friendly).
- `--dry-run` — show what would change without writing.
- `--shell <zsh|bash|fish|powershell>` — override shell detection.
- `--rc <path>` — override the rc file.
Manual alternatives (equivalent, no CLI invocation needed):
- Unix: add `alias kilodev='/path/to/kilocode/bin/kilodev'` to `~/.zshrc` / `~/.bashrc`, or `fish_add_path /path/to/kilocode/bin`.
- Windows: add `C:\path\to\kilocode\bin` to PATH (System Environment Variables), or add `function kilodev { & "C:\path\to\kilocode\bin\kilodev.cmd" @args }` to `$PROFILE`.
Then from anywhere:
```bash
cd ~/some/project
kilodev # opens TUI with project = ~/some/project
kilodev dev-setup --print # prints the alias line (scripting)
kilodev run --dir "$PWD" "…" # subcommands pass through; use --dir for run/serve
```
### Building a "local" binary
To compile a standalone executable:
@@ -90,26 +124,64 @@ This redirects all gateway traffic (auth, model listing, provider routing, profi
There are also optional overrides for other services:
| Variable | Default | Purpose |
| ------------------------- | -------------------------------- | ----------------------------------------- |
| `KILO_API_URL` | `https://api.kilo.ai` | Kilo API (gateway, auth, models, profile) |
| `KILO_SESSION_INGEST_URL` | `https://ingest.kilosessions.ai` | Session export / cloud sync |
| `KILO_MODELS_URL` | `https://models.dev` | Model metadata |
| Variable | Default | Purpose |
|---|---|---|
| `KILO_API_URL` | `https://api.kilo.ai` | Kilo API (gateway, auth, models, profile) |
| `KILO_SESSION_INGEST_URL` | `https://ingest.kilosessions.ai` | Session export / cloud sync |
| `KILO_MODELS_URL` | `https://models.dev` | Model metadata |
> **VS Code:** The repo includes a "VSCode - Run Extension (Local Backend)" launch config in `.vscode/launch.json` that sets `KILO_API_URL=http://localhost:3000` automatically.
### Pull Request Expectations
## Issue Template Requirements
If you open an issue through the GitHub web UI, GitHub will guide you through the correct template automatically.
If you open an issue through `gh issue create`, the API, or another tool that bypasses the web UI, include the equivalent required fields yourself so the issue still matches the template.
Current required fields by issue type:
- **Bug report:** include a `Description`.
- **Feature request:** prefix the title with `[FEATURE]:`, include confirmation that the feature has not already been suggested, and add a description of the enhancement.
- **Question:** include the `Question`.
Recommended fields for bug reports, even when not strictly required by the template:
- Plugins
- Kilo version
- Steps to reproduce
- Screenshot and/or share link
- Operating System
- Terminal
## Pull Request Expectations
- **Issue First Policy:** All PRs must reference an existing issue.
- **UI Changes:** Include screenshots or videos (before/after).
- **Logic Changes:** Explain how you verified it works.
- **PR Titles:** Follow conventional commit standards (`feat:`, `fix:`, `docs:`, etc.).
### Issue and PR Lifecycle
## Issue First Policy
All pull requests must reference an existing issue.
This helps reviewers understand the problem statement, discussion, and intended scope before reviewing the code change.
## PR Titles
Use conventional commit style PR titles such as:
- `feat: add MCP settings tab`
- `fix: correct Windows path handling`
- `docs: clarify issue template requirements`
- `chore: bump TypeScript to 5.8`
- `refactor: extract diff renderer into a hook`
- `test: cover ServerManager orphan cleanup`
## Issue and PR Lifecycle
To keep our backlog manageable, we automatically close inactive issues and PRs after a period of inactivity. This isn't a judgment on quality — older items tend to lose context over time and we'd rather start fresh if they're still relevant. Feel free to reopen or create a new issue/PR if you're still working on something!
### Style Preferences
## Style Preferences
- **Functions:** Keep logic within a single function unless breaking it out adds clear reuse.
- **Destructuring:** Avoid unnecessary destructuring.
+11 -11
View File
@@ -63,7 +63,7 @@ Downloads all build artifacts and publishes to every distribution channel:
- Publishes platform-specific binary packages to **npm** (e.g. `@kilocode/cli-linux-x64`, `@kilocode/cli-darwin-arm64`, etc.).
- Publishes the main `@kilocode/cli` package to **npm** with optional dependencies on the binary packages.
- Builds and pushes a multi-arch **Docker image** (`ghcr.io/kilo-org/kilo`) to GitHub Container Registry (linux/amd64 + linux/arm64).
- Builds and pushes a multi-arch **Docker image** (`ghcr.io/kilo-org/kilocode`) to GitHub Container Registry (linux/amd64 + linux/arm64).
#### SDK (`@kilocode/sdk`)
@@ -102,16 +102,16 @@ The workflow requires these GitHub token permissions:
The following secrets must be configured in the repository:
| Secret | Purpose |
| ---------------------------- | ---------------------------------------------------------------- |
| `KILO_API_KEY` | Kilo API key used during version computation |
| `KILO_ORG_ID` | Kilo organization ID |
| `KILO_MAINTAINER_APP_ID` | GitHub App ID for the kilo-maintainer bot (used for git commits) |
| `KILO_MAINTAINER_APP_SECRET` | GitHub App secret for the kilo-maintainer bot |
| `NPM_TOKEN` | npm authentication token for publishing packages |
| `VSCE_TOKEN` | VS Code Marketplace personal access token |
| `OVSX_TOKEN` | Open VSX Registry token (currently unused but configured) |
| `AUR_KEY` | SSH private key for pushing to the AUR |
| Secret | Purpose |
|---|---|
| `KILO_API_KEY` | Kilo API key used during version computation |
| `KILO_ORG_ID` | Kilo organization ID |
| `KILO_MAINTAINER_APP_ID` | GitHub App ID for the kilo-maintainer bot (used for git commits) |
| `KILO_MAINTAINER_APP_SECRET` | GitHub App secret for the kilo-maintainer bot |
| `NPM_TOKEN` | npm authentication token for publishing packages |
| `VSCE_TOKEN` | VS Code Marketplace personal access token |
| `OVSX_TOKEN` | Open VSX Registry token (currently unused but configured) |
| `AUR_KEY` | SSH private key for pushing to the AUR |
### Concurrency
+7 -7
View File
@@ -24,13 +24,13 @@ Server mode is opt-in only. When enabled, set `KILO_SERVER_PASSWORD` to require
### Out of Scope
| Category | Rationale |
| ------------------------------- | ----------------------------------------------------------------------- |
| **Server access when opted-in** | If you enable server mode, API access is expected behavior |
| **Sandbox escapes** | The permission system is not a sandbox (see above) |
| **LLM provider data handling** | Data sent to your configured LLM provider is governed by their policies |
| **MCP server behavior** | External MCP servers you configure are outside our trust boundary |
| **Malicious config files** | Users control their own config; modifying it is not an attack vector |
| Category | Rationale |
|---|---|
| **Server access when opted-in** | If you enable server mode, API access is expected behavior |
| **Sandbox escapes** | The permission system is not a sandbox (see above) |
| **LLM provider data handling** | Data sent to your configured LLM provider is governed by their policies |
| **MCP server behavior** | External MCP servers you configure are outside our trust boundary |
| **Malicious config files** | Users control their own config; modifying it is not an attack vector |
---
+259
View File
@@ -0,0 +1,259 @@
# TESTING.md
How to spin up the **local main-branch** Kilo backend and test it with `curl` / `fetch`. Aimed at a running Kilo CLI agent iterating on backend fixes without rebuilding the VS Code extension or TUI.
All examples use plain shell + `curl`. Writing TypeScript files is a last resort (see Section 8).
## TL;DR
```bash
# From repo root. Starts the LOCAL main-branch backend in the background.
PASS=$(openssl rand -hex 16)
KILO_SERVER_PASSWORD="$PASS" bun dev serve --port 0 >/tmp/kilo-serve.log 2>&1 &
echo $! >/tmp/kilo-serve.pid
while ! grep -q "kilo server listening" /tmp/kilo-serve.log 2>/dev/null; do sleep 0.1; done
PORT=$(grep -oE "listening on http://[^:]+:[0-9]+" /tmp/kilo-serve.log | grep -oE "[0-9]+$")
AUTH="Authorization: Basic $(printf 'kilo:%s' "$PASS" | base64 | tr -d '\n')"
BASE="http://127.0.0.1:$PORT"
# Call any endpoint
curl -sS -H "$AUTH" -H "x-kilo-directory: $PWD" "$BASE/global/health"
# Stop when done
kill "$(cat /tmp/kilo-serve.pid)" 2>/dev/null || true
rm -f /tmp/kilo-serve.pid /tmp/kilo-serve.log
```
## 1. What this doc is for
Testing local backend fixes against a real running server, talking to it over HTTP the same way the VS Code extension, TUI, and `kilo run --attach` do — but without any of those clients. Every request is a `curl` the agent can copy-paste.
For in-process tests (no socket, fastest loop) see `packages/opencode/test/kilocode/server/permission-allow-everything.test.ts` for the `Server.Default().app.request(...)` pattern. That's the right tool inside the `packages/opencode/` test suite; this doc is for out-of-process HTTP testing.
## 2. `kilo serve` vs `bun dev serve` — important
| Command | What it runs |
|---|---|
| `kilo serve` | The npm-installed production CLI on `$PATH`. **Not the code in this repo.** |
| `bun dev serve …` (repo root) | The local main-branch backend from this worktree. **This is what you want.** |
| `bun run --cwd packages/opencode --conditions=browser src/index.ts serve …` | Same as `bun dev serve`, fully expanded. |
Root `package.json` defines `"dev"` as the full `bun run --cwd packages/opencode --conditions=browser src/index.ts` invocation, so `bun dev <args>` forwards `<args>` to the local CLI entry point (`packages/opencode/src/index.ts`) without touching the installed binary.
`bun dev` imports the source directly — no rebuild is needed between code edits. Just kill the running server and relaunch.
Do **not** use `createKiloServer()` from `@kilocode/sdk/v2` to test local code: it spawns the PATH `kilo` binary (`packages/sdk/js/src/v2/server.ts:38-136`), which is the wrong tool here.
## 3. Starting the backend (background)
### Random port (recommended)
```bash
KILO_SERVER_PASSWORD=$(openssl rand -hex 16) \
bun dev serve --port 0 >/tmp/kilo-serve.log 2>&1 &
echo $! >/tmp/kilo-serve.pid
while ! grep -q "kilo server listening" /tmp/kilo-serve.log; do sleep 0.1; done
PORT=$(grep -oE "listening on http://[^:]+:[0-9]+" /tmp/kilo-serve.log | grep -oE "[0-9]+$")
```
### Fixed port (if you need a stable URL)
```bash
KILO_SERVER_PASSWORD=secret \
bun dev serve --port 4096 --hostname 127.0.0.1 \
>/tmp/kilo-serve.log 2>&1 &
echo $! >/tmp/kilo-serve.pid
while ! grep -q "kilo server listening" /tmp/kilo-serve.log; do sleep 0.1; done
PORT=4096
```
### No-auth quickstart (fastest)
Omit `KILO_SERVER_PASSWORD` entirely. The server prints `Warning: KILO_SERVER_PASSWORD is not set; server is unsecured.` and the auth middleware is bypassed — fine for throwaway local testing, never for anything else.
```bash
bun dev serve --port 0 >/tmp/kilo-serve.log 2>&1 &
echo $! >/tmp/kilo-serve.pid
while ! grep -q "kilo server listening" /tmp/kilo-serve.log; do sleep 0.1; done
PORT=$(grep -oE "listening on http://[^:]+:[0-9]+" /tmp/kilo-serve.log | grep -oE "[0-9]+$")
BASE="http://127.0.0.1:$PORT"
# no AUTH var needed
```
### Flags (`packages/opencode/src/cli/network.ts`)
| Flag | Default | Notes |
|---|---|---|
| `--port` | `0` (OS-assigned) | Must be passed literally when overriding `opencode.json`'s `server.port`. |
| `--hostname` | `127.0.0.1` | Becomes `0.0.0.0` when `--mdns` is set without an override. |
| `--mdns` | `false` | Publishes an mDNS SRV record. |
| `--mdns-domain` | `kilo.local` | |
| `--cors` | `[]` | Extra allowed origins. |
## 4. The two mandatory request knobs
### Auth header (only if `KILO_SERVER_PASSWORD` was set)
```bash
AUTH="Authorization: Basic $(printf 'kilo:%s' "$KILO_SERVER_PASSWORD" | base64 | tr -d '\n')"
```
The username is literally `kilo` (same as the VS Code extension). Skip this whole block if you launched without a password.
### Directory header on every call
```bash
DIR_HEADER="x-kilo-directory: $PWD"
```
`InstanceMiddleware` uses this to scope the request to a project. For `GET` / `HEAD`, pass `?directory=<urlencoded>` in the URL instead — that's what the SDK does internally.
## 5. Common `curl` recipes
All of the below assume `BASE`, `AUTH`, `DIR_HEADER` are set. Drop `-H "$AUTH"` if you're running without a password.
### Health check (no auth, no directory)
```bash
curl -sS "$BASE/global/health"
```
### Full endpoint list (OpenAPI spec)
```bash
curl -sS "$BASE/doc" | jq .
```
This is the source of truth — anything not in this doc is discoverable from `/doc` without reading source.
### Create a session
```bash
SID=$(curl -sS -X POST "$BASE/session" \
-H "$AUTH" -H "$DIR_HEADER" -H "Content-Type: application/json" \
-d '{}' | jq -r .id)
echo "$SID"
```
### List sessions (GET — directory goes in the query)
```bash
curl -sS -H "$AUTH" \
"$BASE/session?directory=$(printf %s "$PWD" | jq -sRr @uri)"
```
### Send a message (fire-and-forget)
```bash
curl -sS -X POST "$BASE/session/$SID/prompt_async" \
-H "$AUTH" -H "$DIR_HEADER" -H "Content-Type: application/json" \
-d '{"parts":[{"type":"text","text":"hello"}]}'
```
### Read messages for a session
```bash
curl -sS -H "$AUTH" \
"$BASE/session/$SID/message?directory=$(printf %s "$PWD" | jq -sRr @uri)"
```
### Abort an in-flight prompt
```bash
curl -sS -X POST -H "$AUTH" -H "$DIR_HEADER" "$BASE/session/$SID/abort"
```
### Get the resolved config (verify a config change took effect)
```bash
curl -sS -H "$AUTH" \
"$BASE/config?directory=$(printf %s "$PWD" | jq -sRr @uri)"
```
### Stream global events (SSE)
```bash
curl -N -sS -H "$AUTH" \
"$BASE/global/event?directory=$(printf %s "$PWD" | jq -sRr @uri)"
```
`-N` disables curl's output buffering so events appear live. Expect lines like `data: {"directory":"…","payload":{"type":"…",…}}`.
## 6. Stopping the backend
```bash
kill "$(cat /tmp/kilo-serve.pid)" 2>/dev/null || true
rm -f /tmp/kilo-serve.pid /tmp/kilo-serve.log
```
`ServeCommand` handles `SIGTERM` / `SIGINT` / `SIGHUP` and runs `Instance.disposeAll()` + `server.stop(true)` before exiting (`packages/opencode/src/cli/cmd/serve.ts:29-31`). `-9` is only needed if the process hangs past ~5 s.
## 7. Useful environment variables
| Var | Why you'd set it |
|---|---|
| `KILO_SERVER_PASSWORD` | Enable Basic auth. Omit for auth-bypassed local testing. |
| `KILO_DB=":memory:"` | Skip on-disk SQLite — hermetic runs. |
| `KILO_DISABLE_DEFAULT_PLUGINS=true` | Don't auto-load bundled plugins. |
| `KILO_WORKSPACE_ID=<id>` | Single-workspace mode; disables control-plane routes. |
| `KILO_TELEMETRY_LEVEL=off` | Disable PostHog during tests. |
| `KILO_CONFIG_CONTENT='{…}'` | Inline JSON config without writing a file. |
## 8. Last resort: typed SDK via a throwaway script
Use this only when `curl` can't express what you need — typed request/response shapes, complex multi-turn orchestration, SSE consumers that need to coalesce events. Keep the file short and delete it after.
```ts
// /tmp/probe.ts — delete after use. Talks to an already-running backend.
import { createKiloClient } from "@kilocode/sdk/v2"
const port = process.env.PORT!
const pass = process.env.KILO_SERVER_PASSWORD
const headers = pass ? { Authorization: "Basic " + Buffer.from("kilo:" + pass).toString("base64") } : undefined
const client = createKiloClient({
baseUrl: `http://127.0.0.1:${port}`,
headers,
directory: process.cwd(),
})
const { data: session } = await client.session.create({}, { throwOnError: true })
await client.session.promptAsync({
sessionID: session.id,
parts: [{ type: "text", text: "hello" }],
})
const events = await client.global.event({})
for await (const ev of events.stream) {
const e = ev as { payload: { type: string } }
console.log(e.payload.type)
if (e.payload.type === "session.idle") break
}
```
```bash
PORT="$PORT" KILO_SERVER_PASSWORD="$KILO_SERVER_PASSWORD" bun /tmp/probe.ts
rm /tmp/probe.ts
```
Reminder: this script **connects to** the server you launched in Section 3 — it does not start one. `createKiloServer()` from the SDK would spawn the PATH `kilo` binary (production CLI), which defeats the point of testing local code.
## 9. Pitfalls
- Running `kilo serve` instead of `bun dev serve` runs the installed prod binary, not your edits.
- Missing `x-kilo-directory` (or `?directory=`) returns `400` from `InstanceMiddleware`.
- `curl` without `-N` buffers SSE output — you won't see events until the connection closes.
- Hardcoding port `4096` breaks when a previous run didn't exit cleanly. Parse the log instead.
- `--port` must appear literally in `argv` to override `opencode.json`'s `server.port` (`packages/opencode/src/cli/network.ts:45`).
- `KILO_SERVER_PASSWORD` must be set **before** launch — changing it after doesn't rotate credentials.
- When sharing `/tmp/kilo-serve.log` / `.pid` across terminals, unique-suffix the paths to avoid clobbering parallel runs.
## 10. After changing server routes
Regenerate the SDK and OpenAPI spec so `/doc` and typed clients stay in sync:
```bash
./script/generate.ts # from repo root
```
See `AGENTS.md` for the full rationale.
Executable
+20
View File
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -e
dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
export KILO_DEV_REPO="$dir"
# Find the first non-flag arg. If there isn't one, the user is launching the
# TUI, so point it at the caller's directory via the project positional.
# Otherwise forward args untouched so subcommands (run, dev-alias, ...) don't
# collide with --project (only TUI/stats accept --project under yargs strict).
first=
for a in "$@"; do
case "$a" in
--) break ;;
-*) ;;
*) first="$a"; break ;;
esac
done
if [[ -z "$first" ]]; then
exec bun run --cwd "$dir/packages/opencode" --conditions=browser src/index.ts --project "$PWD" "$@"
fi
exec bun run --cwd "$dir/packages/opencode" --conditions=browser src/index.ts "$@"
+21
View File
@@ -0,0 +1,21 @@
@echo off
setlocal enabledelayedexpansion
set "dir=%~dp0.."
set "KILO_DEV_REPO=%dir%"
rem Find the first non-flag arg. If there isn't one, the user is launching the
rem TUI, so point it at the caller's directory via --project. Otherwise
rem forward untouched so subcommands don't collide with --project.
set "first="
for %%a in (%*) do (
if not defined first (
set "arg=%%~a"
if "!arg:~0,1!" neq "-" set "first=%%~a"
)
)
if not defined first (
bun run --cwd "%dir%\packages\opencode" --conditions=browser src/index.ts --project "%CD%" %*
) else (
bun run --cwd "%dir%\packages\opencode" --conditions=browser src/index.ts %*
)
set "code=%ERRORLEVEL%"
endlocal & exit /b %code%
+227 -1003
View File
File diff suppressed because it is too large Load Diff
Generated
+3 -3
View File
@@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1775823930,
"narHash": "sha256-ALT447J7FcxP/97J01A/gp/hgdO5lXRsm+zLMt+gIjc=",
"lastModified": 1777270315,
"narHash": "sha256-yKB4G6cKsQsWN7M6rZGk6gkJPDNPIzT05y4qzRyCDlI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "8c11f88bb9573a10a7d6bf87161ef08455ac70b9",
"rev": "6368eda62c9775c38ef7f714b2555a741c20c72d",
"type": "github"
},
"original": {
+56 -9
View File
@@ -21,9 +21,63 @@
devShells = forEachSystem (pkgs: {
default =
let
# Pin bun to the version declared in package.json (packageManager: "bun@1.3.13").
# nixpkgs-unstable currently ships 1.3.11, so we fetch the official release directly.
bun =
let
sources = {
"aarch64-linux" = {
name = "bun-linux-aarch64";
hash = "sha256-cLrkGzkIsKEg4eWMXIrzDnSvrjuNEbDT/djnh937SyI=";
};
"x86_64-linux" = {
name = "bun-linux-x64";
hash = "sha256-ecB3H6i5LDOq5B4VoODTB+qZ0OLwAxfHHGxTI3p44lo=";
};
"aarch64-darwin" = {
name = "bun-darwin-aarch64";
hash = "sha256-VGfj9l26Umuf6pjwzOBO+vwMY+Fpcz7Ce4dqOtMtoZA=";
};
"x86_64-darwin" = {
name = "bun-darwin-x64";
hash = "sha256-5abItk9BmSUjLREeyxPiXwq/VeVPeSNB+YdiP9B3gAk=";
};
};
source =
sources.${pkgs.stdenv.hostPlatform.system}
or (throw "Unsupported system for bun: ${pkgs.stdenv.hostPlatform.system}");
in
pkgs.stdenv.mkDerivation rec {
pname = "bun";
version = "1.3.13";
src = pkgs.fetchurl {
url = "https://github.com/oven-sh/bun/releases/download/bun-v${version}/${source.name}.zip";
inherit (source) hash;
};
nativeBuildInputs = [
pkgs.unzip
] ++ pkgs.lib.optional pkgs.stdenv.isLinux pkgs.autoPatchelfHook;
buildInputs = pkgs.lib.optionals pkgs.stdenv.isLinux [ pkgs.stdenv.cc.cc.lib ];
dontConfigure = true;
dontBuild = true;
installPhase = ''
runHook preInstall
install -Dm755 bun $out/bin/bun
ln -s $out/bin/bun $out/bin/bunx
runHook postInstall
'';
meta = {
description = "Fast all-in-one JavaScript runtime";
homepage = "https://bun.sh";
license = pkgs.lib.licenses.mit;
mainProgram = "bun";
platforms = builtins.attrNames sources;
};
};
kilo-dev = pkgs.writeShellScriptBin "kilo-dev" ''
cd "$KILO_ROOT"
exec ${pkgs.bun}/bin/bun dev "$@"
exec ${bun}/bin/bun dev "$@"
'';
kilo-install-bin = pkgs.writeShellScriptBin "kilo-install" ''
@@ -213,13 +267,9 @@
opencode = final.callPackage ./nix/opencode.nix {
inherit node_modules;
};
desktop = final.callPackage ./nix/desktop.nix {
inherit opencode;
};
in
{
inherit opencode;
opencode-desktop = desktop;
};
};
@@ -232,13 +282,10 @@
kilo = pkgs.callPackage ./nix/kilo.nix {
inherit node_modules;
};
desktop = pkgs.callPackage ./nix/desktop.nix {
inherit kilo;
};
in
{
default = kilo;
inherit kilo desktop;
inherit kilo;
# Updater derivation with fakeHash - build fails and reveals correct hash
node_modules_updater = node_modules.override {
hash = pkgs.lib.fakeHash;
+30
View File
@@ -0,0 +1,30 @@
{
"folders": [
{
"path": ".",
},
],
"settings": {
"window.title": "[main] Fix QuestionDock clickable options",
"peacock.color": "#ff3d00",
"workbench.colorCustomizations": {
"activityBar.activeBackground": "#ff6433",
"activityBar.background": "#ff6433",
"activityBar.foreground": "#15202b",
"activityBar.inactiveForeground": "#15202b99",
"activityBarBadge.background": "#00ff3d",
"activityBarBadge.foreground": "#15202b",
"commandCenter.border": "#e7e7e799",
"sash.hoverBorder": "#ff6433",
"statusBar.background": "#ff3d00",
"statusBar.foreground": "#e7e7e7",
"statusBarItem.hoverBackground": "#ff6433",
"statusBarItem.remoteBackground": "#ff3d00",
"statusBarItem.remoteForeground": "#e7e7e7",
"titleBar.activeBackground": "#ff3d00",
"titleBar.activeForeground": "#e7e7e7",
"titleBar.inactiveBackground": "#ff3d0099",
"titleBar.inactiveForeground": "#e7e7e799"
},
},
}
-100
View File
@@ -1,100 +0,0 @@
{
lib,
stdenv,
rustPlatform,
pkg-config,
cargo-tauri,
bun,
nodejs,
cargo,
rustc,
jq,
wrapGAppsHook4,
makeWrapper,
dbus,
glib,
gtk4,
libsoup_3,
librsvg,
libappindicator,
glib-networking,
openssl,
webkitgtk_4_1,
gst_all_1,
kilo,
}:
rustPlatform.buildRustPackage (finalAttrs: {
pname = "kilo-desktop";
inherit (kilo)
version
src
node_modules
patches
;
cargoRoot = "packages/desktop/src-tauri";
cargoLock.lockFile = ../packages/desktop/src-tauri/Cargo.lock;
buildAndTestSubdir = finalAttrs.cargoRoot;
nativeBuildInputs = [
pkg-config
cargo-tauri.hook
bun
nodejs # for patchShebangs node_modules
cargo
rustc
jq
makeWrapper
] ++ lib.optionals stdenv.hostPlatform.isLinux [ wrapGAppsHook4 ];
buildInputs = lib.optionals stdenv.isLinux [
dbus
glib
gtk4
libsoup_3
librsvg
libappindicator
glib-networking
openssl
webkitgtk_4_1
gst_all_1.gstreamer
gst_all_1.gst-plugins-base
gst_all_1.gst-plugins-good
gst_all_1.gst-plugins-bad
];
strictDeps = true;
preBuild = ''
cp -a ${finalAttrs.node_modules}/{node_modules,packages} .
chmod -R u+w node_modules packages
patchShebangs node_modules
patchShebangs packages/desktop/node_modules
mkdir -p packages/desktop/src-tauri/sidecars
cp ${kilo}/bin/kilo packages/desktop/src-tauri/sidecars/kilo-cli-${stdenv.hostPlatform.rust.rustcTarget}
'';
# see publish-tauri job in .github/workflows/publish.yml
tauriBuildFlags = [
"--config"
"tauri.prod.conf.json"
"--no-sign" # no code signing or auto updates
];
# FIXME: workaround for concerns about case insensitive filesystems
# should be removed once binary is renamed or decided otherwise
# darwin output is a .app bundle so no conflict
postFixup = lib.optionalString stdenv.hostPlatform.isLinux ''
mv $out/bin/Kilo $out/bin/kilo-desktop
sed -i 's|^Exec=Kilo$|Exec=kilo-desktop|' $out/share/applications/Kilo.desktop
'';
meta = {
description = "Kilo Desktop App";
homepage = "https://kilo.ai";
license = lib.licenses.mit;
mainProgram = "kilo-desktop";
inherit (kilo.meta) platforms;
};
})
+4 -4
View File
@@ -1,8 +1,8 @@
{
"nodeModules": {
"x86_64-linux": "sha256-tWXPyyUluVXzMJny376Nly7hJuiGonPRMv1m48hdplY=",
"aarch64-linux": "sha256-MFJ/ihYcJC1QdfJvgalo4/deQ+gaGN+c53Ex1P1ExoI=",
"aarch64-darwin": "sha256-wZ+gntTCb/Wzq6yur/1QoqWp6xX21A+XCsPn32KemyY=",
"x86_64-darwin": "sha256-lBwBWeXR9ca2TKmeE8E4HK4ZQrf83Ks0M0WAGBOGBfM="
"x86_64-linux": "sha256-BkFDOCEvivFrxKOvMjAIhj3QZwmb9vE/KvCZv5puw6E=",
"aarch64-linux": "sha256-sTkfQNjqCNCxmHbpQ8woA6azoiyD37e8Xy7UwB6eNsA=",
"aarch64-darwin": "sha256-lKuagsTHqij5C764DM3d+BOe4JKs2KV8XiwSlJItGMg=",
"x86_64-darwin": "sha256-g5gZMHLq8feSZHVaCOLGU6Tj2Qw56Zogif3pT9EIlz8="
}
}
+18 -17
View File
@@ -7,6 +7,7 @@
sysctl,
makeBinaryWrapper,
models-dev,
ripgrep,
installShellFiles,
versionCheckHook,
writableTmpDirAsHomeHook,
@@ -51,25 +52,25 @@ stdenvNoCC.mkDerivation (finalAttrs: {
runHook postBuild
'';
installPhase =
''
runHook preInstall
installPhase = ''
runHook preInstall
install -Dm755 dist/@kilocode/cli-*/bin/kilo $out/bin/kilo
install -Dm644 schema.json $out/share/kilo/schema.json
''
# bun runs sysctl to detect if dunning on rosetta2
+ lib.optionalString stdenvNoCC.hostPlatform.isDarwin ''
wrapProgram $out/bin/kilo \
--prefix PATH : ${
lib.makeBinPath [
sysctl
install -Dm755 dist/@kilocode/cli-*/bin/kilo $out/bin/kilo
install -Dm644 schema.json $out/share/kilo/schema.json
wrapProgram $out/bin/kilo \
--prefix PATH : ${
lib.makeBinPath (
[
ripgrep
]
}
''
+ ''
runHook postInstall
'';
# bun runs sysctl to detect if running on rosetta2
++ lib.optional stdenvNoCC.hostPlatform.isDarwin sysctl
)
}
runHook postInstall
'';
postInstall = lib.optionalString (stdenvNoCC.buildPlatform.canExecute stdenvNoCC.hostPlatform) ''
# trick yargs into also generating zsh completions
+1 -4
View File
@@ -30,7 +30,7 @@ stdenvNoCC.mkDerivation {
../bun.lock
../package.json
../patches
../install # required by desktop build (cli.rs include_str!)
../install
]
);
};
@@ -52,9 +52,6 @@ stdenvNoCC.mkDerivation {
--os="${bunOs}" \
--filter '!./' \
--filter './packages/opencode' \
--filter './packages/desktop' \
--filter './packages/app' \
--filter './packages/shared' \
--frozen-lockfile \
--ignore-scripts \
--no-progress
+13 -12
View File
@@ -4,16 +4,14 @@
"description": "AI-powered development tool",
"private": true,
"type": "module",
"packageManager": "bun@1.3.11",
"packageManager": "bun@1.3.13",
"scripts": {
"dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts",
"dev:desktop": "bun --cwd packages/desktop tauri dev",
"dev:web": "bun --cwd packages/app dev",
"dev:console": "ulimit -n 10240 2>/dev/null; bun run --cwd packages/console/app dev",
"dev-setup": "bun run --cwd packages/opencode --conditions=browser src/index.ts dev-setup",
"dev:storybook": "bun --cwd packages/storybook storybook",
"lint": "oxlint",
"typecheck": "bun turbo typecheck",
"postinstall": "bun run --cwd packages/opencode fix-node-pty",
"postinstall": "bun run --cwd packages/opencode fix-node-pty && bun run script/setup-git.ts",
"prepare": "husky",
"random": "echo 'Random script'",
"hello": "echo 'Hello World!'",
@@ -26,15 +24,15 @@
"packages/sdk/js"
],
"catalog": {
"@effect/opentelemetry": "4.0.0-beta.48",
"@effect/platform-node": "4.0.0-beta.48",
"@effect/opentelemetry": "4.0.0-beta.57",
"@effect/platform-node": "4.0.0-beta.57",
"@npmcli/arborist": "9.4.0",
"@types/bun": "1.3.11",
"@types/bun": "1.3.12",
"@types/cross-spawn": "6.0.6",
"@octokit/rest": "22.0.0",
"@hono/zod-validator": "0.4.2",
"@opentui/core": "0.1.99",
"@opentui/solid": "0.1.99",
"@opentui/core": "0.1.105",
"@opentui/solid": "0.1.105",
"ulid": "3.0.1",
"@kobalte/core": "0.13.11",
"@types/luxon": "3.7.1",
@@ -45,13 +43,14 @@
"@cloudflare/workers-types": "4.20251008.0",
"@openauthjs/openauth": "0.0.0-20250322224806",
"@pierre/diffs": "1.1.0-beta.18",
"opentui-spinner": "0.0.6",
"@solid-primitives/storage": "4.3.3",
"@tailwindcss/vite": "4.1.11",
"diff": "8.0.4",
"dompurify": "3.3.3",
"drizzle-kit": "1.0.0-beta.19-d95b7a4",
"drizzle-orm": "1.0.0-beta.19-d95b7a4",
"effect": "4.0.0-beta.48",
"effect": "4.0.0-beta.57",
"ai": "6.0.168",
"cross-spawn": "7.0.6",
"hono": "4.12.12",
@@ -83,6 +82,7 @@
"devDependencies": {
"@actions/artifact": "5.0.1",
"@tsconfig/bun": "catalog:",
"@types/bun": "catalog:",
"@types/mime-types": "3.0.1",
"@typescript/native-preview": "catalog:",
"glob": "13.0.5",
@@ -141,10 +141,11 @@
"happy-dom": ">=20.8.9"
},
"patchedDependencies": {
"@npmcli/agent@4.0.0": "patches/@npmcli%2Fagent@4.0.0.patch",
"@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch",
"solid-js@1.9.10": "patches/solid-js@1.9.10.patch",
"stream-chat@9.38.0": "patches/stream-chat@9.38.0.patch"
},
"version": "7.2.24",
"version": "7.2.42",
"peerDependencies": {}
}
-3
View File
@@ -1,3 +0,0 @@
src/assets/theme.css
e2e/test-results
e2e/playwright-report
-30
View File
@@ -1,30 +0,0 @@
## Debugging
- NEVER try to restart the app, or the server process, EVER.
## Local Dev
- `opencode dev web` proxies `https://app.opencode.ai`, so local UI/CSS changes will not show there.
- For local UI changes, run the backend and app dev servers separately.
- Backend (from `packages/opencode`): `bun run --conditions=browser ./src/index.ts serve --port 4096`
- App (from `packages/app`): `bun dev -- --port 4444`
- Open `http://localhost:4444` to verify UI changes (it targets the backend at `http://localhost:4096`).
## SolidJS
- Always prefer `createStore` over multiple `createSignal` calls
## Tool Calling
- ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE.
## Browser Automation
Use `agent-browser` for web automation. Run `agent-browser --help` for all commands.
Core workflow:
1. `agent-browser open <url>` - Navigate to page
2. `agent-browser snapshot -i` - Get interactive elements with refs (@e1, @e2)
3. `agent-browser click @e1` / `fill @e2 "text"` - Interact using refs
4. Re-snapshot after page changes
-50
View File
@@ -1,50 +0,0 @@
## Usage
Dependencies for these templates are managed with [pnpm](https://pnpm.io) using `pnpm up -Lri`.
This is the reason you see a `pnpm-lock.yaml`. That said, any package manager will work. This file can safely be removed once you clone a template.
```bash
$ npm install # or pnpm install or yarn install
```
### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs)
## Available Scripts
In the project directory, you can run:
### `npm run dev` or `npm start`
Runs the app in the development mode.<br>
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.<br>
### `npm run build`
Builds the app for production to the `dist` folder.<br>
It correctly bundles Solid in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.<br>
Your app is ready to be deployed!
## E2E Testing
Playwright starts the Vite dev server automatically via `webServer`, and UI tests expect an opencode backend at `localhost:4096` by default.
```bash
bunx playwright install chromium
bun run test:e2e:local
bun run test:e2e:local -- --grep "settings"
```
Environment options:
- `PLAYWRIGHT_SERVER_HOST` / `PLAYWRIGHT_SERVER_PORT` (backend address, default: `localhost:4096`)
- `PLAYWRIGHT_PORT` (Vite dev server port, default: `3000`)
- `PLAYWRIGHT_BASE_URL` (override base URL, default: `http://localhost:<PLAYWRIGHT_PORT>`)
## Deployment
You can deploy the `dist` folder to any static host provider (netlify, surge, now, etc.)
-3
View File
@@ -1,3 +0,0 @@
[test]
root = "./src"
preload = ["./happydom.ts"]
@@ -1,515 +0,0 @@
# CreateEffect Simplification Implementation Spec
Reduce reactive misuse across `packages/app`.
---
## Context
This work targets `packages/app/src`, which currently has 101 `createEffect` calls across 37 files.
The biggest clusters are `pages/session.tsx` (19), `pages/layout.tsx` (13), `pages/session/file-tabs.tsx` (6), and several context providers that mirror one store into another.
Key issues from the audit:
- Derived state is being written through effects instead of computed directly
- Session and file resets are handled by watch-and-clear effects instead of keyed state boundaries
- User-driven actions are hidden inside reactive effects
- Context layers mirror and hydrate child stores with multiple sync effects
- Several areas repeat the same imperative trigger pattern in multiple effects
Keep the implementation focused on removing unnecessary effects, not on broad UI redesign.
## Goals
- Cut high-churn `createEffect` usage in the hottest files first
- Replace effect-driven derived state with reactive derivation
- Replace reset-on-key effects with keyed ownership boundaries
- Move event-driven work to direct actions and write paths
- Remove mirrored store hydration where a single source of truth can exist
- Leave necessary external sync effects in place, but make them narrower and clearer
## Non-Goals
- Do not rewrite unrelated component structure just to reduce the count
- Do not change product behavior, navigation flow, or persisted data shape unless required for a cleaner write boundary
- Do not remove effects that bridge to DOM, editors, polling, or external APIs unless there is a clearly safer equivalent
- Do not attempt a repo-wide cleanup outside `packages/app`
## Effect Taxonomy And Replacement Rules
Use these rules during implementation.
### Prefer `createMemo`
Use `createMemo` when the target value is pure derived state from other signals or stores.
Do this when an effect only reads reactive inputs and writes another reactive value that could be computed instead.
Apply this to:
- `packages/app/src/pages/session.tsx:141`
- `packages/app/src/pages/layout.tsx:557`
- `packages/app/src/components/terminal.tsx:261`
- `packages/app/src/components/session/session-header.tsx:309`
Rules:
- If no external system is touched, do not use `createEffect`
- Derive once, then read the memo where needed
- If normalization is required, prefer normalizing at the write boundary before falling back to a memo
### Prefer Keyed Remounts
Use keyed remounts when local UI state should reset because an identity changed.
Do this with `sessionKey`, `scope()`, or another stable identity instead of watching the key and manually clearing signals.
Apply this to:
- `packages/app/src/pages/session.tsx:325`
- `packages/app/src/pages/session.tsx:336`
- `packages/app/src/pages/session.tsx:477`
- `packages/app/src/pages/session.tsx:869`
- `packages/app/src/pages/session.tsx:963`
- `packages/app/src/pages/session/message-timeline.tsx:149`
- `packages/app/src/context/file.tsx:100`
Rules:
- If the desired behavior is "new identity, fresh local state," key the owner subtree
- Keep state local to the keyed boundary so teardown and recreation handle the reset naturally
### Prefer Event Handlers And Actions
Use direct handlers, store actions, and async command functions when work happens because a user clicked, selected, reloaded, or navigated.
Do this when an effect is just watching for a flag change, command token, or event-bus signal to trigger imperative logic.
Apply this to:
- `packages/app/src/pages/layout.tsx:484`
- `packages/app/src/pages/layout.tsx:652`
- `packages/app/src/pages/layout.tsx:776`
- `packages/app/src/pages/layout.tsx:1489`
- `packages/app/src/pages/layout.tsx:1519`
- `packages/app/src/components/file-tree.tsx:328`
- `packages/app/src/pages/session/terminal-panel.tsx:55`
- `packages/app/src/context/global-sync.tsx:148`
- Duplicated trigger sets in:
- `packages/app/src/pages/session/review-tab.tsx:122`
- `packages/app/src/pages/session/review-tab.tsx:130`
- `packages/app/src/pages/session/review-tab.tsx:138`
- `packages/app/src/pages/session/file-tabs.tsx:367`
- `packages/app/src/pages/session/file-tabs.tsx:378`
- `packages/app/src/pages/session/file-tabs.tsx:389`
- `packages/app/src/pages/session/use-session-hash-scroll.ts:144`
- `packages/app/src/pages/session/use-session-hash-scroll.ts:149`
- `packages/app/src/pages/session/use-session-hash-scroll.ts:167`
Rules:
- If the trigger is user intent, call the action at the source of that intent
- If the same imperative work is triggered from multiple places, extract one function and call it directly
### Prefer `onMount` And `onCleanup`
Use `onMount` and `onCleanup` for lifecycle-only setup and teardown.
This is the right fit for subscriptions, one-time wiring, timers, and imperative integration that should not rerun for ordinary reactive changes.
Use this when:
- Setup should happen once per owner lifecycle
- Cleanup should always pair with teardown
- The work is not conceptually derived state
### Keep `createEffect` When It Is A Real Bridge
Keep `createEffect` when it synchronizes reactive data to an external imperative sink.
Examples that should remain, though they may be narrowed or split:
- DOM/editor sync in `packages/app/src/components/prompt-input.tsx:690`
- Scroll sync in `packages/app/src/pages/session.tsx:685`
- Scroll/hash sync in `packages/app/src/pages/session/use-session-hash-scroll.ts:149`
- External sync in:
- `packages/app/src/context/language.tsx:207`
- `packages/app/src/context/settings.tsx:110`
- `packages/app/src/context/sdk.tsx:26`
- Polling in:
- `packages/app/src/components/status-popover.tsx:59`
- `packages/app/src/components/dialog-select-server.tsx:273`
Rules:
- Keep the effect single-purpose
- Make dependencies explicit and narrow
- Avoid writing back into the same reactive graph unless absolutely required
## Implementation Plan
### Phase 0: Classification Pass
Before changing code, tag each targeted effect as one of: derive, reset, event, lifecycle, or external bridge.
Acceptance criteria:
- Every targeted effect in this spec is tagged with a replacement strategy before refactoring starts
- Shared helpers to be introduced are identified up front to avoid repeating patterns
### Phase 1: Derived-State Cleanup
Tackle highest-value, lowest-risk derived-state cleanup first.
Priority items:
- Normalize tabs at write boundaries and remove `packages/app/src/pages/session.tsx:141`
- Stop syncing `workspaceOrder` in `packages/app/src/pages/layout.tsx:557`
- Make prompt slash filtering reactive so `packages/app/src/components/prompt-input.tsx:652` can be removed
- Replace other obvious derived-state effects in terminal and session header
Acceptance criteria:
- No behavior change in tab ordering, prompt filtering, terminal display, or header state
- Targeted derived-state effects are deleted, not just moved
### Phase 2: Keyed Reset Cleanup
Replace reset-on-key effects with keyed ownership boundaries.
Priority items:
- Key session-scoped UI and state by `sessionKey`
- Key file-scoped state by `scope()`
- Remove manual clear-and-reseed effects in session and file context
Acceptance criteria:
- Switching session or file scope recreates the intended local state cleanly
- No stale state leaks across session or scope changes
- Target reset effects are deleted
### Phase 3: Event-Driven Work Extraction
Move event-driven work out of reactive effects.
Priority items:
- Replace `globalStore.reload` effect dispatching with direct calls
- Split mixed-responsibility effect in `packages/app/src/pages/layout.tsx:1489`
- Collapse duplicated imperative trigger triplets into single functions
- Move file-tree and terminal-panel imperative work to explicit handlers
Acceptance criteria:
- User-triggered behavior still fires exactly once per intended action
- No effect remains whose only job is to notice a command-like state and trigger an imperative function
### Phase 4: Context Ownership Cleanup
Remove mirrored child-store hydration patterns.
Priority items:
- Remove child-store hydration mirrors in `packages/app/src/context/global-sync/child-store.ts:184`, `:190`, `:193`
- Simplify mirror logic in `packages/app/src/context/global-sync.tsx:130`, `:138`
- Revisit `packages/app/src/context/layout.tsx:424` if it still mirrors instead of deriving
Acceptance criteria:
- There is one clear source of truth for each synced value
- Child stores no longer need effect-based hydration to stay consistent
- Initialization and updates both work without manual mirror effects
### Phase 5: Cleanup And Keeper Review
Clean up remaining targeted hotspots and narrow the effects that should stay.
Acceptance criteria:
- Remaining `createEffect` calls in touched files are all true bridges or clearly justified lifecycle sync
- Mixed-responsibility effects are split into smaller units where still needed
## Detailed Work Items By Area
### 1. Normalize Tab State
Files:
- `packages/app/src/pages/session.tsx:141`
Work:
- Move tab normalization into the functions that create, load, or update tab state
- Make readers consume already-normalized tab data
- Remove the effect that rewrites derived tab state after the fact
Rationale:
- Tabs should become valid when written, not be repaired later
- This removes a feedback loop and makes state easier to trust
Acceptance criteria:
- The effect at `packages/app/src/pages/session.tsx:141` is removed
- Newly created and restored tabs are normalized before they enter local state
- Tab rendering still matches current behavior for valid and edge-case inputs
### 2. Key Session-Owned State
Files:
- `packages/app/src/pages/session.tsx:325`
- `packages/app/src/pages/session.tsx:336`
- `packages/app/src/pages/session.tsx:477`
- `packages/app/src/pages/session.tsx:869`
- `packages/app/src/pages/session.tsx:963`
- `packages/app/src/pages/session/message-timeline.tsx:149`
Work:
- Identify state that should reset when `sessionKey` changes
- Move that state under a keyed subtree or keyed owner boundary
- Remove effects that watch `sessionKey` just to clear local state, refs, or temporary UI flags
Rationale:
- Session identity already defines the lifetime of this UI state
- Keyed ownership makes reset behavior automatic and easier to reason about
Acceptance criteria:
- The targeted reset effects are removed
- Changing sessions resets only the intended session-local state
- Scroll and editor state that should persist are not accidentally reset
### 3. Derive Workspace Order
Files:
- `packages/app/src/pages/layout.tsx:557`
Work:
- Stop writing `workspaceOrder` from live workspace data in an effect
- Represent user overrides separately from live workspace data
- Compute effective order from current data plus overrides with a memo or pure helper
Rationale:
- Persisted user intent and live source data should not mirror each other through an effect
- A computed effective order avoids drift and racey resync behavior
Acceptance criteria:
- The effect at `packages/app/src/pages/layout.tsx:557` is removed
- Workspace order updates correctly when workspaces appear, disappear, or are reordered by the user
- User overrides persist without requiring a sync-back effect
### 4. Remove Child-Store Mirrors
Files:
- `packages/app/src/context/global-sync.tsx:130`
- `packages/app/src/context/global-sync.tsx:138`
- `packages/app/src/context/global-sync.tsx:148`
- `packages/app/src/context/global-sync/child-store.ts:184`
- `packages/app/src/context/global-sync/child-store.ts:190`
- `packages/app/src/context/global-sync/child-store.ts:193`
- `packages/app/src/context/layout.tsx:424`
Work:
- Trace the actual ownership of global and child store values
- Replace hydration and mirror effects with explicit initialization and direct updates
- Remove the `globalStore.reload` event-bus pattern and call the needed reload paths directly
Rationale:
- Mirrors make it hard to tell which state is authoritative
- Event-bus style state toggles hide control flow and create accidental reruns
Acceptance criteria:
- Child store hydration no longer depends on effect-based copying
- Reload work can be followed from the event source to the handler without a reactive relay
- State remains correct on first load, child creation, and subsequent updates
### 5. Key File-Scoped State
Files:
- `packages/app/src/context/file.tsx:100`
Work:
- Move file-scoped local state under a boundary keyed by `scope()`
- Remove any effect that watches `scope()` only to reset file-local state
Rationale:
- File scope changes are identity changes
- Keyed ownership gives a cleaner reset than manual clear logic
Acceptance criteria:
- The effect at `packages/app/src/context/file.tsx:100` is removed
- Switching scopes resets only scope-local state
- No previous-scope data appears after a scope change
### 6. Split Layout Side Effects
Files:
- `packages/app/src/pages/layout.tsx:1489`
- Related event-driven effects near `packages/app/src/pages/layout.tsx:484`, `:652`, `:776`, `:1519`
Work:
- Break the mixed-responsibility effect at `:1489` into direct actions and smaller bridge effects only where required
- Move user-triggered branches into the actual command or handler that causes them
- Remove any branch that only exists because one effect is handling unrelated concerns
Rationale:
- Mixed effects hide cause and make reruns hard to predict
- Smaller units reduce accidental coupling and make future cleanup safer
Acceptance criteria:
- The effect at `packages/app/src/pages/layout.tsx:1489` no longer mixes unrelated responsibilities
- Event-driven branches execute from direct handlers
- Remaining effects in this area each have one clear external sync purpose
### 7. Remove Duplicate Triggers
Files:
- `packages/app/src/pages/session/review-tab.tsx:122`
- `packages/app/src/pages/session/review-tab.tsx:130`
- `packages/app/src/pages/session/review-tab.tsx:138`
- `packages/app/src/pages/session/file-tabs.tsx:367`
- `packages/app/src/pages/session/file-tabs.tsx:378`
- `packages/app/src/pages/session/file-tabs.tsx:389`
- `packages/app/src/pages/session/use-session-hash-scroll.ts:144`
- `packages/app/src/pages/session/use-session-hash-scroll.ts:149`
- `packages/app/src/pages/session/use-session-hash-scroll.ts:167`
Work:
- Extract one explicit imperative function per behavior
- Call that function from each source event instead of replicating the same effect pattern multiple times
- Preserve the scroll-sync effect that is truly syncing with the DOM, but remove duplicate trigger scaffolding around it
Rationale:
- Duplicate triggers make it easy to miss a case or fire twice
- One named action is easier to test and reason about
Acceptance criteria:
- Repeated imperative effect triplets are collapsed into shared functions
- Scroll behavior still works, including hash-based navigation
- No duplicate firing is introduced
### 8. Make Prompt Filtering Reactive
Files:
- `packages/app/src/components/prompt-input.tsx:652`
- Keep `packages/app/src/components/prompt-input.tsx:690` as needed
Work:
- Convert slash filtering into a pure reactive derivation from the current input and candidate command list
- Keep only the editor or DOM bridge effect if it is still needed for imperative syncing
Rationale:
- Filtering is classic derived state
- It should not need an effect if it can be computed from current inputs
Acceptance criteria:
- The effect at `packages/app/src/components/prompt-input.tsx:652` is removed
- Filtered slash-command results update correctly as the input changes
- The editor sync effect at `:690` still behaves correctly
### 9. Clean Up Smaller Derived-State Cases
Files:
- `packages/app/src/components/terminal.tsx:261`
- `packages/app/src/components/session/session-header.tsx:309`
Work:
- Replace effect-written local state with memos or inline derivation
- Remove intermediate setters when the value can be computed directly
Rationale:
- These are low-risk wins that reinforce the same pattern
- They also help keep follow-up cleanup consistent
Acceptance criteria:
- Targeted effects are removed
- UI output remains unchanged under the same inputs
## Verification And Regression Checks
Run focused checks after each phase, not only at the end.
### Suggested Verification
- Switch between sessions rapidly and confirm local session UI resets only where intended
- Open, close, and reorder tabs and confirm order and normalization remain stable
- Change workspaces, reload workspace data, and verify effective ordering is correct
- Change file scope and confirm stale file state does not bleed across scopes
- Trigger layout actions that previously depended on effects and confirm they still fire once
- Use slash commands in the prompt and verify filtering updates as you type
- Test review tab, file tab, and hash-scroll flows for duplicate or missing triggers
- Verify global sync initialization, reload, and child-store creation paths
### Regression Checks
- No accidental infinite reruns
- No double-firing network or command actions
- No lost cleanup for listeners, timers, or scroll handlers
- No preserved stale state after identity changes
- No removed effect that was actually bridging to DOM or an external API
If available, add or update tests around pure helpers introduced during this cleanup.
Favor tests for derived ordering, normalization, and action extraction, since those are easiest to lock down.
## Definition Of Done
This work is done when all of the following are true:
- The highest-leverage targets in this spec are implemented
- Each removed effect has been replaced by a clearer pattern: memo, keyed boundary, direct action, or lifecycle hook
- The "should remain" effects still exist only where they serve a real external sync purpose
- Touched files have fewer mixed-responsibility effects and clearer ownership of state
- Manual verification covers session switching, file scope changes, workspace ordering, prompt filtering, and reload flows
- No behavior regressions are found in the targeted areas
A reduced raw `createEffect` count is helpful, but it is not the main success metric.
The main success metric is clearer ownership and fewer effect-driven state repairs.
## Risks And Rollout Notes
Main risks:
- Keyed remounts can reset too much if state boundaries are drawn too high
- Store mirror removal can break initialization order if ownership is not mapped first
- Moving event work out of effects can accidentally skip triggers that were previously implicit
Rollout notes:
- Land in small phases, with each phase keeping the app behaviorally stable
- Prefer isolated PRs by phase or by file cluster, especially for context-store changes
- Review each remaining effect in touched files and leave it only if it clearly bridges to something external
@@ -1,146 +0,0 @@
import type { ToolPart } from "@kilocode/sdk/v2/client"
import type { Page } from "@playwright/test"
import { test, expect } from "../fixtures"
import { assistantText } from "../actions"
import { promptSelector } from "../selectors"
import { createSdk } from "../utils"
const text = (value: string | null) => (value ?? "").replace(/\u200B/g, "").trim()
type Sdk = ReturnType<typeof createSdk>
const isBash = (part: unknown): part is ToolPart => {
if (!part || typeof part !== "object") return false
if (!("type" in part) || part.type !== "tool") return false
if (!("tool" in part) || part.tool !== "bash") return false
return "state" in part
}
async function wait(page: Page, value: string) {
await expect.poll(async () => text(await page.locator(promptSelector).textContent())).toBe(value)
}
async function reply(sdk: Sdk, sessionID: string, token: string) {
await expect.poll(() => assistantText(sdk, sessionID), { timeout: 90_000 }).toContain(token)
}
async function shell(sdk: Sdk, sessionID: string, cmd: string, token: string) {
await expect
.poll(
async () => {
const messages = await sdk.session.messages({ sessionID, limit: 50 }).then((r) => r.data ?? [])
const part = messages
.filter((item) => item.info.role === "assistant")
.flatMap((item) => item.parts)
.filter(isBash)
.find((item) => item.state.input?.command === cmd && item.state.status === "completed")
if (!part || part.state.status !== "completed") return
return typeof part.state.metadata?.output === "string" ? part.state.metadata.output : part.state.output
},
{ timeout: 90_000 },
)
.toContain(token)
}
test("prompt history restores unsent draft with arrow navigation", async ({ page, project, assistant }) => {
test.setTimeout(120_000)
const firstToken = `E2E_HISTORY_ONE_${Date.now()}`
const secondToken = `E2E_HISTORY_TWO_${Date.now()}`
const first = `Reply with exactly: ${firstToken}`
const second = `Reply with exactly: ${secondToken}`
const draft = `draft ${Date.now()}`
await project.open()
await assistant.reply(firstToken)
const sessionID = await project.prompt(first)
await wait(page, "")
await reply(project.sdk, sessionID, firstToken)
await assistant.reply(secondToken)
await project.prompt(second)
await wait(page, "")
await reply(project.sdk, sessionID, secondToken)
const prompt = page.locator(promptSelector)
await prompt.click()
await page.keyboard.type(draft)
await wait(page, draft)
await prompt.fill("")
await wait(page, "")
await page.keyboard.press("ArrowUp")
await wait(page, second)
await page.keyboard.press("ArrowUp")
await wait(page, first)
await page.keyboard.press("ArrowDown")
await wait(page, second)
await page.keyboard.press("ArrowDown")
await wait(page, "")
})
test.fixme("shell history stays separate from normal prompt history", async ({ page, sdk, gotoSession }) => {
test.setTimeout(120_000)
const firstToken = `E2E_SHELL_ONE_${Date.now()}`
const secondToken = `E2E_SHELL_TWO_${Date.now()}`
const normalToken = `E2E_NORMAL_${Date.now()}`
const first = `echo ${firstToken}`
const second = `echo ${secondToken}`
const normal = `Reply with exactly: ${normalToken}`
await gotoSession()
const prompt = page.locator(promptSelector)
await prompt.click()
await page.keyboard.type("!")
await page.keyboard.type(first)
await page.keyboard.press("Enter")
await wait(page, "")
await expect(page).toHaveURL(/\/session\/[^/?#]+/, { timeout: 30_000 })
const sessionID = sessionIDFromUrl(page.url())!
await shell(sdk, sessionID, first, firstToken)
await prompt.click()
await page.keyboard.type("!")
await page.keyboard.type(second)
await page.keyboard.press("Enter")
await wait(page, "")
await shell(sdk, sessionID, second, secondToken)
await page.keyboard.press("Escape")
await wait(page, "")
await prompt.click()
await page.keyboard.type("!")
await page.keyboard.press("ArrowUp")
await wait(page, second)
await page.keyboard.press("ArrowUp")
await wait(page, first)
await page.keyboard.press("ArrowDown")
await wait(page, second)
await page.keyboard.press("ArrowDown")
await wait(page, "")
await page.keyboard.press("Escape")
await wait(page, "")
await prompt.click()
await page.keyboard.type(normal)
await page.keyboard.press("Enter")
await wait(page, "")
await reply(sdk, sessionID, normalToken)
await prompt.click()
await page.keyboard.press("ArrowUp")
await wait(page, normal)
})
@@ -1,74 +0,0 @@
import type { ToolPart } from "@kilocode/sdk/v2/client"
import { test, expect } from "../fixtures"
import { closeDialog, openSettings, withSession } from "../actions"
import { promptModelSelector, promptSelector, promptVariantSelector } from "../selectors"
const isBash = (part: unknown): part is ToolPart => {
if (!part || typeof part !== "object") return false
if (!("type" in part) || part.type !== "tool") return false
if (!("tool" in part) || part.tool !== "bash") return false
return "state" in part
}
test("shell mode runs a command in the project directory", async ({ page, project }) => {
test.setTimeout(120_000)
await project.open()
const cmd = process.platform === "win32" ? "dir" : "command ls"
await withSession(project.sdk, `e2e shell ${Date.now()}`, async (session) => {
project.trackSession(session.id)
await project.gotoSession(session.id)
const dialog = await openSettings(page)
const toggle = dialog.locator('[data-action="settings-auto-accept-permissions"]').first()
const input = toggle.locator('[data-slot="switch-input"]').first()
await expect(toggle).toBeVisible()
if ((await input.getAttribute("aria-checked")) !== "true") {
await toggle.locator('[data-slot="switch-control"]').click()
await expect(input).toHaveAttribute("aria-checked", "true")
}
await closeDialog(page, dialog)
await project.shell(cmd)
await expect
.poll(
async () => {
const list = await project.sdk.session
.messages({ sessionID: session.id, limit: 50 })
.then((x) => x.data ?? [])
const msg = list.findLast(
(item) => item.info.role === "assistant" && "path" in item.info && item.info.path.cwd === project.directory,
)
if (!msg) return
const part = msg.parts
.filter(isBash)
.find((item) => item.state.input?.command === cmd && item.state.status === "completed")
if (!part || part.state.status !== "completed") return
const output =
typeof part.state.metadata?.output === "string" ? part.state.metadata.output : part.state.output
if (!output.includes("README.md")) return
return { cwd: project.directory, output }
},
{ timeout: 90_000 },
)
.toEqual(expect.objectContaining({ cwd: project.directory, output: expect.stringContaining("README.md") }))
})
})
test("shell mode unmounts model and variant controls", async ({ page, project }) => {
await project.open()
const prompt = page.locator(promptSelector).first()
await expect(page.locator(promptModelSelector)).toHaveCount(1)
await expect(page.locator(promptVariantSelector)).toHaveCount(1)
await prompt.click()
await page.keyboard.type("!")
await expect(prompt).toHaveAttribute("aria-label", /enter shell command/i)
await expect(page.locator(promptModelSelector)).toHaveCount(0)
await expect(page.locator(promptVariantSelector)).toHaveCount(0)
})
@@ -1,66 +0,0 @@
import { test, expect } from "../fixtures"
import { promptSelector } from "../selectors"
import { withSession } from "../actions"
const shareDisabled = process.env.KILO_DISABLE_SHARE === "true" || process.env.KILO_DISABLE_SHARE === "1"
async function seed(sdk: Parameters<typeof withSession>[0], sessionID: string) {
await sdk.session.promptAsync({
sessionID,
noReply: true,
parts: [{ type: "text", text: "e2e share seed" }],
})
await expect
.poll(
async () => {
const messages = await sdk.session.messages({ sessionID, limit: 1 }).then((r) => r.data ?? [])
return messages.length
},
{ timeout: 30_000 },
)
.toBeGreaterThan(0)
}
test("/share and /unshare update session share state", async ({ page, project }) => {
test.skip(shareDisabled, "Share is disabled in this environment (KILO_DISABLE_SHARE).")
await project.open()
await withSession(project.sdk, `e2e slash share ${Date.now()}`, async (session) => {
project.trackSession(session.id)
const prompt = page.locator(promptSelector)
await seed(project.sdk, session.id)
await project.gotoSession(session.id)
await prompt.click()
await page.keyboard.type("/share")
await expect(page.locator('[data-slash-id="session.share"]').first()).toBeVisible()
await page.keyboard.press("Enter")
await expect
.poll(
async () => {
const data = await project.sdk.session.get({ sessionID: session.id }).then((r) => r.data)
return data?.share?.url || undefined
},
{ timeout: 30_000 },
)
.not.toBeUndefined()
await prompt.click()
await page.keyboard.type("/unshare")
await expect(page.locator('[data-slash-id="session.unshare"]').first()).toBeVisible()
await page.keyboard.press("Enter")
await expect
.poll(
async () => {
const data = await project.sdk.session.get({ sessionID: session.id }).then((r) => r.data)
return data?.share?.url || undefined
},
{ timeout: 30_000 },
)
.toBeUndefined()
})
})
-182
View File
@@ -1,182 +0,0 @@
import { test, expect } from "../fixtures"
import {
openSidebar,
openSessionMoreMenu,
clickMenuItem,
confirmDialog,
openSharePopover,
withSession,
} from "../actions"
import { sessionItemSelector, inlineInputSelector } from "../selectors"
const shareDisabled = process.env.KILO_DISABLE_SHARE === "true" || process.env.KILO_DISABLE_SHARE === "1"
type Sdk = Parameters<typeof withSession>[0]
async function seedMessage(sdk: Sdk, sessionID: string) {
await sdk.session.promptAsync({
sessionID,
noReply: true,
parts: [{ type: "text", text: "e2e seed" }],
})
await expect
.poll(
async () => {
const messages = await sdk.session.messages({ sessionID, limit: 1 }).then((r) => r.data ?? [])
return messages.length
},
{ timeout: 30_000 },
)
.toBeGreaterThan(0)
}
test("session can be renamed via header menu", async ({ page, project }) => {
const stamp = Date.now()
const originalTitle = `e2e rename test ${stamp}`
const renamedTitle = `e2e renamed ${stamp}`
await project.open()
await withSession(project.sdk, originalTitle, async (session) => {
project.trackSession(session.id)
await seedMessage(project.sdk, session.id)
await project.gotoSession(session.id)
await expect(page.getByRole("heading", { level: 1 }).first()).toHaveText(originalTitle)
const menu = await openSessionMoreMenu(page, session.id)
await clickMenuItem(menu, /rename/i)
const input = page.locator(".scroll-view__viewport").locator(inlineInputSelector).first()
await expect(input).toBeVisible()
await expect(input).toBeFocused()
await input.fill(renamedTitle)
await expect(input).toHaveValue(renamedTitle)
await input.press("Enter")
await expect
.poll(
async () => {
const data = await project.sdk.session.get({ sessionID: session.id }).then((r) => r.data)
return data?.title
},
{ timeout: 30_000 },
)
.toBe(renamedTitle)
await expect(page.getByRole("heading", { level: 1 }).first()).toHaveText(renamedTitle)
})
})
test("session can be archived via header menu", async ({ page, project }) => {
const stamp = Date.now()
const title = `e2e archive test ${stamp}`
await project.open()
await withSession(project.sdk, title, async (session) => {
project.trackSession(session.id)
await seedMessage(project.sdk, session.id)
await project.gotoSession(session.id)
const menu = await openSessionMoreMenu(page, session.id)
await clickMenuItem(menu, /archive/i)
await expect
.poll(
async () => {
const data = await project.sdk.session.get({ sessionID: session.id }).then((r) => r.data)
return data?.time?.archived
},
{ timeout: 30_000 },
)
.not.toBeUndefined()
await openSidebar(page)
await expect(page.locator(sessionItemSelector(session.id))).toHaveCount(0)
})
})
test("session can be deleted via header menu", async ({ page, project }) => {
const stamp = Date.now()
const title = `e2e delete test ${stamp}`
await project.open()
await withSession(project.sdk, title, async (session) => {
project.trackSession(session.id)
await seedMessage(project.sdk, session.id)
await project.gotoSession(session.id)
const menu = await openSessionMoreMenu(page, session.id)
await clickMenuItem(menu, /delete/i)
await confirmDialog(page, /delete/i)
await expect
.poll(
async () => {
const data = await project.sdk.session
.get({ sessionID: session.id })
.then((r) => r.data)
.catch(() => undefined)
return data?.id
},
{ timeout: 30_000 },
)
.toBeUndefined()
await openSidebar(page)
await expect(page.locator(sessionItemSelector(session.id))).toHaveCount(0)
})
})
test("session can be shared and unshared via header button", async ({ page, project }) => {
test.skip(shareDisabled, "Share is disabled in this environment (KILO_DISABLE_SHARE).")
const stamp = Date.now()
const title = `e2e share test ${stamp}`
await project.open()
await withSession(project.sdk, title, async (session) => {
project.trackSession(session.id)
await project.gotoSession(session.id)
await project.prompt(`share seed ${stamp}`)
const shared = await openSharePopover(page)
const publish = shared.popoverBody.getByRole("button", { name: "Publish" }).first()
await expect(publish).toBeVisible({ timeout: 30_000 })
await publish.click()
await expect(shared.popoverBody.getByRole("button", { name: "Unpublish" }).first()).toBeVisible({
timeout: 30_000,
})
await expect
.poll(
async () => {
const data = await project.sdk.session.get({ sessionID: session.id }).then((r) => r.data)
return data?.share?.url || undefined
},
{ timeout: 30_000 },
)
.not.toBeUndefined()
const unpublish = shared.popoverBody.getByRole("button", { name: "Unpublish" }).first()
await expect(unpublish).toBeVisible({ timeout: 30_000 })
await unpublish.click()
await expect(shared.popoverBody.getByRole("button", { name: "Publish" }).first()).toBeVisible({
timeout: 30_000,
})
await expect
.poll(
async () => {
const data = await project.sdk.session.get({ sessionID: session.id }).then((r) => r.data)
return data?.share?.url || undefined
},
{ timeout: 30_000 },
)
.toBeUndefined()
const unshared = await openSharePopover(page)
await expect(unshared.popoverBody.getByRole("button", { name: "Publish" }).first()).toBeVisible({
timeout: 30_000,
})
})
})
-11
View File
@@ -1,11 +0,0 @@
import { test } from "@playwright/test"
test(
"test something cool",
{
annotation: { type: "todo" },
},
async () => {
test.fixme()
},
)
-9
View File
@@ -1,9 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"noEmit": true,
"rootDir": "..",
"types": ["node", "bun"]
},
"include": ["./**/*.ts"]
}
-75
View File
@@ -1,75 +0,0 @@
import { GlobalRegistrator } from "@happy-dom/global-registrator"
GlobalRegistrator.register()
const originalGetContext = HTMLCanvasElement.prototype.getContext
// @ts-expect-error - we're overriding with a simplified mock
HTMLCanvasElement.prototype.getContext = function (contextType: string, _options?: unknown) {
if (contextType === "2d") {
return {
canvas: this,
fillStyle: "#000000",
strokeStyle: "#000000",
font: "12px monospace",
textAlign: "start",
textBaseline: "alphabetic",
globalAlpha: 1,
globalCompositeOperation: "source-over",
imageSmoothingEnabled: true,
lineWidth: 1,
lineCap: "butt",
lineJoin: "miter",
miterLimit: 10,
shadowBlur: 0,
shadowColor: "rgba(0, 0, 0, 0)",
shadowOffsetX: 0,
shadowOffsetY: 0,
fillRect: () => {},
strokeRect: () => {},
clearRect: () => {},
fillText: () => {},
strokeText: () => {},
measureText: (text: string) => ({ width: text.length * 8 }),
drawImage: () => {},
save: () => {},
restore: () => {},
scale: () => {},
rotate: () => {},
translate: () => {},
transform: () => {},
setTransform: () => {},
resetTransform: () => {},
createLinearGradient: () => ({ addColorStop: () => {} }),
createRadialGradient: () => ({ addColorStop: () => {} }),
createPattern: () => null,
beginPath: () => {},
closePath: () => {},
moveTo: () => {},
lineTo: () => {},
bezierCurveTo: () => {},
quadraticCurveTo: () => {},
arc: () => {},
arcTo: () => {},
ellipse: () => {},
rect: () => {},
fill: () => {},
stroke: () => {},
clip: () => {},
isPointInPath: () => false,
isPointInStroke: () => false,
getTransform: () => ({}),
getImageData: () => ({
data: new Uint8ClampedArray(0),
width: 0,
height: 0,
}),
putImageData: () => {},
createImageData: () => ({
data: new Uint8ClampedArray(0),
width: 0,
height: 0,
}),
} as unknown as CanvasRenderingContext2D
}
return originalGetContext.call(this, contextType as "2d", _options)
}
-23
View File
@@ -1,23 +0,0 @@
<!doctype html>
<html lang="en" style="background-color: var(--background-base)">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, interactive-widget=resizes-content" />
<title>OpenCode</title>
<link rel="icon" type="image/png" href="/favicon-96x96-v3.png" sizes="96x96" />
<link rel="icon" type="image/svg+xml" href="/favicon-v3.svg" />
<link rel="shortcut icon" href="/favicon-v3.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-v3.png" />
<link rel="manifest" href="/site.webmanifest" />
<meta name="theme-color" content="#F8F7F7" />
<meta name="theme-color" content="#131010" media="(prefers-color-scheme: dark)" />
<meta property="og:image" content="/social-share.png" />
<meta property="twitter:image" content="/social-share.png" />
<script id="oc-theme-preload-script" src="/oc-theme-preload.js"></script>
</head>
<body class="antialiased overscroll-none text-12-regular overflow-hidden">
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root" class="flex flex-col h-dvh p-px"></div>
<script src="/src/entry.tsx" type="module"></script>
</body>
</html>
-80
View File
@@ -1,80 +0,0 @@
{
"name": "@opencode-ai/app",
"version": "7.2.24",
"description": "",
"type": "module",
"exports": {
".": "./src/index.ts",
"./vite": "./vite.js",
"./index.css": "./src/index.css"
},
"scripts": {
"typecheck": "tsgo -b",
"start": "vite",
"dev": "vite",
"build": "vite build",
"serve": "vite preview",
"test": "bun run test:unit",
"test:ci": "mkdir -p .artifacts/unit && bun test --preload ./happydom.ts ./src --reporter=junit --reporter-outfile=.artifacts/unit/junit.xml",
"test:unit": "bun test --preload ./happydom.ts ./src",
"test:unit:watch": "bun test --watch --preload ./happydom.ts ./src",
"test:e2e": "playwright test",
"test:e2e:local": "playwright test",
"test:e2e:ui": "playwright test --ui",
"test:e2e:report": "playwright show-report e2e/playwright-report"
},
"license": "MIT",
"devDependencies": {
"@happy-dom/global-registrator": "20.0.11",
"@playwright/test": "catalog:",
"@tailwindcss/vite": "catalog:",
"@tsconfig/bun": "1.0.9",
"@types/bun": "catalog:",
"@types/luxon": "catalog:",
"@types/node": "catalog:",
"@typescript/native-preview": "catalog:",
"typescript": "catalog:",
"vite": "catalog:",
"vite-plugin-icons-spritesheet": "3.0.1",
"vite-plugin-solid": "catalog:"
},
"dependencies": {
"@kobalte/core": "catalog:",
"@kilocode/sdk": "workspace:*",
"@opencode-ai/ui": "workspace:*",
"@opencode-ai/shared": "workspace:*",
"@shikijs/transformers": "3.9.2",
"@solid-primitives/active-element": "2.1.3",
"@solid-primitives/audio": "1.4.2",
"@solid-primitives/event-bus": "1.1.2",
"@solid-primitives/event-listener": "2.4.5",
"@solid-primitives/i18n": "2.2.1",
"@solid-primitives/media": "2.3.3",
"@solid-primitives/resize-observer": "2.1.5",
"@solid-primitives/scroll": "2.1.3",
"@solid-primitives/storage": "catalog:",
"@solid-primitives/timer": "1.4.4",
"@solid-primitives/websocket": "1.3.1",
"@solidjs/meta": "catalog:",
"@solidjs/router": "catalog:",
"@tanstack/solid-query": "5.91.4",
"@thisbeyond/solid-dnd": "0.7.5",
"diff": "catalog:",
"effect": "catalog:",
"fuzzysort": "catalog:",
"ghostty-web": "github:anomalyco/ghostty-web#main",
"luxon": "catalog:",
"marked": "catalog:",
"marked-shiki": "catalog:",
"remeda": "catalog:",
"shiki": "catalog:",
"solid-js": "catalog:",
"solid-list": "catalog:",
"tailwindcss": "catalog:",
"virtua": "catalog:",
"zod": "catalog:",
"@kilocode/kilo-ui": "workspace:*",
"@kilocode/kilo-i18n": "workspace:*"
},
"peerDependencies": {}
}
-50
View File
@@ -1,50 +0,0 @@
import { defineConfig, devices } from "@playwright/test"
const port = Number(process.env.PLAYWRIGHT_PORT ?? 3000)
const baseURL = process.env.PLAYWRIGHT_BASE_URL ?? `http://127.0.0.1:${port}`
const serverHost = process.env.PLAYWRIGHT_SERVER_HOST ?? "127.0.0.1"
const serverPort = process.env.PLAYWRIGHT_SERVER_PORT ?? "4096"
const command = `bun run dev -- --host 0.0.0.0 --port ${port}`
const reuse = !process.env.CI
const workers = Number(process.env.PLAYWRIGHT_WORKERS ?? (process.env.CI ? 5 : 0)) || undefined
const reporter = [["html", { outputFolder: "e2e/playwright-report", open: "never" }], ["line"]] as const
if (process.env.PLAYWRIGHT_JUNIT_OUTPUT) {
reporter.push(["junit", { outputFile: process.env.PLAYWRIGHT_JUNIT_OUTPUT }])
}
export default defineConfig({
testDir: "./e2e",
outputDir: "./e2e/test-results",
timeout: 60_000,
expect: {
timeout: 10_000,
},
fullyParallel: process.env.PLAYWRIGHT_FULLY_PARALLEL === "1",
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers,
reporter,
webServer: {
command,
url: baseURL,
reuseExistingServer: reuse,
timeout: 120_000,
env: {
VITE_KILO_SERVER_HOST: serverHost,
VITE_KILO_SERVER_PORT: serverPort,
},
},
use: {
baseURL,
trace: "on-first-retry",
screenshot: "only-on-failure",
video: "retain-on-failure",
},
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},
],
})
-17
View File
@@ -1,17 +0,0 @@
/assets/*.js
Content-Type: application/javascript
/assets/*.mjs
Content-Type: application/javascript
/assets/*.css
Content-Type: text/css
/*.js
Content-Type: application/javascript
/*.mjs
Content-Type: application/javascript
/*.css
Content-Type: text/css
@@ -1 +0,0 @@
../../ui/src/assets/favicon/apple-touch-icon-v3.png
-1
View File
@@ -1 +0,0 @@
../../ui/src/assets/favicon/apple-touch-icon.png
-1
View File
@@ -1 +0,0 @@
../../ui/src/assets/favicon/favicon-96x96-v3.png
-1
View File
@@ -1 +0,0 @@
../../ui/src/assets/favicon/favicon-96x96.png
-1
View File
@@ -1 +0,0 @@
../../ui/src/assets/favicon/favicon-v3.ico
-1
View File
@@ -1 +0,0 @@
../../ui/src/assets/favicon/favicon-v3.svg
-1
View File
@@ -1 +0,0 @@
../../ui/src/assets/favicon/favicon.ico

Some files were not shown because too many files have changed in this diff Show More