mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-24 16:02:55 +08:00
Merge branch 'main' into fix/plan-mode-mkdir-recursive-onedrive-9755
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
---
|
||||
"kilo-code": minor
|
||||
"@kilocode/cli": minor
|
||||
---
|
||||
|
||||
Support starting Agent Manager local sessions and worktree sessions from an experimental agent tool.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"kilo-code": patch
|
||||
---
|
||||
|
||||
Dispose autocomplete editor listeners when inline completions are disabled.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@kilocode/kilo-jetbrains": patch
|
||||
---
|
||||
|
||||
Improve the JetBrains model picker with sections, favorites, and smart filtering.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"kilo-code": patch
|
||||
---
|
||||
|
||||
Center the sidebar prompt toolbar on wider sidebars and hide unavailable toolbar actions.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"kilo-code": patch
|
||||
---
|
||||
|
||||
Center welcome notifications in wide VS Code views.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@kilocode/cli": patch
|
||||
---
|
||||
|
||||
Avoid repeated command approval prompts when multiple sessions request the same saved command permission, without widening bash permission matching.
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
"@kilocode/cli": patch
|
||||
"kilo-code": patch
|
||||
---
|
||||
|
||||
Scope the custom commit message prompt to the current project. Setting it in the VS Code settings now writes to the workspace's `kilo.json` so different repositories can have different conventions, instead of silently applying globally. Also fixes the project-level config update endpoint, which previously wrote to a file that wasn't loaded.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@kilocode/cli": patch
|
||||
"kilo-code": patch
|
||||
---
|
||||
|
||||
Preserve the selected thinking level after compacting a session.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@kilocode/kilo-jetbrains": patch
|
||||
---
|
||||
|
||||
Scale JetBrains chat transcript fonts from the editor font settings.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"kilo-code": minor
|
||||
---
|
||||
|
||||
Keep reasoning blocks expanded by default and add a setting to auto-collapse them after completion.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@kilocode/cli": patch
|
||||
---
|
||||
|
||||
Honor allowed read-only external-directory access to Kilo config paths without repeated permission prompts.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@kilocode/kilo-jetbrains": patch
|
||||
---
|
||||
|
||||
Keep the JetBrains model picker at the same row position when models are favorited or unfavorited.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@kilocode/cli": patch
|
||||
---
|
||||
|
||||
Use a GPT-5.5-specific coding prompt that improves autonomous task handling while keeping older Codex generations on their existing prompt.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@kilocode/kilo-jetbrains": patch
|
||||
---
|
||||
|
||||
Match VS Code-style reasoning and tool run rendering in JetBrains chat.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@kilocode/kilo-jetbrains": patch
|
||||
---
|
||||
|
||||
Improve the JetBrains mode picker with descriptions, deprecated badges, and native IntelliJ selection behavior.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@kilocode/kilo-jetbrains": patch
|
||||
---
|
||||
|
||||
Support stopping active JetBrains sessions from the prompt action button.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"kilo-code": patch
|
||||
---
|
||||
|
||||
Preserve Agent Manager local sessions across panel restarts when session refreshes complete out of order.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@kilocode/kilo-jetbrains": patch
|
||||
---
|
||||
|
||||
Size the JetBrains model picker from its rendered model rows instead of fixed popup dimensions.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"kilo-code": patch
|
||||
---
|
||||
|
||||
Update regular Kilo editor tab titles to follow the current session title.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"kilo-code": patch
|
||||
---
|
||||
|
||||
Support configuring custom agent tool permissions from the VS Code agent editor.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@kilocode/kilo-jetbrains": patch
|
||||
---
|
||||
|
||||
Support reasoning effort selection and resetting JetBrains chat model overrides to the default model.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@kilocode/kilo-jetbrains": patch
|
||||
---
|
||||
|
||||
Align checked and unchecked reasoning picker rows in the JetBrains plugin.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@kilocode/cli": patch
|
||||
---
|
||||
|
||||
Keep Remote status visible in the TUI while remote control is connecting.
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
"kilo-code": patch
|
||||
"@kilocode/cli": patch
|
||||
---
|
||||
|
||||
Stop emitting `ai.*` and `gen_ai.*` OpenTelemetry spans from AI SDK calls, and remove the PostHog bridge that forwarded them. Tool/session/indexing telemetry is unchanged.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@kilocode/kilo-jetbrains": patch
|
||||
---
|
||||
|
||||
Preserve JetBrains chat scroll position when reviewing earlier session output.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"kilo-code": minor
|
||||
---
|
||||
|
||||
Add a Display setting to collapse terminal command blocks by default instead of keeping them expanded.
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -14,7 +14,7 @@ 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 }}
|
||||
|
||||
|
||||
@@ -6,9 +6,6 @@ on:
|
||||
- ".github/**"
|
||||
- "github/**"
|
||||
- "sdks/vscode/**"
|
||||
- "packages/app/**"
|
||||
- "packages/desktop/**"
|
||||
- "packages/desktop-electron/**"
|
||||
- "packages/extensions/**"
|
||||
- "packages/opencode/**"
|
||||
- "packages/script/**"
|
||||
@@ -24,7 +21,7 @@ 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
|
||||
@@ -40,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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
-1
@@ -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
|
||||
+2
-5
@@ -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}
|
||||
|
||||
+2
-5
@@ -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
-23
@@ -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
|
||||
+31
-20
@@ -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:
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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-*
|
||||
|
||||
+26
-136
@@ -42,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
|
||||
|
||||
@@ -78,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
|
||||
|
||||
@@ -99,19 +99,20 @@ jobs:
|
||||
# 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. compression-level: 0 on the outer zip avoids double-compression.
|
||||
# zstd + GNU tar are preinstalled on both github-hosted and blacksmith ubuntu-2404.
|
||||
# See actions/upload-artifact#36, actions/toolkit#1533.
|
||||
# ~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@v4
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: kilo-cli
|
||||
path: /tmp/kilo-cli.tar.zst
|
||||
compression-level: 0
|
||||
archive: false
|
||||
# kilocode_change end
|
||||
|
||||
outputs:
|
||||
@@ -122,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"
|
||||
@@ -135,10 +136,11 @@ jobs:
|
||||
run: bun install -g @vscode/vsce
|
||||
|
||||
# kilocode_change start - download into /tmp and extract the tar.zst packed by build-cli
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: kilo-cli
|
||||
name: kilo-cli.tar.zst
|
||||
path: /tmp
|
||||
skip-decompress: true
|
||||
|
||||
- name: Unpack CLI dist
|
||||
run: |
|
||||
@@ -154,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.
|
||||
@@ -295,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
|
||||
|
||||
@@ -315,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"
|
||||
@@ -333,23 +222,24 @@ jobs:
|
||||
# kilocode_change end
|
||||
|
||||
# kilocode_change start - download into /tmp and extract the tar.zst packed by build-cli
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: kilo-cli
|
||||
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
|
||||
- uses: actions/download-artifact@v4
|
||||
- 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') }}
|
||||
|
||||
@@ -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: |
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -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.
|
||||
Generated
+8
-8
@@ -5,16 +5,16 @@
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@kilocode/plugin": "7.2.24"
|
||||
"@kilocode/plugin": "7.2.31"
|
||||
}
|
||||
},
|
||||
"node_modules/@kilocode/plugin": {
|
||||
"version": "7.2.24",
|
||||
"resolved": "https://registry.npmjs.org/@kilocode/plugin/-/plugin-7.2.24.tgz",
|
||||
"integrity": "sha512-b2Xh3N/KaGdYj9hZbzZLKlDHXUsdMey0zL8G+RsnxDQCuhNm46LPyixtlJQhbCyir/zEEvy3uJI9kqKCsOWmQg==",
|
||||
"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.24",
|
||||
"@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.24",
|
||||
"resolved": "https://registry.npmjs.org/@kilocode/sdk/-/sdk-7.2.24.tgz",
|
||||
"integrity": "sha512-Yos6WeyvMl86OGXLQqQ9YWDm/geAC77xetoptEsS3DLtPVgvz1Nqkvas77fAuAqwkSfGL2mLIiHUTqJUTVflOw==",
|
||||
"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.
|
||||
Generated
+8
-8
@@ -5,16 +5,16 @@
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@kilocode/plugin": "7.2.24"
|
||||
"@kilocode/plugin": "7.2.31"
|
||||
}
|
||||
},
|
||||
"node_modules/@kilocode/plugin": {
|
||||
"version": "7.2.24",
|
||||
"resolved": "https://registry.npmjs.org/@kilocode/plugin/-/plugin-7.2.24.tgz",
|
||||
"integrity": "sha512-b2Xh3N/KaGdYj9hZbzZLKlDHXUsdMey0zL8G+RsnxDQCuhNm46LPyixtlJQhbCyir/zEEvy3uJI9kqKCsOWmQg==",
|
||||
"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.24",
|
||||
"@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.24",
|
||||
"resolved": "https://registry.npmjs.org/@kilocode/sdk/-/sdk-7.2.24.tgz",
|
||||
"integrity": "sha512-Yos6WeyvMl86OGXLQqQ9YWDm/geAC77xetoptEsS3DLtPVgvz1Nqkvas77fAuAqwkSfGL2mLIiHUTqJUTVflOw==",
|
||||
"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"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,6 +21,7 @@ Kilo CLI is an open source AI coding agent that generates code from natural lang
|
||||
- **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
|
||||
@@ -43,15 +44,18 @@ All products are clients of the **CLI** (`packages/opencode/`), which contains t
|
||||
|
||||
| Product | Package | Description |
|
||||
|---|---|---|
|
||||
| Kilo CLI | `packages/opencode/` | Core engine. TUI, `kilo run`, `kilo serve`, `kilo web`. Fork of upstream OpenCode. |
|
||||
| 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:
|
||||
@@ -64,9 +68,7 @@ Turborepo + Bun workspaces. The packages you'll work with most:
|
||||
| `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/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 |
|
||||
|
||||
@@ -254,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.
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# 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, TUI, or desktop app.
|
||||
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).
|
||||
|
||||
|
||||
@@ -267,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;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -286,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;
|
||||
|
||||
@@ -385,6 +385,57 @@ add_to_path() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Persistently add $INSTALL_DIR to the Windows user PATH via the registry.
|
||||
# Works from Git Bash (MINGW64), MSYS2, and Cygwin.
|
||||
# Converts the MSYS/Cygwin path to native Windows form so that cmd and
|
||||
# PowerShell also see the entry.
|
||||
add_to_windows_path() {
|
||||
local win_install_dir
|
||||
if command -v cygpath >/dev/null 2>&1; then
|
||||
win_install_dir=$(cygpath -w "$INSTALL_DIR")
|
||||
else
|
||||
# Fallback: naive conversion /c/Users/... -> C:\Users\...
|
||||
win_install_dir=$(echo "$INSTALL_DIR" | sed -e 's|^/\([a-zA-Z]\)/|\1:\\|' -e 's|/|\\|g')
|
||||
fi
|
||||
|
||||
# Escape single-quotes for PowerShell single-quoted strings ('' is the escape sequence)
|
||||
local win_install_dir_ps="${win_install_dir//\'/\'\'}"
|
||||
|
||||
local ps_cmd
|
||||
ps_cmd=$(cat <<EOFPS
|
||||
\$kiloPath = '$win_install_dir_ps'
|
||||
\$currentPath = [Environment]::GetEnvironmentVariable('Path', 'User')
|
||||
\$pathParts = if ([string]::IsNullOrEmpty(\$currentPath)) { @() } else { \$currentPath -split ';' }
|
||||
if (\$pathParts -notcontains \$kiloPath) {
|
||||
\$newPath = if ([string]::IsNullOrEmpty(\$currentPath)) { \$kiloPath } else { "\$currentPath;\$kiloPath" }
|
||||
[Environment]::SetEnvironmentVariable('Path', \$newPath, 'User')
|
||||
Write-Output 'added'
|
||||
}
|
||||
EOFPS
|
||||
)
|
||||
|
||||
local ps_output
|
||||
if command -v powershell.exe >/dev/null 2>&1; then
|
||||
ps_output=$(powershell.exe -NoProfile -NonInteractive -Command "$ps_cmd" 2>/dev/null) || return 1
|
||||
elif command -v pwsh >/dev/null 2>&1; then
|
||||
ps_output=$(pwsh -NoProfile -NonInteractive -Command "$ps_cmd" 2>/dev/null) || return 1
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ "$ps_output" == *"added"* ]]; then
|
||||
print_message info "${MUTED}Successfully added ${NC}kilo ${MUTED}to Windows user PATH${NC}"
|
||||
fi
|
||||
|
||||
# Also add to the current bash session so `kilo` works immediately
|
||||
# without the user needing to open a new terminal.
|
||||
if [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then
|
||||
export PATH="$INSTALL_DIR:$PATH"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
XDG_CONFIG_HOME=${XDG_CONFIG_HOME:-$HOME/.config}
|
||||
|
||||
current_shell=$(basename "$SHELL")
|
||||
@@ -411,6 +462,13 @@ case $current_shell in
|
||||
esac
|
||||
|
||||
if [[ "$no_modify_path" != "true" ]]; then
|
||||
# Detect if we are running on Windows (MINGW, MSYS, Cygwin) regardless
|
||||
# of whether the binary was installed from download or --binary.
|
||||
_running_on_windows=false
|
||||
case "$(uname -s)" in
|
||||
MINGW*|MSYS*|CYGWIN*) _running_on_windows=true ;;
|
||||
esac
|
||||
|
||||
config_file=""
|
||||
for file in $config_files; do
|
||||
if [[ -f $file ]]; then
|
||||
@@ -419,7 +477,12 @@ if [[ "$no_modify_path" != "true" ]]; then
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -z $config_file ]]; then
|
||||
if [[ "$_running_on_windows" == "true" ]] && add_to_windows_path; then
|
||||
# Handled via the Windows user-level PATH in the registry.
|
||||
# The change is visible in new cmd / PowerShell / Git Bash sessions.
|
||||
# We also exported it into the current session above.
|
||||
:
|
||||
elif [[ -z $config_file ]]; then
|
||||
print_message warning "No config file found for $current_shell. You may need to manually add to PATH:"
|
||||
print_message info " export PATH=$INSTALL_DIR:\$PATH"
|
||||
elif [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then
|
||||
|
||||
@@ -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
@@ -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
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"nodeModules": {
|
||||
"x86_64-linux": "sha256-4hLG583KyBLxAOYvIiVMh+xLtVAdR3QgWFEqnGlZLZw=",
|
||||
"aarch64-linux": "sha256-+60DxQ/GKAAoMFuAQRFoYM0HlZMzpLpLrTD7qmweUUA=",
|
||||
"aarch64-darwin": "sha256-3WCWVqaxu+lCIc51N7wOttM/hoXbbnmtw7EP2eGstQg=",
|
||||
"x86_64-darwin": "sha256-K8iG+OHkgC/lwud6hQ91sXF3c7UbdQQYRU/KK2P+B/4="
|
||||
"x86_64-linux": "sha256-BkFDOCEvivFrxKOvMjAIhj3QZwmb9vE/KvCZv5puw6E=",
|
||||
"aarch64-linux": "sha256-sTkfQNjqCNCxmHbpQ8woA6azoiyD37e8Xy7UwB6eNsA=",
|
||||
"aarch64-darwin": "sha256-lKuagsTHqij5C764DM3d+BOe4JKs2KV8XiwSlJItGMg=",
|
||||
"x86_64-darwin": "sha256-g5gZMHLq8feSZHVaCOLGU6Tj2Qw56Zogif3pT9EIlz8="
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ stdenvNoCC.mkDerivation (finalAttrs: {
|
||||
[
|
||||
ripgrep
|
||||
]
|
||||
# bun runs sysctl to detect if dunning on rosetta2
|
||||
# bun runs sysctl to detect if running on rosetta2
|
||||
++ lib.optional stdenvNoCC.hostPlatform.isDarwin sysctl
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
+8
-10
@@ -8,13 +8,10 @@
|
||||
"scripts": {
|
||||
"dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts",
|
||||
"dev-setup": "bun run --cwd packages/opencode --conditions=browser src/index.ts dev-setup",
|
||||
"dev:desktop": "bun --cwd packages/desktop-electron dev",
|
||||
"dev:web": "bun --cwd packages/app dev",
|
||||
"dev:console": "ulimit -n 10240 2>/dev/null; bun run --cwd packages/console/app dev",
|
||||
"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!'",
|
||||
@@ -27,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.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",
|
||||
@@ -46,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",
|
||||
@@ -148,6 +146,6 @@
|
||||
"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.31",
|
||||
"version": "7.2.42",
|
||||
"peerDependencies": {}
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
src/assets/theme.css
|
||||
e2e/test-results
|
||||
e2e/playwright-report
|
||||
@@ -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
|
||||
@@ -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.)
|
||||
@@ -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()
|
||||
})
|
||||
})
|
||||
@@ -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,
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,11 +0,0 @@
|
||||
import { test } from "@playwright/test"
|
||||
|
||||
test(
|
||||
"test something cool",
|
||||
{
|
||||
annotation: { type: "todo" },
|
||||
},
|
||||
async () => {
|
||||
test.fixme()
|
||||
},
|
||||
)
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
"rootDir": "..",
|
||||
"types": ["node", "bun"]
|
||||
},
|
||||
"include": ["./**/*.ts"]
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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>
|
||||
@@ -1,80 +0,0 @@
|
||||
{
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "7.2.31",
|
||||
"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": {}
|
||||
}
|
||||
@@ -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"] },
|
||||
},
|
||||
],
|
||||
})
|
||||
@@ -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 +0,0 @@
|
||||
../../ui/src/assets/favicon/apple-touch-icon.png
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
../../ui/src/assets/favicon/favicon-96x96-v3.png
|
||||
@@ -1 +0,0 @@
|
||||
../../ui/src/assets/favicon/favicon-96x96.png
|
||||
@@ -1 +0,0 @@
|
||||
../../ui/src/assets/favicon/favicon-v3.ico
|
||||
@@ -1 +0,0 @@
|
||||
../../ui/src/assets/favicon/favicon-v3.svg
|
||||
@@ -1 +0,0 @@
|
||||
../../ui/src/assets/favicon/favicon.ico
|
||||
@@ -1 +0,0 @@
|
||||
../../ui/src/assets/favicon/favicon.svg
|
||||
@@ -1,35 +0,0 @@
|
||||
;(function () {
|
||||
var key = "opencode-theme-id"
|
||||
var themeId = localStorage.getItem(key) || "oc-2"
|
||||
|
||||
if (themeId === "oc-1") {
|
||||
themeId = "oc-2"
|
||||
localStorage.setItem(key, themeId)
|
||||
localStorage.removeItem("opencode-theme-css-light")
|
||||
localStorage.removeItem("opencode-theme-css-dark")
|
||||
}
|
||||
|
||||
var scheme = localStorage.getItem("opencode-color-scheme") || "system"
|
||||
var isDark = scheme === "dark" || (scheme === "system" && matchMedia("(prefers-color-scheme: dark)").matches)
|
||||
var mode = isDark ? "dark" : "light"
|
||||
|
||||
document.documentElement.dataset.theme = themeId
|
||||
document.documentElement.dataset.colorScheme = mode
|
||||
|
||||
if (themeId === "oc-2") return
|
||||
|
||||
var css = localStorage.getItem("opencode-theme-css-" + mode)
|
||||
if (css) {
|
||||
var style = document.createElement("style")
|
||||
style.id = "oc-theme-preload"
|
||||
style.textContent =
|
||||
":root{color-scheme:" +
|
||||
mode +
|
||||
";--text-mix-blend-mode:" +
|
||||
(isDark ? "plus-lighter" : "multiply") +
|
||||
";" +
|
||||
css +
|
||||
"}"
|
||||
document.head.appendChild(style)
|
||||
}
|
||||
})()
|
||||
@@ -1 +0,0 @@
|
||||
../../ui/src/assets/favicon/site.webmanifest
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user