diff --git a/.github/workflows/disabled/review.yml.disabled b/.github/workflows/disabled/review.yml.disabled index 542e3a6e4f..b44d262df7 100644 --- a/.github/workflows/disabled/review.yml.disabled +++ b/.github/workflows/disabled/review.yml.disabled @@ -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}' ${{ steps.pr-number.outputs.number }} diff --git a/.opencode/skills/effect/SKILL.md b/.opencode/skills/effect/SKILL.md index 4758146377..78216ab01c 100644 --- a/.opencode/skills/effect/SKILL.md +++ b/.opencode/skills/effect/SKILL.md @@ -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. diff --git a/.opencode/tool/github-triage.ts b/.opencode/tool/github-triage.ts index 2f8635826f..8f80f86857 100644 --- a/.opencode/tool/github-triage.ts +++ b/.opencode/tool/github-triage.ts @@ -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 diff --git a/bun.lock b/bun.lock index be06087afe..f2bf0dfaf4 100644 --- a/bun.lock +++ b/bun.lock @@ -38,6 +38,7 @@ "@kilocode/kilo-ui": "workspace:*", "@kilocode/sdk": "workspace:*", "@kobalte/core": "catalog:", + "@opencode-ai/core": "workspace:*", "@opencode-ai/shared": "workspace:*", "@opencode-ai/ui": "workspace:*", "@shikijs/transformers": "3.9.2", @@ -86,6 +87,40 @@ "vite-plugin-solid": "catalog:", }, }, + "packages/core": { + "name": "@opencode-ai/core", + "version": "7.2.30", + "bin": { + "opencode": "./bin/opencode", + }, + "dependencies": { + "@effect/opentelemetry": "catalog:", + "@effect/platform-node": "catalog:", + "@npmcli/arborist": "9.4.0", + "@npmcli/config": "10.8.1", + "@opentelemetry/api": "1.9.0", + "@opentelemetry/context-async-hooks": "2.6.1", + "@opentelemetry/exporter-trace-otlp-http": "0.214.0", + "@opentelemetry/sdk-trace-base": "2.6.1", + "cross-spawn": "catalog:", + "effect": "catalog:", + "glob": "13.0.5", + "mime-types": "3.0.2", + "minimatch": "10.2.5", + "npm-package-arg": "13.0.2", + "semver": "^7.6.3", + "xdg-basedir": "5.1.0", + "zod": "catalog:", + }, + "devDependencies": { + "@tsconfig/bun": "catalog:", + "@types/bun": "catalog:", + "@types/cross-spawn": "catalog:", + "@types/npm-package-arg": "6.1.4", + "@types/npmcli__arborist": "6.3.3", + "@types/semver": "catalog:", + }, + }, "packages/desktop": { "name": "@opencode-ai/desktop", "version": "7.2.30", @@ -457,8 +492,8 @@ "@opentelemetry/exporter-trace-otlp-http": "0.214.0", "@opentelemetry/sdk-trace-base": "2.6.1", "@opentelemetry/sdk-trace-node": "2.6.1", - "@opentui/core": "0.1.99", - "@opentui/solid": "0.1.99", + "@opentui/core": "catalog:", + "@opentui/solid": "catalog:", "@parcel/watcher": "2.5.1", "@pierre/diffs": "catalog:", "@solid-primitives/event-bus": "1.1.2", @@ -495,7 +530,7 @@ "open": "10.1.2", "opencode-gitlab-auth": "2.0.1", "opencode-poe-auth": "0.0.1", - "opentui-spinner": "0.0.6", + "opentui-spinner": "catalog:", "partial-json": "0.1.7", "remeda": "catalog:", "ripgrep": "0.3.1", @@ -523,6 +558,7 @@ "@babel/core": "7.28.4", "@effect/language-service": "0.84.2", "@octokit/webhooks-types": "7.6.1", + "@opencode-ai/core": "workspace:*", "@opencode-ai/script": "workspace:*", "@opencode-ai/shared": "workspace:*", "@parcel/watcher-darwin-arm64": "2.5.1", @@ -548,6 +584,7 @@ "@typescript/native-preview": "catalog:", "drizzle-kit": "catalog:", "drizzle-orm": "catalog:", + "prettier": "3.6.2", "typescript": "catalog:", "vscode-languageserver-types": "3.17.5", "why-is-node-running": "3.2.2", @@ -563,16 +600,16 @@ "zod": "catalog:", }, "devDependencies": { - "@opentui/core": "0.1.99", - "@opentui/solid": "0.1.99", + "@opentui/core": "catalog:", + "@opentui/solid": "catalog:", "@tsconfig/node22": "catalog:", "@types/node": "catalog:", "@typescript/native-preview": "catalog:", "typescript": "catalog:", }, "peerDependencies": { - "@opentui/core": ">=0.1.100", - "@opentui/solid": ">=0.1.100", + "@opentui/core": ">=0.1.105", + "@opentui/solid": ">=0.1.105", }, "optionalPeers": [ "@opentui/core", @@ -658,6 +695,7 @@ "dependencies": { "@kilocode/sdk": "workspace:*", "@kobalte/core": "catalog:", + "@opencode-ai/core": "workspace:*", "@opencode-ai/shared": "workspace:*", "@pierre/diffs": "catalog:", "@shikijs/transformers": "3.9.2", @@ -734,16 +772,16 @@ }, "catalog": { "@cloudflare/workers-types": "4.20251008.0", - "@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", "@hono/zod-validator": "0.4.2", "@kobalte/core": "0.13.11", "@lydell/node-pty": "1.2.0-beta.10", "@npmcli/arborist": "9.4.0", "@octokit/rest": "22.0.0", "@openauthjs/openauth": "0.0.0-20250322224806", - "@opentui/core": "0.1.99", - "@opentui/solid": "0.1.99", + "@opentui/core": "0.1.105", + "@opentui/solid": "0.1.105", "@pierre/diffs": "1.1.0-beta.18", "@playwright/test": "1.59.1", "@solid-primitives/storage": "4.3.3", @@ -765,13 +803,14 @@ "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", "fuzzysort": "3.1.0", "hono": "4.12.12", "hono-openapi": "1.1.2", "luxon": "3.6.1", "marked": "17.0.1", "marked-shiki": "1.2.1", + "opentui-spinner": "0.0.6", "remeda": "2.26.0", "remend": "1.3.0", "semver": "7.7.4", @@ -1139,9 +1178,9 @@ "@effect/language-service": ["@effect/language-service@0.84.2", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-l04qNxpiA8rY5yXWckRPJ7Mk5MNerXuNymSFf+IdflfI5i8jgL1bpBNLuP6ijg7wgjdHc/KmTnCj2kT0SCntuA=="], - "@effect/opentelemetry": ["@effect/opentelemetry@4.0.0-beta.48", "", { "peerDependencies": { "@opentelemetry/api": "^1.9", "@opentelemetry/resources": "^2.0.0", "@opentelemetry/sdk-logs": ">=0.203.0 <0.300.0", "@opentelemetry/sdk-metrics": "^2.0.0", "@opentelemetry/sdk-trace-base": "^2.0.0", "@opentelemetry/sdk-trace-node": "^2.0.0", "@opentelemetry/sdk-trace-web": "^2.0.0", "@opentelemetry/semantic-conventions": "^1.33.0", "effect": "^4.0.0-beta.48" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/resources", "@opentelemetry/sdk-logs", "@opentelemetry/sdk-metrics", "@opentelemetry/sdk-trace-base", "@opentelemetry/sdk-trace-node", "@opentelemetry/sdk-trace-web"] }, "sha512-vHk/X1vgDrviGcOTHQqzm2D81TtyPE/C7Qdksg5eAdbGpnqL4Dm4lk6PzTReQ0pO1/avIvWqpxy315IURV0Ldw=="], + "@effect/opentelemetry": ["@effect/opentelemetry@4.0.0-beta.57", "", { "peerDependencies": { "@opentelemetry/api": "^1.9", "@opentelemetry/resources": "^2.0.0", "@opentelemetry/sdk-logs": ">=0.203.0 <0.300.0", "@opentelemetry/sdk-metrics": "^2.0.0", "@opentelemetry/sdk-trace-base": "^2.0.0", "@opentelemetry/sdk-trace-node": "^2.0.0", "@opentelemetry/sdk-trace-web": "^2.0.0", "@opentelemetry/semantic-conventions": "^1.33.0", "effect": "^4.0.0-beta.57" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/resources", "@opentelemetry/sdk-logs", "@opentelemetry/sdk-metrics", "@opentelemetry/sdk-trace-base", "@opentelemetry/sdk-trace-node", "@opentelemetry/sdk-trace-web"] }, "sha512-gdjZPEP0QQg4qmI1vd+443kheeQZKytrjJIzCJncy6ZEpyk/SfrqeStLqLXdTRcms3IB0ls0vOV7KNq7YmBRVA=="], - "@effect/platform-node": ["@effect/platform-node@4.0.0-beta.48", "", { "dependencies": { "@effect/platform-node-shared": "^4.0.0-beta.48", "mime": "^4.1.0", "undici": "^8.0.2" }, "peerDependencies": { "effect": "^4.0.0-beta.48", "ioredis": "^5.7.0" } }, "sha512-8J6H0k9rtbp9O1QvKOyOPRcCTJ8WrR7IzZLJtYFTZ4bXVEEMCTo84h0CRpi7ccpA9t7DLqotip0NeFgiBosNKQ=="], + "@effect/platform-node": ["@effect/platform-node@4.0.0-beta.57", "", { "dependencies": { "@effect/platform-node-shared": "^4.0.0-beta.57", "mime": "^4.1.0", "undici": "^8.0.2" }, "peerDependencies": { "effect": "^4.0.0-beta.57", "ioredis": "^5.7.0" } }, "sha512-la0xxPSAYOsY0d+uVxEBxok3jYB31iPQmIaZZRUj2SNWqcGGHJc6KorKtI8guqSLuv9FGZ255kBWXRbG6hMeeg=="], "@effect/platform-node-shared": ["@effect/platform-node-shared@4.0.0-beta.46", "", { "dependencies": { "@types/ws": "^8.18.1", "ws": "^8.19.0" }, "peerDependencies": { "effect": "^4.0.0-beta.46" } }, "sha512-Yzci82XbZ1W3tuiownsJawrJZTGeTrTZKLD0uxdBWCBzlVyqDwoSwRwO5qh33DurJj9B7iS8MDf14fpGRBPNGQ=="], @@ -1627,6 +1666,8 @@ "@opencode-ai/app": ["@opencode-ai/app@workspace:packages/app"], + "@opencode-ai/core": ["@opencode-ai/core@workspace:packages/core"], + "@opencode-ai/desktop": ["@opencode-ai/desktop@workspace:packages/desktop"], "@opencode-ai/desktop-electron": ["@opencode-ai/desktop-electron@workspace:packages/desktop-electron"], @@ -1673,21 +1714,21 @@ "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="], - "@opentui/core": ["@opentui/core@0.1.99", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "marked": "17.0.1", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.99", "@opentui/core-darwin-x64": "0.1.99", "@opentui/core-linux-arm64": "0.1.99", "@opentui/core-linux-x64": "0.1.99", "@opentui/core-win32-arm64": "0.1.99", "@opentui/core-win32-x64": "0.1.99", "bun-webgpu": "0.1.5", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-I3+AEgGzqNWIpWX9g2WOscSPwtQDNOm4KlBjxBWCZjLxkF07u77heWXF7OiAdhKLtNUW6TFiyt6yznqAZPdG3A=="], + "@opentui/core": ["@opentui/core@0.1.105", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "marked": "17.0.1", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.105", "@opentui/core-darwin-x64": "0.1.105", "@opentui/core-linux-arm64": "0.1.105", "@opentui/core-linux-x64": "0.1.105", "@opentui/core-win32-arm64": "0.1.105", "@opentui/core-win32-x64": "0.1.105", "bun-webgpu": "0.1.5", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-vllSOOCW6VIThV/96GRLJ1IxIBuR+ci6FDvnPIAG4s7SJ/FW6zAkqDn1xrtBwwk/lM3QWjLqy8BZc+zwWvveJA=="], - "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.99", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bzVrqeX2vb5iWrc/ftOUOqeUY8XO+qSgoTwj5TXHuwagavgwD3Hpeyjx8+icnTTeM4pao0som1WR9xfye6/X5Q=="], + "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.105", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1pIL7aer9amwj8EpYoMNtvavKetIe+nX8uBRmYsMQb+KvJoUAZUqENfRW+qHE5WrsOyxx8/QoyXTHw15GG5iLQ=="], - "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.99", "", { "os": "darwin", "cpu": "x64" }, "sha512-VE4FrXBYpkxnvkqcCV1a8aN9jyyMJMihVW+V2NLCtp+4yQsj0AapG5TiUSN76XnmSZRptxDy5rBmEempeoIZbg=="], + "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.105", "", { "os": "darwin", "cpu": "x64" }, "sha512-hLIRSWlK3gY2NRXJGWiTBiMYSmRDjOYFZF6WtUVXhY2SL3sp08dhmr/6dmAVH+3pKCsCipLEsrrcQX6SAihCTA=="], - "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.99", "", { "os": "linux", "cpu": "arm64" }, "sha512-viXQsbpS7yHjYkl7+am32JdvG96QU9lvHh1UiZtpOxcNUUqiYmA2ZwZFPD2Bi54jNyj5l2hjH6YkD3DzE2FEWA=="], + "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.105", "", { "os": "linux", "cpu": "arm64" }, "sha512-jlRKfPkozTZEkHEePuCWYcTIUtPm+ieInAwGVqGmjbvqjxdVv1/W/Dt6LEZ/9jpRiOPd+FjXAfLe6wa/XWHr+w=="], - "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.99", "", { "os": "linux", "cpu": "x64" }, "sha512-WLoEFINOSp0tZSR9y4LUuGc7n4Y7H1wcpjUPzQ9vChkYDXrfZltEanzoDWbDcQ4kZQW5tHVC7LrZHpAsRLwFZg=="], + "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.105", "", { "os": "linux", "cpu": "x64" }, "sha512-kfWS1WMg6qHShmxZX9s1tZc/8JcXw6uyy2UtyTbJdRFExtXGH37oKHi8QK8iPL2ExCx4z7zqVnVJfO3X/Wh7lA=="], - "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.99", "", { "os": "win32", "cpu": "arm64" }, "sha512-yWMOLWCEO8HdrctU1dMkgZC8qGkiO4Dwr4/e11tTvVpRmYhDsP/IR89ZjEEtOwnKwFOFuB/MxvflqaEWVQ2g5Q=="], + "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.105", "", { "os": "win32", "cpu": "arm64" }, "sha512-UFx6A8OpBVbGWK6OAw4GqAqKZgIITJfSOd35pG9yDVKQouHN2OGc2HeeXrH2A4h42p40Xl6IfcqqfllkpC13Dg=="], - "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.99", "", { "os": "win32", "cpu": "x64" }, "sha512-aYRlsL2w8YRL6vPd7/hrqlNVkXU3QowWb01TOvAcHS8UAsXaGFUr47kSDyjxDi1wg1MzmVduCfsC7T3NoThV1w=="], + "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.105", "", { "os": "win32", "cpu": "x64" }, "sha512-f9FqqUmxehwhF+cgyazm0YT0v0BYTTCPzd6eztqhl74N3x/kC+jOOz2rdJDC/tTBo1JVsF64KupOnhIs6/Cogg=="], - "@opentui/solid": ["@opentui/solid@0.1.99", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.99", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.10", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.11" } }, "sha512-DrqqO4h2V88FmeIP2cErYkMU0ZK5MrUsZw3w6IzZpoXyyiL4/9qpWzUq+CXx+r16VP2iGxDJwGKUmtFAzUch2Q=="], + "@opentui/solid": ["@opentui/solid@0.1.105", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.105", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.10", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.11" } }, "sha512-uxnaMP802sCI487pv/Hk9xdFdIj9mkg3eNliAqbqR0Shmd4phcjKEZvPRpijjmI99j4s9nul71jzF3h1oz31Nw=="], "@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="], @@ -3157,7 +3198,7 @@ "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], - "effect": ["effect@4.0.0-beta.48", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.6.0", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.9", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^13.0.0", "yaml": "^2.8.3" } }, "sha512-MMAM/ZabuNdNmgXiin+BAanQXK7qM8mlt7nfXDoJ/Gn9V8i89JlCq+2N0AiWmqFLXjGLA0u3FjiOjSOYQk5uMw=="], + "effect": ["effect@4.0.0-beta.57", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.6.0", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.9", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^13.0.0", "yaml": "^2.8.3" } }, "sha512-rg32VgXnLKaPRs9tbRDaZ5jxmzNY7ojXt85gSHGUTwdlbWH5Ik+OCUY2q14TXliygPGoHwCAvNWS4bQJOqf00g=="], "ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="], diff --git a/kilocode-2.code-workspace b/kilocode-2.code-workspace new file mode 100644 index 0000000000..0efadfb81b --- /dev/null +++ b/kilocode-2.code-workspace @@ -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" + }, + }, +} diff --git a/nix/kilo.nix b/nix/kilo.nix index 6c6375405f..9d01a0ee26 100644 --- a/nix/kilo.nix +++ b/nix/kilo.nix @@ -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 ) } diff --git a/package.json b/package.json index 254cc89838..96f94bfef4 100644 --- a/package.json +++ b/package.json @@ -27,15 +27,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 +46,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", diff --git a/packages/app/package.json b/packages/app/package.json index 9311d276b2..6b9e055fa9 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -74,7 +74,8 @@ "virtua": "catalog:", "zod": "catalog:", "@kilocode/kilo-ui": "workspace:*", - "@kilocode/kilo-i18n": "workspace:*" + "@kilocode/kilo-i18n": "workspace:*", + "@opencode-ai/core": "workspace:*" }, "peerDependencies": {} } diff --git a/packages/app/src/app.tsx b/packages/app/src/app.tsx index 6bec1de84f..db5a81bd95 100644 --- a/packages/app/src/app.tsx +++ b/packages/app/src/app.tsx @@ -82,7 +82,15 @@ declare global { } function QueryProvider(props: ParentProps) { - const client = new QueryClient() + const client = new QueryClient({ + defaultOptions: { + queries: { + refetchOnReconnect: false, + refetchOnMount: false, + refetchOnWindowFocus: false, + }, + }, + }) return {props.children} } diff --git a/packages/app/src/components/dialog-edit-project.tsx b/packages/app/src/components/dialog-edit-project.tsx index 8eb12daf52..b4b69246cb 100644 --- a/packages/app/src/components/dialog-edit-project.tsx +++ b/packages/app/src/components/dialog-edit-project.tsx @@ -9,7 +9,7 @@ import { createStore } from "solid-js/store" import { useGlobalSDK } from "@/context/global-sdk" import { useGlobalSync } from "@/context/global-sync" import { type LocalProject, getAvatarColors } from "@/context/layout" -import { getFilename } from "@opencode-ai/shared/util/path" +import { getFilename } from "@opencode-ai/core/util/path" import { Avatar } from "@opencode-ai/ui/avatar" import { useLanguage } from "@/context/language" import { getProjectAvatarSource } from "@/pages/layout/sidebar-items" diff --git a/packages/app/src/components/dialog-fork.tsx b/packages/app/src/components/dialog-fork.tsx index 4cdcca33ac..f39982f1a8 100644 --- a/packages/app/src/components/dialog-fork.tsx +++ b/packages/app/src/components/dialog-fork.tsx @@ -9,7 +9,7 @@ import { List } from "@opencode-ai/ui/list" import { showToast } from "@opencode-ai/ui/toast" import { extractPromptFromParts } from "@/utils/prompt" import type { TextPart as SDKTextPart } from "@kilocode/sdk/v2/client" -import { base64Encode } from "@opencode-ai/shared/util/encode" +import { base64Encode } from "@opencode-ai/core/util/encode" import { useLanguage } from "@/context/language" interface ForkableMessage { diff --git a/packages/app/src/components/dialog-select-directory.tsx b/packages/app/src/components/dialog-select-directory.tsx index 903cb1915d..005d287091 100644 --- a/packages/app/src/components/dialog-select-directory.tsx +++ b/packages/app/src/components/dialog-select-directory.tsx @@ -3,7 +3,7 @@ import { Dialog } from "@opencode-ai/ui/dialog" import { FileIcon } from "@opencode-ai/ui/file-icon" import { List } from "@opencode-ai/ui/list" import type { ListRef } from "@opencode-ai/ui/list" -import { getDirectory, getFilename } from "@opencode-ai/shared/util/path" +import { getDirectory, getFilename } from "@opencode-ai/core/util/path" import fuzzysort from "fuzzysort" import { createMemo, createResource, createSignal } from "solid-js" import { useGlobalSDK } from "@/context/global-sdk" diff --git a/packages/app/src/components/dialog-select-file.tsx b/packages/app/src/components/dialog-select-file.tsx index 186906f920..63a321e46a 100644 --- a/packages/app/src/components/dialog-select-file.tsx +++ b/packages/app/src/components/dialog-select-file.tsx @@ -4,8 +4,8 @@ import { FileIcon } from "@opencode-ai/ui/file-icon" import { Icon } from "@opencode-ai/ui/icon" import { Keybind } from "@opencode-ai/ui/keybind" import { List } from "@opencode-ai/ui/list" -import { base64Encode } from "@opencode-ai/shared/util/encode" -import { getDirectory, getFilename } from "@opencode-ai/shared/util/path" +import { base64Encode } from "@opencode-ai/core/util/encode" +import { getDirectory, getFilename } from "@opencode-ai/core/util/path" import { useNavigate } from "@solidjs/router" import { createMemo, createSignal, Match, onCleanup, Show, Switch } from "solid-js" import { formatKeybind, useCommand, type CommandOption } from "@/context/command" diff --git a/packages/app/src/components/dialog-select-mcp.tsx b/packages/app/src/components/dialog-select-mcp.tsx index 98f262ce5a..9bb36d32d8 100644 --- a/packages/app/src/components/dialog-select-mcp.tsx +++ b/packages/app/src/components/dialog-select-mcp.tsx @@ -1,13 +1,12 @@ -import { useMutation } from "@tanstack/solid-query" -import { Component, createEffect, createMemo, on, Show } from "solid-js" -import { createStore } from "solid-js/store" +import { useMutation, useQueryClient } from "@tanstack/solid-query" +import { Component, createMemo, Show } from "solid-js" import { useSync } from "@/context/sync" import { useSDK } from "@/context/sdk" import { Dialog } from "@opencode-ai/ui/dialog" import { List } from "@opencode-ai/ui/list" import { Switch } from "@opencode-ai/ui/switch" -import { showToast } from "@opencode-ai/ui/toast" import { useLanguage } from "@/context/language" +import { loadMcpQuery } from "@/context/global-sync" const statusLabels = { connected: "mcp.status.connected", @@ -20,48 +19,7 @@ export const DialogSelectMcp: Component = () => { const sync = useSync() const sdk = useSDK() const language = useLanguage() - const [state, setState] = createStore({ - done: false, - loading: false, - }) - - createEffect( - on( - () => sync.data.mcp_ready, - (ready, prev) => { - if (!ready && prev) setState("done", false) - }, - { defer: true }, - ), - ) - - createEffect(() => { - if (state.done || state.loading) return - if (sync.data.mcp_ready) { - setState("done", true) - return - } - - setState("loading", true) - void sdk.client.mcp - .status() - .then((result) => { - sync.set("mcp", result.data ?? {}) - sync.set("mcp_ready", true) - setState("done", true) - }) - .catch((err) => { - setState("done", true) - showToast({ - variant: "error", - title: language.t("common.requestFailed"), - description: err instanceof Error ? err.message : String(err), - }) - }) - .finally(() => { - setState("loading", false) - }) - }) + const queryClient = useQueryClient() const items = createMemo(() => Object.entries(sync.data.mcp ?? {}) @@ -71,16 +29,10 @@ export const DialogSelectMcp: Component = () => { const toggle = useMutation(() => ({ mutationFn: async (name: string) => { - const status = sync.data.mcp[name] - if (status?.status === "connected") { - await sdk.client.mcp.disconnect({ name }) - } else { - await sdk.client.mcp.connect({ name }) - } - - const result = await sdk.client.mcp.status() - if (result.data) sync.set("mcp", result.data) + if (sync.data.mcp[name]?.status === "connected") await sdk.client.mcp.disconnect({ name }) + else await sdk.client.mcp.connect({ name }) }, + onSuccess: () => queryClient.refetchQueries({ queryKey: loadMcpQuery(sync.directory).queryKey }), })) const enabledCount = createMemo(() => items().filter((i) => i.status === "connected").length) diff --git a/packages/app/src/components/prompt-input/build-request-parts.ts b/packages/app/src/components/prompt-input/build-request-parts.ts index 9a62d0b71d..ad8a51b3ba 100644 --- a/packages/app/src/components/prompt-input/build-request-parts.ts +++ b/packages/app/src/components/prompt-input/build-request-parts.ts @@ -1,4 +1,4 @@ -import { getFilename } from "@opencode-ai/shared/util/path" +import { getFilename } from "@opencode-ai/core/util/path" import { type AgentPartInput, type FilePartInput, type Part, type TextPartInput } from "@kilocode/sdk/v2/client" import type { FileSelection } from "@/context/file" import { encodeFilePath } from "@/context/file/path" diff --git a/packages/app/src/components/prompt-input/context-items.tsx b/packages/app/src/components/prompt-input/context-items.tsx index 9f20f1c04b..95289f9894 100644 --- a/packages/app/src/components/prompt-input/context-items.tsx +++ b/packages/app/src/components/prompt-input/context-items.tsx @@ -2,7 +2,7 @@ import { Component, For, Show } from "solid-js" import { FileIcon } from "@opencode-ai/ui/file-icon" import { IconButton } from "@opencode-ai/ui/icon-button" import { Tooltip } from "@opencode-ai/ui/tooltip" -import { getDirectory, getFilename, getFilenameTruncated } from "@opencode-ai/shared/util/path" +import { getDirectory, getFilename, getFilenameTruncated } from "@opencode-ai/core/util/path" import type { ContextItem } from "@/context/prompt" type PromptContextItem = ContextItem & { key: string } diff --git a/packages/app/src/components/prompt-input/slash-popover.tsx b/packages/app/src/components/prompt-input/slash-popover.tsx index 0c8c959234..d8c4bd035c 100644 --- a/packages/app/src/components/prompt-input/slash-popover.tsx +++ b/packages/app/src/components/prompt-input/slash-popover.tsx @@ -1,7 +1,7 @@ import { Component, For, Match, Show, Switch } from "solid-js" import { FileIcon } from "@opencode-ai/ui/file-icon" import { Icon } from "@opencode-ai/ui/icon" -import { getDirectory, getFilename } from "@opencode-ai/shared/util/path" +import { getDirectory, getFilename } from "@opencode-ai/core/util/path" export type AtOption = | { type: "agent"; name: string; display: string } diff --git a/packages/app/src/components/prompt-input/submit.test.ts b/packages/app/src/components/prompt-input/submit.test.ts index e1b959e8dd..32378436da 100644 --- a/packages/app/src/components/prompt-input/submit.test.ts +++ b/packages/app/src/components/prompt-input/submit.test.ts @@ -74,7 +74,7 @@ beforeAll(async () => { showToast: () => 0, })) - mock.module("@opencode-ai/shared/util/encode", () => ({ + mock.module("@opencode-ai/core/util/encode", () => ({ base64Encode: (value: string) => value, })) diff --git a/packages/app/src/components/prompt-input/submit.ts b/packages/app/src/components/prompt-input/submit.ts index fdd2889efa..98445a512d 100644 --- a/packages/app/src/components/prompt-input/submit.ts +++ b/packages/app/src/components/prompt-input/submit.ts @@ -1,7 +1,7 @@ import type { Message, Session } from "@kilocode/sdk/v2/client" import { showToast } from "@opencode-ai/ui/toast" -import { base64Encode } from "@opencode-ai/shared/util/encode" -import { Binary } from "@opencode-ai/shared/util/binary" +import { base64Encode } from "@opencode-ai/core/util/encode" +import { Binary } from "@opencode-ai/core/util/binary" import { useNavigate, useParams } from "@solidjs/router" import { batch, type Accessor } from "solid-js" import type { FileSelection } from "@/context/file" diff --git a/packages/app/src/components/session/session-context-tab.tsx b/packages/app/src/components/session/session-context-tab.tsx index 2fa44f1c7e..5ca3f0e944 100644 --- a/packages/app/src/components/session/session-context-tab.tsx +++ b/packages/app/src/components/session/session-context-tab.tsx @@ -1,8 +1,8 @@ import { createMemo, createEffect, on, onCleanup, For, Show } from "solid-js" import type { JSX } from "solid-js" import { useSync } from "@/context/sync" -import { checksum } from "@opencode-ai/shared/util/encode" -import { findLast } from "@opencode-ai/shared/util/array" +import { checksum } from "@opencode-ai/core/util/encode" +import { findLast } from "@opencode-ai/core/util/array" import { same } from "@/utils/same" import { Icon } from "@opencode-ai/ui/icon" import { Accordion } from "@opencode-ai/ui/accordion" diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx index d61335d9f0..670e4506bf 100644 --- a/packages/app/src/components/session/session-header.tsx +++ b/packages/app/src/components/session/session-header.tsx @@ -7,7 +7,7 @@ import { Keybind } from "@opencode-ai/ui/keybind" import { Spinner } from "@opencode-ai/ui/spinner" import { showToast } from "@opencode-ai/ui/toast" import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip" -import { getFilename } from "@opencode-ai/shared/util/path" +import { getFilename } from "@opencode-ai/core/util/path" import { createEffect, createMemo, createSignal, For, onMount, Show } from "solid-js" import { createStore } from "solid-js/store" import { Portal } from "solid-js/web" diff --git a/packages/app/src/components/session/session-new-view.tsx b/packages/app/src/components/session/session-new-view.tsx index d2cac28fc4..36c1eb42c3 100644 --- a/packages/app/src/components/session/session-new-view.tsx +++ b/packages/app/src/components/session/session-new-view.tsx @@ -5,7 +5,7 @@ import { useSDK } from "@/context/sdk" import { useLanguage } from "@/context/language" import { Icon } from "@opencode-ai/ui/icon" import { Mark } from "@opencode-ai/ui/logo" -import { getDirectory, getFilename } from "@opencode-ai/shared/util/path" +import { getDirectory, getFilename } from "@opencode-ai/core/util/path" const MAIN_WORKTREE = "main" const CREATE_WORKTREE = "create" diff --git a/packages/app/src/components/session/session-sortable-tab.tsx b/packages/app/src/components/session/session-sortable-tab.tsx index fb2275c445..f04228ca66 100644 --- a/packages/app/src/components/session/session-sortable-tab.tsx +++ b/packages/app/src/components/session/session-sortable-tab.tsx @@ -5,7 +5,7 @@ import { FileIcon } from "@opencode-ai/ui/file-icon" import { IconButton } from "@opencode-ai/ui/icon-button" import { TooltipKeybind } from "@opencode-ai/ui/tooltip" import { Tabs } from "@opencode-ai/ui/tabs" -import { getFilename } from "@opencode-ai/shared/util/path" +import { getFilename } from "@opencode-ai/core/util/path" import { useFile } from "@/context/file" import { useLanguage } from "@/context/language" import { useCommand } from "@/context/command" diff --git a/packages/app/src/components/settings-general.tsx b/packages/app/src/components/settings-general.tsx index 50c511d0dd..eb86f760d9 100644 --- a/packages/app/src/components/settings-general.tsx +++ b/packages/app/src/components/settings-general.tsx @@ -11,7 +11,9 @@ import { showToast } from "@opencode-ai/ui/toast" import { useParams } from "@solidjs/router" import { useLanguage } from "@/context/language" import { usePermission } from "@/context/permission" -import { usePlatform } from "@/context/platform" +import { usePlatform, type DisplayBackend } from "@/context/platform" +import { useGlobalSync } from "@/context/global-sync" +import { useGlobalSDK } from "@/context/global-sdk" import { monoDefault, monoFontFamily, @@ -40,6 +42,18 @@ type ThemeOption = { name: string } +type ShellOption = { + path: string + name: string + acceptable: boolean +} + +type ShellSelectOption = { + id: string + value: string + label: string +} + // To prevent audio from overlapping/playing very quickly when navigating the settings menus, // delay the playback by 100ms during quick selection changes and pause existing sounds. const stopDemoSound = () => { @@ -75,10 +89,6 @@ export const SettingsGeneral: Component = () => { const params = useParams() const settings = useSettings() - onMount(() => { - void theme.loadThemes() - }) - const [store, setStore] = createStore({ checking: false, }) @@ -165,6 +175,70 @@ export const SettingsGeneral: Component = () => { const themeOptions = createMemo(() => theme.ids().map((id) => ({ id, name: theme.name(id) }))) + const globalSync = useGlobalSync() + const globalSdk = useGlobalSDK() + + const [shells] = createResource( + () => + globalSdk.client.pty + .shells() + .then((res) => res.data ?? []) + .catch(() => [] as ShellOption[]), + { initialValue: [] as ShellOption[] }, + ) + + const [displayBackend, { refetch: refetchDisplayBackend }] = createResource( + () => (linux() && platform.getDisplayBackend ? true : false), + () => Promise.resolve(platform.getDisplayBackend?.() ?? null).catch(() => null as DisplayBackend | null), + { initialValue: null as DisplayBackend | null }, + ) + + onMount(() => { + void theme.loadThemes() + }) + + const autoOption = { id: "auto", value: "", label: language.t("settings.general.row.shell.autoDefault") } + const currentShell = createMemo(() => globalSync.data.config.shell ?? "") + + const shellOptions = createMemo(() => { + const list = shells.latest + const current = globalSync.data.config.shell + + const nameCounts = new Map() + for (const s of list) { + nameCounts.set(s.name, (nameCounts.get(s.name) || 0) + 1) + } + + const options = [ + autoOption, + ...list.map((s) => { + const ambiguousName = (nameCounts.get(s.name) || 0) > 1 + const text = ambiguousName ? s.path : s.name + const label = s.acceptable ? text : `${text} (${language.t("settings.general.row.shell.terminalOnly")})` + return { + id: s.path, + // Prefer name over path - "bash" is much cleaner than the explicit full route even when it may change due to PATH. + value: ambiguousName ? s.path : s.name, + label, + } + }), + ] + + if (current && !options.some((o) => o.value === current)) { + options.push({ id: current, value: current, label: current }) + } + + return options + }) + + const onDisplayBackendChange = (checked: boolean) => { + const update = platform.setDisplayBackend?.(checked ? "wayland" : "auto") + if (!update) return + void update.finally(() => { + void refetchDisplayBackend() + }) + } + const colorSchemeOptions = createMemo((): { value: ColorScheme; label: string }[] => [ { value: "system", label: language.t("theme.scheme.system") }, { value: "light", label: language.t("theme.scheme.light") }, @@ -243,6 +317,27 @@ export const SettingsGeneral: Component = () => { + +