From ef9b1dcec01f4ef83fa2e448ff09c4623cdd4c7a Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Wed, 24 Jun 2026 17:01:32 +0200 Subject: [PATCH] fix(cli): stop loading .opencode config directories --- .changeset/prefer-kilo-config.md | 2 +- .../pages/automate/extending/plugins.md | 6 +- .../pages/code-with-ai/platforms/cli.md | 4 +- .../contributing/architecture/cli-runtime.md | 2 +- .../kilo-docs/pages/customize/custom-modes.md | 14 ++-- packages/opencode/specs/tui-plugins.md | 13 ++-- packages/opencode/src/cli/cmd/mcp.ts | 33 ++++---- .../opencode/src/cli/cmd/tui/config/tui.ts | 16 ++-- .../src/cli/cmd/tui/context/theme.tsx | 2 +- .../tui/feature-plugins/home/tips-view.tsx | 14 ++-- .../src/cli/cmd/tui/plugin/runtime.ts | 9 ++- packages/opencode/src/config/config.ts | 4 +- packages/opencode/src/config/paths.ts | 4 +- packages/opencode/src/file/ripgrep.ts | 2 +- .../opencode/src/kilocode/config/config.ts | 9 +-- .../opencode/src/kilocode/config/overlay.ts | 9 +-- .../opencode/src/kilocode/config/sources.ts | 2 +- .../opencode/src/kilocode/docs/migration.md | 17 +---- .../src/kilocode/docs/rules-migration.md | 2 +- .../src/kilocode/permission/config-paths.ts | 4 +- .../src/kilocode/skills/kilo-config.md | 23 +++--- packages/opencode/src/kilocode/tui/config.ts | 2 +- packages/opencode/src/plugin/install.ts | 2 +- packages/opencode/test/agent/agent.test.ts | 2 +- .../test/cli/tui/plugin-loader.test.ts | 8 +- packages/opencode/test/config/config.test.ts | 2 +- packages/opencode/test/config/tui.test.ts | 8 +- .../test/kilocode/config/config.test.ts | 23 +++--- .../test/kilocode/plugin-dependencies.ts | 2 +- .../kilocode/server/config-overlay.test.ts | 9 ++- .../kilocode/server/config-sources.test.ts | 4 +- .../server/httpapi-exercise-scenarios.ts | 8 +- .../test/kilocode/server/tui-config.test.ts | 19 +++++ .../test/plugin/auth-override.test.ts | 2 +- .../test/plugin/install-concurrency.test.ts | 12 +-- packages/opencode/test/plugin/install.test.ts | 76 +++++++++++-------- .../opencode/test/provider/provider.test.ts | 4 +- .../test/server/httpapi-provider.test.ts | 8 +- .../opencode/test/server/httpapi-sdk.test.ts | 2 +- packages/opencode/test/skill/skill.test.ts | 36 ++++++--- packages/opencode/test/tool/registry.test.ts | 26 +++---- packages/opencode/test/tool/skill.test.ts | 2 +- 42 files changed, 245 insertions(+), 203 deletions(-) diff --git a/.changeset/prefer-kilo-config.md b/.changeset/prefer-kilo-config.md index a740f85e58..6fe9a3d09c 100644 --- a/.changeset/prefer-kilo-config.md +++ b/.changeset/prefer-kilo-config.md @@ -2,4 +2,4 @@ "@kilocode/cli": patch --- -Prefer project `.kilo` configuration over legacy `.kilocode` and `.opencode` directories when entries conflict. +Stop loading `.opencode` config directories and use `.kilo` instead, while retaining `.kilocode` as a legacy fallback. diff --git a/packages/kilo-docs/pages/automate/extending/plugins.md b/packages/kilo-docs/pages/automate/extending/plugins.md index e0f87943db..ae789ea681 100644 --- a/packages/kilo-docs/pages/automate/extending/plugins.md +++ b/packages/kilo-docs/pages/automate/extending/plugins.md @@ -58,7 +58,7 @@ Config files live in the same locations as the rest of your CLI configuration Drop TypeScript or JavaScript files into a `plugin/` or `plugins/` folder inside any config directory: - Global: `~/.config/kilo/plugin/` -- Project: `.kilo/plugin/`, `.kilocode/plugin/`, or `.opencode/plugin/` +- Project: `.kilo/plugin/` or legacy `.kilocode/plugin/` Every `.ts` or `.js` file in those directories is auto-registered at startup — no need to list them in the config file. @@ -86,7 +86,7 @@ kilo plugin my-plugin --global kilo plugin my-plugin --force ``` -The command resolves the package, reads its `package.json` for plugin entrypoints, and writes the entry into the appropriate config file (currently `.opencode/opencode.jsonc` / `.opencode/tui.jsonc` for local installs, or `~/.config/kilo/opencode.jsonc` / `~/.config/kilo/tui.jsonc` for `--global`) while preserving JSONC comments. +The command resolves the package, reads its `package.json` for plugin entrypoints, and writes the entry into the appropriate config file (`.kilo/opencode.jsonc` / `.kilo/tui.jsonc` for local installs, or `~/.config/kilo/opencode.jsonc` / `~/.config/kilo/tui.jsonc` for `--global`) while preserving JSONC comments. ### How plugins are installed @@ -446,7 +446,7 @@ If a custom tool uses the same name as a built-in tool, **the custom tool wins** ### Alternative: standalone tool files -For tools that don't need the full plugin context, drop them in a `tool/` or `tools/` folder inside any config directory — for example `.kilo/tool/database.ts` or `~/.config/kilo/tool/database.ts`. The filename becomes the tool name, and each file exports a `tool()` definition directly. The layout is identical to the [OpenCode custom tools guide](https://opencode.ai/docs/custom-tools); substitute `.kilo/` (or `.kilocode/` / `.opencode/`) for `.opencode/`. +For tools that don't need the full plugin context, drop them in a `tool/` or `tools/` folder inside any config directory — for example `.kilo/tool/database.ts` or `~/.config/kilo/tool/database.ts`. The filename becomes the tool name, and each file exports a `tool()` definition directly. The layout is identical to the [OpenCode custom tools guide](https://opencode.ai/docs/custom-tools); use `.kilo/`, or legacy `.kilocode/`, instead of `.opencode/`. --- diff --git a/packages/kilo-docs/pages/code-with-ai/platforms/cli.md b/packages/kilo-docs/pages/code-with-ai/platforms/cli.md index 6c6e5cb652..1699cb4455 100644 --- a/packages/kilo-docs/pages/code-with-ai/platforms/cli.md +++ b/packages/kilo-docs/pages/code-with-ai/platforms/cli.md @@ -335,8 +335,8 @@ The Kilo CLI is a fork of [OpenCode](https://opencode.ai) and supports the same | Scope | Path | |---|---| -| **Global** | `~/.config/kilo/opencode.json` or `opencode.jsonc` (Windows: config dir may vary; same filenames) | -| **Project** | `./opencode.json` or `./.opencode/` in project root | +| **Global** | `~/.config/kilo/kilo.json[c]` or legacy `opencode.json[c]` (Windows config dir may vary) | +| **Project** | `./kilo.json[c]`, legacy `./opencode.json[c]`, or config inside `./.kilo/` (legacy `./.kilocode/` is also read) | Project-level configuration takes precedence over global settings. diff --git a/packages/kilo-docs/pages/contributing/architecture/cli-runtime.md b/packages/kilo-docs/pages/contributing/architecture/cli-runtime.md index 02963d704f..ce188c485a 100644 --- a/packages/kilo-docs/pages/contributing/architecture/cli-runtime.md +++ b/packages/kilo-docs/pages/contributing/architecture/cli-runtime.md @@ -262,7 +262,7 @@ Later sources override earlier values during instance config load: | 11 | macOS managed preferences | | 12 | Runtime flag-derived permission, tool, compaction, and plugin behavior | -Global config files load from `${Global.Path.config}`. Project updates prefer existing config files found in ancestor `.kilo`, `.kilocode`, or `.opencode` directories, then existing project root config files, then create `.kilo/kilo.json`. Global indexing settings can carry provider and storage defaults, but global `indexing.enabled` is stripped so project enablement remains local in effective instance config. +Global config files load from `${Global.Path.config}`. Project updates prefer existing config files found in ancestor `.kilo` or legacy `.kilocode` directories, then existing project root config files, then create `.kilo/kilo.json`. Global indexing settings can carry provider and storage defaults, but global `indexing.enabled` is stripped so project enablement remains local in effective instance config. Signed-in organization modes become normal agent configuration during load. They override migrated legacy modes and remain overridable by later config sources in table. diff --git a/packages/kilo-docs/pages/customize/custom-modes.md b/packages/kilo-docs/pages/customize/custom-modes.md index 95f82ce3da..fb68c4c276 100644 --- a/packages/kilo-docs/pages/customize/custom-modes.md +++ b/packages/kilo-docs/pages/customize/custom-modes.md @@ -76,7 +76,7 @@ Create `.md` files in any of these directories: ``` .kilo/agents/my-agent.md .kilo/agent/my-agent.md -.opencode/agents/my-agent.md +.kilocode/agents/my-agent.md ``` For global agents, place files in your global config directory: @@ -191,7 +191,7 @@ Agent configurations merge from lowest to highest priority: 1. Built-in (native) agent defaults 2. Global config (`~/.config/kilo/kilo.jsonc`) 3. Project config (`kilo.jsonc` at project root) -4. `.kilo/` / `.opencode/` directory configs and agent `.md` files +4. `.kilo/` / legacy `.kilocode/` directory configs and agent `.md` files 5. Environment variable overrides (`KILO_CONFIG_CONTENT`) When the same agent name appears at multiple levels, properties are merged (not replaced wholesale), so you can override just a model or temperature without redefining the entire agent. @@ -312,7 +312,7 @@ Create `.md` files in any of these directories: ``` .kilo/agents/my-agent.md .kilo/agent/my-agent.md -.opencode/agents/my-agent.md +.kilocode/agents/my-agent.md ``` For global agents, place files in your global config directory: @@ -427,7 +427,7 @@ Agent configurations merge from lowest to highest priority: 1. Built-in (native) agent defaults 2. Global config (`~/.config/kilo/kilo.jsonc`) 3. Project config (`kilo.jsonc` at project root) -4. `.kilo/` / `.opencode/` directory configs and agent `.md` files +4. `.kilo/` / legacy `.kilocode/` directory configs and agent `.md` files 5. Environment variable overrides (`KILO_CONFIG_CONTENT`) When the same agent name appears at multiple levels, properties are merged (not replaced wholesale), so you can override just a model or temperature without redefining the entire agent. @@ -579,7 +579,7 @@ When importing modes, you can change the slug in the exported YAML file before i Custom agents are defined as Markdown files with optional YAML frontmatter. You can place them in: -- **Project agents:** `.kilo/agents/*.md` (or `.opencode/agents/*.md`) +- **Project agents:** `.kilo/agents/*.md` (or legacy `.kilocode/agents/*.md`) - **Global agents:** `~/.config/kilo/agents/*.md` ### Agent File Format @@ -1256,7 +1256,7 @@ customModes: ### Common Issues -- **Agent not appearing:** Ensure the `.md` file is in a recognized directory (`.kilo/agents/`, `.kilo/agent/`, `.opencode/agents/`). Check that the `mode` property is `primary` or `all` if you expect it in the agent picker. +- **Agent not appearing:** Ensure the `.md` file is in a recognized directory (`.kilo/agents/`, `.kilo/agent/`, `.kilocode/agents/`). Check that the `mode` property is `primary` or `all` if you expect it in the agent picker. - **Permission errors:** Permission rules are evaluated last-match-wins. If an agent can't use a tool you expect, check that an `allow` rule appears after any `deny` rules for that permission. - **YAML frontmatter parse errors:** Ensure the frontmatter block starts and ends with `---` on its own line. Validate that YAML keys match expected property names (e.g., `top_p` not `topP`). - **Agent overrides not working:** Config merges from global to project level. If a global config sets a property, your project config can override it, but both must use the same agent name. @@ -1273,7 +1273,7 @@ customModes: ### Common Issues -- **Agent not appearing:** Ensure the `.md` file is in a recognized directory (`.kilo/agents/`, `.kilo/agent/`, `.opencode/agents/`). Check that the `mode` property is `primary` or `all` if you expect it in the agent picker. +- **Agent not appearing:** Ensure the `.md` file is in a recognized directory (`.kilo/agents/`, `.kilo/agent/`, `.kilocode/agents/`). Check that the `mode` property is `primary` or `all` if you expect it in the agent picker. - **Permission errors:** Permission rules are evaluated last-match-wins. If an agent can't use a tool you expect, check that an `allow` rule appears after any `deny` rules for that permission. - **YAML frontmatter parse errors:** Ensure the frontmatter block starts and ends with `---` on its own line. Validate that YAML keys match expected property names (e.g., `top_p` not `topP`). - **Agent overrides not working:** Config merges from global to project level. If a global config sets a property, your project config can override it, but both must use the same agent name. diff --git a/packages/opencode/specs/tui-plugins.md b/packages/opencode/specs/tui-plugins.md index 89ebc1ffbc..fc4c7894eb 100644 --- a/packages/opencode/specs/tui-plugins.md +++ b/packages/opencode/specs/tui-plugins.md @@ -195,8 +195,8 @@ npm plugins can declare a version compatibility range in `package.json` using th - Alias: `kilo plug `. - `-g` / `--global` writes into the global config dir. - Local installs resolve target dir inside `patchPluginConfig`. -- For local scope, path is `/.opencode` only when VCS is git and `worktree !== "/"`; otherwise `/.opencode`. -- Root-worktree fallback (`worktree === "/"` uses `/.opencode`) is covered by regression tests. +- For local scope, path is `/.kilo` only when VCS is git and `worktree !== "/"`; otherwise `/.kilo`. +- Root-worktree fallback (`worktree === "/"` uses `/.kilo`) is covered by regression tests. - `patchPluginConfig` applies all detected targets (`server` and/or `tui`) in one call. - `patchPluginConfig` returns structured result unions (`ok`, `code`, fields by error kind) instead of custom thrown errors. - `patchPluginConfig` serializes per-target config writes with `Flock.acquire(...)`. @@ -213,7 +213,7 @@ npm plugins can declare a version compatibility range in `package.json` using th - There is no uninstall, list, or update CLI command for external plugins. - Local file plugins are configured directly in `tui.json`. -When `plugin` entries exist in a writable `.opencode` dir or `KILO_CONFIG_DIR`, Kilo installs `@kilocode/plugin` into that dir and writes: +When `plugin` entries exist in a writable `.kilo` or legacy `.kilocode` directory, or `KILO_CONFIG_DIR`, Kilo installs `@kilocode/plugin` into that directory and writes: - `package.json` - `bun.lock` @@ -413,7 +413,7 @@ Theme install behavior: - If the theme name already exists, install is skipped unless plugin metadata state is `updated`. - On `updated`, host skips rewrite when tracked `mtime`/`size` is unchanged. - When a theme already exists and state is not `updated`, host can still persist theme metadata when destination already exists. -- Local plugins persist installed themes under the local `.opencode/themes` area near the plugin config source. +- Local plugins persist installed themes under `.kilo/themes`, or under `themes` beside a config source in `.kilo` or legacy `.kilocode`. - Global plugins persist installed themes under the global `themes` dir. - Invalid or unreadable theme files are ignored. @@ -538,7 +538,4 @@ The plugin manager is exposed as a command with title `Plugins` and value `plugi ## Current in-repo examples -- Local smoke plugin: `.opencode/plugins/tui-smoke.tsx` -- Local vim plugin: `.opencode/plugins/tui-vim.tsx` -- Local smoke config: `.opencode/tui.json` -- Local smoke theme: `.opencode/plugins/smoke-theme.json` +The upstream `.opencode` smoke fixtures remain in the repository for OpenCode compatibility, but Kilo does not auto-load them. Kilo-local examples must live under `.kilo` or legacy `.kilocode`. diff --git a/packages/opencode/src/cli/cmd/mcp.ts b/packages/opencode/src/cli/cmd/mcp.ts index a18a49de4b..9ff46f91d9 100644 --- a/packages/opencode/src/cli/cmd/mcp.ts +++ b/packages/opencode/src/cli/cmd/mcp.ts @@ -397,25 +397,26 @@ export const McpLogoutCommand = effectCmd({ }) async function resolveConfigPath(baseDir: string, global = false) { - // kilocode_change start - prefer kilo.json/.kilo over opencode.json/.opencode - // Check for existing config files (prefer .jsonc over .json, check .kilo/ and .opencode/ subdirectory too) - const candidates = [ - path.join(baseDir, "kilo.json"), + // kilocode_change start - prefer supported Kilo config directories over root files + const roots = [ path.join(baseDir, "kilo.jsonc"), - path.join(baseDir, "opencode.json"), + path.join(baseDir, "kilo.json"), path.join(baseDir, "opencode.jsonc"), + path.join(baseDir, "opencode.json"), ] - - if (!global) { - candidates.push( - path.join(baseDir, ".kilo", "kilo.json"), - path.join(baseDir, ".kilo", "kilo.jsonc"), - path.join(baseDir, ".kilo", "opencode.json"), - path.join(baseDir, ".kilo", "opencode.jsonc"), - path.join(baseDir, ".opencode", "opencode.json"), - path.join(baseDir, ".opencode", "opencode.jsonc"), - ) - } + const candidates = global + ? roots + : [ + path.join(baseDir, ".kilo", "kilo.jsonc"), + path.join(baseDir, ".kilo", "kilo.json"), + path.join(baseDir, ".kilo", "opencode.jsonc"), + path.join(baseDir, ".kilo", "opencode.json"), + path.join(baseDir, ".kilocode", "kilo.jsonc"), + path.join(baseDir, ".kilocode", "kilo.json"), + path.join(baseDir, ".kilocode", "opencode.jsonc"), + path.join(baseDir, ".kilocode", "opencode.json"), + ...roots, + ] for (const candidate of candidates) { if (await Filesystem.exists(candidate)) { diff --git a/packages/opencode/src/cli/cmd/tui/config/tui.ts b/packages/opencode/src/cli/cmd/tui/config/tui.ts index d9ff1c293b..4363e249fa 100644 --- a/packages/opencode/src/cli/cmd/tui/config/tui.ts +++ b/packages/opencode/src/cli/cmd/tui/config/tui.ts @@ -191,8 +191,10 @@ const loadState = Effect.fn("TuiConfig.loadState")(function* (ctx: { directory: acc.plugin_origins = plugins }) - // Every config dir we may read from: global config dir, any `.opencode` + // kilocode_change start - discover canonical and legacy Kilo config directories + // Every config dir we may read from: global config, .kilo and legacy .kilocode // folders between cwd and home, and KILO_CONFIG_DIR. + // kilocode_change end const directories = yield* ConfigPaths.directories(ctx.directory) yield* Effect.promise(() => migrateTuiConfig({ directories, cwd: ctx.directory })) @@ -220,18 +222,16 @@ const loadState = Effect.fn("TuiConfig.loadState")(function* (ctx: { directory: yield* mergeFile(acc, file) } - // 4. `.opencode` directories (and KILO_CONFIG_DIR) discovered while - // walking up the tree. Also returned below so callers can install plugin - // dependencies from each location. - // kilocode_change start - also load tui.json from .kilo/.kilocode + // kilocode_change start - load tui.json from supported Kilo config directories + // 4. `.kilo` and legacy `.kilocode` directories (and KILO_CONFIG_DIR) + // discovered while walking up the tree. Also returned below so callers can + // install plugin dependencies from each location. const dirs = unique(directories).filter( - (dir) => - dir.endsWith(".kilo") || dir.endsWith(".kilocode") || dir.endsWith(".opencode") || dir === Flag.KILO_CONFIG_DIR, + (dir) => dir.endsWith(".kilo") || dir.endsWith(".kilocode") || dir === Flag.KILO_CONFIG_DIR, ) // kilocode_change end for (const dir of dirs) { - // if (!dir.endsWith(".opencode") && dir !== Flag.KILO_CONFIG_DIR) continue // kilocode_change for (const file of ConfigPaths.fileInDirectory(dir, "tui")) { yield* mergeFile(acc, file) } diff --git a/packages/opencode/src/cli/cmd/tui/context/theme.tsx b/packages/opencode/src/cli/cmd/tui/context/theme.tsx index 8f2c55477f..9299d901c4 100644 --- a/packages/opencode/src/cli/cmd/tui/context/theme.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/theme.tsx @@ -508,7 +508,7 @@ async function getCustomThemes() { Global.Path.config, ...(await Array.fromAsync( Filesystem.up({ - targets: [".kilo", ".opencode"], // kilocode_change + targets: [".kilocode", ".kilo"], // kilocode_change start: process.cwd(), }), )), diff --git a/packages/opencode/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx b/packages/opencode/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx index 7de14fa8b7..a608005cfb 100644 --- a/packages/opencode/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx +++ b/packages/opencode/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx @@ -135,10 +135,12 @@ export function Tips(props: { api: TuiPluginApi; connected?: boolean }) { } const tip = createMemo(() => { if (props.connected === false) return NO_MODELS_TIP - const tips = KILO_TIPS.flatMap((item) => { // kilocode_change + // kilocode_change start + const tips = KILO_TIPS.flatMap((item) => { const value = typeof item === "string" ? item : item(shortcuts) return value ? [value] : [] }) + // kilocode_change end return tips[Math.floor(tipOffset * tips.length)] ?? NO_MODELS_TIP }, NO_MODELS_TIP) // Solid can expose a memo's initial value while a pure computation is pending. @@ -219,10 +221,10 @@ const TIPS: Tip[] = [ "Override any keybind in {highlight}tui.json{/highlight} via the {highlight}keybinds{/highlight} section", "Set any keybind to {highlight}none{/highlight} to disable it completely", "Configure local or remote MCP servers in the {highlight}mcp{/highlight} config section", - "Add {highlight}.md{/highlight} files to {highlight}.opencode/commands/{/highlight} to define reusable custom prompts", + "Add {highlight}.md{/highlight} files to {highlight}.kilo/commands/{/highlight} to define reusable custom prompts", // kilocode_change "Use {highlight}$ARGUMENTS{/highlight}, {highlight}$1{/highlight}, {highlight}$2{/highlight} in custom commands for dynamic input", "Use backticks in commands to inject shell output (e.g., {highlight}`git status`{/highlight})", - "Add {highlight}.md{/highlight} files to {highlight}.opencode/agents/{/highlight} for specialized AI personas", + "Add {highlight}.md{/highlight} files to {highlight}.kilo/agents/{/highlight} for specialized AI personas", // kilocode_change "Configure per-agent permissions for {highlight}edit{/highlight}, {highlight}bash{/highlight}, and {highlight}webfetch{/highlight} tools", 'Use patterns like {highlight}"git *": "allow"{/highlight} for granular bash permissions', 'Set {highlight}"rm -rf *": "deny"{/highlight} to block destructive commands', @@ -231,9 +233,9 @@ const TIPS: Tip[] = [ 'Set {highlight}"formatter": false{/highlight} in config to disable formatters enabled by another config layer', "Define custom formatter commands with file extensions in config", 'Set {highlight}"lsp": true{/highlight} in config to enable built-in LSP servers for code analysis', - "Create {highlight}.ts{/highlight} files in {highlight}.opencode/tools/{/highlight} to define new LLM tools", + "Create {highlight}.ts{/highlight} files in {highlight}.kilo/tools/{/highlight} to define new LLM tools", // kilocode_change "Tool definitions can invoke scripts written in Python, Go, etc", - "Add {highlight}.ts{/highlight} files to {highlight}.opencode/plugins/{/highlight} for event hooks", + "Add {highlight}.ts{/highlight} files to {highlight}.kilo/plugins/{/highlight} for event hooks", // kilocode_change "Use plugins to send OS notifications when sessions complete", "Create a plugin to prevent OpenCode from reading sensitive files", "Use {highlight}opencode run{/highlight} for non-interactive scripting", @@ -250,7 +252,7 @@ const TIPS: Tip[] = [ "Comment {highlight}/opencode fix this{/highlight} on issues to auto-create PRs", "Comment {highlight}/oc{/highlight} on PR code lines for targeted code reviews", 'Use {highlight}"theme": "system"{/highlight} to match your terminal\'s colors', - "Create JSON theme files in {highlight}.opencode/themes/{/highlight} directory", + "Create JSON theme files in {highlight}.kilo/themes/{/highlight} directory", // kilocode_change "Themes support dark/light variants for both modes", "Use numeric xterm color codes 0-255 in custom theme JSON", "Use {highlight}{env:VAR_NAME}{/highlight} syntax to reference environment variables in config", diff --git a/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts b/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts index b33efd469b..1ef62bd496 100644 --- a/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts +++ b/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts @@ -254,10 +254,13 @@ function createThemeInstaller( const src = Filesystem.resolveFilePath(root, file) const name = path.basename(src, path.extname(src)) const source_dir = path.dirname(meta.source) + // kilocode_change start - install local themes into supported Kilo config directories + const base = path.basename(source_dir) const local_dir = - path.basename(source_dir) === ".opencode" + base === ".kilo" || base === ".kilocode" ? path.join(source_dir, "themes") - : path.join(source_dir, ".opencode", "themes") + : path.join(source_dir, ".kilo", "themes") + // kilocode_change end const dest_dir = meta.scope === "local" ? local_dir : path.join(Global.Path.config, "themes") const dest = path.join(dest_dir, `${name}.json`) const stat = await Filesystem.statAsync(src) @@ -843,7 +846,7 @@ function defaultPluginOrigin(state: RuntimeState, spec: string): ConfigPlugin.Or return { spec, scope: "local", - source: state.api.state.path.config || path.join(state.directory, ".opencode", "tui.json"), + source: state.api.state.path.config || path.join(state.directory, ".kilo", "tui.json"), // kilocode_change } } diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 6090b78b10..a00b133b73 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -865,7 +865,7 @@ export const layer = Layer.effect( const directories = yield* ConfigPaths.directories(ctx.directory, ctx.worktree) const primary = Flag.KILO_DISABLE_PROJECT_CONFIG ? [] - : yield* primaryPaths(ctx.directory, ctx.worktree, [".opencode", ".kilocode", ".kilo"]) + : yield* primaryPaths(ctx.directory, ctx.worktree, [".kilocode", ".kilo"]) // Load primary fallbacks before active-worktree config, then track them as local. directories.splice(1, 0, ...primary) const primarySet = new Set(primary) @@ -934,7 +934,7 @@ export const layer = Layer.effect( result.agent = mergeDeep(result.agent ?? {}, yield* Effect.promise(() => ConfigAgent.load(dir, warnings))) result.agent = mergeDeep(result.agent ?? {}, yield* Effect.promise(() => ConfigAgent.loadMode(dir, warnings))) // kilocode_change end - // Auto-discovered plugins under `.opencode/plugin(s)` are already local files, so ConfigPlugin.load + // kilocode_change - Auto-discovered plugins under config directories are already local files, so ConfigPlugin.load // returns normalized Specs and we only need to attach origin metadata here. const list = yield* Effect.promise(() => ConfigPlugin.load(dir)) yield* mergePluginOrigins(dir, list, scope) // kilocode_change diff --git a/packages/opencode/src/config/paths.ts b/packages/opencode/src/config/paths.ts index 0119b4023f..8c28a4d07f 100644 --- a/packages/opencode/src/config/paths.ts +++ b/packages/opencode/src/config/paths.ts @@ -26,13 +26,13 @@ export const directories = Effect.fn("ConfigPaths.directories")(function* (direc Global.Path.config, ...(!Flag.KILO_DISABLE_PROJECT_CONFIG ? yield* afs.up({ - targets: [".opencode", ".kilocode", ".kilo"], // kilocode_change + targets: [".kilocode", ".kilo"], // kilocode_change start: directory, stop: worktree, }) : []), ...(yield* afs.up({ - targets: [".opencode", ".kilocode", ".kilo"], // kilocode_change + targets: [".kilocode", ".kilo"], // kilocode_change start: Global.Path.home, stop: Global.Path.home, })), diff --git a/packages/opencode/src/file/ripgrep.ts b/packages/opencode/src/file/ripgrep.ts index ee5283f1a8..5b22ef9bec 100644 --- a/packages/opencode/src/file/ripgrep.ts +++ b/packages/opencode/src/file/ripgrep.ts @@ -441,7 +441,7 @@ export const layer: Layer.Layer { diff --git a/packages/opencode/src/kilocode/config/sources.ts b/packages/opencode/src/kilocode/config/sources.ts index 721987fecd..1dedcb0dca 100644 --- a/packages/opencode/src/kilocode/config/sources.ts +++ b/packages/opencode/src/kilocode/config/sources.ts @@ -56,7 +56,7 @@ export namespace KilocodeConfigSources { type Pending = Omit - const roots = [".opencode", ".kilocode", ".kilo"] as const + const roots = [".kilocode", ".kilo"] as const const global = ["config.json", "kilo.json", "kilo.jsonc", "opencode.json", "opencode.jsonc"] as const export async function list(input: Input): Promise { diff --git a/packages/opencode/src/kilocode/docs/migration.md b/packages/opencode/src/kilocode/docs/migration.md index a3537a8961..ef12e34a12 100644 --- a/packages/opencode/src/kilocode/docs/migration.md +++ b/packages/opencode/src/kilocode/docs/migration.md @@ -192,7 +192,7 @@ Kilocode skills are automatically discovered and made available in Opencode. Thi ## How It Works -Opencode scans additional directories for skills alongside its native `.opencode/skill/` locations. The `KilocodePaths.skillDirectories()` function provides these paths. +Kilo scans canonical `.kilo/skill/` and `.kilo/skills/` directories alongside legacy `.kilocode/` equivalents. The `KilocodePaths.skillDirectories()` function provides these paths. ## Source Locations @@ -249,16 +249,7 @@ Detailed instructions for the agent... ## Priority / Override Behavior -When the same skill name exists in multiple locations, **last one wins**: - -1. `.claude/skills/` (lowest priority) -2. `.kilocode/skills/` (walk-up) -3. `~/.kilocode/skills/` -4. VSCode extension storage -5. `.opencode/skill/` (walk-up) -6. `~/.opencode/skill/` (highest priority) - -This means Opencode native skills take precedence over Kilocode skills with the same name. +When the same skill name exists in multiple locations, **last one wins**. Canonical `.kilo/` skills take precedence over legacy `.kilocode/` skills at the same project level. Explicit external skill paths and compatibility directories are loaded according to the configured scan order. ## Mode-Specific Skills @@ -286,8 +277,8 @@ Skills can be symlinked from a shared location: ``` .agents/skills/shared-skill/ # Actual skill -.kilocode/skills/shared-skill -> ... # Symlink -.opencode/skill/shared-skill -> ... # Symlink +.kilocode/skills/shared-skill -> ... # Legacy symlink +.kilo/skill/shared-skill -> ... # Canonical symlink ``` The scanner follows symlinks, so a skill installed once can be available to both Kilo VSCode and Opencode CLI. diff --git a/packages/opencode/src/kilocode/docs/rules-migration.md b/packages/opencode/src/kilocode/docs/rules-migration.md index ebec954562..84fee0e642 100644 --- a/packages/opencode/src/kilocode/docs/rules-migration.md +++ b/packages/opencode/src/kilocode/docs/rules-migration.md @@ -70,7 +70,7 @@ The migrator reads rules from these locations: - `AGENTS.md` in project root - `CLAUDE.md` in project root -- `~/.opencode/AGENTS.md` (global) +- `~/.config/kilo/AGENTS.md` (global) ## Not Migrated diff --git a/packages/opencode/src/kilocode/permission/config-paths.ts b/packages/opencode/src/kilocode/permission/config-paths.ts index 4946ec92a8..c120bc4330 100644 --- a/packages/opencode/src/kilocode/permission/config-paths.ts +++ b/packages/opencode/src/kilocode/permission/config-paths.ts @@ -5,9 +5,9 @@ import { KilocodePaths } from "@/kilocode/paths" export namespace ConfigProtection { /** * Config directory prefixes (relative paths, forward-slash normalized). - * Matches .kilo/, .kilocode/, .opencode/ at any depth within the project. + * Matches .kilo/ and legacy .kilocode/ at any depth within the project. */ - const CONFIG_DIRS = [".kilo/", ".kilocode/", ".opencode/"] + const CONFIG_DIRS = [".kilo/", ".kilocode/"] /** * Subdirectories under CONFIG_DIRS that are NOT config files (e.g. plan files). diff --git a/packages/opencode/src/kilocode/skills/kilo-config.md b/packages/opencode/src/kilocode/skills/kilo-config.md index a1a26eaaca..341cbfa898 100644 --- a/packages/opencode/src/kilocode/skills/kilo-config.md +++ b/packages/opencode/src/kilocode/skills/kilo-config.md @@ -2,11 +2,11 @@ All config lives in `kilo.json` (or `kilo.jsonc`). Precedence low-to-high: remote well-known, global (`~/.config/kilo/kilo.json`), env `KILO_CONFIG`, project `./kilo.json`, `.kilo/kilo.json`, `KILO_CONFIG_CONTENT`, managed (see Config File Locations). Deep-merged; later wins. -This also covers where Kilo looks for config files, commands, agents, and skills across project, global, and legacy paths such as `.kilo/`, `.kilocode/`, `.opencode/`, and `~/.config/kilo/`, plus Agent Manager setup/run scripts in the VS Code extension. +This also covers where Kilo looks for config files, commands, agents, and skills across project, global, and legacy paths such as `.kilo/`, `.kilocode/`, and `~/.config/kilo/`, plus Agent Manager setup/run scripts in the VS Code extension. ## Commands (`.kilo/command/*.md`) -Markdown files with YAML frontmatter. The filename (minus `.md`) becomes the command name invoked via `/name`. Commands can live in `.kilo/`, `.kilocode/`, `.opencode/`, and global config roots, with both `command/` and `commands/` directory names supported. See Config File Locations for the full search order. +Markdown files with YAML frontmatter. The filename (minus `.md`) becomes the command name invoked via `/name`. Commands can live in `.kilo/`, legacy `.kilocode/`, and global config roots, with both `command/` and `commands/` directory names supported. See Config File Locations for the full search order. ```yaml --- @@ -29,9 +29,8 @@ When asked where `/name` lives, do not search only the repo root. Search these r 1. `~/.config/kilo/` 2. `~/.kilo/` 3. `~/.kilocode/` -4. `~/.opencode/` -5. The `KILO_CONFIG_DIR` directory (if the env var is set) -6. project `.kilo/`, `.kilocode/`, and `.opencode/` directories from the current working directory up to the worktree root +4. The `KILO_CONFIG_DIR` directory (if the env var is set) +5. project `.kilo/` and `.kilocode/` directories from the current working directory up to the worktree root Use exact patterns first: @@ -42,7 +41,7 @@ If found, return the full path. If not found in those roots, explain that the co ## Agents (`.kilo/agent/*.md`) -Also loaded from `.kilocode/` and `.opencode/` directories (legacy), and plural `agents/` variants. +Also loaded from legacy `.kilocode/` directories and plural `agents/` variants. ```yaml --- @@ -257,7 +256,7 @@ Additional skill directories and remote URLs: } ``` -Skills are markdown files at `skills//SKILL.md` (or `skill//SKILL.md`) with `name` and `description` in frontmatter. Discovered inside `.kilo/`, `.kilocode/`, and `.opencode/` directories. +Skills are markdown files at `skills//SKILL.md` (or `skill//SKILL.md`) with `name` and `description` in frontmatter. Discovered inside `.kilo/` and legacy `.kilocode/` directories. ## Other Top-Level Fields @@ -340,14 +339,14 @@ Notification settings are managed through `kilo console` under **Settings > CLI | Global | `~/.config/kilo/kilo.json`, `~/.config/kilo/kilo.jsonc`, `~/.config/kilo/opencode.json` (legacy), `~/.config/kilo/opencode.jsonc` (legacy), `~/.config/kilo/config.json` (legacy) | | Managed | Linux: `/etc/kilo/`, macOS: `/Library/Application Support/kilo/`, Windows: `%ProgramData%\kilo\` — loads `kilo.json`, `kilo.jsonc`, `opencode.json`, `opencode.jsonc` (enterprise, highest priority) | -Each config directory (`.kilo/`, `.kilocode/`, `.opencode/`) can also contain `kilo.json`, `kilo.jsonc`, `opencode.json`, or `opencode.jsonc`. +Each config directory (`.kilo/` and legacy `.kilocode/`) can also contain `kilo.json`, `kilo.jsonc`, `opencode.json`, or `opencode.jsonc`. ### Config directories -Three directory names are scanned: `.kilo` (modern), `.kilocode` (legacy), `.opencode` (legacy). All three are checked at each level: +Two directory names are scanned: `.kilo` (canonical) and `.kilocode` (legacy fallback). Both are checked at each level, and `.kilo` wins when both define the same entry. `.opencode` directories are not loaded. -- **Project**: walks up from CWD to the git worktree root, checking for all three at each directory level -- **Home**: `~/.kilo/`, `~/.kilocode/`, `~/.opencode/` +- **Project**: walks up from CWD to the git worktree root, checking both directories at each level +- **Home**: `~/.kilo/` and `~/.kilocode/` - **XDG global**: `~/.config/kilo/` (always loaded, lowest file-based precedence) ### Commands, agents, modes, plugins @@ -361,7 +360,7 @@ Glob patterns run inside every discovered config directory (including legacy): | Mode | `{mode,modes}/*.md` | | Plugin | `{plugin,plugins}/*.{ts,js}` | -Example: `~/.config/kilo/command/*.md` (modern global), `~/.kilocode/command/*.md` (legacy global), `.opencode/commands/*.md` (legacy project) all load commands. +Example: `~/.config/kilo/command/*.md` (global), `~/.kilocode/command/*.md` (legacy home), and `.kilo/commands/*.md` (project) all load commands. ### Skills and instructions diff --git a/packages/opencode/src/kilocode/tui/config.ts b/packages/opencode/src/kilocode/tui/config.ts index a5d8beeeec..5d61466649 100644 --- a/packages/opencode/src/kilocode/tui/config.ts +++ b/packages/opencode/src/kilocode/tui/config.ts @@ -23,7 +23,7 @@ export namespace KilocodeTuiConfig { export type Editable = Omit & { keybinds?: Record } const files = ["tui.jsonc", "tui.json"] as const - const dirs = [".kilo", ".kilocode", ".opencode"] as const + const dirs = [".kilo", ".kilocode"] as const export async function get(input: { directory: string }) { const cfg = await Effect.runPromise( diff --git a/packages/opencode/src/plugin/install.ts b/packages/opencode/src/plugin/install.ts index f07b78bc36..f40916aa5a 100644 --- a/packages/opencode/src/plugin/install.ts +++ b/packages/opencode/src/plugin/install.ts @@ -334,7 +334,7 @@ function patchDir(input: PatchInput) { if (input.global) return input.config ?? Global.Path.config const git = input.vcs === "git" && input.worktree !== "/" const root = git ? input.worktree : input.directory - return path.join(root, ".opencode") + return path.join(root, ".kilo") // kilocode_change } function patchName(kind: Kind): "opencode" | "tui" { diff --git a/packages/opencode/test/agent/agent.test.ts b/packages/opencode/test/agent/agent.test.ts index 8253471efb..d1bc93c160 100644 --- a/packages/opencode/test/agent/agent.test.ts +++ b/packages/opencode/test/agent/agent.test.ts @@ -594,7 +594,7 @@ it.instance( () => Effect.gen(function* () { const test = yield* TestInstance - const skillDir = path.join(test.directory, ".opencode", "skill", "perm-skill") + const skillDir = path.join(test.directory, ".kilo", "skill", "perm-skill") // kilocode_change yield* Effect.promise(() => Bun.write( path.join(skillDir, "SKILL.md"), diff --git a/packages/opencode/test/cli/tui/plugin-loader.test.ts b/packages/opencode/test/cli/tui/plugin-loader.test.ts index 0aed67e63d..df7d3b9b0d 100644 --- a/packages/opencode/test/cli/tui/plugin-loader.test.ts +++ b/packages/opencode/test/cli/tui/plugin-loader.test.ts @@ -168,9 +168,9 @@ async function load(): Promise { const invalidThemePath = path.join(dir, invalidThemeFile) const globalThemePath = path.join(dir, globalThemeFile) const preloadedThemePath = path.join(dir, preloadedThemeFile) - const localDest = path.join(dir, ".opencode", "themes", localThemeFile) + const localDest = path.join(dir, ".kilo", "themes", localThemeFile) // kilocode_change const globalDest = path.join(Global.Path.config, "themes", globalThemeFile) - const preloadedDest = path.join(dir, ".opencode", "themes", preloadedThemeFile) + const preloadedDest = path.join(dir, ".kilo", "themes", preloadedThemeFile) // kilocode_change const fnMarker = path.join(dir, "function-called.txt") const localMarker = path.join(dir, "local-called.json") const invalidMarker = path.join(dir, "invalid-called.json") @@ -549,7 +549,7 @@ export default { .then(() => true) .catch(() => false) const leaked_global_to_local = await fs - .stat(path.join(tmp.path, ".opencode", "themes", tmp.extra.globalThemeFile)) + .stat(path.join(tmp.path, ".kilo", "themes", tmp.extra.globalThemeFile)) // kilocode_change .then(() => true) .catch(() => false) @@ -1231,7 +1231,7 @@ test("updates installed theme when plugin metadata changes", async () => { const spec = pathToFileURL(pluginPath).href const themeFile = "theme-update.json" const themePath = path.join(dir, themeFile) - const dest = path.join(dir, ".opencode", "themes", themeFile) + const dest = path.join(dir, ".kilo", "themes", themeFile) // kilocode_change const themeName = themeFile.replace(/\.json$/, "") const configPath = path.join(dir, "tui.json") diff --git a/packages/opencode/test/config/config.test.ts b/packages/opencode/test/config/config.test.ts index b298f51a79..4b22d4bb87 100644 --- a/packages/opencode/test/config/config.test.ts +++ b/packages/opencode/test/config/config.test.ts @@ -817,7 +817,7 @@ it.instance("agent markdown permission config preserves user key order", () => Effect.gen(function* () { const test = yield* TestInstance yield* AppFileSystem.use.writeWithDirs( - path.join(test.directory, ".opencode", "agent", "ordered.md"), + path.join(test.directory, ".kilo", "agent", "ordered.md"), // kilocode_change `--- permission: bash: allow diff --git a/packages/opencode/test/config/tui.test.ts b/packages/opencode/test/config/tui.test.ts index 5695da5534..1506e0e84f 100644 --- a/packages/opencode/test/config/tui.test.ts +++ b/packages/opencode/test/config/tui.test.ts @@ -87,7 +87,7 @@ it.instance("keeps server and tui plugin merge semantics aligned", () => Effect.gen(function* () { const fs = yield* AppFileSystem.Service const test = yield* TestInstance - const local = path.join(test.directory, ".opencode") + const local = path.join(test.directory, ".kilo") // kilocode_change yield* fs.makeDirectory(local, { recursive: true }) yield* fs.writeJson(path.join(Global.Path.config, "kilo.json"), { @@ -129,7 +129,7 @@ it.instance("loads tui config with the same precedence order as server config pa yield* fs.writeJson(path.join(Global.Path.config, "tui.json"), { theme: "global" }) yield* fs.writeJson(path.join(test.directory, "tui.json"), { theme: "project" }) yield* fs.writeWithDirs( - path.join(test.directory, ".opencode", "tui.json"), + path.join(test.directory, ".kilo", "tui.json"), // kilocode_change JSON.stringify({ theme: "local", diff_style: "stacked" }, null, 2), ) @@ -854,7 +854,7 @@ it.instance("silently skips malformed tui.json - load failures degrade to {}", ( const fs = yield* AppFileSystem.Service const test = yield* TestInstance yield* fs.writeFileString(path.join(test.directory, "tui.json"), '{ "theme": "broken",') - yield* fs.writeWithDirs(path.join(test.directory, ".opencode", "tui.json"), JSON.stringify({ theme: "fallback" })) + yield* fs.writeWithDirs(path.join(test.directory, ".kilo", "tui.json"), JSON.stringify({ theme: "fallback" })) // kilocode_change const config = yield* getTuiConfig(test.directory) expect(config.theme).toBe("fallback") @@ -868,7 +868,7 @@ it.instance("silently skips non-ENOENT read failures (e.g. tui.json is a directo const fs = yield* AppFileSystem.Service const test = yield* TestInstance yield* fs.makeDirectory(path.join(test.directory, "tui.json"), { recursive: true }) - yield* fs.writeWithDirs(path.join(test.directory, ".opencode", "tui.json"), JSON.stringify({ theme: "fallback" })) + yield* fs.writeWithDirs(path.join(test.directory, ".kilo", "tui.json"), JSON.stringify({ theme: "fallback" })) // kilocode_change const config = yield* getTuiConfig(test.directory) expect(config.theme).toBe("fallback") diff --git a/packages/opencode/test/kilocode/config/config.test.ts b/packages/opencode/test/kilocode/config/config.test.ts index 2d12965412..e405398721 100644 --- a/packages/opencode/test/kilocode/config/config.test.ts +++ b/packages/opencode/test/kilocode/config/config.test.ts @@ -414,7 +414,7 @@ describe("agent config", () => { }) describe("project config directory precedence", () => { - test("prefers .kilo, then .kilocode, then .opencode", async () => { + test("prefers .kilo over legacy .kilocode and ignores .opencode", async () => { await using tmp = await tmpdir() const entries = [ { @@ -462,6 +462,7 @@ describe("project config directory precedence", () => { `---\ndescription: ${item.source} agent\nmode: subagent\n---\n${item.source} agent prompt`, ) } + await Filesystem.write(path.join(dir, "plugin", `${item.source}.ts`), "export default {}") } await provideTestInstance({ @@ -471,7 +472,7 @@ describe("project config directory precedence", () => { expect(config.username).toBe("kilo") expect(config.model).toBe("test/kilocode") - expect(config.small_model).toBe("test/opencode") + expect(config.small_model).toBeUndefined() expect(config.command?.shared).toMatchObject({ description: "kilo command", @@ -481,10 +482,7 @@ describe("project config directory precedence", () => { description: "kilocode command", template: "kilocode command template", }) - expect(config.command?.["opencode-only"]).toMatchObject({ - description: "opencode command", - template: "opencode command template", - }) + expect(config.command?.["opencode-only"]).toBeUndefined() expect(config.agent?.shared).toMatchObject({ description: "kilo agent", @@ -494,10 +492,12 @@ describe("project config directory precedence", () => { description: "kilocode agent", prompt: "kilocode agent prompt", }) - expect(config.agent?.["opencode-only"]).toMatchObject({ - description: "opencode agent", - prompt: "opencode agent prompt", - }) + expect(config.agent?.["opencode-only"]).toBeUndefined() + + const plugins = JSON.stringify(config.plugin) + expect(plugins).toContain("kilocode.ts") + expect(plugins).toContain("kilo.ts") + expect(plugins).not.toContain("opencode.ts") }, }) }) @@ -540,7 +540,7 @@ describe("linked worktree config", () => { await Bun.write(path.join(directory, "placeholder"), "") await Bun.write( path.join(primary.path, "packages", ".opencode", "kilo.jsonc"), - JSON.stringify({ snapshot: true, autoupdate: false, share: "auto" }), + JSON.stringify({ snapshot: true, autoupdate: false, share: "auto", default_agent: "opencode-only" }), ) await Bun.write( path.join(primary.path, "packages", ".kilocode", "kilo.jsonc"), @@ -554,6 +554,7 @@ describe("linked worktree config", () => { expect(config.snapshot).toBe(false) expect(config.autoupdate).toBe("notify") expect(config.share).toBe("manual") + expect(config.default_agent).toBeUndefined() } finally { await $`git worktree remove --force ${worktree}`.cwd(primary.path).quiet().nothrow() } diff --git a/packages/opencode/test/kilocode/plugin-dependencies.ts b/packages/opencode/test/kilocode/plugin-dependencies.ts index 79c4fc3249..175b7bb67e 100644 --- a/packages/opencode/test/kilocode/plugin-dependencies.ts +++ b/packages/opencode/test/kilocode/plugin-dependencies.ts @@ -11,5 +11,5 @@ async function mark(dir: string) { } export function preparePluginDependencies(dir: string) { - return Promise.all([mark(path.join(dir, ".opencode")), mark(Global.Path.config)]) + return Promise.all([mark(path.join(dir, ".kilo")), mark(Global.Path.config)]) } diff --git a/packages/opencode/test/kilocode/server/config-overlay.test.ts b/packages/opencode/test/kilocode/server/config-overlay.test.ts index 95f2f1ec31..6767b9fa01 100644 --- a/packages/opencode/test/kilocode/server/config-overlay.test.ts +++ b/packages/opencode/test/kilocode/server/config-overlay.test.ts @@ -92,7 +92,7 @@ describe("config overlay routes", () => { expect(Object.hasOwn(patched, "prototype")).toBe(false) }) - test("prefers .kilo, then .kilocode, then .opencode in project overlays", async () => { + test("prefers .kilo over legacy .kilocode and ignores .opencode in project overlays", async () => { await using project = await tmpdir() const entries = [ { @@ -120,6 +120,10 @@ describe("config overlay routes", () => { `---\ndescription: ${item.source} agent\nmode: subagent\n---\n${item.source} agent prompt`, ) } + await Filesystem.write( + path.join(project.path, ".opencode", "agent", "opencode-only.md"), + "---\ndescription: opencode-only agent\nmode: subagent\n---\nopencode-only agent prompt", + ) const body = await KilocodeConfigOverlay.resolve({ directory: project.path, @@ -131,11 +135,12 @@ describe("config overlay routes", () => { expect(body.project.username).toBe("kilo") expect(body.project.model).toBe("test/kilocode") - expect(body.project.small_model).toBe("test/opencode") + expect(body.project.small_model).toBeUndefined() expect(body.project.agent?.shared).toMatchObject({ description: "kilo agent", prompt: "kilo agent prompt", }) + expect(body.project.agent?.["opencode-only"]).toBeUndefined() expect(body.targets.project).toBe(path.join(project.path, ".kilo", "kilo.json")) }) diff --git a/packages/opencode/test/kilocode/server/config-sources.test.ts b/packages/opencode/test/kilocode/server/config-sources.test.ts index b3198adc2e..660389117b 100644 --- a/packages/opencode/test/kilocode/server/config-sources.test.ts +++ b/packages/opencode/test/kilocode/server/config-sources.test.ts @@ -112,9 +112,9 @@ describe("config source routes", () => { const inline = body.sources.find((source) => source.source === "KILO_CONFIG_CONTENT") expect(order(body, envFile)).toBeLessThan(order(body, projectFile)) - expect(order(body, projectFile)).toBeLessThan(order(body, opencodeFile)) - expect(order(body, opencodeFile)).toBeLessThan(order(body, kilocodeFile)) + expect(order(body, projectFile)).toBeLessThan(order(body, kilocodeFile)) expect(order(body, kilocodeFile)).toBeLessThan(order(body, configFile)) + expect(body.sources.some((source) => source.path === opencodeFile)).toBe(false) expect(order(body, configFile)).toBeLessThan(order(body, extraFile)) expect(inline?.order).toBeGreaterThan(order(body, extraFile)) expect(inline?.order).toBeLessThan(order(body, managedFile)) diff --git a/packages/opencode/test/kilocode/server/httpapi-exercise-scenarios.ts b/packages/opencode/test/kilocode/server/httpapi-exercise-scenarios.ts index 6ac923569f..c0dd15636f 100644 --- a/packages/opencode/test/kilocode/server/httpapi-exercise-scenarios.ts +++ b/packages/opencode/test/kilocode/server/httpapi-exercise-scenarios.ts @@ -245,10 +245,10 @@ export const kiloScenarios: Scenario[] = [ Effect.gen(function* () { const location = yield* file( ctx, - ".opencode/skill/httpapi-remove/SKILL.md", + ".kilo/skill/httpapi-remove/SKILL.md", "---\nname: httpapi-remove\ndescription: HTTP API removal fixture.\n---\n# HTTP API remove\n", ) - const sentinel = yield* file(ctx, ".opencode/skill/httpapi-remove/KEEP.txt", "synthetic sentinel\n") + const sentinel = yield* file(ctx, ".kilo/skill/httpapi-remove/KEEP.txt", "synthetic sentinel\n") return { location, sentinel } }), ) @@ -273,9 +273,7 @@ export const kiloScenarios: Scenario[] = [ http.protected .post("/kilocode/agent/remove", "kilocode.removeAgent") .mutating() - .seeded((ctx) => - file(ctx, ".opencode/agent/httpapi-remove.md", "---\ndescription: HTTP API remove\n---\nRemove me.\n"), - ) + .seeded((ctx) => file(ctx, ".kilo/agent/httpapi-remove.md", "---\ndescription: HTTP API remove\n---\nRemove me.\n")) .at((ctx) => ({ path: "/kilocode/agent/remove", headers: ctx.headers(), body: { name: "httpapi-remove" } })) .jsonEffect(200, (body, ctx) => Effect.gen(function* () { diff --git a/packages/opencode/test/kilocode/server/tui-config.test.ts b/packages/opencode/test/kilocode/server/tui-config.test.ts index 2bba0a2f92..22909b26fe 100644 --- a/packages/opencode/test/kilocode/server/tui-config.test.ts +++ b/packages/opencode/test/kilocode/server/tui-config.test.ts @@ -43,6 +43,25 @@ describe("TUI config routes", () => { expect(body.plugin_origins).toBeUndefined() }) + test("loads legacy .kilocode TUI config and ignores .opencode", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await fs.mkdir(path.join(dir, ".opencode"), { recursive: true }) + await fs.mkdir(path.join(dir, ".kilocode"), { recursive: true }) + await Bun.write(path.join(dir, ".opencode", "tui.json"), JSON.stringify({ theme: "dracula" })) + await Bun.write(path.join(dir, ".kilocode", "tui.json"), JSON.stringify({ theme: "nord" })) + }, + }) + + const response = await Server.Default().app.request("/tui/config", { + headers: { "x-kilo-directory": tmp.path }, + }) + + expect(response.status).toBe(200) + const body = (await response.json()) as { theme?: string } + expect(body.theme).toBe("nord") + }) + test("lists valid TUI keybinds", async () => { await using tmp = await tmpdir() diff --git a/packages/opencode/test/plugin/auth-override.test.ts b/packages/opencode/test/plugin/auth-override.test.ts index 298180e73a..092716c7e5 100644 --- a/packages/opencode/test/plugin/auth-override.test.ts +++ b/packages/opencode/test/plugin/auth-override.test.ts @@ -51,7 +51,7 @@ describe("plugin.auth-override", () => { Effect.gen(function* () { const tmp = yield* TestInstance const fs = yield* AppFileSystem.Service - const pluginDir = path.join(tmp.directory, ".opencode", "plugin") + const pluginDir = path.join(tmp.directory, ".kilo", "plugin") // kilocode_change yield* fs.writeWithDirs( path.join(pluginDir, "custom-copilot-auth.ts"), diff --git a/packages/opencode/test/plugin/install-concurrency.test.ts b/packages/opencode/test/plugin/install-concurrency.test.ts index 6160bd6e25..728d8b6dbe 100644 --- a/packages/opencode/test/plugin/install-concurrency.test.ts +++ b/packages/opencode/test/plugin/install-concurrency.test.ts @@ -82,7 +82,7 @@ describe("plugin.install.concurrent", () => { expect(out.map((x) => x.code)).toEqual(Array.from({ length: all.length }, () => 0)) expect(out.map((x) => x.stderr.toString()).filter(Boolean)).toEqual([]) - const cfg = await read(path.join(tmp.path, ".opencode", "opencode.jsonc")) + const cfg = await read(path.join(tmp.path, ".kilo", "opencode.jsonc")) // kilocode_change expectPlugins(cfg.plugin, all) }, 25_000) @@ -105,8 +105,10 @@ describe("plugin.install.concurrent", () => { expect(out.map((x) => x.code)).toEqual(Array.from({ length: all.length }, () => 0)) expect(out.map((x) => x.stderr.toString()).filter(Boolean)).toEqual([]) - const server = await read(path.join(tmp.path, ".opencode", "opencode.jsonc")) - const tui = await read(path.join(tmp.path, ".opencode", "tui.jsonc")) + // kilocode_change start + const server = await read(path.join(tmp.path, ".kilo", "opencode.jsonc")) + const tui = await read(path.join(tmp.path, ".kilo", "tui.jsonc")) + // kilocode_change end expectPlugins(server.plugin, all) expectPlugins(tui.plugin, all) }, 25_000) @@ -114,7 +116,7 @@ describe("plugin.install.concurrent", () => { test("preserves updates when existing config uses .json", async () => { await using tmp = await tmpdir() const target = await plugin(tmp.path, ["server"]) - const cfg = path.join(tmp.path, ".opencode", "opencode.json") + const cfg = path.join(tmp.path, ".kilo", "opencode.json") // kilocode_change await fs.mkdir(path.dirname(cfg), { recursive: true }) await Bun.write(cfg, JSON.stringify({ plugin: ["seed@1.0.0"] }, null, 2)) @@ -135,6 +137,6 @@ describe("plugin.install.concurrent", () => { const json = await read(cfg) expectPlugins(json.plugin, ["seed@1.0.0", ...next]) - expect(await Filesystem.exists(path.join(tmp.path, ".opencode", "opencode.jsonc"))).toBe(false) + expect(await Filesystem.exists(path.join(tmp.path, ".kilo", "opencode.jsonc"))).toBe(false) // kilocode_change }, 25_000) }) diff --git a/packages/opencode/test/plugin/install.test.ts b/packages/opencode/test/plugin/install.test.ts index 6dc9175be4..4a05be769a 100644 --- a/packages/opencode/test/plugin/install.test.ts +++ b/packages/opencode/test/plugin/install.test.ts @@ -122,8 +122,10 @@ describe("plugin.install.task", () => { const ok = await run(ctx(tmp.path)) expect(ok).toBe(true) - const server = await read(path.join(tmp.path, ".opencode", "opencode.jsonc")) - const tui = await read(path.join(tmp.path, ".opencode", "tui.jsonc")) + // kilocode_change start + const server = await read(path.join(tmp.path, ".kilo", "opencode.jsonc")) + const tui = await read(path.join(tmp.path, ".kilo", "tui.jsonc")) + // kilocode_change end expect(server.plugin).toEqual(["acme@1.2.3"]) expect(tui.plugin).toEqual(["acme@1.2.3"]) }) @@ -144,8 +146,10 @@ describe("plugin.install.task", () => { const ok = await run(ctx(tmp.path)) expect(ok).toBe(true) - const server = await read(path.join(tmp.path, ".opencode", "opencode.jsonc")) - const tui = await read(path.join(tmp.path, ".opencode", "tui.jsonc")) + // kilocode_change start + const server = await read(path.join(tmp.path, ".kilo", "opencode.jsonc")) + const tui = await read(path.join(tmp.path, ".kilo", "tui.jsonc")) + // kilocode_change end expect(server.plugin).toEqual([["acme@1.2.3", { custom: true, other: false }]]) expect(tui.plugin).toEqual([["acme@1.2.3", { compact: true }]]) }) @@ -153,7 +157,7 @@ describe("plugin.install.task", () => { test("preserves JSONC comments when adding plugins to server and tui config", async () => { await using tmp = await tmpdir() const target = await plugin(tmp.path, ["server", "tui"]) - const cfg = path.join(tmp.path, ".opencode") + const cfg = path.join(tmp.path, ".kilo") // kilocode_change const server = path.join(cfg, "opencode.jsonc") const tui = path.join(cfg, "tui.jsonc") await fs.mkdir(cfg, { recursive: true }) @@ -212,7 +216,7 @@ describe("plugin.install.task", () => { test("preserves JSONC comments when force replacing plugin version", async () => { await using tmp = await tmpdir() const target = await plugin(tmp.path, ["server"]) - const cfg = path.join(tmp.path, ".opencode", "opencode.jsonc") + const cfg = path.join(tmp.path, ".kilo", "opencode.jsonc") // kilocode_change await fs.mkdir(path.dirname(cfg), { recursive: true }) await Bun.write( cfg, @@ -257,14 +261,14 @@ describe("plugin.install.task", () => { const ok = await run(ctx(tmp.path)) expect(ok).toBe(true) - const server = await read(path.join(tmp.path, ".opencode", "opencode.jsonc")) + const server = await read(path.join(tmp.path, ".kilo", "opencode.jsonc")) // kilocode_change expect(server.plugin).toEqual(["acme@1.2.3"]) }) test("does not change configured package version without force", async () => { await using tmp = await tmpdir() const target = await plugin(tmp.path, ["server"]) - const cfg = path.join(tmp.path, ".opencode", "opencode.json") + const cfg = path.join(tmp.path, ".kilo", "opencode.json") // kilocode_change await fs.mkdir(path.dirname(cfg), { recursive: true }) await Bun.write(cfg, JSON.stringify({ plugin: ["acme@1.0.0"] }, null, 2)) @@ -284,7 +288,7 @@ describe("plugin.install.task", () => { test("does not change scoped package version without force", async () => { await using tmp = await tmpdir() const target = await plugin(tmp.path, ["server"]) - const cfg = path.join(tmp.path, ".opencode", "opencode.json") + const cfg = path.join(tmp.path, ".kilo", "opencode.json") // kilocode_change await fs.mkdir(path.dirname(cfg), { recursive: true }) await Bun.write(cfg, JSON.stringify({ plugin: ["@scope/acme@1.0.0"] }, null, 2)) @@ -304,7 +308,7 @@ describe("plugin.install.task", () => { test("keeps file plugin entries and still adds npm plugin", async () => { await using tmp = await tmpdir() const target = await plugin(tmp.path, ["server"]) - const cfg = path.join(tmp.path, ".opencode", "opencode.json") + const cfg = path.join(tmp.path, ".kilo", "opencode.json") // kilocode_change await fs.mkdir(path.dirname(cfg), { recursive: true }) await Bun.write(cfg, JSON.stringify({ plugin: ["file:///tmp/acme.ts"] }, null, 2)) @@ -324,7 +328,7 @@ describe("plugin.install.task", () => { test("force replaces configured package version and keeps tuple options", async () => { await using tmp = await tmpdir() const target = await plugin(tmp.path, ["server"]) - const cfg = path.join(tmp.path, ".opencode", "opencode.json") + const cfg = path.join(tmp.path, ".kilo", "opencode.json") // kilocode_change await fs.mkdir(path.dirname(cfg), { recursive: true }) await Bun.write( cfg, @@ -367,7 +371,7 @@ describe("plugin.install.task", () => { expect(ok).toBe(true) expect(await Filesystem.exists(path.join(global, "opencode.jsonc"))).toBe(true) - expect(await Filesystem.exists(path.join(tmp.path, ".opencode", "opencode.jsonc"))).toBe(false) + expect(await Filesystem.exists(path.join(tmp.path, ".kilo", "opencode.jsonc"))).toBe(false) // kilocode_change }) test("writes local scope under directory when vcs is not git", async () => { @@ -386,8 +390,10 @@ describe("plugin.install.task", () => { const ok = await run(ctxDir(directory, worktree)) expect(ok).toBe(true) - expect(await Filesystem.exists(path.join(directory, ".opencode", "opencode.jsonc"))).toBe(true) - expect(await Filesystem.exists(path.join(worktree, ".opencode", "opencode.jsonc"))).toBe(false) + // kilocode_change start + expect(await Filesystem.exists(path.join(directory, ".kilo", "opencode.jsonc"))).toBe(true) + expect(await Filesystem.exists(path.join(worktree, ".kilo", "opencode.jsonc"))).toBe(false) + // kilocode_change end }) test("writes local scope under directory when worktree is root slash", async () => { @@ -404,7 +410,7 @@ describe("plugin.install.task", () => { const ok = await run(ctxRoot(directory)) expect(ok).toBe(true) - expect(await Filesystem.exists(path.join(directory, ".opencode", "opencode.jsonc"))).toBe(true) + expect(await Filesystem.exists(path.join(directory, ".kilo", "opencode.jsonc"))).toBe(true) // kilocode_change }) test("writes tui local scope under directory when worktree is root slash", async () => { @@ -421,7 +427,7 @@ describe("plugin.install.task", () => { const ok = await run(ctxRoot(directory)) expect(ok).toBe(true) - expect(await Filesystem.exists(path.join(directory, ".opencode", "tui.jsonc"))).toBe(true) + expect(await Filesystem.exists(path.join(directory, ".kilo", "tui.jsonc"))).toBe(true) // kilocode_change }) test("writes only tui config for tui-only plugins", async () => { @@ -436,8 +442,10 @@ describe("plugin.install.task", () => { const ok = await run(ctx(tmp.path)) expect(ok).toBe(true) - expect(await Filesystem.exists(path.join(tmp.path, ".opencode", "tui.jsonc"))).toBe(true) - expect(await Filesystem.exists(path.join(tmp.path, ".opencode", "opencode.jsonc"))).toBe(false) + // kilocode_change start + expect(await Filesystem.exists(path.join(tmp.path, ".kilo", "tui.jsonc"))).toBe(true) + expect(await Filesystem.exists(path.join(tmp.path, ".kilo", "opencode.jsonc"))).toBe(false) + // kilocode_change end }) test("writes tui config for oc-themes-only packages", async () => { @@ -454,10 +462,12 @@ describe("plugin.install.task", () => { const ok = await run(ctx(tmp.path)) expect(ok).toBe(true) - expect(await Filesystem.exists(path.join(tmp.path, ".opencode", "tui.jsonc"))).toBe(true) - expect(await Filesystem.exists(path.join(tmp.path, ".opencode", "opencode.jsonc"))).toBe(false) + // kilocode_change start + expect(await Filesystem.exists(path.join(tmp.path, ".kilo", "tui.jsonc"))).toBe(true) + expect(await Filesystem.exists(path.join(tmp.path, ".kilo", "opencode.jsonc"))).toBe(false) + // kilocode_change end - const tui = await read(path.join(tmp.path, ".opencode", "tui.jsonc")) + const tui = await read(path.join(tmp.path, ".kilo", "tui.jsonc")) // kilocode_change expect(tui.plugin).toEqual(["acme@1.2.3"]) }) @@ -473,15 +483,19 @@ describe("plugin.install.task", () => { const ok = await run(ctx(tmp.path)) expect(ok).toBe(false) - expect(await Filesystem.exists(path.join(tmp.path, ".opencode", "tui.jsonc"))).toBe(false) - expect(await Filesystem.exists(path.join(tmp.path, ".opencode", "opencode.jsonc"))).toBe(false) + // kilocode_change start + expect(await Filesystem.exists(path.join(tmp.path, ".kilo", "tui.jsonc"))).toBe(false) + expect(await Filesystem.exists(path.join(tmp.path, ".kilo", "opencode.jsonc"))).toBe(false) + // kilocode_change end }) test("force replaces version in both server and tui configs", async () => { await using tmp = await tmpdir() const target = await plugin(tmp.path, ["server", "tui"]) - const server = path.join(tmp.path, ".opencode", "opencode.json") - const tui = path.join(tmp.path, ".opencode", "tui.json") + // kilocode_change start + const server = path.join(tmp.path, ".kilo", "opencode.json") + const tui = path.join(tmp.path, ".kilo", "tui.json") + // kilocode_change end await fs.mkdir(path.dirname(server), { recursive: true }) await Bun.write(server, JSON.stringify({ plugin: ["acme@1.0.0", "other@1.0.0"] }, null, 2)) await Bun.write(tui, JSON.stringify({ plugin: [["acme@1.0.0", { mode: "safe" }], "other@1.0.0"] }, null, 2)) @@ -505,7 +519,7 @@ describe("plugin.install.task", () => { test("returns false and keeps config unchanged for invalid JSONC", async () => { await using tmp = await tmpdir() const target = await plugin(tmp.path, ["server"]) - const cfg = path.join(tmp.path, ".opencode", "opencode.jsonc") + const cfg = path.join(tmp.path, ".kilo", "opencode.jsonc") // kilocode_change await fs.mkdir(path.dirname(cfg), { recursive: true }) const bad = '{"plugin": ["acme@1.0.0",}' await Bun.write(cfg, bad) @@ -534,8 +548,10 @@ describe("plugin.install.task", () => { const ok = await run(ctx(tmp.path)) expect(ok).toBe(false) - expect(await Filesystem.exists(path.join(tmp.path, ".opencode", "opencode.jsonc"))).toBe(false) - expect(await Filesystem.exists(path.join(tmp.path, ".opencode", "tui.jsonc"))).toBe(false) + // kilocode_change start + expect(await Filesystem.exists(path.join(tmp.path, ".kilo", "opencode.jsonc"))).toBe(false) + expect(await Filesystem.exists(path.join(tmp.path, ".kilo", "tui.jsonc"))).toBe(false) + // kilocode_change end }) test("returns false when manifest cannot be read", async () => { @@ -551,7 +567,7 @@ describe("plugin.install.task", () => { const ok = await run(ctx(tmp.path)) expect(ok).toBe(false) - expect(await Filesystem.exists(path.join(tmp.path, ".opencode", "opencode.jsonc"))).toBe(false) + expect(await Filesystem.exists(path.join(tmp.path, ".kilo", "opencode.jsonc"))).toBe(false) // kilocode_change }) test("returns false when install fails", async () => { @@ -565,6 +581,6 @@ describe("plugin.install.task", () => { const ok = await run(ctx(tmp.path)) expect(ok).toBe(false) - expect(await Filesystem.exists(path.join(tmp.path, ".opencode", "opencode.jsonc"))).toBe(false) + expect(await Filesystem.exists(path.join(tmp.path, ".kilo", "opencode.jsonc"))).toBe(false) // kilocode_change }) }) diff --git a/packages/opencode/test/provider/provider.test.ts b/packages/opencode/test/provider/provider.test.ts index 07721ed9d5..cbd0ba4469 100644 --- a/packages/opencode/test/provider/provider.test.ts +++ b/packages/opencode/test/provider/provider.test.ts @@ -1640,7 +1640,7 @@ const provideMultiInstance = (eff: Effect.Effect) => it.effect("plugin config providers persist after instance dispose", () => Effect.gen(function* () { const dir = yield* tmpdirScoped() - const configDir = path.join(dir, ".opencode") + const configDir = path.join(dir, ".kilo") // kilocode_change const root = path.join(configDir, "plugin") yield* Effect.promise(() => mkdir(root, { recursive: true })) yield* Effect.promise(() => markPluginDependenciesReady(configDir)) @@ -1697,7 +1697,7 @@ it.instance( "plugin config enabled and disabled providers are honored", Effect.gen(function* () { const instance = yield* TestInstance - const configDir = path.join(instance.directory, ".opencode") + const configDir = path.join(instance.directory, ".kilo") // kilocode_change const root = path.join(configDir, "plugin") yield* Effect.promise(() => mkdir(root, { recursive: true })) yield* Effect.promise(() => markPluginDependenciesReady(configDir)) diff --git a/packages/opencode/test/server/httpapi-provider.test.ts b/packages/opencode/test/server/httpapi-provider.test.ts index d1ca0fe463..a2a6535f0d 100644 --- a/packages/opencode/test/server/httpapi-provider.test.ts +++ b/packages/opencode/test/server/httpapi-provider.test.ts @@ -122,7 +122,7 @@ function writeProviderAuthPlugin(dir: string) { yield* Effect.promise(() => preparePluginDependencies(dir)) // kilocode_change yield* fs.writeWithDirs( - path.join(dir, ".opencode", "plugin", "provider-oauth-parity.ts"), + path.join(dir, ".kilo", "plugin", "provider-oauth-parity.ts"), // kilocode_change [ "export default {", ' id: "test.provider-oauth-parity",', @@ -157,7 +157,7 @@ function writeProviderAuthValidationPlugin(dir: string) { yield* Effect.promise(() => preparePluginDependencies(dir)) // kilocode_change yield* fs.writeWithDirs( - path.join(dir, ".opencode", "plugin", "provider-oauth-validation.ts"), + path.join(dir, ".kilo", "plugin", "provider-oauth-validation.ts"), // kilocode_change [ "export default {", ' id: "test.provider-oauth-validation",', @@ -199,7 +199,7 @@ function writeFunctionOptionsPlugin(dir: string) { yield* Effect.promise(() => preparePluginDependencies(dir)) // kilocode_change yield* fs.writeWithDirs( - path.join(dir, ".opencode", "plugin", "provider-function-options.ts"), + path.join(dir, ".kilo", "plugin", "provider-function-options.ts"), // kilocode_change [ "export default {", ' id: "test.provider-function-options",', @@ -231,7 +231,7 @@ function writeProviderModelsMutationPlugin(dir: string) { yield* Effect.promise(() => preparePluginDependencies(dir)) // kilocode_change yield* fs.writeWithDirs( - path.join(dir, ".opencode", "plugin", "provider-models-mutation.ts"), + path.join(dir, ".kilo", "plugin", "provider-models-mutation.ts"), // kilocode_change [ "export default {", ' id: "test.provider-models-mutation",', diff --git a/packages/opencode/test/server/httpapi-sdk.test.ts b/packages/opencode/test/server/httpapi-sdk.test.ts index 7962d18e86..8f3179ebba 100644 --- a/packages/opencode/test/server/httpapi-sdk.test.ts +++ b/packages/opencode/test/server/httpapi-sdk.test.ts @@ -294,7 +294,7 @@ function writeStandardFiles(dir: string) { function writeProjectSkill(dir: string) { return AppFileSystem.Service.use((fs) => fs.writeWithDirs( - path.join(dir, ".opencode", "skills", "project-rest-skill", "SKILL.md"), + path.join(dir, ".kilo", "skills", "project-rest-skill", "SKILL.md"), // kilocode_change `--- name: project-rest-skill description: A project skill visible to REST API prompts. diff --git a/packages/opencode/test/skill/skill.test.ts b/packages/opencode/test/skill/skill.test.ts index 7cae92b8b2..318344d1d8 100644 --- a/packages/opencode/test/skill/skill.test.ts +++ b/packages/opencode/test/skill/skill.test.ts @@ -194,10 +194,12 @@ Just some content without YAML frontmatter. provideTmpdirInstance( (dir) => Effect.gen(function* () { + // kilocode_change start - load .kilo skills without falling back to .opencode yield* Effect.promise(() => - Bun.write( - path.join(dir, ".opencode", "skill", "manual-skill", "SKILL.md"), - `--- + Promise.all([ + Bun.write( + path.join(dir, ".kilo", "skill", "manual-skill", "SKILL.md"), + `--- name: manual-skill --- @@ -205,8 +207,18 @@ name: manual-skill Instructions here. `, - ), + ), + Bun.write( + path.join(dir, ".opencode", "skill", "ignored-skill", "SKILL.md"), + `--- +name: ignored-skill +description: This skill must not load. +--- +`, + ), + ]), ) + // kilocode_change end const skill = yield* Skill.Service const list = discovered(yield* skill.all()) // kilocode_change @@ -485,16 +497,18 @@ description: A skill in the .agents/skills directory. # Agent Skill `, ), + // kilocode_change start Bun.write( - path.join(dir, ".opencode", "skill", "opencode-skill", "SKILL.md"), + path.join(dir, ".kilo", "skill", "opencode-skill", "SKILL.md"), `--- name: opencode-skill -description: A skill in the .opencode/skill directory. +description: A skill in the .kilo/skill directory. --- # OpenCode Skill `, ), + // kilocode_change end ]), ) @@ -532,26 +546,28 @@ description: A skill in the .agents/skills directory. # Agent Skill `, ), + // kilocode_change start Bun.write( - path.join(dir, ".opencode", "skill", "agent-skill", "SKILL.md"), + path.join(dir, ".kilo", "skill", "agent-skill", "SKILL.md"), `--- name: opencode-skill -description: A skill in the .opencode/skill directory. +description: A skill in the .kilo/skill directory. --- # OpenCode Skill `, ), Bun.write( - path.join(dir, ".opencode", "skills", "agent-skill", "SKILL.md"), + path.join(dir, ".kilo", "skills", "agent-skill", "SKILL.md"), `--- name: opencode-skill -description: A skill in the .opencode/skills directory. +description: A skill in the .kilo/skills directory. --- # OpenCode Skill `, ), + // kilocode_change end ]), ) diff --git a/packages/opencode/test/tool/registry.test.ts b/packages/opencode/test/tool/registry.test.ts index 7dd6bd7aa6..d9a50c80d8 100644 --- a/packages/opencode/test/tool/registry.test.ts +++ b/packages/opencode/test/tool/registry.test.ts @@ -40,7 +40,7 @@ import { run as runSandbox, type Profile } from "@kilocode/sandbox" // kilocode_ const node = CrossSpawnSpawner.defaultLayer const configLayer = TestConfig.layer({ - directories: () => InstanceState.directory.pipe(Effect.map((dir) => [path.join(dir, ".opencode")])), + directories: () => InstanceState.directory.pipe(Effect.map((dir) => [path.join(dir, ".kilo")])), // kilocode_change }) type RegistryLayerOptions = { @@ -206,10 +206,10 @@ describe("tool.registry", () => { }), ) - it.instance("loads tools from .opencode/tool (singular)", () => + it.instance("loads tools from .kilo/tool (singular)" /* kilocode_change */, () => Effect.gen(function* () { const test = yield* TestInstance - const opencode = path.join(test.directory, ".opencode") + const opencode = path.join(test.directory, ".kilo") // kilocode_change const tool = path.join(opencode, "tool") yield* Effect.promise(() => fs.mkdir(tool, { recursive: true })) yield* Effect.promise(() => @@ -233,10 +233,10 @@ describe("tool.registry", () => { }), ) - it.instance("ignores non-tool exports in .opencode/tool files", () => + it.instance("ignores non-tool exports in .kilo/tool files" /* kilocode_change */, () => Effect.gen(function* () { const test = yield* TestInstance - const tool = path.join(test.directory, ".opencode", "tool") + const tool = path.join(test.directory, ".kilo", "tool") // kilocode_change yield* Effect.promise(() => fs.mkdir(tool, { recursive: true })) yield* Effect.promise(() => Bun.write( @@ -269,7 +269,7 @@ describe("tool.registry", () => { it.instance("tolerates a custom tool exporting null/undefined args (no-args fallback)", () => Effect.gen(function* () { const test = yield* TestInstance - const tool = path.join(test.directory, ".opencode", "tool") + const tool = path.join(test.directory, ".kilo", "tool") // kilocode_change yield* Effect.promise(() => fs.mkdir(tool, { recursive: true })) yield* Effect.promise(() => Bun.write( @@ -311,10 +311,10 @@ describe("tool.registry", () => { }), ) - it.instance("loads tools from .opencode/tools (plural)", () => + it.instance("loads tools from .kilo/tools (plural)" /* kilocode_change */, () => Effect.gen(function* () { const test = yield* TestInstance - const opencode = path.join(test.directory, ".opencode") + const opencode = path.join(test.directory, ".kilo") // kilocode_change const tools = path.join(opencode, "tools") yield* Effect.promise(() => fs.mkdir(tools, { recursive: true })) yield* Effect.promise(() => @@ -341,7 +341,7 @@ describe("tool.registry", () => { it.instance("loads Zod-schema custom tools with JSON Schema and validation", () => Effect.gen(function* () { const test = yield* TestInstance - const customTools = path.join(test.directory, ".opencode", "tools") + const customTools = path.join(test.directory, ".kilo", "tools") // kilocode_change const pluginTool = pathToFileURL(path.resolve(import.meta.dir, "../../../plugin/src/tool.ts")).href yield* Effect.promise(() => fs.mkdir(customTools, { recursive: true })) yield* Effect.promise(() => @@ -394,7 +394,7 @@ describe("tool.registry", () => { () => Effect.gen(function* () { const test = yield* TestInstance - const opencode = path.join(test.directory, ".opencode") + const opencode = path.join(test.directory, ".kilo") // kilocode_change const customTools = path.join(opencode, "tools") const plugin = path.join(opencode, "node_modules", "@kilocode", "plugin") // kilocode_change yield* Effect.promise(() => fs.mkdir(path.join(plugin, "dist"), { recursive: true })) @@ -459,7 +459,7 @@ describe("tool.registry", () => { it.instance("preserves attachments from structured custom tool results", () => Effect.gen(function* () { const test = yield* TestInstance - const customTools = path.join(test.directory, ".opencode", "tools") + const customTools = path.join(test.directory, ".kilo", "tools") // kilocode_change const pluginTool = pathToFileURL(path.resolve(import.meta.dir, "../../../plugin/src/tool.ts")).href yield* Effect.promise(() => fs.mkdir(customTools, { recursive: true })) yield* Effect.promise(() => @@ -504,7 +504,7 @@ describe("tool.registry", () => { it.instance("loads legacy JSON-schema-shaped custom tools with wire schema", () => Effect.gen(function* () { const test = yield* TestInstance - const tools = path.join(test.directory, ".opencode", "tools") + const tools = path.join(test.directory, ".kilo", "tools") // kilocode_change yield* Effect.promise(() => fs.mkdir(tools, { recursive: true })) yield* Effect.promise(() => Bun.write( @@ -536,7 +536,7 @@ describe("tool.registry", () => { it.instance("loads tools with external dependencies without crashing", () => Effect.gen(function* () { const test = yield* TestInstance - const opencode = path.join(test.directory, ".opencode") + const opencode = path.join(test.directory, ".kilo") // kilocode_change const tools = path.join(opencode, "tools") yield* Effect.promise(() => fs.mkdir(tools, { recursive: true })) yield* Effect.promise(() => diff --git a/packages/opencode/test/tool/skill.test.ts b/packages/opencode/test/tool/skill.test.ts index df5378a74d..5b85895ae5 100644 --- a/packages/opencode/test/tool/skill.test.ts +++ b/packages/opencode/test/tool/skill.test.ts @@ -36,7 +36,7 @@ describe("tool.skill", () => { unix("execute returns skill content block with files", () => provideTmpdirInstance((dir) => Effect.gen(function* () { - const skill = path.join(dir, ".opencode", "skill", "tool-skill") + const skill = path.join(dir, ".kilo", "skill", "tool-skill") // kilocode_change yield* Effect.promise(() => Bun.write( path.join(skill, "SKILL.md"),