From dfeb7bf722c7000a2d7a318ee5f56a36908a0173 Mon Sep 17 00:00:00 2001 From: Thomas Boom Date: Tue, 24 Mar 2026 21:01:28 +0100 Subject: [PATCH 01/29] Update bonus credits and AI model versions in README Updated the bonus credits offer and AI model versions in the README. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 31d9e5cf348..b1557add3c9 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ - ⚡ Inline autocomplete suggestions - 🤖 Latest AI models - 🎁 API keys optional -- 💡 **Get $20 in bonus credits when you top-up for the first time** Credits can be used with 500+ models like Gemini 3.1 Pro, Claude 4.6 Sonnet & Opus, and GPT-5.2 +- 💡 **Get $20 in bonus credits when you top-up for the first time** Credits can be used with 500+ models like Gemini 3.1 Pro, Claude 4.6 Sonnet & Opus, and GPT-5.4 ## Quick Links @@ -38,7 +38,7 @@ ## Get Started in Visual Studio Code 1. Install the Kilo Code extension from the [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=kilocode.Kilo-Code). -2. Create your account to access 500+ cutting-edge AI models including Gemini 3 Pro, Claude 4.5 Sonnet & Opus, and GPT-5 – with transparent pricing that matches provider rates exactly. +2. Create your account to access 500+ cutting-edge AI models including Gemini 3.1 Pro, Claude 4.6 Sonnet & Opus, and GPT-5.4 – with transparent pricing that matches provider rates exactly. 3. Start coding with AI that adapts to your workflow. Watch our quick-start guide to see Kilo in action: [![Watch the video](https://img.youtube.com/vi/pqGfYXgrhig/maxresdefault.jpg)](https://youtu.be/pqGfYXgrhig) From 9cafa6f8b9aadb17be63977617b94b811b358210 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Tue, 7 Apr 2026 10:48:30 +0300 Subject: [PATCH 02/29] test(cli): add regression tests for config resilience Add 4 tests that reproduce the crash when agent or command markdown files have valid YAML but invalid Zod schema values (e.g. mode: banana). These currently pass against the fix that will follow. --- .../test/kilocode/config-resilience.test.ts | 155 ++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 packages/opencode/test/kilocode/config-resilience.test.ts diff --git a/packages/opencode/test/kilocode/config-resilience.test.ts b/packages/opencode/test/kilocode/config-resilience.test.ts new file mode 100644 index 00000000000..a5d013c746c --- /dev/null +++ b/packages/opencode/test/kilocode/config-resilience.test.ts @@ -0,0 +1,155 @@ +import { afterEach, describe, expect, test } from "bun:test" +import path from "path" +import { Bus } from "../../src/bus" +import { Config } from "../../src/config/config" +import { Instance } from "../../src/project/instance" +import { Filesystem } from "../../src/util/filesystem" +import { tmpdir } from "../fixture/fixture" + +afterEach(async () => { + await Instance.disposeAll() + Config.global.reset() +}) + +describe("config resilience", () => { + test("skips invalid agent markdown configs", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Filesystem.write( + path.join(dir, ".kilo", "agent", "skip.md"), + `--- +mode: "banana" +--- +Broken agent prompt`, + ) + await Filesystem.write( + path.join(dir, ".kilo", "agent", "keep.md"), + `--- +model: test/model +--- +Valid agent prompt`, + ) + }, + }) + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const cfg = await Config.get() + + expect(cfg.agent?.["skip"]).toBeUndefined() + expect(cfg.agent?.["keep"]).toMatchObject({ + name: "keep", + model: "test/model", + prompt: "Valid agent prompt", + }) + }, + }) + }) + + test("publishes an error for invalid agent markdown configs", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Filesystem.write( + path.join(dir, ".kilo", "agent", "skip.md"), + `--- +mode: "banana" +--- +Broken agent prompt`, + ) + }, + }) + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const seen: Array<{ type: string; properties: { error: { name: string; data: { message: string } } } }> = [] + const unsub = Bus.subscribeAll((event) => { + if (event.type === "session.error") seen.push(event) + }) + + await Config.get() + unsub() + + expect( + seen.some( + (item) => + item.properties.error.name === "UnknownError" && + item.properties.error.data.message.includes("skip.md") && + item.properties.error.data.message.includes("mode"), + ), + ).toBe(true) + }, + }) + }) + + test("skips invalid command markdown configs", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Filesystem.write( + path.join(dir, ".kilo", "command", "skip.md"), + `--- +subtask: "banana" +--- +Broken command template`, + ) + await Filesystem.write( + path.join(dir, ".kilo", "command", "keep.md"), + `--- +description: Valid command +--- +Valid command template`, + ) + }, + }) + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const cfg = await Config.get() + + expect(cfg.command?.["skip"]).toBeUndefined() + expect(cfg.command?.["keep"]).toEqual({ + description: "Valid command", + template: "Valid command template", + }) + }, + }) + }) + + test("publishes an error for invalid command markdown configs", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Filesystem.write( + path.join(dir, ".kilo", "command", "skip.md"), + `--- +subtask: "banana" +--- +Broken command template`, + ) + }, + }) + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const seen: Array<{ type: string; properties: { error: { name: string; data: { message: string } } } }> = [] + const unsub = Bus.subscribeAll((event) => { + if (event.type === "session.error") seen.push(event) + }) + + await Config.get() + unsub() + + expect( + seen.some( + (item) => + item.properties.error.name === "UnknownError" && + item.properties.error.data.message.includes("skip.md") && + item.properties.error.data.message.includes("subtask"), + ), + ).toBe(true) + }, + }) + }) +}) From e3a5b69054a490a6ac32363859484f3e8bb32c96 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Tue, 7 Apr 2026 10:48:37 +0300 Subject: [PATCH 03/29] fix(cli): skip invalid agent/command configs instead of crashing Replace throw with log-and-skip in loadAgent() and loadCommand() when Zod validation fails on markdown config files. Publish a session.error bus event so the UI can surface the problem. Add a try/catch safety net around the per-directory config loading loop in stateInit(). --- packages/opencode/src/config/config.ts | 51 +++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 5b3e3b28de4..60d80eb3f8a 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -30,6 +30,7 @@ import { Installation } from "@/installation" import { ConfigMarkdown } from "./markdown" import { constants, existsSync } from "fs" import { Bus } from "@/bus" +import { BusEvent } from "@/bus/bus-event" // kilocode_change import { GlobalBus } from "@/bus/global" import { Event } from "../server/event" import { Glob } from "../util/glob" @@ -282,10 +283,16 @@ export namespace Config { }), ) - result.command = mergeDeep(result.command ?? {}, await loadCommand(dir)) - result.agent = mergeDeep(result.agent, await loadAgent(dir)) - result.agent = mergeDeep(result.agent, await loadMode(dir)) - result.plugin.push(...(await loadPlugin(dir))) + // kilocode_change start + try { + result.command = mergeDeep(result.command ?? {}, await loadCommand(dir)) + result.agent = mergeDeep(result.agent, await loadAgent(dir)) + result.agent = mergeDeep(result.agent, await loadMode(dir)) + result.plugin.push(...(await loadPlugin(dir))) + } catch (err: unknown) { + log.error("failed to load config directory", { dir, err }) + } + // kilocode_change end } // Inline config content overrides all non-managed config sources. @@ -463,6 +470,34 @@ export namespace Config { return ext.length ? file.slice(0, -ext.length) : file } + // kilocode_change start + // Local event definition that matches Session.Event.Error's type string. + // Avoids importing @/session during config init (which causes a circular dependency). + const SessionError = BusEvent.define("session.error", z.object({ error: z.any() })) + + function detail(issues: z.core.$ZodIssue[]) { + return issues + .map((issue) => { + const loc = issue.path.map(String).join(".") + if (!loc) return issue.message + return `${loc}: ${issue.message}` + }) + .join("\n") + } + + async function invalid(kind: "agent" | "command", item: string, issues: z.core.$ZodIssue[], cause: Error) { + const text = detail(issues) + const message = text ? `Config file at ${item} is invalid: ${text}` : `Config file at ${item} is invalid` + Bus.publish(SessionError, { error: new NamedError.Unknown({ message }).toObject() }) + const err = new InvalidError({ path: item, issues }, { cause }) + if (kind === "command") { + log.error("failed to load command", { command: item, err }) + return + } + log.error("failed to load agent", { agent: item, err }) + } + // kilocode_change end + async function loadCommand(dir: string) { const result: Record = {} for (const item of await Glob.scan("{command,commands}/**/*.md", { @@ -505,7 +540,9 @@ export namespace Config { result[config.name] = parsed.data continue } - throw new InvalidError({ path: item, issues: parsed.error.issues }, { cause: parsed.error }) + // kilocode_change start + await invalid("command", item, parsed.error.issues, parsed.error) + // kilocode_change end } return result } @@ -555,7 +592,9 @@ export namespace Config { result[config.name] = parsed.data continue } - throw new InvalidError({ path: item, issues: parsed.error.issues }, { cause: parsed.error }) + // kilocode_change start + await invalid("agent", item, parsed.error.issues, parsed.error) + // kilocode_change end } return result } From 6c98dd062c3a6fd85161ba3d09523beffa6c0362 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Tue, 7 Apr 2026 11:25:00 +0300 Subject: [PATCH 04/29] fix(cli): remove unscoped session.error publish --- packages/opencode/src/config/config.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 60d80eb3f8a..a091703982e 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -30,7 +30,6 @@ import { Installation } from "@/installation" import { ConfigMarkdown } from "./markdown" import { constants, existsSync } from "fs" import { Bus } from "@/bus" -import { BusEvent } from "@/bus/bus-event" // kilocode_change import { GlobalBus } from "@/bus/global" import { Event } from "../server/event" import { Glob } from "../util/glob" @@ -471,10 +470,6 @@ export namespace Config { } // kilocode_change start - // Local event definition that matches Session.Event.Error's type string. - // Avoids importing @/session during config init (which causes a circular dependency). - const SessionError = BusEvent.define("session.error", z.object({ error: z.any() })) - function detail(issues: z.core.$ZodIssue[]) { return issues .map((issue) => { @@ -488,13 +483,12 @@ export namespace Config { async function invalid(kind: "agent" | "command", item: string, issues: z.core.$ZodIssue[], cause: Error) { const text = detail(issues) const message = text ? `Config file at ${item} is invalid: ${text}` : `Config file at ${item} is invalid` - Bus.publish(SessionError, { error: new NamedError.Unknown({ message }).toObject() }) const err = new InvalidError({ path: item, issues }, { cause }) if (kind === "command") { - log.error("failed to load command", { command: item, err }) + log.error("failed to load command", { command: item, err, message }) return } - log.error("failed to load agent", { agent: item, err }) + log.error("failed to load agent", { agent: item, err, message }) } // kilocode_change end From a8a2e3b8725c1c7dc258aa941e9e07de839f6d79 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Tue, 7 Apr 2026 11:38:13 +0300 Subject: [PATCH 05/29] fix(cli): surface schema validation errors to user --- packages/opencode/src/config/config.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index a091703982e..7fabc3481e6 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -484,6 +484,8 @@ export namespace Config { const text = detail(issues) const message = text ? `Config file at ${item} is invalid: ${text}` : `Config file at ${item} is invalid` const err = new InvalidError({ path: item, issues }, { cause }) + const { Session } = await import("@/session") + Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) if (kind === "command") { log.error("failed to load command", { command: item, err, message }) return From f2dea0a67759ff073f3a881082a3404f9bb42f52 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Tue, 7 Apr 2026 10:49:46 +0300 Subject: [PATCH 06/29] docs(cli): add legacy paths to kilo-config skill The built-in kilo-config skill only documented modern config paths (.kilo/, ~/.config/kilo/), but the CLI still loads legacy directories (.kilocode/, .opencode/) at every session. Users with configs in legacy locations got no guidance from the agent. Expand the Config File Locations section to document all scanned directories, glob patterns with plural variants, legacy config filenames, and environment variable overrides. --- .../src/kilocode/skills/kilo-config.md | 61 +++++++++++++++---- 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/packages/opencode/src/kilocode/skills/kilo-config.md b/packages/opencode/src/kilocode/skills/kilo-config.md index 70289d08dcd..e34a77f7518 100644 --- a/packages/opencode/src/kilocode/skills/kilo-config.md +++ b/packages/opencode/src/kilocode/skills/kilo-config.md @@ -4,7 +4,7 @@ All config lives in `kilo.json` (or `kilo.jsonc`). Precedence low-to-high: remot ## Commands (`.kilo/command/*.md`) -Markdown files with YAML frontmatter. The filename (minus `.md`) becomes the command name invoked via `/name`. +Markdown files with YAML frontmatter. The filename (minus `.md`) becomes the command name invoked via `/name`. Also loaded from `.kilocode/` and `.opencode/` directories (legacy), and plural `commands/` variants. See Config File Locations for the full search order. ```yaml --- @@ -22,6 +22,8 @@ Template variables: `$1`-`$N` (positional args), `$ARGUMENTS` (full string), `@f ## Agents (`.kilo/agent/*.md`) +Also loaded from `.kilocode/` and `.opencode/` directories (legacy), and plural `agents/` variants. + ```yaml --- description: When to use this agent @@ -127,7 +129,7 @@ Additional skill directories and remote URLs: } ``` -Skills are markdown files at `skills//SKILL.md` with `name` and `description` in frontmatter. +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. ## Other Top-Level Fields @@ -200,12 +202,49 @@ Toggle notifications, Toggle animations, Toggle diff wrapping, Toggle sidebar (` ## Config File Locations -| Scope | Path | -| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Project | `./kilo.json`, `./.kilo/kilo.json` | -| Global | `~/.config/kilo/kilo.json` | -| Managed | Linux: `/etc/kilo/kilo.json`, macOS: `/Library/Application Support/kilo/kilo.json`, Windows: `%ProgramData%\kilo\kilo.json` (enterprise, highest priority) | -| Commands | `.kilo/command/*.md` (project), `~/.config/kilo/command/*.md` (global) | -| Agents | `.kilo/agent/*.md` (project), `~/.config/kilo/agent/*.md` (global) | -| Skills | `.kilo/skill/*/SKILL.md`, `.kilo/skills/*/SKILL.md` | -| Instructions | `AGENTS.md`, `.kilo/instructions.md`, glob patterns from `instructions` | +### Config files (kilo.json) + +| Scope | Path | +| ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Project | `./kilo.json`, `./kilo.jsonc`, `./opencode.json` (legacy), `./opencode.jsonc` (legacy) | +| Global | `~/.config/kilo/kilo.json`, `~/.config/kilo/kilo.jsonc`, `~/.config/kilo/opencode.json` (legacy), `~/.config/kilo/config.json` (legacy) | +| Managed | Linux: `/etc/kilo/kilo.json`, macOS: `/Library/Application Support/kilo/kilo.json`, Windows: `%ProgramData%\kilo\kilo.json` (enterprise, highest priority) | + +Each config directory (`.kilo/`, `.kilocode/`, `.opencode/`) 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: + +- **Project**: walks up from CWD to the git worktree root, checking for all three at each directory level +- **Home**: `~/.kilo/`, `~/.kilocode/`, `~/.opencode/` +- **XDG global**: `~/.config/kilo/` (always loaded, lowest file-based precedence) + +### Commands, agents, modes, plugins + +Glob patterns run inside every discovered config directory (including legacy): + +| Type | Pattern | +| ------- | ---------------------------- | +| Command | `{command,commands}/**/*.md` | +| Agent | `{agent,agents}/**/*.md` | +| 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. + +### Skills and instructions + +| Scope | Path | +| ------------ | ----------------------------------------------------------------------- | +| Skills | `{skill,skills}//SKILL.md` inside any config directory | +| Instructions | `AGENTS.md`, `.kilo/instructions.md`, glob patterns from `instructions` | + +### Environment variable overrides + +| Variable | Description | +| ----------------------------- | ---------------------------------------------------------------- | +| `KILO_CONFIG` | Path to an additional config file (loaded after global) | +| `KILO_CONFIG_DIR` | Path to an additional config directory (appended to search list) | +| `KILO_CONFIG_CONTENT` | Inline JSON config string (high precedence, after project dirs) | +| `KILO_DISABLE_PROJECT_CONFIG` | Skip project-level `.kilo`/`.kilocode`/`.opencode` directories | From 6a7c296d341291d8a773846df9846cc0ba62d18b Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Tue, 7 Apr 2026 10:59:54 +0300 Subject: [PATCH 07/29] fix(cli): widen kilo-config skill to cover path location questions The skill description and opening paragraph now explicitly mention locating config/command/agent/skill paths across global, project, and legacy directories (.kilo/, .kilocode/, .opencode/, ~/.config/kilo/). This ensures the agent loads the skill for 'where is this command?' prompts, not just configure/customize prompts. --- packages/opencode/src/kilocode/skills/builtin.ts | 2 +- packages/opencode/src/kilocode/skills/kilo-config.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/kilocode/skills/builtin.ts b/packages/opencode/src/kilocode/skills/builtin.ts index 9172a39cf95..96accd8f5ad 100644 --- a/packages/opencode/src/kilocode/skills/builtin.ts +++ b/packages/opencode/src/kilocode/skills/builtin.ts @@ -15,7 +15,7 @@ export const BUILTIN_SKILLS: BuiltinSkill[] = [ { name: "kilo-config", description: - "Guide for configuring Kilo CLI: commands, agents, MCP servers, skills, permissions, instructions, plugins, providers, all kilo.json fields, and TUI settings (themes, appearance, keybinds, ctrl+p commands). Use when the user asks about configuring, customizing, or changing settings in Kilo.", + "Guide for configuring Kilo CLI and locating config, command, agent, and skill paths (global, project, legacy), plus MCP servers, permissions, instructions, plugins, providers, kilo.json fields, and TUI settings (themes, appearance, keybinds, ctrl+p commands). Use when the user asks about configuring Kilo, where it loads things from, or how to change settings.", content: KILO_CONFIG, }, ] diff --git a/packages/opencode/src/kilocode/skills/kilo-config.md b/packages/opencode/src/kilocode/skills/kilo-config.md index e34a77f7518..cc2540678f8 100644 --- a/packages/opencode/src/kilocode/skills/kilo-config.md +++ b/packages/opencode/src/kilocode/skills/kilo-config.md @@ -2,6 +2,8 @@ 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/`. + ## Commands (`.kilo/command/*.md`) Markdown files with YAML frontmatter. The filename (minus `.md`) becomes the command name invoked via `/name`. Also loaded from `.kilocode/` and `.opencode/` directories (legacy), and plural `commands/` variants. See Config File Locations for the full search order. From cafccaf68760f59eb6a4a754fd1b7533b6ba311a Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Tue, 7 Apr 2026 14:10:10 +0300 Subject: [PATCH 08/29] fix(cli): add named-command lookup guidance to kilo-config skill The skill loaded but the agent still only searched the repo root when asked 'where is /review-pr?'. Add an explicit procedural subsection that tells the agent to search ~/.config/kilo/, ~/.kilo/, ~/.kilocode/, and ~/.opencode/ with explicit path arguments before falling back to project directories. Add a regression test verifying the guidance is present in the builtin skill output. --- .../src/kilocode/skills/kilo-config.md | 19 ++++++++++- packages/opencode/test/tool/skill.test.ts | 32 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/kilocode/skills/kilo-config.md b/packages/opencode/src/kilocode/skills/kilo-config.md index cc2540678f8..e8e763a2e16 100644 --- a/packages/opencode/src/kilocode/skills/kilo-config.md +++ b/packages/opencode/src/kilocode/skills/kilo-config.md @@ -6,7 +6,7 @@ This also covers where Kilo looks for config files, commands, agents, and skills ## Commands (`.kilo/command/*.md`) -Markdown files with YAML frontmatter. The filename (minus `.md`) becomes the command name invoked via `/name`. Also loaded from `.kilocode/` and `.opencode/` directories (legacy), and plural `commands/` variants. 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/`, `.kilocode/`, `.opencode/`, and global config roots, with both `command/` and `commands/` directory names supported. See Config File Locations for the full search order. ```yaml --- @@ -22,6 +22,23 @@ Reference files with @file and shell output with !`cmd`. Template variables: `$1`-`$N` (positional args), `$ARGUMENTS` (full string), `@file` (file contents), `` !`cmd` `` (shell output). +### Finding a named command + +When asked where `/name` lives, do not search only the repo root. Search these roots explicitly, and use an explicit search `path` for each one: + +1. `~/.config/kilo/` +2. `~/.kilo/` +3. `~/.kilocode/` +4. `~/.opencode/` +5. project `.kilo/`, `.kilocode/`, and `.opencode/` directories from the current working directory up to the worktree root + +Use exact patterns first: + +- `**/command/.md` +- `**/commands/.md` + +If found, return the full path. If not found in those roots, explain that the command is not present in the loaded config paths. + ## Agents (`.kilo/agent/*.md`) Also loaded from `.kilocode/` and `.opencode/` directories (legacy), and plural `agents/` variants. diff --git a/packages/opencode/test/tool/skill.test.ts b/packages/opencode/test/tool/skill.test.ts index 70649dd9016..0fa1f13d4be 100644 --- a/packages/opencode/test/tool/skill.test.ts +++ b/packages/opencode/test/tool/skill.test.ts @@ -109,4 +109,36 @@ Use this skill. process.env.KILO_TEST_HOME = home } }) + + test("built-in kilo-config includes named command lookup guidance", async () => { + await using tmp = await tmpdir({ git: true }) + + const home = process.env.KILO_TEST_HOME + process.env.KILO_TEST_HOME = tmp.path + + try { + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const tool = await SkillTool.init() + const ctx: Tool.Context = { + ...baseCtx, + ask: async () => {}, + } + + const result = await tool.execute({ name: "kilo-config" }, ctx) + + expect(tool.description).toContain("where it loads things from") + expect(result.metadata.dir).toBe("builtin") + expect(result.output).toContain("### Finding a named command") + expect(result.output).toContain("`~/.config/kilo/`") + expect(result.output).toContain("`~/.kilocode/`") + expect(result.output).toContain("`**/command/.md`") + expect(result.output).toContain("explicit search `path`") + }, + }) + } finally { + process.env.KILO_TEST_HOME = home + } + }) }) From 6a91d2a6aab351a2ff38e87512ae02181a9bc141 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Tue, 7 Apr 2026 14:16:06 +0300 Subject: [PATCH 09/29] docs(cli): add missing opencode.jsonc to global paths --- packages/opencode/src/kilocode/skills/kilo-config.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/opencode/src/kilocode/skills/kilo-config.md b/packages/opencode/src/kilocode/skills/kilo-config.md index e8e763a2e16..972542739a5 100644 --- a/packages/opencode/src/kilocode/skills/kilo-config.md +++ b/packages/opencode/src/kilocode/skills/kilo-config.md @@ -223,11 +223,11 @@ Toggle notifications, Toggle animations, Toggle diff wrapping, Toggle sidebar (` ### Config files (kilo.json) -| Scope | Path | -| ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Project | `./kilo.json`, `./kilo.jsonc`, `./opencode.json` (legacy), `./opencode.jsonc` (legacy) | -| Global | `~/.config/kilo/kilo.json`, `~/.config/kilo/kilo.jsonc`, `~/.config/kilo/opencode.json` (legacy), `~/.config/kilo/config.json` (legacy) | -| Managed | Linux: `/etc/kilo/kilo.json`, macOS: `/Library/Application Support/kilo/kilo.json`, Windows: `%ProgramData%\kilo\kilo.json` (enterprise, highest priority) | +| Scope | Path | +| ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Project | `./kilo.json`, `./kilo.jsonc`, `./opencode.json` (legacy), `./opencode.jsonc` (legacy) | +| 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/kilo.json`, macOS: `/Library/Application Support/kilo/kilo.json`, Windows: `%ProgramData%\kilo\kilo.json` (enterprise, highest priority) | Each config directory (`.kilo/`, `.kilocode/`, `.opencode/`) can also contain `kilo.json`, `kilo.jsonc`, `opencode.json`, or `opencode.jsonc`. From 2302f7f67acc46b48207059e76cdf2699a10e2e7 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Tue, 7 Apr 2026 14:16:40 +0300 Subject: [PATCH 10/29] docs(cli): fix instructions auto-discovery list --- packages/opencode/src/kilocode/skills/kilo-config.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/opencode/src/kilocode/skills/kilo-config.md b/packages/opencode/src/kilocode/skills/kilo-config.md index 972542739a5..7c2d52b9c0d 100644 --- a/packages/opencode/src/kilocode/skills/kilo-config.md +++ b/packages/opencode/src/kilocode/skills/kilo-config.md @@ -254,10 +254,10 @@ Example: `~/.config/kilo/command/*.md` (modern global), `~/.kilocode/command/*.m ### Skills and instructions -| Scope | Path | -| ------------ | ----------------------------------------------------------------------- | -| Skills | `{skill,skills}//SKILL.md` inside any config directory | -| Instructions | `AGENTS.md`, `.kilo/instructions.md`, glob patterns from `instructions` | +| Scope | Path | +| ------------ | ------------------------------------------------------------------------ | +| Skills | `{skill,skills}//SKILL.md` inside any config directory | +| Instructions | `AGENTS.md`, `CLAUDE.md`, glob patterns from `instructions` config field | ### Environment variable overrides From b86265102389ebe6a704744f535d3a0988cec7a1 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Tue, 7 Apr 2026 14:17:15 +0300 Subject: [PATCH 11/29] docs(cli): list all managed config files --- packages/opencode/src/kilocode/skills/kilo-config.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/opencode/src/kilocode/skills/kilo-config.md b/packages/opencode/src/kilocode/skills/kilo-config.md index 7c2d52b9c0d..edb460c2eb5 100644 --- a/packages/opencode/src/kilocode/skills/kilo-config.md +++ b/packages/opencode/src/kilocode/skills/kilo-config.md @@ -223,11 +223,11 @@ Toggle notifications, Toggle animations, Toggle diff wrapping, Toggle sidebar (` ### Config files (kilo.json) -| Scope | Path | -| ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Project | `./kilo.json`, `./kilo.jsonc`, `./opencode.json` (legacy), `./opencode.jsonc` (legacy) | -| 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/kilo.json`, macOS: `/Library/Application Support/kilo/kilo.json`, Windows: `%ProgramData%\kilo\kilo.json` (enterprise, highest priority) | +| Scope | Path | +| ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Project | `./kilo.json`, `./kilo.jsonc`, `./opencode.json` (legacy), `./opencode.jsonc` (legacy) | +| 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`. From eb1494f122c963fb27671d01e75c46bc65604a95 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Tue, 7 Apr 2026 14:17:52 +0300 Subject: [PATCH 12/29] docs(cli): widen KILO_DISABLE_PROJECT_CONFIG desc --- packages/opencode/src/kilocode/skills/kilo-config.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opencode/src/kilocode/skills/kilo-config.md b/packages/opencode/src/kilocode/skills/kilo-config.md index edb460c2eb5..df435e5815c 100644 --- a/packages/opencode/src/kilocode/skills/kilo-config.md +++ b/packages/opencode/src/kilocode/skills/kilo-config.md @@ -266,4 +266,4 @@ Example: `~/.config/kilo/command/*.md` (modern global), `~/.kilocode/command/*.m | `KILO_CONFIG` | Path to an additional config file (loaded after global) | | `KILO_CONFIG_DIR` | Path to an additional config directory (appended to search list) | | `KILO_CONFIG_CONTENT` | Inline JSON config string (high precedence, after project dirs) | -| `KILO_DISABLE_PROJECT_CONFIG` | Skip project-level `.kilo`/`.kilocode`/`.opencode` directories | +| `KILO_DISABLE_PROJECT_CONFIG` | Skip all project-level config (files and directories) | From 5a9520d3f0a0e5e757d54a5d53ba2ea3056b0cea Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Tue, 7 Apr 2026 14:26:46 +0300 Subject: [PATCH 13/29] docs(cli): add KILO_CONFIG_DIR to command lookup --- packages/opencode/src/kilocode/skills/kilo-config.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/kilocode/skills/kilo-config.md b/packages/opencode/src/kilocode/skills/kilo-config.md index df435e5815c..682e5ba747e 100644 --- a/packages/opencode/src/kilocode/skills/kilo-config.md +++ b/packages/opencode/src/kilocode/skills/kilo-config.md @@ -30,7 +30,8 @@ When asked where `/name` lives, do not search only the repo root. Search these r 2. `~/.kilo/` 3. `~/.kilocode/` 4. `~/.opencode/` -5. project `.kilo/`, `.kilocode/`, and `.opencode/` directories from the current working directory up to the worktree root +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 Use exact patterns first: From 27057648d326e43972d1e76a58537813d071480b Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Tue, 7 Apr 2026 14:32:15 +0300 Subject: [PATCH 14/29] docs(cli): add CONTEXT.md to instructions list --- packages/opencode/src/kilocode/skills/kilo-config.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/opencode/src/kilocode/skills/kilo-config.md b/packages/opencode/src/kilocode/skills/kilo-config.md index 682e5ba747e..a7ca25d7b53 100644 --- a/packages/opencode/src/kilocode/skills/kilo-config.md +++ b/packages/opencode/src/kilocode/skills/kilo-config.md @@ -255,10 +255,10 @@ Example: `~/.config/kilo/command/*.md` (modern global), `~/.kilocode/command/*.m ### Skills and instructions -| Scope | Path | -| ------------ | ------------------------------------------------------------------------ | -| Skills | `{skill,skills}//SKILL.md` inside any config directory | -| Instructions | `AGENTS.md`, `CLAUDE.md`, glob patterns from `instructions` config field | +| Scope | Path | +| ------------ | -------------------------------------------------------------------------------------- | +| Skills | `{skill,skills}//SKILL.md` inside any config directory | +| Instructions | `AGENTS.md`, `CLAUDE.md`, `CONTEXT.md`, glob patterns from `instructions` config field | ### Environment variable overrides From f0118047e539112c6c6c06228a10a3bd38cbd232 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Tue, 7 Apr 2026 16:26:46 +0300 Subject: [PATCH 15/29] feat(cli): add config warnings endpoint and surface errors to TUI/extension Collect warnings during config loading instead of crashing when config files have invalid JSON, schema errors, or frontmatter issues. Warnings are exposed via a new GET /config/warnings pull-based endpoint. - Add Config.Warning schema and warnings collection in stateInit() - Wrap all loadFile/load calls with try/catch to collect warnings - Thread warnings through loadCommand/loadAgent/loadMode/invalid - Wrap well-known remote config block to prevent network/parse crashes - Add GET /config/warnings server route with OpenAPI spec - Regenerate SDK with config.warnings() method - TUI: fetch warnings in bootstrap non-blocking phase, show as toast - VS Code: fetch warnings on SSE connect, show as native notifications - Add 5 new tests for warning collection (JSON, schema, markdown) --- packages/kilo-vscode/src/KiloProvider.ts | 24 + .../opencode/src/cli/cmd/tui/context/sync.tsx | 19 + packages/opencode/src/config/config.ts | 173 +- packages/opencode/src/server/routes/config.ts | 23 + .../test/kilocode/config-resilience.test.ts | 104 + packages/sdk/js/src/v2/gen/sdk.gen.ts | 31 + packages/sdk/js/src/v2/gen/types.gen.ts | 22 + packages/sdk/openapi.json | 2157 ++++------------- 8 files changed, 792 insertions(+), 1761 deletions(-) diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index de9c78c8176..05194727b75 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -124,6 +124,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper private cachedMcpStatusMessage: unknown = null /** Ref-count of in-flight handleUpdateConfig calls; prevents fetchAndSendConfig from sending stale data */ private pending = 0 + private configWarningsShown = false // kilocode_change - prevent repeated config warnings on SSE reconnect /** Cached notificationsLoaded payload */ private cachedNotificationsMessage: unknown = null private pendingReviewComments: { comments: unknown[]; autoSend: boolean }[] = [] @@ -1058,6 +1059,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper await this.flushPendingSessionRefresh("sse-connected") await fetchAndSendPendingPermissions(this.permissionCtx) await fetchAndSendPendingQuestions(this.questionCtx) + await this.checkConfigWarnings() // kilocode_change } catch (error) { console.error("[Kilo New] KiloProvider: ❌ Failed during connected state handling:", error) this.postMessage({ @@ -1947,6 +1949,28 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper } } + // kilocode_change start + /** + * Fetch config warnings from the server and display them as VS Code warning messages. + * Only shown once per provider lifecycle (flag resets on dispose/re-create, not on SSE reconnect). + */ + private async checkConfigWarnings(): Promise { + if (this.configWarningsShown || !this.client) return + try { + const dir = this.getWorkspaceDirectory() + const result = await this.client.config.warnings({ directory: dir }) + const list = result?.data ?? [] + if (list.length === 0) return + this.configWarningsShown = true + for (const warning of list) { + vscode.window.showWarningMessage(`Config: ${warning.message}`) + } + } catch { + // Endpoint may not exist on older CLI versions — silently ignore + } + } + // kilocode_change end + /** * Fetch Kilo news/notifications and send to webview. * Uses the cached message pattern so the webview gets data immediately on refresh. diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx index e7049fcbe4e..36119ccd7a2 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx @@ -29,6 +29,7 @@ import { batch, onMount } from "solid-js" import { Log } from "@/util/log" import type { Path } from "@kilocode/sdk" import type { Workspace } from "@kilocode/sdk/v2" +import { useToast } from "../ui/toast" // kilocode_change export const { use: useSync, provider: SyncProvider } = createSimpleContext({ name: "Sync", @@ -106,6 +107,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ }) const sdk = useSDK() + const toast = useToast() // kilocode_change const fullSyncedSessions = new Set() // kilocode_change @@ -464,6 +466,23 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ sdk.client.vcs.get().then((x) => setStore("vcs", reconcile(x.data))), sdk.client.path.get().then((x) => setStore("path", reconcile(x.data!))), syncWorkspaces(), + // kilocode_change start - show config warnings as toasts + sdk.client.config + .warnings() + .then((x) => { + const list = x.data ?? [] + if (list.length === 0) return + const first = list[0] + const suffix = list.length > 1 ? ` (and ${list.length - 1} more)` : "" + toast.show({ + title: "Config Warning", + message: first.message + suffix, + variant: "warning", + duration: 8000, + }) + }) + .catch(() => {}), + // kilocode_change end ]).then(() => { setStore("status", "complete") }) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 7fabc3481e6..152fb3fe21b 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -52,6 +52,14 @@ export namespace Config { const log = Log.create({ service: "config" }) + // kilocode_change start + export const Warning = z.object({ + path: z.string(), + message: z.string(), + }) + export type Warning = z.infer + // kilocode_change end + // Managed settings directory for enterprise deployments (highest priority, admin-controlled) // These settings override all user and project settings function systemManagedConfigDir(): string { @@ -85,6 +93,18 @@ export namespace Config { // kilocode_change start — capture init so resetState() can invalidate the cache entry const stateInit = async () => { + // kilocode_change end + // kilocode_change start + const warnings: Warning[] = [] + const caught = (err: unknown, source: string) => { + const w = toWarning(err) + if (w) { + warnings.push(w) + log.warn("skipped config due to error", { source, err }) + return + } + throw err + } // kilocode_change end const auth = await Auth.all() @@ -200,24 +220,32 @@ export namespace Config { for (const [key, value] of Object.entries(auth)) { if (value.type === "wellknown") { const url = key.replace(/\/+$/, "") - process.env[value.key] = value.token - log.debug("fetching remote config", { url: `${url}/.well-known/opencode` }) - const response = await fetch(`${url}/.well-known/opencode`) - if (!response.ok) { - throw new Error(`failed to fetch remote config from ${url}: ${response.status}`) + const source = `${url}/.well-known/opencode` + try { + process.env[value.key] = value.token + log.debug("fetching remote config", { url: source }) + const response = await fetch(source) + if (!response.ok) { + throw new Error(`failed to fetch remote config from ${url}: ${response.status}`) + } + const wellknown = (await response.json()) as any + const remoteConfig = wellknown.config ?? {} + // Add $schema to prevent load() from trying to write back to a non-existent file + if (!remoteConfig.$schema) remoteConfig.$schema = "https://app.kilo.ai/config.json" // kilocode_change + result = mergeConfigConcatArrays( + result, + await load(JSON.stringify(remoteConfig), { + dir: path.dirname(source), + source, + }), + ) + log.debug("loaded remote config from well-known", { url }) + } catch (err) { + const w = toWarning(err) + if (w) warnings.push(w) + else warnings.push({ path: source, message: err instanceof Error ? err.message : String(err) }) + log.warn("skipped remote config due to error", { url, err }) } - const wellknown = (await response.json()) as any - const remoteConfig = wellknown.config ?? {} - // Add $schema to prevent load() from trying to write back to a non-existent file - if (!remoteConfig.$schema) remoteConfig.$schema = "https://app.kilo.ai/config.json" // kilocode_change - result = mergeConfigConcatArrays( - result, - await load(JSON.stringify(remoteConfig), { - dir: path.dirname(`${url}/.well-known/opencode`), - source: `${url}/.well-known/opencode`, - }), - ) - log.debug("loaded remote config from well-known", { url }) } } @@ -226,12 +254,20 @@ export namespace Config { } // Global user config overrides remote config. - result = mergeConfigConcatArrays(result, await global()) + try { + result = mergeConfigConcatArrays(result, await global()) + } catch (err) { + caught(err, "global config") + } // Custom config path overrides global config. if (Flag.KILO_CONFIG) { - result = mergeConfigConcatArrays(result, await loadFile(Flag.KILO_CONFIG)) - log.debug("loaded custom config", { path: Flag.KILO_CONFIG }) + try { + result = mergeConfigConcatArrays(result, await loadFile(Flag.KILO_CONFIG)) + log.debug("loaded custom config", { path: Flag.KILO_CONFIG }) + } catch (err) { + caught(err, Flag.KILO_CONFIG) + } } // Project config overrides global and remote config. @@ -239,7 +275,11 @@ export namespace Config { // kilocode_change start for (const file of ["kilo.jsonc", "kilo.json", "opencode.jsonc", "opencode.json"]) { // kilocode_change end - result = mergeConfigConcatArrays(result, await loadFile(file)) + try { + result = mergeConfigConcatArrays(result, await loadFile(file)) + } catch (err) { + caught(err, file) + } } } @@ -267,7 +307,11 @@ export namespace Config { for (const file of ["kilo.jsonc", "kilo.json", "opencode.jsonc", "opencode.json"]) { // kilocode_change end log.debug(`loading config from ${path.join(dir, file)}`) - result = mergeConfigConcatArrays(result, await loadFile(path.join(dir, file))) + try { + result = mergeConfigConcatArrays(result, await loadFile(path.join(dir, file))) + } catch (err) { + caught(err, path.join(dir, file)) + } // to satisfy the type checker result.agent ??= {} result.mode ??= {} @@ -284,9 +328,9 @@ export namespace Config { // kilocode_change start try { - result.command = mergeDeep(result.command ?? {}, await loadCommand(dir)) - result.agent = mergeDeep(result.agent, await loadAgent(dir)) - result.agent = mergeDeep(result.agent, await loadMode(dir)) + result.command = mergeDeep(result.command ?? {}, await loadCommand(dir, warnings)) + result.agent = mergeDeep(result.agent, await loadAgent(dir, warnings)) + result.agent = mergeDeep(result.agent, await loadMode(dir, warnings)) result.plugin.push(...(await loadPlugin(dir))) } catch (err: unknown) { log.error("failed to load config directory", { dir, err }) @@ -296,14 +340,18 @@ export namespace Config { // Inline config content overrides all non-managed config sources. if (process.env.KILO_CONFIG_CONTENT) { - result = mergeConfigConcatArrays( - result, - await load(process.env.KILO_CONFIG_CONTENT, { - dir: Instance.directory, - source: "KILO_CONFIG_CONTENT", - }), - ) - log.debug("loaded custom config from KILO_CONFIG_CONTENT") + try { + result = mergeConfigConcatArrays( + result, + await load(process.env.KILO_CONFIG_CONTENT, { + dir: Instance.directory, + source: "KILO_CONFIG_CONTENT", + }), + ) + log.debug("loaded custom config from KILO_CONFIG_CONTENT") + } catch (err) { + caught(err, "KILO_CONFIG_CONTENT") + } } // Load managed config files last (highest priority) - enterprise admin-controlled @@ -314,7 +362,11 @@ export namespace Config { // kilocode_change start for (const file of ["kilo.jsonc", "kilo.json", "opencode.jsonc", "opencode.json"]) { // kilocode_change end - result = mergeConfigConcatArrays(result, await loadFile(path.join(managedDir, file))) + try { + result = mergeConfigConcatArrays(result, await loadFile(path.join(managedDir, file))) + } catch (err) { + caught(err, path.join(managedDir, file)) + } } } @@ -367,6 +419,7 @@ export namespace Config { config: result, directories, deps, + warnings, } } // kilocode_change start — create state from named init so resetState() can invalidate it @@ -470,6 +523,25 @@ export namespace Config { } // kilocode_change start + function toWarning(err: unknown): Warning | undefined { + if (ConfigPaths.JsonError.isInstance(err)) + return { + path: err.data.path, + message: + `Config file at ${err.data.path} is not valid JSON(C)` + (err.data.message ? `: ${err.data.message}` : ""), + } + if (ConfigPaths.InvalidError.isInstance(err)) { + const text = err.data.issues ? detail(err.data.issues) : err.data.message + return { + path: err.data.path, + message: text + ? `Configuration is invalid at ${err.data.path}: ${text}` + : `Configuration is invalid at ${err.data.path}`, + } + } + return undefined + } + function detail(issues: z.core.$ZodIssue[]) { return issues .map((issue) => { @@ -480,10 +552,17 @@ export namespace Config { .join("\n") } - async function invalid(kind: "agent" | "command", item: string, issues: z.core.$ZodIssue[], cause: Error) { + async function invalid( + kind: "agent" | "command", + item: string, + issues: z.core.$ZodIssue[], + cause: Error, + warnings?: Warning[], + ) { const text = detail(issues) const message = text ? `Config file at ${item} is invalid: ${text}` : `Config file at ${item} is invalid` const err = new InvalidError({ path: item, issues }, { cause }) + if (warnings) warnings.push({ path: item, message }) const { Session } = await import("@/session") Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) if (kind === "command") { @@ -494,7 +573,7 @@ export namespace Config { } // kilocode_change end - async function loadCommand(dir: string) { + async function loadCommand(dir: string, warnings?: Warning[]) { const result: Record = {} for (const item of await Glob.scan("{command,commands}/**/*.md", { cwd: dir, @@ -506,6 +585,7 @@ export namespace Config { const message = ConfigMarkdown.FrontmatterError.isInstance(err) ? err.data.message : `Failed to parse command ${item}` + if (warnings) warnings.push({ path: item, message }) const { Session } = await import("@/session") Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) log.error("failed to load command", { command: item, err }) @@ -537,13 +617,13 @@ export namespace Config { continue } // kilocode_change start - await invalid("command", item, parsed.error.issues, parsed.error) + await invalid("command", item, parsed.error.issues, parsed.error, warnings) // kilocode_change end } return result } - async function loadAgent(dir: string) { + async function loadAgent(dir: string, warnings?: Warning[]) { const result: Record = {} for (const item of await Glob.scan("{agent,agents}/**/*.md", { @@ -556,6 +636,7 @@ export namespace Config { const message = ConfigMarkdown.FrontmatterError.isInstance(err) ? err.data.message : `Failed to parse agent ${item}` + if (warnings) warnings.push({ path: item, message }) const { Session } = await import("@/session") Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) log.error("failed to load agent", { agent: item, err }) @@ -589,13 +670,13 @@ export namespace Config { continue } // kilocode_change start - await invalid("agent", item, parsed.error.issues, parsed.error) + await invalid("agent", item, parsed.error.issues, parsed.error, warnings) // kilocode_change end } return result } - async function loadMode(dir: string) { + async function loadMode(dir: string, warnings?: Warning[]) { const result: Record = {} for (const item of await Glob.scan("{mode,modes}/*.md", { cwd: dir, @@ -607,6 +688,7 @@ export namespace Config { const message = ConfigMarkdown.FrontmatterError.isInstance(err) ? err.data.message : `Failed to parse mode ${item}` + if (warnings) warnings.push({ path: item, message }) const { Session } = await import("@/session") Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) log.error("failed to load mode", { mode: item, err }) @@ -627,6 +709,9 @@ export namespace Config { } continue } + // kilocode_change start + await invalid("agent", item, parsed.error.issues, parsed.error, warnings) + // kilocode_change end } return result } @@ -1532,6 +1617,12 @@ export namespace Config { return state().then((x) => x.config) } + // kilocode_change start + export async function warnings() { + return state().then((x) => x.warnings) + } + // kilocode_change end + export async function getGlobal() { return global() } @@ -1741,5 +1832,3 @@ export namespace Config { return state().then((x) => x.directories) } } -Filesystem.write -Filesystem.write diff --git a/packages/opencode/src/server/routes/config.ts b/packages/opencode/src/server/routes/config.ts index f9ac7e42976..faa46c40972 100644 --- a/packages/opencode/src/server/routes/config.ts +++ b/packages/opencode/src/server/routes/config.ts @@ -62,6 +62,29 @@ export const ConfigRoutes = lazy(() => return c.json(config) }, ) + // kilocode_change start + .get( + "/warnings", + describeRoute({ + summary: "Get config warnings", + description: "Get warnings generated during config loading (e.g., invalid JSON, schema errors).", + operationId: "config.warnings", + responses: { + 200: { + description: "Config warnings", + content: { + "application/json": { + schema: resolver(Config.Warning.array()), + }, + }, + }, + }, + }), + async (c) => { + return c.json(await Config.warnings()) + }, + ) + // kilocode_change end .get( "/providers", describeRoute({ diff --git a/packages/opencode/test/kilocode/config-resilience.test.ts b/packages/opencode/test/kilocode/config-resilience.test.ts index a5d013c746c..7e48d68dec9 100644 --- a/packages/opencode/test/kilocode/config-resilience.test.ts +++ b/packages/opencode/test/kilocode/config-resilience.test.ts @@ -152,4 +152,108 @@ Broken command template`, }, }) }) + + test("collects warnings for invalid agent markdown configs", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Filesystem.write( + path.join(dir, ".kilo", "agent", "broken.md"), + `--- +mode: "banana" +--- +Broken agent`, + ) + }, + }) + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + await Config.get() + const warns = await Config.warnings() + + expect(warns.some((w) => w.path.includes("broken.md") && w.message.includes("invalid"))).toBe(true) + }, + }) + }) + + test("collects warnings for invalid command markdown configs", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Filesystem.write( + path.join(dir, ".kilo", "command", "broken.md"), + `--- +subtask: "banana" +--- +Broken command`, + ) + }, + }) + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + await Config.get() + const warns = await Config.warnings() + + expect(warns.some((w) => w.path.includes("broken.md") && w.message.includes("invalid"))).toBe(true) + }, + }) + }) + + test("collects warnings for invalid JSON in .kilo directory config", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Filesystem.write(path.join(dir, ".kilo", "kilo.json"), "{ not valid json !!!") + }, + }) + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const cfg = await Config.get() + const warns = await Config.warnings() + + // Config loading should not crash + expect(cfg).toBeDefined() + // Warning should reference the bad file + expect(warns.some((w) => w.path.includes("kilo.json") && w.message.includes("not valid JSON"))).toBe(true) + }, + }) + }) + + test("collects warnings for invalid schema in .kilo directory config", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Filesystem.write(path.join(dir, ".kilo", "kilo.json"), JSON.stringify({ unknownField: true })) + }, + }) + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const cfg = await Config.get() + const warns = await Config.warnings() + + expect(cfg).toBeDefined() + expect(warns.some((w) => w.path.includes("kilo.json") && w.message.includes("invalid"))).toBe(true) + }, + }) + }) + + test("returns empty warnings when config is valid", async () => { + await using tmp = await tmpdir({ + config: { model: "test/model" }, + }) + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + await Config.get() + const warns = await Config.warnings() + + expect(warns).toEqual([]) + }, + }) + }) }) diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index 0217a99a5fd..fa72a0bb006 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -21,6 +21,7 @@ import type { ConfigProvidersResponses, ConfigUpdateErrors, ConfigUpdateResponses, + ConfigWarningsResponses, EnhancePromptEnhanceErrors, EnhancePromptEnhanceResponses, EventSubscribeResponses, @@ -843,6 +844,36 @@ export class Config2 extends HeyApiClient { }) } + /** + * Get config warnings + * + * Get warnings generated during config loading (e.g., invalid JSON, schema errors). + */ + public warnings( + parameters?: { + directory?: string + workspace?: string + }, + options?: Options, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "query", key: "directory" }, + { in: "query", key: "workspace" }, + ], + }, + ], + ) + return (options?.client ?? this.client).get({ + url: "/config/warnings", + ...options, + ...params, + }) + } + /** * List config providers * diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 4b719f8b994..e8f9bd24bfc 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -2462,6 +2462,28 @@ export type ConfigUpdateResponses = { export type ConfigUpdateResponse = ConfigUpdateResponses[keyof ConfigUpdateResponses] +export type ConfigWarningsData = { + body?: never + path?: never + query?: { + directory?: string + workspace?: string + } + url: "/config/warnings" +} + +export type ConfigWarningsResponses = { + /** + * Config warnings + */ + 200: Array<{ + path: string + message: string + }> +} + +export type ConfigWarningsResponse = ConfigWarningsResponses[keyof ConfigWarningsResponses] + export type ConfigProvidersData = { body?: never path?: never diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index 905852293d2..b8192c91aa8 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -27,10 +27,7 @@ "type": "string" } }, - "required": [ - "healthy", - "version" - ] + "required": ["healthy", "version"] } } } @@ -745,10 +742,7 @@ "type": "number" } }, - "required": [ - "rows", - "cols" - ] + "required": ["rows", "cols"] } } } @@ -978,6 +972,59 @@ ] } }, + "/config/warnings": { + "get": { + "operationId": "config.warnings", + "parameters": [ + { + "in": "query", + "name": "directory", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "workspace", + "schema": { + "type": "string" + } + } + ], + "summary": "Get config warnings", + "description": "Get warnings generated during config loading (e.g., invalid JSON, schema errors).", + "responses": { + "200": { + "description": "Config warnings", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": ["path", "message"] + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "js", + "source": "import { createKiloClient } from \"@kilocode/sdk\n\nconst client = createKiloClient()\nawait client.config.warnings({\n ...\n})" + } + ] + } + }, "/config/providers": { "get": { "operationId": "config.providers", @@ -1023,10 +1070,7 @@ } } }, - "required": [ - "providers", - "default" - ] + "required": ["providers", "default"] } } } @@ -1233,11 +1277,7 @@ ] } }, - "required": [ - "type", - "branch", - "extra" - ] + "required": ["type", "branch", "extra"] } } } @@ -2148,9 +2188,7 @@ ], "summary": "Get session", "description": "Retrieve detailed information about a specific Kilo session.", - "tags": [ - "Session" - ], + "tags": ["Session"], "responses": { "200": { "description": "Get session", @@ -2377,9 +2415,7 @@ } ], "summary": "Get session children", - "tags": [ - "Session" - ], + "tags": ["Session"], "description": "Retrieve all child sessions that were forked from the specified parent session.", "responses": { "200": { @@ -2576,11 +2612,7 @@ "pattern": "^msg.*" } }, - "required": [ - "modelID", - "providerID", - "messageID" - ] + "required": ["modelID", "providerID", "messageID"] } } } @@ -3004,10 +3036,7 @@ "type": "boolean" } }, - "required": [ - "providerID", - "modelID" - ] + "required": ["providerID", "modelID"] } } } @@ -3077,10 +3106,7 @@ } } }, - "required": [ - "info", - "parts" - ] + "required": ["info", "parts"] } } } @@ -3161,10 +3187,7 @@ } } }, - "required": [ - "info", - "parts" - ] + "required": ["info", "parts"] } } } @@ -3210,10 +3233,7 @@ "type": "string" } }, - "required": [ - "providerID", - "modelID" - ] + "required": ["providerID", "modelID"] }, "agent": { "type": "string" @@ -3283,9 +3303,7 @@ } } }, - "required": [ - "parts" - ] + "required": ["parts"] } } } @@ -3355,10 +3373,7 @@ } } }, - "required": [ - "info", - "parts" - ] + "required": ["info", "parts"] } } } @@ -3725,10 +3740,7 @@ "type": "string" } }, - "required": [ - "providerID", - "modelID" - ] + "required": ["providerID", "modelID"] }, "agent": { "type": "string" @@ -3798,9 +3810,7 @@ } } }, - "required": [ - "parts" - ] + "required": ["parts"] } } } @@ -3861,10 +3871,7 @@ } } }, - "required": [ - "info", - "parts" - ] + "required": ["info", "parts"] } } } @@ -3942,20 +3949,13 @@ "$ref": "#/components/schemas/FilePartSource" } }, - "required": [ - "type", - "mime", - "url" - ] + "required": ["type", "mime", "url"] } ] } } }, - "required": [ - "arguments", - "command" - ] + "required": ["arguments", "command"] } } } @@ -4049,19 +4049,13 @@ "type": "string" } }, - "required": [ - "providerID", - "modelID" - ] + "required": ["providerID", "modelID"] }, "command": { "type": "string" } }, - "required": [ - "agent", - "command" - ] + "required": ["agent", "command"] } } } @@ -4150,9 +4144,7 @@ "pattern": "^prt.*" } }, - "required": [ - "messageID" - ] + "required": ["messageID"] } } } @@ -4312,16 +4304,10 @@ "properties": { "response": { "type": "string", - "enum": [ - "once", - "always", - "reject" - ] + "enum": ["once", "always", "reject"] } }, - "required": [ - "response" - ] + "required": ["response"] } } } @@ -4458,19 +4444,13 @@ "properties": { "reply": { "type": "string", - "enum": [ - "once", - "always", - "reject" - ] + "enum": ["once", "always", "reject"] }, "message": { "type": "string" } }, - "required": [ - "reply" - ] + "required": ["reply"] } } } @@ -4738,9 +4718,7 @@ } } }, - "required": [ - "answers" - ] + "required": ["answers"] } } } @@ -4917,15 +4895,10 @@ "properties": { "field": { "type": "string", - "enum": [ - "reasoning_content", - "reasoning_details" - ] + "enum": ["reasoning_content", "reasoning_details"] } }, - "required": [ - "field" - ], + "required": ["field"], "additionalProperties": false } ] @@ -4961,16 +4934,10 @@ "type": "number" } }, - "required": [ - "input", - "output" - ] + "required": ["input", "output"] } }, - "required": [ - "input", - "output" - ] + "required": ["input", "output"] }, "limit": { "type": "object", @@ -4985,10 +4952,7 @@ "type": "number" } }, - "required": [ - "context", - "output" - ] + "required": ["context", "output"] }, "modalities": { "type": "object", @@ -4997,70 +4961,39 @@ "type": "array", "items": { "type": "string", - "enum": [ - "text", - "audio", - "image", - "video", - "pdf" - ] + "enum": ["text", "audio", "image", "video", "pdf"] } }, "output": { "type": "array", "items": { "type": "string", - "enum": [ - "text", - "audio", - "image", - "video", - "pdf" - ] + "enum": ["text", "audio", "image", "video", "pdf"] } } }, - "required": [ - "input", - "output" - ] + "required": ["input", "output"] }, "recommendedIndex": { "type": "number" }, "prompt": { "type": "string", - "enum": [ - "codex", - "gemini", - "beast", - "anthropic", - "trinity", - "anthropic_without_todo" - ] + "enum": ["codex", "gemini", "beast", "anthropic", "trinity", "anthropic_without_todo"] }, "isFree": { "type": "boolean" }, "ai_sdk_provider": { "type": "string", - "enum": [ - "anthropic", - "openai", - "openai-compatible", - "openrouter" - ] + "enum": ["anthropic", "openai", "openai-compatible", "openrouter"] }, "experimental": { "type": "boolean" }, "status": { "type": "string", - "enum": [ - "alpha", - "beta", - "deprecated" - ] + "enum": ["alpha", "beta", "deprecated"] }, "options": { "type": "object", @@ -5117,12 +5050,7 @@ } } }, - "required": [ - "name", - "env", - "id", - "models" - ] + "required": ["name", "env", "id", "models"] } }, "default": { @@ -5141,11 +5069,7 @@ } } }, - "required": [ - "all", - "default", - "connected" - ] + "required": ["all", "default", "connected"] } } } @@ -5272,9 +5196,7 @@ "type": "number" } }, - "required": [ - "method" - ] + "required": ["method"] } } } @@ -5354,9 +5276,7 @@ "type": "string" } }, - "required": [ - "method" - ] + "required": ["method"] } } } @@ -5431,9 +5351,7 @@ "additionalProperties": {} } }, - "required": [ - "event" - ] + "required": ["event"] } } } @@ -5482,10 +5400,7 @@ "type": "boolean" } }, - "required": [ - "enabled", - "connected" - ] + "required": ["enabled", "connected"] } } } @@ -5535,10 +5450,7 @@ "type": "boolean" } }, - "required": [ - "enabled", - "connected" - ] + "required": ["enabled", "connected"] } } } @@ -5588,10 +5500,7 @@ "type": "boolean" } }, - "required": [ - "enabled", - "connected" - ] + "required": ["enabled", "connected"] } } } @@ -5638,9 +5547,7 @@ "type": "string" } }, - "required": [ - "message" - ] + "required": ["message"] } } } @@ -5678,9 +5585,7 @@ "type": "string" } }, - "required": [ - "path" - ] + "required": ["path"] } } } @@ -5726,9 +5631,7 @@ "type": "string" } }, - "required": [ - "text" - ] + "required": ["text"] } } } @@ -5756,9 +5659,7 @@ "minLength": 1 } }, - "required": [ - "text" - ] + "required": ["text"] } } } @@ -5810,10 +5711,7 @@ "type": "boolean" } }, - "required": [ - "ok", - "id" - ] + "required": ["ok", "id"] } } } @@ -5877,13 +5775,7 @@ } } }, - "required": [ - "id", - "worktree", - "timeCreated", - "timeUpdated", - "sandboxes" - ] + "required": ["id", "worktree", "timeCreated", "timeUpdated", "sandboxes"] } } } @@ -5935,10 +5827,7 @@ "type": "boolean" } }, - "required": [ - "ok", - "id" - ] + "required": ["ok", "id"] } } } @@ -5966,6 +5855,9 @@ "projectID": { "type": "string" }, + "force": { + "type": "boolean" + }, "workspaceID": { "type": "string" }, @@ -6010,11 +5902,7 @@ } } }, - "required": [ - "additions", - "deletions", - "files" - ] + "required": ["additions", "deletions", "files"] }, "revert": { "type": "object", @@ -6032,9 +5920,7 @@ "type": "string" } }, - "required": [ - "messageID" - ] + "required": ["messageID"] }, "permission": { "type": "object", @@ -6056,16 +5942,7 @@ "type": "number" } }, - "required": [ - "id", - "projectID", - "slug", - "directory", - "title", - "version", - "timeCreated", - "timeUpdated" - ] + "required": ["id", "projectID", "slug", "directory", "title", "version", "timeCreated", "timeUpdated"] } } } @@ -6117,10 +5994,7 @@ "type": "boolean" } }, - "required": [ - "ok", - "id" - ] + "required": ["ok", "id"] } } } @@ -6167,9 +6041,7 @@ "type": "number" } }, - "required": [ - "created" - ] + "required": ["created"] }, "agent": { "type": "string" @@ -6184,10 +6056,7 @@ "type": "string" } }, - "required": [ - "providerID", - "modelID" - ] + "required": ["providerID", "modelID"] }, "tools": { "type": "object", @@ -6199,12 +6068,7 @@ } } }, - "required": [ - "role", - "time", - "agent", - "model" - ] + "required": ["role", "time", "agent", "model"] }, { "type": "object", @@ -6223,9 +6087,7 @@ "type": "number" } }, - "required": [ - "created" - ] + "required": ["created"] }, "parentID": { "type": "string" @@ -6252,10 +6114,7 @@ "type": "string" } }, - "required": [ - "cwd", - "root" - ] + "required": ["cwd", "root"] }, "summary": { "type": "boolean" @@ -6288,18 +6147,10 @@ "type": "number" } }, - "required": [ - "read", - "write" - ] + "required": ["read", "write"] } }, - "required": [ - "input", - "output", - "reasoning", - "cache" - ] + "required": ["input", "output", "reasoning", "cache"] }, "structured": {}, "variant": { @@ -6325,12 +6176,7 @@ ] } }, - "required": [ - "id", - "sessionID", - "timeCreated", - "data" - ] + "required": ["id", "sessionID", "timeCreated", "data"] } } } @@ -6382,10 +6228,7 @@ "type": "boolean" } }, - "required": [ - "ok", - "id" - ] + "required": ["ok", "id"] } } } @@ -6447,9 +6290,7 @@ "type": "number" } }, - "required": [ - "start" - ] + "required": ["start"] }, "metadata": { "type": "object", @@ -6459,10 +6300,7 @@ "additionalProperties": {} } }, - "required": [ - "type", - "text" - ] + "required": ["type", "text"] }, { "type": "object", @@ -6491,16 +6329,10 @@ "type": "number" } }, - "required": [ - "start" - ] + "required": ["start"] } }, - "required": [ - "type", - "text", - "time" - ] + "required": ["type", "text", "time"] }, { "type": "object", @@ -6535,11 +6367,7 @@ "type": "string" } }, - "required": [ - "status", - "input", - "raw" - ] + "required": ["status", "input", "raw"] }, { "type": "object", @@ -6572,16 +6400,10 @@ "type": "number" } }, - "required": [ - "start" - ] + "required": ["start"] } }, - "required": [ - "status", - "input", - "time" - ] + "required": ["status", "input", "time"] }, { "type": "object", @@ -6623,20 +6445,10 @@ "type": "number" } }, - "required": [ - "start", - "end" - ] + "required": ["start", "end"] } }, - "required": [ - "status", - "input", - "output", - "title", - "metadata", - "time" - ] + "required": ["status", "input", "output", "title", "metadata", "time"] }, { "type": "object", @@ -6672,18 +6484,10 @@ "type": "number" } }, - "required": [ - "start", - "end" - ] + "required": ["start", "end"] } }, - "required": [ - "status", - "input", - "error", - "time" - ] + "required": ["status", "input", "error", "time"] } ] }, @@ -6695,22 +6499,12 @@ "additionalProperties": {} } }, - "required": [ - "type", - "callID", - "tool", - "state" - ] + "required": ["type", "callID", "tool", "state"] } ] } }, - "required": [ - "id", - "messageID", - "sessionID", - "data" - ] + "required": ["id", "messageID", "sessionID", "data"] } } } @@ -6776,9 +6570,7 @@ "type": "string" } }, - "required": [ - "location" - ] + "required": ["location"] } } } @@ -6844,9 +6636,7 @@ "type": "string" } }, - "required": [ - "name" - ] + "required": ["name"] } } } @@ -6912,17 +6702,11 @@ "type": "string" } }, - "required": [ - "id", - "name", - "role" - ] + "required": ["id", "name", "role"] } } }, - "required": [ - "email" - ] + "required": ["email"] }, "balance": { "anyOf": [ @@ -6933,9 +6717,7 @@ "type": "number" } }, - "required": [ - "balance" - ] + "required": ["balance"] }, { "type": "null" @@ -6953,11 +6735,7 @@ ] } }, - "required": [ - "profile", - "balance", - "currentOrgId" - ] + "required": ["profile", "balance", "currentOrgId"] } } } @@ -7041,9 +6819,7 @@ ] } }, - "required": [ - "organizationId" - ] + "required": ["organizationId"] } } } @@ -7171,9 +6947,7 @@ } } }, - "required": [ - "modes" - ] + "required": ["modes"] } } } @@ -7284,10 +7058,7 @@ "type": "number" } }, - "required": [ - "prefix", - "suffix" - ] + "required": ["prefix", "suffix"] } } } @@ -7350,10 +7121,7 @@ "type": "string" } }, - "required": [ - "actionText", - "actionURL" - ] + "required": ["actionText", "actionURL"] }, "showIn": { "type": "array", @@ -7365,11 +7133,7 @@ "type": "string" } }, - "required": [ - "id", - "title", - "message" - ] + "required": ["id", "title", "message"] } } } @@ -7512,9 +7276,7 @@ "type": "string" } }, - "required": [ - "sessionId" - ] + "required": ["sessionId"] } } } @@ -7560,14 +7322,7 @@ "anyOf": [ { "type": "string", - "enum": [ - "provisioned", - "starting", - "restarting", - "running", - "stopped", - "destroying" - ] + "enum": ["provisioned", "starting", "restarting", "running", "stopped", "destroying"] }, { "type": "null" @@ -7590,10 +7345,7 @@ "type": "number" } }, - "required": [ - "cpus", - "memory_mb" - ] + "required": ["cpus", "memory_mb"] }, "openclawVersion": { "anyOf": [ @@ -7635,9 +7387,7 @@ "type": "string" } }, - "required": [ - "status" - ] + "required": ["status"] } } } @@ -7695,12 +7445,7 @@ "type": "string" } }, - "required": [ - "apiKey", - "userId", - "userToken", - "channelId" - ] + "required": ["apiKey", "userId", "userToken", "channelId"] }, { "type": "null" @@ -7797,13 +7542,7 @@ "type": "number" } }, - "required": [ - "session_id", - "title", - "created_at", - "updated_at", - "version" - ] + "required": ["session_id", "title", "created_at", "updated_at", "version"] } }, "nextCursor": { @@ -7817,10 +7556,7 @@ ] } }, - "required": [ - "cliSessions", - "nextCursor" - ] + "required": ["cliSessions", "nextCursor"] } } } @@ -7890,9 +7626,7 @@ "type": "string" } }, - "required": [ - "text" - ] + "required": ["text"] }, "lines": { "type": "object", @@ -7901,9 +7635,7 @@ "type": "string" } }, - "required": [ - "text" - ] + "required": ["text"] }, "line_number": { "type": "number" @@ -7923,9 +7655,7 @@ "type": "string" } }, - "required": [ - "text" - ] + "required": ["text"] }, "start": { "type": "number" @@ -7934,21 +7664,11 @@ "type": "number" } }, - "required": [ - "match", - "start", - "end" - ] + "required": ["match", "start", "end"] } } }, - "required": [ - "path", - "lines", - "line_number", - "absolute_offset", - "submatches" - ] + "required": ["path", "lines", "line_number", "absolute_offset", "submatches"] } } } @@ -7994,10 +7714,7 @@ "name": "dirs", "schema": { "type": "string", - "enum": [ - "true", - "false" - ] + "enum": ["true", "false"] } }, { @@ -8005,10 +7722,7 @@ "name": "type", "schema": { "type": "string", - "enum": [ - "file", - "directory" - ] + "enum": ["file", "directory"] } }, { @@ -8357,10 +8071,7 @@ ] } }, - "required": [ - "name", - "config" - ] + "required": ["name", "config"] } } } @@ -8415,9 +8126,7 @@ "type": "string" } }, - "required": [ - "authorizationUrl" - ] + "required": ["authorizationUrl"] } } } @@ -8491,9 +8200,7 @@ "const": true } }, - "required": [ - "success" - ] + "required": ["success"] } } } @@ -8589,9 +8296,7 @@ "type": "string" } }, - "required": [ - "code" - ] + "required": ["code"] } } } @@ -8822,9 +8527,7 @@ "type": "string" } }, - "required": [ - "text" - ] + "required": ["text"] } } } @@ -9136,9 +8839,7 @@ "type": "string" } }, - "required": [ - "command" - ] + "required": ["command"] } } } @@ -9198,12 +8899,7 @@ }, "variant": { "type": "string", - "enum": [ - "info", - "success", - "warning", - "error" - ] + "enum": ["info", "success", "warning", "error"] }, "duration": { "description": "Duration in milliseconds", @@ -9211,10 +8907,7 @@ "type": "number" } }, - "required": [ - "message", - "variant" - ] + "required": ["message", "variant"] } } } @@ -9365,9 +9058,7 @@ "pattern": "^ses" } }, - "required": [ - "sessionID" - ] + "required": ["sessionID"] } } } @@ -9414,10 +9105,7 @@ }, "body": {} }, - "required": [ - "path", - "body" - ] + "required": ["path", "body"] } } } @@ -9702,12 +9390,7 @@ "level": { "description": "Log level", "type": "string", - "enum": [ - "debug", - "info", - "error", - "warn" - ] + "enum": ["debug", "info", "error", "warn"] }, "message": { "description": "Log message", @@ -9722,11 +9405,7 @@ "additionalProperties": {} } }, - "required": [ - "service", - "level", - "message" - ] + "required": ["service", "level", "message"] } } } @@ -9827,12 +9506,7 @@ "type": "string" } }, - "required": [ - "name", - "description", - "location", - "content" - ] + "required": ["name", "description", "location", "content"] } } } @@ -9993,15 +9667,10 @@ "type": "string" } }, - "required": [ - "version" - ] + "required": ["version"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.installation.update-available": { "type": "object", @@ -10017,15 +9686,10 @@ "type": "string" } }, - "required": [ - "version" - ] + "required": ["version"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Project": { "type": "object", @@ -10079,10 +9743,7 @@ "type": "number" } }, - "required": [ - "created", - "updated" - ] + "required": ["created", "updated"] }, "sandboxes": { "type": "array", @@ -10091,12 +9752,7 @@ } } }, - "required": [ - "id", - "worktree", - "time", - "sandboxes" - ] + "required": ["id", "worktree", "time", "sandboxes"] }, "Event.project.updated": { "type": "object", @@ -10109,10 +9765,7 @@ "$ref": "#/components/schemas/Project" } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.server.instance.disposed": { "type": "object", @@ -10128,15 +9781,10 @@ "type": "string" } }, - "required": [ - "directory" - ] + "required": ["directory"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.server.connected": { "type": "object", @@ -10150,10 +9798,7 @@ "properties": {} } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.global.disposed": { "type": "object", @@ -10167,10 +9812,7 @@ "properties": {} } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.global.config.updated": { "type": "object", @@ -10184,10 +9826,7 @@ "properties": {} } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.lsp.client.diagnostics": { "type": "object", @@ -10206,16 +9845,10 @@ "type": "string" } }, - "required": [ - "serverID", - "path" - ] + "required": ["serverID", "path"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.lsp.updated": { "type": "object", @@ -10229,10 +9862,7 @@ "properties": {} } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.file.edited": { "type": "object", @@ -10248,15 +9878,10 @@ "type": "string" } }, - "required": [ - "file" - ] + "required": ["file"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "OutputFormatText": { "type": "object", @@ -10266,9 +9891,7 @@ "const": "text" } }, - "required": [ - "type" - ] + "required": ["type"] }, "JSONSchema": { "type": "object", @@ -10294,10 +9917,7 @@ "maximum": 9007199254740991 } }, - "required": [ - "type", - "schema" - ] + "required": ["type", "schema"] }, "OutputFormat": { "anyOf": [ @@ -10329,20 +9949,10 @@ }, "status": { "type": "string", - "enum": [ - "added", - "deleted", - "modified" - ] + "enum": ["added", "deleted", "modified"] } }, - "required": [ - "file", - "before", - "after", - "additions", - "deletions" - ] + "required": ["file", "before", "after", "additions", "deletions"] }, "UserMessage": { "type": "object", @@ -10364,9 +9974,7 @@ "type": "number" } }, - "required": [ - "created" - ] + "required": ["created"] }, "format": { "$ref": "#/components/schemas/OutputFormat" @@ -10387,9 +9995,7 @@ } } }, - "required": [ - "diffs" - ] + "required": ["diffs"] }, "agent": { "type": "string" @@ -10404,10 +10010,7 @@ "type": "string" } }, - "required": [ - "providerID", - "modelID" - ] + "required": ["providerID", "modelID"] }, "system": { "type": "string" @@ -10448,14 +10051,7 @@ } } }, - "required": [ - "id", - "sessionID", - "role", - "time", - "agent", - "model" - ] + "required": ["id", "sessionID", "role", "time", "agent", "model"] }, "ProviderAuthError": { "type": "object", @@ -10474,16 +10070,10 @@ "type": "string" } }, - "required": [ - "providerID", - "message" - ] + "required": ["providerID", "message"] } }, - "required": [ - "name", - "data" - ] + "required": ["name", "data"] }, "UnknownError": { "type": "object", @@ -10499,15 +10089,10 @@ "type": "string" } }, - "required": [ - "message" - ] + "required": ["message"] } }, - "required": [ - "name", - "data" - ] + "required": ["name", "data"] }, "MessageOutputLengthError": { "type": "object", @@ -10521,10 +10106,7 @@ "properties": {} } }, - "required": [ - "name", - "data" - ] + "required": ["name", "data"] }, "MessageAbortedError": { "type": "object", @@ -10540,15 +10122,10 @@ "type": "string" } }, - "required": [ - "message" - ] + "required": ["message"] } }, - "required": [ - "name", - "data" - ] + "required": ["name", "data"] }, "StructuredOutputError": { "type": "object", @@ -10567,16 +10144,10 @@ "type": "number" } }, - "required": [ - "message", - "retries" - ] + "required": ["message", "retries"] } }, - "required": [ - "name", - "data" - ] + "required": ["name", "data"] }, "ContextOverflowError": { "type": "object", @@ -10595,15 +10166,10 @@ "type": "string" } }, - "required": [ - "message" - ] + "required": ["message"] } }, - "required": [ - "name", - "data" - ] + "required": ["name", "data"] }, "APIError": { "type": "object", @@ -10646,16 +10212,10 @@ } } }, - "required": [ - "message", - "isRetryable" - ] + "required": ["message", "isRetryable"] } }, - "required": [ - "name", - "data" - ] + "required": ["name", "data"] }, "AssistantMessage": { "type": "object", @@ -10680,9 +10240,7 @@ "type": "number" } }, - "required": [ - "created" - ] + "required": ["created"] }, "error": { "anyOf": [ @@ -10734,10 +10292,7 @@ "type": "string" } }, - "required": [ - "cwd", - "root" - ] + "required": ["cwd", "root"] }, "summary": { "type": "boolean" @@ -10770,18 +10325,10 @@ "type": "number" } }, - "required": [ - "read", - "write" - ] + "required": ["read", "write"] } }, - "required": [ - "input", - "output", - "reasoning", - "cache" - ] + "required": ["input", "output", "reasoning", "cache"] }, "structured": {}, "variant": { @@ -10830,15 +10377,10 @@ "$ref": "#/components/schemas/Message" } }, - "required": [ - "info" - ] + "required": ["info"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.message.removed": { "type": "object", @@ -10857,16 +10399,10 @@ "type": "string" } }, - "required": [ - "sessionID", - "messageID" - ] + "required": ["sessionID", "messageID"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "TextPart": { "type": "object", @@ -10903,9 +10439,7 @@ "type": "number" } }, - "required": [ - "start" - ] + "required": ["start"] }, "metadata": { "type": "object", @@ -10915,13 +10449,7 @@ "additionalProperties": {} } }, - "required": [ - "id", - "sessionID", - "messageID", - "type", - "text" - ] + "required": ["id", "sessionID", "messageID", "type", "text"] }, "SubtaskPart": { "type": "object", @@ -10958,24 +10486,13 @@ "type": "string" } }, - "required": [ - "providerID", - "modelID" - ] + "required": ["providerID", "modelID"] }, "command": { "type": "string" } }, - "required": [ - "id", - "sessionID", - "messageID", - "type", - "prompt", - "description", - "agent" - ] + "required": ["id", "sessionID", "messageID", "type", "prompt", "description", "agent"] }, "ReasoningPart": { "type": "object", @@ -11013,19 +10530,10 @@ "type": "number" } }, - "required": [ - "start" - ] + "required": ["start"] } }, - "required": [ - "id", - "sessionID", - "messageID", - "type", - "text", - "time" - ] + "required": ["id", "sessionID", "messageID", "type", "text", "time"] }, "FilePartSourceText": { "type": "object", @@ -11044,11 +10552,7 @@ "maximum": 9007199254740991 } }, - "required": [ - "value", - "start", - "end" - ] + "required": ["value", "start", "end"] }, "FileSource": { "type": "object", @@ -11064,11 +10568,7 @@ "type": "string" } }, - "required": [ - "text", - "type", - "path" - ] + "required": ["text", "type", "path"] }, "Range": { "type": "object", @@ -11083,10 +10583,7 @@ "type": "number" } }, - "required": [ - "line", - "character" - ] + "required": ["line", "character"] }, "end": { "type": "object", @@ -11098,16 +10595,10 @@ "type": "number" } }, - "required": [ - "line", - "character" - ] + "required": ["line", "character"] } }, - "required": [ - "start", - "end" - ] + "required": ["start", "end"] }, "SymbolSource": { "type": "object", @@ -11134,14 +10625,7 @@ "maximum": 9007199254740991 } }, - "required": [ - "text", - "type", - "path", - "range", - "name", - "kind" - ] + "required": ["text", "type", "path", "range", "name", "kind"] }, "ResourceSource": { "type": "object", @@ -11160,12 +10644,7 @@ "type": "string" } }, - "required": [ - "text", - "type", - "clientName", - "uri" - ] + "required": ["text", "type", "clientName", "uri"] }, "FilePartSource": { "anyOf": [ @@ -11209,14 +10688,7 @@ "$ref": "#/components/schemas/FilePartSource" } }, - "required": [ - "id", - "sessionID", - "messageID", - "type", - "mime", - "url" - ] + "required": ["id", "sessionID", "messageID", "type", "mime", "url"] }, "ToolStatePending": { "type": "object", @@ -11236,11 +10708,7 @@ "type": "string" } }, - "required": [ - "status", - "input", - "raw" - ] + "required": ["status", "input", "raw"] }, "ToolStateRunning": { "type": "object", @@ -11273,16 +10741,10 @@ "type": "number" } }, - "required": [ - "start" - ] + "required": ["start"] } }, - "required": [ - "status", - "input", - "time" - ] + "required": ["status", "input", "time"] }, "ToolStateCompleted": { "type": "object", @@ -11324,10 +10786,7 @@ "type": "number" } }, - "required": [ - "start", - "end" - ] + "required": ["start", "end"] }, "attachments": { "type": "array", @@ -11336,14 +10795,7 @@ } } }, - "required": [ - "status", - "input", - "output", - "title", - "metadata", - "time" - ] + "required": ["status", "input", "output", "title", "metadata", "time"] }, "ToolStateError": { "type": "object", @@ -11379,18 +10831,10 @@ "type": "number" } }, - "required": [ - "start", - "end" - ] + "required": ["start", "end"] } }, - "required": [ - "status", - "input", - "error", - "time" - ] + "required": ["status", "input", "error", "time"] }, "ToolState": { "anyOf": [ @@ -11441,15 +10885,7 @@ "additionalProperties": {} } }, - "required": [ - "id", - "sessionID", - "messageID", - "type", - "callID", - "tool", - "state" - ] + "required": ["id", "sessionID", "messageID", "type", "callID", "tool", "state"] }, "StepStartPart": { "type": "object", @@ -11471,12 +10907,7 @@ "type": "string" } }, - "required": [ - "id", - "sessionID", - "messageID", - "type" - ] + "required": ["id", "sessionID", "messageID", "type"] }, "StepFinishPart": { "type": "object", @@ -11528,29 +10959,13 @@ "type": "number" } }, - "required": [ - "read", - "write" - ] + "required": ["read", "write"] } }, - "required": [ - "input", - "output", - "reasoning", - "cache" - ] + "required": ["input", "output", "reasoning", "cache"] } }, - "required": [ - "id", - "sessionID", - "messageID", - "type", - "reason", - "cost", - "tokens" - ] + "required": ["id", "sessionID", "messageID", "type", "reason", "cost", "tokens"] }, "SnapshotPart": { "type": "object", @@ -11572,13 +10987,7 @@ "type": "string" } }, - "required": [ - "id", - "sessionID", - "messageID", - "type", - "snapshot" - ] + "required": ["id", "sessionID", "messageID", "type", "snapshot"] }, "PatchPart": { "type": "object", @@ -11606,14 +11015,7 @@ } } }, - "required": [ - "id", - "sessionID", - "messageID", - "type", - "hash", - "files" - ] + "required": ["id", "sessionID", "messageID", "type", "hash", "files"] }, "AgentPart": { "type": "object", @@ -11651,20 +11053,10 @@ "maximum": 9007199254740991 } }, - "required": [ - "value", - "start", - "end" - ] + "required": ["value", "start", "end"] } }, - "required": [ - "id", - "sessionID", - "messageID", - "type", - "name" - ] + "required": ["id", "sessionID", "messageID", "type", "name"] }, "RetryPart": { "type": "object", @@ -11695,20 +11087,10 @@ "type": "number" } }, - "required": [ - "created" - ] + "required": ["created"] } }, - "required": [ - "id", - "sessionID", - "messageID", - "type", - "attempt", - "error", - "time" - ] + "required": ["id", "sessionID", "messageID", "type", "attempt", "error", "time"] }, "CompactionPart": { "type": "object", @@ -11733,13 +11115,7 @@ "type": "boolean" } }, - "required": [ - "id", - "sessionID", - "messageID", - "type", - "auto" - ] + "required": ["id", "sessionID", "messageID", "type", "auto"] }, "Part": { "anyOf": [ @@ -11795,15 +11171,10 @@ "$ref": "#/components/schemas/Part" } }, - "required": [ - "part" - ] + "required": ["part"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.message.part.delta": { "type": "object", @@ -11831,19 +11202,10 @@ "type": "string" } }, - "required": [ - "sessionID", - "messageID", - "partID", - "field", - "delta" - ] + "required": ["sessionID", "messageID", "partID", "field", "delta"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.message.part.removed": { "type": "object", @@ -11865,17 +11227,10 @@ "type": "string" } }, - "required": [ - "sessionID", - "messageID", - "partID" - ] + "required": ["sessionID", "messageID", "partID"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "PermissionRequest": { "type": "object", @@ -11920,20 +11275,10 @@ "type": "string" } }, - "required": [ - "messageID", - "callID" - ] + "required": ["messageID", "callID"] } }, - "required": [ - "id", - "sessionID", - "permission", - "patterns", - "metadata", - "always" - ] + "required": ["id", "sessionID", "permission", "patterns", "metadata", "always"] }, "Event.permission.asked": { "type": "object", @@ -11946,10 +11291,7 @@ "$ref": "#/components/schemas/PermissionRequest" } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.permission.replied": { "type": "object", @@ -11969,24 +11311,13 @@ }, "reply": { "type": "string", - "enum": [ - "once", - "always", - "reject" - ] + "enum": ["once", "always", "reject"] } }, - "required": [ - "sessionID", - "requestID", - "reply" - ] + "required": ["sessionID", "requestID", "reply"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "SessionStatus": { "anyOf": [ @@ -11998,9 +11329,7 @@ "const": "idle" } }, - "required": [ - "type" - ] + "required": ["type"] }, { "type": "object", @@ -12019,12 +11348,7 @@ "type": "number" } }, - "required": [ - "type", - "attempt", - "message", - "next" - ] + "required": ["type", "attempt", "message", "next"] }, { "type": "object", @@ -12034,9 +11358,7 @@ "const": "busy" } }, - "required": [ - "type" - ] + "required": ["type"] } ] }, @@ -12057,16 +11379,10 @@ "$ref": "#/components/schemas/SessionStatus" } }, - "required": [ - "sessionID", - "status" - ] + "required": ["sessionID", "status"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.session.idle": { "type": "object", @@ -12082,15 +11398,10 @@ "type": "string" } }, - "required": [ - "sessionID" - ] + "required": ["sessionID"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "QuestionOption": { "type": "object", @@ -12108,10 +11419,7 @@ "type": "string" } }, - "required": [ - "label", - "description" - ] + "required": ["label", "description"] }, "QuestionInfo": { "type": "object", @@ -12140,11 +11448,7 @@ "type": "boolean" } }, - "required": [ - "question", - "header", - "options" - ] + "required": ["question", "header", "options"] }, "QuestionRequest": { "type": "object", @@ -12174,17 +11478,10 @@ "type": "string" } }, - "required": [ - "messageID", - "callID" - ] + "required": ["messageID", "callID"] } }, - "required": [ - "id", - "sessionID", - "questions" - ] + "required": ["id", "sessionID", "questions"] }, "Event.question.asked": { "type": "object", @@ -12197,10 +11494,7 @@ "$ref": "#/components/schemas/QuestionRequest" } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "QuestionAnswer": { "type": "array", @@ -12231,17 +11525,10 @@ } } }, - "required": [ - "sessionID", - "requestID", - "answers" - ] + "required": ["sessionID", "requestID", "answers"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.question.rejected": { "type": "object", @@ -12260,16 +11547,10 @@ "type": "string" } }, - "required": [ - "sessionID", - "requestID" - ] + "required": ["sessionID", "requestID"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.session.compacted": { "type": "object", @@ -12285,15 +11566,10 @@ "type": "string" } }, - "required": [ - "sessionID" - ] + "required": ["sessionID"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.file.watcher.updated": { "type": "object", @@ -12325,16 +11601,10 @@ ] } }, - "required": [ - "file", - "event" - ] + "required": ["file", "event"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Todo": { "type": "object", @@ -12352,11 +11622,7 @@ "type": "string" } }, - "required": [ - "content", - "status", - "priority" - ] + "required": ["content", "status", "priority"] }, "Event.todo.updated": { "type": "object", @@ -12378,16 +11644,10 @@ } } }, - "required": [ - "sessionID", - "todos" - ] + "required": ["sessionID", "todos"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.tui.prompt.append": { "type": "object", @@ -12403,15 +11663,10 @@ "type": "string" } }, - "required": [ - "text" - ] + "required": ["text"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.tui.command.execute": { "type": "object", @@ -12452,15 +11707,10 @@ ] } }, - "required": [ - "command" - ] + "required": ["command"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.tui.toast.show": { "type": "object", @@ -12480,12 +11730,7 @@ }, "variant": { "type": "string", - "enum": [ - "info", - "success", - "warning", - "error" - ] + "enum": ["info", "success", "warning", "error"] }, "duration": { "description": "Duration in milliseconds", @@ -12493,16 +11738,10 @@ "type": "number" } }, - "required": [ - "message", - "variant" - ] + "required": ["message", "variant"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.tui.session.select": { "type": "object", @@ -12520,15 +11759,10 @@ "pattern": "^ses" } }, - "required": [ - "sessionID" - ] + "required": ["sessionID"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.mcp.tools.changed": { "type": "object", @@ -12544,15 +11778,10 @@ "type": "string" } }, - "required": [ - "server" - ] + "required": ["server"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.mcp.browser.open.failed": { "type": "object", @@ -12571,16 +11800,10 @@ "type": "string" } }, - "required": [ - "mcpName", - "url" - ] + "required": ["mcpName", "url"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.command.executed": { "type": "object", @@ -12607,26 +11830,14 @@ "pattern": "^msg.*" } }, - "required": [ - "name", - "sessionID", - "arguments", - "messageID" - ] + "required": ["name", "sessionID", "arguments", "messageID"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "PermissionAction": { "type": "string", - "enum": [ - "allow", - "deny", - "ask" - ] + "enum": ["allow", "deny", "ask"] }, "PermissionRule": { "type": "object", @@ -12641,11 +11852,7 @@ "$ref": "#/components/schemas/PermissionAction" } }, - "required": [ - "permission", - "pattern", - "action" - ] + "required": ["permission", "pattern", "action"] }, "PermissionRuleset": { "type": "array", @@ -12704,26 +11911,14 @@ }, "status": { "type": "string", - "enum": [ - "added", - "deleted", - "modified" - ] + "enum": ["added", "deleted", "modified"] } }, - "required": [ - "file", - "additions", - "deletions" - ] + "required": ["file", "additions", "deletions"] } } }, - "required": [ - "additions", - "deletions", - "files" - ] + "required": ["additions", "deletions", "files"] }, "share": { "type": "object", @@ -12732,9 +11927,7 @@ "type": "string" } }, - "required": [ - "url" - ] + "required": ["url"] }, "title": { "type": "string" @@ -12758,10 +11951,7 @@ "type": "number" } }, - "required": [ - "created", - "updated" - ] + "required": ["created", "updated"] }, "permission": { "$ref": "#/components/schemas/PermissionRuleset" @@ -12782,20 +11972,10 @@ "type": "string" } }, - "required": [ - "messageID" - ] + "required": ["messageID"] } }, - "required": [ - "id", - "slug", - "projectID", - "directory", - "title", - "version", - "time" - ] + "required": ["id", "slug", "projectID", "directory", "title", "version", "time"] }, "Event.session.created": { "type": "object", @@ -12811,15 +11991,10 @@ "$ref": "#/components/schemas/Session" } }, - "required": [ - "info" - ] + "required": ["info"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.session.updated": { "type": "object", @@ -12835,15 +12010,10 @@ "$ref": "#/components/schemas/Session" } }, - "required": [ - "info" - ] + "required": ["info"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.session.deleted": { "type": "object", @@ -12859,15 +12029,10 @@ "$ref": "#/components/schemas/Session" } }, - "required": [ - "info" - ] + "required": ["info"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.session.diff": { "type": "object", @@ -12889,16 +12054,10 @@ } } }, - "required": [ - "sessionID", - "diff" - ] + "required": ["sessionID", "diff"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.session.error": { "type": "object", @@ -12941,10 +12100,7 @@ } } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.session.turn.open": { "type": "object", @@ -12960,15 +12116,10 @@ "type": "string" } }, - "required": [ - "sessionID" - ] + "required": ["sessionID"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.session.turn.close": { "type": "object", @@ -12985,23 +12136,13 @@ }, "reason": { "type": "string", - "enum": [ - "completed", - "error", - "interrupted" - ] + "enum": ["completed", "error", "interrupted"] } }, - "required": [ - "sessionID", - "reason" - ] + "required": ["sessionID", "reason"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.vcs.branch.updated": { "type": "object", @@ -13019,10 +12160,7 @@ } } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.workspace.ready": { "type": "object", @@ -13038,15 +12176,10 @@ "type": "string" } }, - "required": [ - "name" - ] + "required": ["name"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.workspace.failed": { "type": "object", @@ -13062,15 +12195,10 @@ "type": "string" } }, - "required": [ - "message" - ] + "required": ["message"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Pty": { "type": "object", @@ -13096,24 +12224,13 @@ }, "status": { "type": "string", - "enum": [ - "running", - "exited" - ] + "enum": ["running", "exited"] }, "pid": { "type": "number" } }, - "required": [ - "id", - "title", - "command", - "args", - "cwd", - "status", - "pid" - ] + "required": ["id", "title", "command", "args", "cwd", "status", "pid"] }, "Event.pty.created": { "type": "object", @@ -13129,15 +12246,10 @@ "$ref": "#/components/schemas/Pty" } }, - "required": [ - "info" - ] + "required": ["info"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.pty.updated": { "type": "object", @@ -13153,15 +12265,10 @@ "$ref": "#/components/schemas/Pty" } }, - "required": [ - "info" - ] + "required": ["info"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.pty.exited": { "type": "object", @@ -13181,16 +12288,10 @@ "type": "number" } }, - "required": [ - "id", - "exitCode" - ] + "required": ["id", "exitCode"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.pty.deleted": { "type": "object", @@ -13207,15 +12308,10 @@ "pattern": "^pty.*" } }, - "required": [ - "id" - ] + "required": ["id"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.worktree.ready": { "type": "object", @@ -13234,16 +12330,10 @@ "type": "string" } }, - "required": [ - "name", - "branch" - ] + "required": ["name", "branch"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event.worktree.failed": { "type": "object", @@ -13259,15 +12349,10 @@ "type": "string" } }, - "required": [ - "message" - ] + "required": ["message"] } }, - "required": [ - "type", - "properties" - ] + "required": ["type", "properties"] }, "Event": { "anyOf": [ @@ -13427,20 +12512,12 @@ "$ref": "#/components/schemas/Event" } }, - "required": [ - "directory", - "payload" - ] + "required": ["directory", "payload"] }, "LogLevel": { "description": "Log level", "type": "string", - "enum": [ - "DEBUG", - "INFO", - "WARN", - "ERROR" - ] + "enum": ["DEBUG", "INFO", "WARN", "ERROR"] }, "ServerConfig": { "description": "Server configuration for kilo serve and web commands", @@ -13478,11 +12555,7 @@ "anyOf": [ { "type": "string", - "enum": [ - "ask", - "allow", - "deny" - ] + "enum": ["ask", "allow", "deny"] }, { "type": "null" @@ -13625,11 +12698,7 @@ }, "mode": { "type": "string", - "enum": [ - "subagent", - "primary", - "all" - ] + "enum": ["subagent", "primary", "all"] }, "hidden": { "description": "Hide this subagent from the @ autocomplete menu (default: false, only applies to mode: subagent)", @@ -13651,15 +12720,7 @@ }, { "type": "string", - "enum": [ - "primary", - "secondary", - "accent", - "success", - "warning", - "error", - "info" - ] + "enum": ["primary", "secondary", "accent", "success", "warning", "error", "info"] } ] }, @@ -13745,15 +12806,10 @@ "properties": { "field": { "type": "string", - "enum": [ - "reasoning_content", - "reasoning_details" - ] + "enum": ["reasoning_content", "reasoning_details"] } }, - "required": [ - "field" - ], + "required": ["field"], "additionalProperties": false } ] @@ -13789,16 +12845,10 @@ "type": "number" } }, - "required": [ - "input", - "output" - ] + "required": ["input", "output"] } }, - "required": [ - "input", - "output" - ] + "required": ["input", "output"] }, "limit": { "type": "object", @@ -13813,10 +12863,7 @@ "type": "number" } }, - "required": [ - "context", - "output" - ] + "required": ["context", "output"] }, "modalities": { "type": "object", @@ -13825,70 +12872,39 @@ "type": "array", "items": { "type": "string", - "enum": [ - "text", - "audio", - "image", - "video", - "pdf" - ] + "enum": ["text", "audio", "image", "video", "pdf"] } }, "output": { "type": "array", "items": { "type": "string", - "enum": [ - "text", - "audio", - "image", - "video", - "pdf" - ] + "enum": ["text", "audio", "image", "video", "pdf"] } } }, - "required": [ - "input", - "output" - ] + "required": ["input", "output"] }, "recommendedIndex": { "type": "number" }, "prompt": { "type": "string", - "enum": [ - "codex", - "gemini", - "beast", - "anthropic", - "trinity", - "anthropic_without_todo" - ] + "enum": ["codex", "gemini", "beast", "anthropic", "trinity", "anthropic_without_todo"] }, "isFree": { "type": "boolean" }, "ai_sdk_provider": { "type": "string", - "enum": [ - "anthropic", - "openai", - "openai-compatible", - "openrouter" - ] + "enum": ["anthropic", "openai", "openai-compatible", "openrouter"] }, "experimental": { "type": "boolean" }, "status": { "type": "string", - "enum": [ - "alpha", - "beta", - "deprecated" - ] + "enum": ["alpha", "beta", "deprecated"] }, "options": { "type": "object", @@ -14024,10 +13040,7 @@ "maximum": 9007199254740991 } }, - "required": [ - "type", - "command" - ], + "required": ["type", "command"], "additionalProperties": false }, "McpOAuthConfig": { @@ -14093,19 +13106,13 @@ "maximum": 9007199254740991 } }, - "required": [ - "type", - "url" - ], + "required": ["type", "url"], "additionalProperties": false }, "LayoutConfig": { "description": "@deprecated Always uses stretch layout.", "type": "string", - "enum": [ - "auto", - "stretch" - ] + "enum": ["auto", "stretch"] }, "Config": { "type": "object", @@ -14145,9 +13152,7 @@ "type": "boolean" } }, - "required": [ - "template" - ] + "required": ["template"] } }, "skills": { @@ -14193,11 +13198,7 @@ "share": { "description": "Control sharing behavior:'manual' allows manual sharing via commands, 'auto' enables automatic sharing, 'disabled' disables all sharing", "type": "string", - "enum": [ - "manual", - "auto", - "disabled" - ] + "enum": ["manual", "auto", "disabled"] }, "autoshare": { "description": "@deprecated Use 'share' field instead. Share newly created sessions automatically", @@ -14352,9 +13353,7 @@ "type": "boolean" } }, - "required": [ - "enabled" - ], + "required": ["enabled"], "additionalProperties": false } ] @@ -14424,9 +13423,7 @@ "const": true } }, - "required": [ - "disabled" - ] + "required": ["disabled"] }, { "type": "object", @@ -14463,9 +13460,7 @@ "additionalProperties": {} } }, - "required": [ - "command" - ] + "required": ["command"] } ] } @@ -14582,11 +13577,7 @@ "const": false } }, - "required": [ - "data", - "errors", - "success" - ] + "required": ["data", "errors", "success"] }, "OAuth": { "type": "object", @@ -14611,12 +13602,7 @@ "type": "string" } }, - "required": [ - "type", - "refresh", - "access", - "expires" - ] + "required": ["type", "refresh", "access", "expires"] }, "ApiAuth": { "type": "object", @@ -14629,10 +13615,7 @@ "type": "string" } }, - "required": [ - "type", - "key" - ] + "required": ["type", "key"] }, "WellKnownAuth": { "type": "object", @@ -14648,11 +13631,7 @@ "type": "string" } }, - "required": [ - "type", - "key", - "token" - ] + "required": ["type", "key", "token"] }, "Auth": { "anyOf": [ @@ -14681,15 +13660,10 @@ "type": "string" } }, - "required": [ - "message" - ] + "required": ["message"] } }, - "required": [ - "name", - "data" - ] + "required": ["name", "data"] }, "Model": { "type": "object", @@ -14713,11 +13687,7 @@ "type": "string" } }, - "required": [ - "id", - "url", - "npm" - ] + "required": ["id", "url", "npm"] }, "name": { "type": "string" @@ -14759,13 +13729,7 @@ "type": "boolean" } }, - "required": [ - "text", - "audio", - "image", - "video", - "pdf" - ] + "required": ["text", "audio", "image", "video", "pdf"] }, "output": { "type": "object", @@ -14786,13 +13750,7 @@ "type": "boolean" } }, - "required": [ - "text", - "audio", - "image", - "video", - "pdf" - ] + "required": ["text", "audio", "image", "video", "pdf"] }, "interleaved": { "anyOf": [ @@ -14804,28 +13762,15 @@ "properties": { "field": { "type": "string", - "enum": [ - "reasoning_content", - "reasoning_details" - ] + "enum": ["reasoning_content", "reasoning_details"] } }, - "required": [ - "field" - ] + "required": ["field"] } ] } }, - "required": [ - "temperature", - "reasoning", - "attachment", - "toolcall", - "input", - "output", - "interleaved" - ] + "required": ["temperature", "reasoning", "attachment", "toolcall", "input", "output", "interleaved"] }, "cost": { "type": "object", @@ -14846,10 +13791,7 @@ "type": "number" } }, - "required": [ - "read", - "write" - ] + "required": ["read", "write"] }, "experimentalOver200K": { "type": "object", @@ -14870,24 +13812,13 @@ "type": "number" } }, - "required": [ - "read", - "write" - ] + "required": ["read", "write"] } }, - "required": [ - "input", - "output", - "cache" - ] + "required": ["input", "output", "cache"] } }, - "required": [ - "input", - "output", - "cache" - ] + "required": ["input", "output", "cache"] }, "limit": { "type": "object", @@ -14902,19 +13833,11 @@ "type": "number" } }, - "required": [ - "context", - "output" - ] + "required": ["context", "output"] }, "status": { "type": "string", - "enum": [ - "alpha", - "beta", - "deprecated", - "active" - ] + "enum": ["alpha", "beta", "deprecated", "active"] }, "options": { "type": "object", @@ -14953,26 +13876,14 @@ }, "prompt": { "type": "string", - "enum": [ - "codex", - "gemini", - "beast", - "anthropic", - "trinity", - "anthropic_without_todo" - ] + "enum": ["codex", "gemini", "beast", "anthropic", "trinity", "anthropic_without_todo"] }, "isFree": { "type": "boolean" }, "ai_sdk_provider": { "type": "string", - "enum": [ - "anthropic", - "openai", - "openai-compatible", - "openrouter" - ] + "enum": ["anthropic", "openai", "openai-compatible", "openrouter"] } }, "required": [ @@ -15000,12 +13911,7 @@ }, "source": { "type": "string", - "enum": [ - "env", - "config", - "custom", - "api" - ] + "enum": ["env", "config", "custom", "api"] }, "env": { "type": "array", @@ -15033,14 +13939,7 @@ } } }, - "required": [ - "id", - "name", - "source", - "env", - "options", - "models" - ] + "required": ["id", "name", "source", "env", "options", "models"] }, "ToolIDs": { "type": "array", @@ -15059,11 +13958,7 @@ }, "parameters": {} }, - "required": [ - "id", - "description", - "parameters" - ] + "required": ["id", "description", "parameters"] }, "ToolList": { "type": "array", @@ -15123,15 +14018,7 @@ "type": "string" } }, - "required": [ - "id", - "type", - "branch", - "name", - "directory", - "extra", - "projectID" - ] + "required": ["id", "type", "branch", "name", "directory", "extra", "projectID"] }, "Worktree": { "type": "object", @@ -15146,11 +14033,7 @@ "type": "string" } }, - "required": [ - "name", - "branch", - "directory" - ] + "required": ["name", "branch", "directory"] }, "WorktreeCreateInput": { "type": "object", @@ -15171,9 +14054,7 @@ "type": "string" } }, - "required": [ - "directory" - ] + "required": ["directory"] }, "WorktreeResetInput": { "type": "object", @@ -15182,9 +14063,7 @@ "type": "string" } }, - "required": [ - "directory" - ] + "required": ["directory"] }, "WorktreeDiffItem": { "type": "object", @@ -15206,11 +14085,7 @@ }, "status": { "type": "string", - "enum": [ - "added", - "deleted", - "modified" - ] + "enum": ["added", "deleted", "modified"] }, "tracked": { "type": "boolean" @@ -15250,10 +14125,7 @@ "type": "string" } }, - "required": [ - "id", - "worktree" - ] + "required": ["id", "worktree"] }, "GlobalSession": { "type": "object", @@ -15306,26 +14178,14 @@ }, "status": { "type": "string", - "enum": [ - "added", - "deleted", - "modified" - ] + "enum": ["added", "deleted", "modified"] } }, - "required": [ - "file", - "additions", - "deletions" - ] + "required": ["file", "additions", "deletions"] } } }, - "required": [ - "additions", - "deletions", - "files" - ] + "required": ["additions", "deletions", "files"] }, "share": { "type": "object", @@ -15334,9 +14194,7 @@ "type": "string" } }, - "required": [ - "url" - ] + "required": ["url"] }, "title": { "type": "string" @@ -15360,10 +14218,7 @@ "type": "number" } }, - "required": [ - "created", - "updated" - ] + "required": ["created", "updated"] }, "permission": { "$ref": "#/components/schemas/PermissionRuleset" @@ -15384,9 +14239,7 @@ "type": "string" } }, - "required": [ - "messageID" - ] + "required": ["messageID"] }, "project": { "anyOf": [ @@ -15399,16 +14252,7 @@ ] } }, - "required": [ - "id", - "slug", - "projectID", - "directory", - "title", - "version", - "time", - "project" - ] + "required": ["id", "slug", "projectID", "directory", "title", "version", "time", "project"] }, "McpResource": { "type": "object", @@ -15429,11 +14273,7 @@ "type": "string" } }, - "required": [ - "name", - "uri", - "client" - ] + "required": ["name", "uri", "client"] }, "TextPartInput": { "type": "object", @@ -15464,9 +14304,7 @@ "type": "number" } }, - "required": [ - "start" - ] + "required": ["start"] }, "metadata": { "type": "object", @@ -15476,10 +14314,7 @@ "additionalProperties": {} } }, - "required": [ - "type", - "text" - ] + "required": ["type", "text"] }, "FilePartInput": { "type": "object", @@ -15504,11 +14339,7 @@ "$ref": "#/components/schemas/FilePartSource" } }, - "required": [ - "type", - "mime", - "url" - ] + "required": ["type", "mime", "url"] }, "AgentPartInput": { "type": "object", @@ -15540,17 +14371,10 @@ "maximum": 9007199254740991 } }, - "required": [ - "value", - "start", - "end" - ] + "required": ["value", "start", "end"] } }, - "required": [ - "type", - "name" - ] + "required": ["type", "name"] }, "SubtaskPartInput": { "type": "object", @@ -15581,21 +14405,13 @@ "type": "string" } }, - "required": [ - "providerID", - "modelID" - ] + "required": ["providerID", "modelID"] }, "command": { "type": "string" } }, - "required": [ - "type", - "prompt", - "description", - "agent" - ] + "required": ["type", "prompt", "description", "agent"] }, "ProviderAuthMethod": { "type": "object", @@ -15616,10 +14432,7 @@ "type": "string" } }, - "required": [ - "type", - "label" - ] + "required": ["type", "label"] }, "ProviderAuthAuthorization": { "type": "object", @@ -15643,11 +14456,7 @@ "type": "string" } }, - "required": [ - "url", - "method", - "instructions" - ] + "required": ["url", "method", "instructions"] }, "Symbol": { "type": "object", @@ -15668,17 +14477,10 @@ "$ref": "#/components/schemas/Range" } }, - "required": [ - "uri", - "range" - ] + "required": ["uri", "range"] } }, - "required": [ - "name", - "kind", - "location" - ] + "required": ["name", "kind", "location"] }, "FileNode": { "type": "object", @@ -15694,32 +14496,20 @@ }, "type": { "type": "string", - "enum": [ - "file", - "directory" - ] + "enum": ["file", "directory"] }, "ignored": { "type": "boolean" } }, - "required": [ - "name", - "path", - "absolute", - "type", - "ignored" - ] + "required": ["name", "path", "absolute", "type", "ignored"] }, "FileContent": { "type": "object", "properties": { "type": { "type": "string", - "enum": [ - "text", - "binary" - ] + "enum": ["text", "binary"] }, "content": { "type": "string" @@ -15766,24 +14556,14 @@ } } }, - "required": [ - "oldStart", - "oldLines", - "newStart", - "newLines", - "lines" - ] + "required": ["oldStart", "oldLines", "newStart", "newLines", "lines"] } }, "index": { "type": "string" } }, - "required": [ - "oldFileName", - "newFileName", - "hunks" - ] + "required": ["oldFileName", "newFileName", "hunks"] }, "encoding": { "type": "string", @@ -15793,10 +14573,7 @@ "type": "string" } }, - "required": [ - "type", - "content" - ] + "required": ["type", "content"] }, "File": { "type": "object", @@ -15816,19 +14593,10 @@ }, "status": { "type": "string", - "enum": [ - "added", - "deleted", - "modified" - ] + "enum": ["added", "deleted", "modified"] } }, - "required": [ - "path", - "added", - "removed", - "status" - ] + "required": ["path", "added", "removed", "status"] }, "MCPStatusConnected": { "type": "object", @@ -15838,9 +14606,7 @@ "const": "connected" } }, - "required": [ - "status" - ] + "required": ["status"] }, "MCPStatusDisabled": { "type": "object", @@ -15850,9 +14616,7 @@ "const": "disabled" } }, - "required": [ - "status" - ] + "required": ["status"] }, "MCPStatusFailed": { "type": "object", @@ -15865,10 +14629,7 @@ "type": "string" } }, - "required": [ - "status", - "error" - ] + "required": ["status", "error"] }, "MCPStatusNeedsAuth": { "type": "object", @@ -15878,9 +14639,7 @@ "const": "needs_auth" } }, - "required": [ - "status" - ] + "required": ["status"] }, "MCPStatusNeedsClientRegistration": { "type": "object", @@ -15893,10 +14652,7 @@ "type": "string" } }, - "required": [ - "status", - "error" - ] + "required": ["status", "error"] }, "MCPStatus": { "anyOf": [ @@ -15936,13 +14692,7 @@ "type": "string" } }, - "required": [ - "home", - "state", - "config", - "worktree", - "directory" - ] + "required": ["home", "state", "config", "worktree", "directory"] }, "VcsInfo": { "type": "object", @@ -15951,9 +14701,7 @@ "type": "string" } }, - "required": [ - "branch" - ] + "required": ["branch"] }, "Command": { "type": "object", @@ -15972,11 +14720,7 @@ }, "source": { "type": "string", - "enum": [ - "command", - "mcp", - "skill" - ] + "enum": ["command", "mcp", "skill"] }, "template": { "anyOf": [ @@ -15998,11 +14742,7 @@ } } }, - "required": [ - "name", - "template", - "hints" - ] + "required": ["name", "template", "hints"] }, "Agent": { "type": "object", @@ -16018,11 +14758,7 @@ }, "mode": { "type": "string", - "enum": [ - "subagent", - "primary", - "all" - ] + "enum": ["subagent", "primary", "all"] }, "native": { "type": "boolean" @@ -16055,10 +14791,7 @@ "type": "string" } }, - "required": [ - "modelID", - "providerID" - ] + "required": ["modelID", "providerID"] }, "variant": { "type": "string" @@ -16079,12 +14812,7 @@ "maximum": 9007199254740991 } }, - "required": [ - "name", - "mode", - "permission", - "options" - ] + "required": ["name", "mode", "permission", "options"] }, "LSPStatus": { "type": "object", @@ -16111,12 +14839,7 @@ ] } }, - "required": [ - "id", - "name", - "root", - "status" - ] + "required": ["id", "name", "root", "status"] }, "FormatterStatus": { "type": "object", @@ -16134,12 +14857,8 @@ "type": "boolean" } }, - "required": [ - "name", - "extensions", - "enabled" - ] + "required": ["name", "extensions", "enabled"] } } } -} \ No newline at end of file +} From 770887e96ceb5417f46dafdfe1494605145c0437 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Tue, 7 Apr 2026 17:05:08 +0300 Subject: [PATCH 16/29] fix(cli): improve config warning UX across CLI, TUI, and VS Code Separate verbose parse details into a new Warning.detail field so toast messages stay short while 'kilo config check' and VS Code's Show Details action expose the full diagnostic. Make the TUI config-warning toast persistent (dismissed on first prompt submit) and fire VS Code's checkConfigWarnings() independently so prior failures in the SSE handler cannot suppress it. Add 'kilo config check' subcommand that prints all warnings with color and exits non-zero. --- packages/kilo-vscode/src/KiloProvider.ts | 28 ++++++++++--- packages/opencode/src/cli/cmd/config.ts | 40 +++++++++++++++++++ .../cli/cmd/tui/component/prompt/index.tsx | 1 + .../opencode/src/cli/cmd/tui/context/sync.tsx | 4 +- .../opencode/src/cli/cmd/tui/ui/toast.tsx | 15 +++++-- packages/opencode/src/config/config.ts | 9 +++-- packages/opencode/src/index.ts | 2 + packages/sdk/js/src/v2/gen/types.gen.ts | 1 + packages/sdk/openapi.json | 3 ++ 9 files changed, 88 insertions(+), 15 deletions(-) create mode 100644 packages/opencode/src/cli/cmd/config.ts diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index 05194727b75..55c2408bb0b 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -1048,6 +1048,10 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper this.postMessage({ type: "connectionState", state }) if (state === "connected") { + // kilocode_change start - fire config warnings independently so a failure in the + // sequential await chain doesn't prevent warnings from being shown + void this.checkConfigWarnings() + // kilocode_change end try { // Profile fetch is best-effort — returns 401 when user isn't logged into gateway. const sdkClient = this.client @@ -1059,7 +1063,6 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper await this.flushPendingSessionRefresh("sse-connected") await fetchAndSendPendingPermissions(this.permissionCtx) await fetchAndSendPendingQuestions(this.questionCtx) - await this.checkConfigWarnings() // kilocode_change } catch (error) { console.error("[Kilo New] KiloProvider: ❌ Failed during connected state handling:", error) this.postMessage({ @@ -1951,7 +1954,8 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper // kilocode_change start /** - * Fetch config warnings from the server and display them as VS Code warning messages. + * Fetch config warnings from the server and display a single consolidated + * VS Code warning with a "Show Details" action button. * Only shown once per provider lifecycle (flag resets on dispose/re-create, not on SSE reconnect). */ private async checkConfigWarnings(): Promise { @@ -1962,11 +1966,23 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper const list = result?.data ?? [] if (list.length === 0) return this.configWarningsShown = true - for (const warning of list) { - vscode.window.showWarningMessage(`Config: ${warning.message}`) + + const first = list[0]! + const summary = list.length === 1 ? first.message : `${first.message} (and ${list.length - 1} more)` + + const action = await vscode.window.showWarningMessage(`Config: ${summary}`, "Show Details") + if (action === "Show Details") { + const lines = list.map((w) => { + const base = `${w.path}\n ${w.message}` + return w.detail ? `${base}\n ${w.detail}` : base + }) + const channel = vscode.window.createOutputChannel("Kilo Config Warnings") + channel.clear() + channel.appendLine(lines.join("\n\n")) + channel.show() } - } catch { - // Endpoint may not exist on older CLI versions — silently ignore + } catch (err) { + console.warn("[Kilo New] KiloProvider: checkConfigWarnings failed:", err) } } // kilocode_change end diff --git a/packages/opencode/src/cli/cmd/config.ts b/packages/opencode/src/cli/cmd/config.ts new file mode 100644 index 00000000000..746946bff81 --- /dev/null +++ b/packages/opencode/src/cli/cmd/config.ts @@ -0,0 +1,40 @@ +// kilocode_change - new file +import { EOL } from "os" +import { Config } from "../../config/config" +import { bootstrap } from "../bootstrap" +import { cmd } from "./cmd" +import { UI } from "../ui" + +export const ConfigCommand = cmd({ + command: "config", + describe: "configuration tools", + builder: (yargs) => + yargs + .command({ + command: "check", + describe: "check configuration for warnings and errors", + async handler() { + await bootstrap(process.cwd(), async () => { + const list = await Config.warnings() + if (list.length === 0) { + process.stdout.write("No config warnings." + EOL) + return + } + const S = UI.Style + for (const warning of list) { + process.stderr.write(S.TEXT_WARNING_BOLD + warning.path + S.TEXT_NORMAL + EOL) + process.stderr.write(" " + warning.message + EOL) + if (warning.detail) { + for (const line of warning.detail.split("\n")) { + process.stderr.write(" " + S.TEXT_DIM + line + S.TEXT_NORMAL + EOL) + } + } + process.stderr.write(EOL) + } + process.exitCode = 1 + }) + }, + }) + .demandCommand(), + async handler() {}, +}) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index 68660bb9ed1..f8935c321bb 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -636,6 +636,7 @@ export function Prompt(props: PromptProps) { }) .catch(() => {}) } + toast.dismiss() // kilocode_change - dismiss persistent config warning on first submit history.append({ ...store.prompt, mode: currentMode, diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx index 36119ccd7a2..aa4766bf959 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx @@ -466,7 +466,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ sdk.client.vcs.get().then((x) => setStore("vcs", reconcile(x.data))), sdk.client.path.get().then((x) => setStore("path", reconcile(x.data!))), syncWorkspaces(), - // kilocode_change start - show config warnings as toasts + // kilocode_change start - show config warnings as persistent toast sdk.client.config .warnings() .then((x) => { @@ -478,7 +478,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ title: "Config Warning", message: first.message + suffix, variant: "warning", - duration: 8000, + duration: 0, }) }) .catch(() => {}), diff --git a/packages/opencode/src/cli/cmd/tui/ui/toast.tsx b/packages/opencode/src/cli/cmd/tui/ui/toast.tsx index 36095580fb0..32b8deac14d 100644 --- a/packages/opencode/src/cli/cmd/tui/ui/toast.tsx +++ b/packages/opencode/src/cli/cmd/tui/ui/toast.tsx @@ -60,9 +60,18 @@ function init() { const { duration, ...currentToast } = parsedOptions setStore("currentToast", currentToast) if (timeoutHandle) clearTimeout(timeoutHandle) - timeoutHandle = setTimeout(() => { - setStore("currentToast", null) - }, duration).unref() + timeoutHandle = null + if (duration && duration > 0) { + timeoutHandle = setTimeout(() => { + setStore("currentToast", null) + timeoutHandle = null + }, duration).unref() + } + }, + dismiss() { + if (timeoutHandle) clearTimeout(timeoutHandle) + timeoutHandle = null + setStore("currentToast", null) }, error: (err: any) => { if (err instanceof Error) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 152fb3fe21b..36d1b550559 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -56,6 +56,7 @@ export namespace Config { export const Warning = z.object({ path: z.string(), message: z.string(), + detail: z.string().optional(), }) export type Warning = z.infer // kilocode_change end @@ -527,8 +528,8 @@ export namespace Config { if (ConfigPaths.JsonError.isInstance(err)) return { path: err.data.path, - message: - `Config file at ${err.data.path} is not valid JSON(C)` + (err.data.message ? `: ${err.data.message}` : ""), + message: `Config file at ${err.data.path} is not valid JSON(C)`, + detail: err.data.message || undefined, } if (ConfigPaths.InvalidError.isInstance(err)) { const text = err.data.issues ? detail(err.data.issues) : err.data.message @@ -560,9 +561,9 @@ export namespace Config { warnings?: Warning[], ) { const text = detail(issues) - const message = text ? `Config file at ${item} is invalid: ${text}` : `Config file at ${item} is invalid` + const message = `Config file at ${item} is invalid` const err = new InvalidError({ path: item, issues }, { cause }) - if (warnings) warnings.push({ path: item, message }) + if (warnings) warnings.push({ path: item, message, detail: text || undefined }) const { Session } = await import("@/session") Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) if (kind === "command") { diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts index 688bc25b742..0b5bc16bc36 100644 --- a/packages/opencode/src/index.ts +++ b/packages/opencode/src/index.ts @@ -15,6 +15,7 @@ import { FormatError } from "./cli/error" import { ServeCommand } from "./cli/cmd/serve" import { WorkspaceServeCommand } from "./cli/cmd/workspace-serve" import { Filesystem } from "./util/filesystem" +import { ConfigCommand as ConfigCLICommand } from "./cli/cmd/config" // kilocode_change import { DebugCommand } from "./cli/cmd/debug" import { StatsCommand } from "./cli/cmd/stats" import { McpCommand } from "./cli/cmd/mcp" @@ -195,6 +196,7 @@ let cli = yargs(hideBin(process.argv)) .command(SessionCommand) .command(RemoteCommand) // kilocode_change .command(DbCommand) + .command(ConfigCLICommand) // kilocode_change if (Installation.isLocal()) { cli = cli.command(WorkspaceServeCommand) diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index e8f9bd24bfc..44dfca17bb5 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -2479,6 +2479,7 @@ export type ConfigWarningsResponses = { 200: Array<{ path: string message: string + detail?: string }> } diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index b8192c91aa8..65994bb0b9e 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -1008,6 +1008,9 @@ }, "message": { "type": "string" + }, + "detail": { + "type": "string" } }, "required": ["path", "message"] From 5accaead16bd807aa2af4a8d894b57f70f3678b1 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Tue, 7 Apr 2026 17:57:10 +0300 Subject: [PATCH 17/29] fix(cli): include detail in validation error --- packages/opencode/src/config/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 36d1b550559..16800e74f34 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -561,7 +561,7 @@ export namespace Config { warnings?: Warning[], ) { const text = detail(issues) - const message = `Config file at ${item} is invalid` + const message = text ? `Config file at ${item} is invalid: ${text}` : `Config file at ${item} is invalid` const err = new InvalidError({ path: item, issues }, { cause }) if (warnings) warnings.push({ path: item, message, detail: text || undefined }) const { Session } = await import("@/session") From f0a30751b835c5f2dfe95506adcbca62a8e4756c Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Tue, 7 Apr 2026 17:57:34 +0300 Subject: [PATCH 18/29] fix(cli): let managed config errors propagate --- packages/opencode/src/config/config.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 16800e74f34..794e034e9d2 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -363,11 +363,7 @@ export namespace Config { // kilocode_change start for (const file of ["kilo.jsonc", "kilo.json", "opencode.jsonc", "opencode.json"]) { // kilocode_change end - try { - result = mergeConfigConcatArrays(result, await loadFile(path.join(managedDir, file))) - } catch (err) { - caught(err, path.join(managedDir, file)) - } + result = mergeConfigConcatArrays(result, await loadFile(path.join(managedDir, file))) } } From 0de8282c04377ea562998720ccd8e0aceb29bdb6 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Tue, 7 Apr 2026 17:58:24 +0300 Subject: [PATCH 19/29] style(vscode): remove kilocode_change markers --- packages/kilo-vscode/src/KiloProvider.ts | 33 +++++++++++++++++------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index 55c2408bb0b..cd1019b2a93 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -124,7 +124,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper private cachedMcpStatusMessage: unknown = null /** Ref-count of in-flight handleUpdateConfig calls; prevents fetchAndSendConfig from sending stale data */ private pending = 0 - private configWarningsShown = false // kilocode_change - prevent repeated config warnings on SSE reconnect + private configWarningsShown = false /** Cached notificationsLoaded payload */ private cachedNotificationsMessage: unknown = null private pendingReviewComments: { comments: unknown[]; autoSend: boolean }[] = [] @@ -1048,10 +1048,9 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper this.postMessage({ type: "connectionState", state }) if (state === "connected") { - // kilocode_change start - fire config warnings independently so a failure in the + // Fire config warnings independently so a failure in the // sequential await chain doesn't prevent warnings from being shown - void this.checkConfigWarnings() - // kilocode_change end + void this.checkConfigWarnings("state") try { // Profile fetch is best-effort — returns 401 when user isn't logged into gateway. const sdkClient = this.client @@ -1127,6 +1126,14 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper } this.postMessage({ type: "connectionState", state: this.connectionState }) + + // connect() can resolve after SSE reaches "connected" but before this + // provider subscribes to onStateChange(). In that case the initial + // connected callback is missed, so run the warning check here too. + if (this.connectionState === "connected") { + void this.checkConfigWarnings("init") + } + await this.syncWebviewState("initializeConnection") await this.flushPendingSessionRefresh("initializeConnection") @@ -1952,23 +1959,32 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper } } - // kilocode_change start /** * Fetch config warnings from the server and display a single consolidated * VS Code warning with a "Show Details" action button. * Only shown once per provider lifecycle (flag resets on dispose/re-create, not on SSE reconnect). */ - private async checkConfigWarnings(): Promise { - if (this.configWarningsShown || !this.client) return + private async checkConfigWarnings(from: string): Promise { + if (this.configWarningsShown) { + console.log("[Kilo New] KiloProvider: config warnings already shown", { from }) + return + } + if (!this.client) { + console.log("[Kilo New] KiloProvider: config warnings skipped (no client)", { from }) + return + } try { const dir = this.getWorkspaceDirectory() + console.log("[Kilo New] KiloProvider: checking config warnings", { from, dir }) const result = await this.client.config.warnings({ directory: dir }) const list = result?.data ?? [] + console.log("[Kilo New] KiloProvider: config warnings fetched", { from, count: list.length }) if (list.length === 0) return this.configWarningsShown = true const first = list[0]! const summary = list.length === 1 ? first.message : `${first.message} (and ${list.length - 1} more)` + console.warn("[Kilo New] KiloProvider: showing config warnings", { from, count: list.length, path: first.path }) const action = await vscode.window.showWarningMessage(`Config: ${summary}`, "Show Details") if (action === "Show Details") { @@ -1982,10 +1998,9 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper channel.show() } } catch (err) { - console.warn("[Kilo New] KiloProvider: checkConfigWarnings failed:", err) + console.warn("[Kilo New] KiloProvider: checkConfigWarnings failed:", { from, err }) } } - // kilocode_change end /** * Fetch Kilo news/notifications and send to webview. From 5d184aba94faec942e36a64aef221044f7340605 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Tue, 7 Apr 2026 18:01:37 +0300 Subject: [PATCH 20/29] style(cli): fix kilocode_change markers --- packages/opencode/src/config/config.ts | 30 +++++++++++++++++++------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 794e034e9d2..eb0e363e05a 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -221,6 +221,7 @@ export namespace Config { for (const [key, value] of Object.entries(auth)) { if (value.type === "wellknown") { const url = key.replace(/\/+$/, "") + // kilocode_change start const source = `${url}/.well-known/opencode` try { process.env[value.key] = value.token @@ -232,7 +233,7 @@ export namespace Config { const wellknown = (await response.json()) as any const remoteConfig = wellknown.config ?? {} // Add $schema to prevent load() from trying to write back to a non-existent file - if (!remoteConfig.$schema) remoteConfig.$schema = "https://app.kilo.ai/config.json" // kilocode_change + if (!remoteConfig.$schema) remoteConfig.$schema = "https://app.kilo.ai/config.json" result = mergeConfigConcatArrays( result, await load(JSON.stringify(remoteConfig), { @@ -246,6 +247,7 @@ export namespace Config { if (w) warnings.push(w) else warnings.push({ path: source, message: err instanceof Error ? err.message : String(err) }) log.warn("skipped remote config due to error", { url, err }) + // kilocode_change end } } } @@ -255,33 +257,37 @@ export namespace Config { } // Global user config overrides remote config. + // kilocode_change start try { result = mergeConfigConcatArrays(result, await global()) } catch (err) { caught(err, "global config") } + // kilocode_change end // Custom config path overrides global config. if (Flag.KILO_CONFIG) { + // kilocode_change start try { result = mergeConfigConcatArrays(result, await loadFile(Flag.KILO_CONFIG)) log.debug("loaded custom config", { path: Flag.KILO_CONFIG }) } catch (err) { caught(err, Flag.KILO_CONFIG) } + // kilocode_change end } // Project config overrides global and remote config. if (!Flag.KILO_DISABLE_PROJECT_CONFIG) { // kilocode_change start for (const file of ["kilo.jsonc", "kilo.json", "opencode.jsonc", "opencode.json"]) { - // kilocode_change end try { result = mergeConfigConcatArrays(result, await loadFile(file)) } catch (err) { caught(err, file) } } + // kilocode_change end } result.agent = result.agent || {} @@ -306,7 +312,6 @@ export namespace Config { dir === Flag.KILO_CONFIG_DIR ) { for (const file of ["kilo.jsonc", "kilo.json", "opencode.jsonc", "opencode.json"]) { - // kilocode_change end log.debug(`loading config from ${path.join(dir, file)}`) try { result = mergeConfigConcatArrays(result, await loadFile(path.join(dir, file))) @@ -318,6 +323,7 @@ export namespace Config { result.mode ??= {} result.plugin ??= [] } + // kilocode_change end } deps.push( @@ -341,6 +347,7 @@ export namespace Config { // Inline config content overrides all non-managed config sources. if (process.env.KILO_CONFIG_CONTENT) { + // kilocode_change start try { result = mergeConfigConcatArrays( result, @@ -353,6 +360,7 @@ export namespace Config { } catch (err) { caught(err, "KILO_CONFIG_CONTENT") } + // kilocode_change end } // Load managed config files last (highest priority) - enterprise admin-controlled @@ -362,9 +370,9 @@ export namespace Config { if (existsSync(managedDir)) { // kilocode_change start for (const file of ["kilo.jsonc", "kilo.json", "opencode.jsonc", "opencode.json"]) { - // kilocode_change end result = mergeConfigConcatArrays(result, await loadFile(path.join(managedDir, file))) } + // kilocode_change end } // Migrate deprecated mode field to agent field @@ -416,7 +424,7 @@ export namespace Config { config: result, directories, deps, - warnings, + warnings, // kilocode_change } } // kilocode_change start — create state from named init so resetState() can invalidate it @@ -570,7 +578,9 @@ export namespace Config { } // kilocode_change end + // kilocode_change start async function loadCommand(dir: string, warnings?: Warning[]) { + // kilocode_change end const result: Record = {} for (const item of await Glob.scan("{command,commands}/**/*.md", { cwd: dir, @@ -582,7 +592,7 @@ export namespace Config { const message = ConfigMarkdown.FrontmatterError.isInstance(err) ? err.data.message : `Failed to parse command ${item}` - if (warnings) warnings.push({ path: item, message }) + if (warnings) warnings.push({ path: item, message }) // kilocode_change const { Session } = await import("@/session") Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) log.error("failed to load command", { command: item, err }) @@ -620,7 +630,9 @@ export namespace Config { return result } + // kilocode_change start async function loadAgent(dir: string, warnings?: Warning[]) { + // kilocode_change end const result: Record = {} for (const item of await Glob.scan("{agent,agents}/**/*.md", { @@ -633,7 +645,7 @@ export namespace Config { const message = ConfigMarkdown.FrontmatterError.isInstance(err) ? err.data.message : `Failed to parse agent ${item}` - if (warnings) warnings.push({ path: item, message }) + if (warnings) warnings.push({ path: item, message }) // kilocode_change const { Session } = await import("@/session") Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) log.error("failed to load agent", { agent: item, err }) @@ -673,7 +685,9 @@ export namespace Config { return result } + // kilocode_change start async function loadMode(dir: string, warnings?: Warning[]) { + // kilocode_change end const result: Record = {} for (const item of await Glob.scan("{mode,modes}/*.md", { cwd: dir, @@ -685,7 +699,7 @@ export namespace Config { const message = ConfigMarkdown.FrontmatterError.isInstance(err) ? err.data.message : `Failed to parse mode ${item}` - if (warnings) warnings.push({ path: item, message }) + if (warnings) warnings.push({ path: item, message }) // kilocode_change const { Session } = await import("@/session") Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) log.error("failed to load mode", { mode: item, err }) From 2ca9542254bdd2aa9b29110a77e65c68c88cbc4b Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Tue, 7 Apr 2026 18:06:48 +0300 Subject: [PATCH 21/29] fix(cli): guard session import in config error handlers --- packages/opencode/src/config/config.ts | 32 ++++++++++++---- .../test/kilocode/config-resilience.test.ts | 37 +++---------------- 2 files changed, 30 insertions(+), 39 deletions(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index eb0e363e05a..f7cb7268915 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -568,8 +568,12 @@ export namespace Config { const message = text ? `Config file at ${item} is invalid: ${text}` : `Config file at ${item} is invalid` const err = new InvalidError({ path: item, issues }, { cause }) if (warnings) warnings.push({ path: item, message, detail: text || undefined }) - const { Session } = await import("@/session") - Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) + try { + const { Session } = await import("@/session") + Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) + } catch (e) { + log.warn("could not publish session error", { message, err: e }) + } if (kind === "command") { log.error("failed to load command", { command: item, err, message }) return @@ -593,8 +597,12 @@ export namespace Config { ? err.data.message : `Failed to parse command ${item}` if (warnings) warnings.push({ path: item, message }) // kilocode_change - const { Session } = await import("@/session") - Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) + try { + const { Session } = await import("@/session") + Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) + } catch (e) { + log.warn("could not publish session error", { message, err: e }) + } log.error("failed to load command", { command: item, err }) return undefined }) @@ -646,8 +654,12 @@ export namespace Config { ? err.data.message : `Failed to parse agent ${item}` if (warnings) warnings.push({ path: item, message }) // kilocode_change - const { Session } = await import("@/session") - Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) + try { + const { Session } = await import("@/session") + Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) + } catch (e) { + log.warn("could not publish session error", { message, err: e }) + } log.error("failed to load agent", { agent: item, err }) return undefined }) @@ -700,8 +712,12 @@ export namespace Config { ? err.data.message : `Failed to parse mode ${item}` if (warnings) warnings.push({ path: item, message }) // kilocode_change - const { Session } = await import("@/session") - Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) + try { + const { Session } = await import("@/session") + Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) + } catch (e) { + log.warn("could not publish session error", { message, err: e }) + } log.error("failed to load mode", { mode: item, err }) return undefined }) diff --git a/packages/opencode/test/kilocode/config-resilience.test.ts b/packages/opencode/test/kilocode/config-resilience.test.ts index 7e48d68dec9..5cb72a91385 100644 --- a/packages/opencode/test/kilocode/config-resilience.test.ts +++ b/packages/opencode/test/kilocode/config-resilience.test.ts @@ -1,6 +1,5 @@ import { afterEach, describe, expect, test } from "bun:test" import path from "path" -import { Bus } from "../../src/bus" import { Config } from "../../src/config/config" import { Instance } from "../../src/project/instance" import { Filesystem } from "../../src/util/filesystem" @@ -47,7 +46,7 @@ Valid agent prompt`, }) }) - test("publishes an error for invalid agent markdown configs", async () => { + test("reports a warning for invalid agent markdown configs", async () => { await using tmp = await tmpdir({ init: async (dir) => { await Filesystem.write( @@ -63,22 +62,10 @@ Broken agent prompt`, await Instance.provide({ directory: tmp.path, fn: async () => { - const seen: Array<{ type: string; properties: { error: { name: string; data: { message: string } } } }> = [] - const unsub = Bus.subscribeAll((event) => { - if (event.type === "session.error") seen.push(event) - }) - await Config.get() - unsub() + const warns = await Config.warnings() - expect( - seen.some( - (item) => - item.properties.error.name === "UnknownError" && - item.properties.error.data.message.includes("skip.md") && - item.properties.error.data.message.includes("mode"), - ), - ).toBe(true) + expect(warns.some((w) => w.path.includes("skip.md") && w.message.includes("mode"))).toBe(true) }, }) }) @@ -117,7 +104,7 @@ Valid command template`, }) }) - test("publishes an error for invalid command markdown configs", async () => { + test("reports a warning for invalid command markdown configs", async () => { await using tmp = await tmpdir({ init: async (dir) => { await Filesystem.write( @@ -133,22 +120,10 @@ Broken command template`, await Instance.provide({ directory: tmp.path, fn: async () => { - const seen: Array<{ type: string; properties: { error: { name: string; data: { message: string } } } }> = [] - const unsub = Bus.subscribeAll((event) => { - if (event.type === "session.error") seen.push(event) - }) - await Config.get() - unsub() + const warns = await Config.warnings() - expect( - seen.some( - (item) => - item.properties.error.name === "UnknownError" && - item.properties.error.data.message.includes("skip.md") && - item.properties.error.data.message.includes("subtask"), - ), - ).toBe(true) + expect(warns.some((w) => w.path.includes("skip.md") && w.message.includes("subtask"))).toBe(true) }, }) }) From 634cec88b141caf546f4dbd6843aaf9bbfe7e2e6 Mon Sep 17 00:00:00 2001 From: Marius Date: Wed, 8 Apr 2026 10:34:41 +0200 Subject: [PATCH 22/29] fix(vscode): show stop button during rate limit retry state (#8552) The PromptInput isBusy() check only matched 'busy' status, hiding the stop button and disabling Escape-to-abort when the session was in 'retry' state (e.g. rate limited). Changed to match any non-idle status so users can always abort. --- .../kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx b/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx index feaca6ac761..3326f9872e6 100644 --- a/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx @@ -244,7 +244,7 @@ export const PromptInput: Component = (props) => { window.addEventListener("compactSession", onCompact) onCleanup(() => window.removeEventListener("compactSession", onCompact)) - const isBusy = () => session.status() === "busy" + const isBusy = () => session.status() !== "idle" const isDisabled = () => !server.isConnected() const hasInput = () => text().trim().length > 0 || imageAttach.images().length > 0 || reviewComments().length > 0 const canSend = () => hasInput() && !isDisabled() && !props.blocked?.() From b8714986902b8704a98f47fc271eeadfe59232d1 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Wed, 8 Apr 2026 11:46:40 +0300 Subject: [PATCH 23/29] style(cli): add kilocode markers in toast --- packages/opencode/src/cli/cmd/tui/ui/toast.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/opencode/src/cli/cmd/tui/ui/toast.tsx b/packages/opencode/src/cli/cmd/tui/ui/toast.tsx index 32b8deac14d..cbe2eb8550c 100644 --- a/packages/opencode/src/cli/cmd/tui/ui/toast.tsx +++ b/packages/opencode/src/cli/cmd/tui/ui/toast.tsx @@ -60,6 +60,7 @@ function init() { const { duration, ...currentToast } = parsedOptions setStore("currentToast", currentToast) if (timeoutHandle) clearTimeout(timeoutHandle) + // kilocode_change start timeoutHandle = null if (duration && duration > 0) { timeoutHandle = setTimeout(() => { @@ -73,6 +74,7 @@ function init() { timeoutHandle = null setStore("currentToast", null) }, + // kilocode_change end error: (err: any) => { if (err instanceof Error) return toast.show({ From bb8b807dff8363be39b9411f567e618ede94d558 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Wed, 8 Apr 2026 11:46:42 +0300 Subject: [PATCH 24/29] docs(cli): add KILO_CONFIG_DIR to command paths --- packages/opencode/src/kilocode/skills/kilo-config.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opencode/src/kilocode/skills/kilo-config.md b/packages/opencode/src/kilocode/skills/kilo-config.md index 70289d08dcd..96861a41e9e 100644 --- a/packages/opencode/src/kilocode/skills/kilo-config.md +++ b/packages/opencode/src/kilocode/skills/kilo-config.md @@ -205,7 +205,7 @@ Toggle notifications, Toggle animations, Toggle diff wrapping, Toggle sidebar (` | Project | `./kilo.json`, `./.kilo/kilo.json` | | Global | `~/.config/kilo/kilo.json` | | Managed | Linux: `/etc/kilo/kilo.json`, macOS: `/Library/Application Support/kilo/kilo.json`, Windows: `%ProgramData%\kilo\kilo.json` (enterprise, highest priority) | -| Commands | `.kilo/command/*.md` (project), `~/.config/kilo/command/*.md` (global) | +| Commands | `.kilo/command/*.md` (project), `~/.config/kilo/command/*.md` (global), `KILO_CONFIG_DIR/command/*.md` if set | | Agents | `.kilo/agent/*.md` (project), `~/.config/kilo/agent/*.md` (global) | | Skills | `.kilo/skill/*/SKILL.md`, `.kilo/skills/*/SKILL.md` | | Instructions | `AGENTS.md`, `.kilo/instructions.md`, glob patterns from `instructions` | From e0e116e9ccdaae0118931312b12450e756e8f8c6 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Wed, 8 Apr 2026 11:47:05 +0300 Subject: [PATCH 25/29] docs(cli): add workflows section --- packages/opencode/src/kilocode/skills/kilo-config.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/opencode/src/kilocode/skills/kilo-config.md b/packages/opencode/src/kilocode/skills/kilo-config.md index 96861a41e9e..572d1d23018 100644 --- a/packages/opencode/src/kilocode/skills/kilo-config.md +++ b/packages/opencode/src/kilocode/skills/kilo-config.md @@ -41,6 +41,10 @@ System prompt for this agent. `mode` values: `primary` = selectable as main agent, `subagent` = only via Task tool, `all` = both. +## Workflows (legacy) + +Markdown files in `.kilo/workflows/` or `.kilocode/workflows/` (project-level) and `~/.kilo/workflows/` or `~/.kilocode/workflows/` (global). These are automatically converted to commands at startup. The filename (minus `.md`) becomes the command name. Project workflows override global ones with the same name. + ## Permissions Scalar form applies to all patterns. Object form maps glob patterns to actions. Evaluated top-to-bottom; first match wins. From b5c756ef72b1ca621167a103c39097a67cbac21f Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Wed, 8 Apr 2026 11:47:24 +0300 Subject: [PATCH 26/29] style(cli): add kilocode markers on command block --- packages/opencode/src/config/config.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index f7cb7268915..73defd0732f 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -596,7 +596,8 @@ export namespace Config { const message = ConfigMarkdown.FrontmatterError.isInstance(err) ? err.data.message : `Failed to parse command ${item}` - if (warnings) warnings.push({ path: item, message }) // kilocode_change + // kilocode_change start + if (warnings) warnings.push({ path: item, message }) try { const { Session } = await import("@/session") Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) @@ -605,6 +606,7 @@ export namespace Config { } log.error("failed to load command", { command: item, err }) return undefined + // kilocode_change end }) if (!md) continue From cef00b9df3b2408ffb9b481f5dd673c286f0eb6b Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Wed, 8 Apr 2026 11:47:50 +0300 Subject: [PATCH 27/29] style(cli): add kilocode markers on agent block --- packages/opencode/src/config/config.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 73defd0732f..4d0a6e453a8 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -655,7 +655,8 @@ export namespace Config { const message = ConfigMarkdown.FrontmatterError.isInstance(err) ? err.data.message : `Failed to parse agent ${item}` - if (warnings) warnings.push({ path: item, message }) // kilocode_change + // kilocode_change start + if (warnings) warnings.push({ path: item, message }) try { const { Session } = await import("@/session") Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) @@ -664,6 +665,7 @@ export namespace Config { } log.error("failed to load agent", { agent: item, err }) return undefined + // kilocode_change end }) if (!md) continue From 0a2c10956e1e31a0084dc0d18a2ed6a82bcb9d55 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Wed, 8 Apr 2026 11:48:10 +0300 Subject: [PATCH 28/29] style(cli): add kilocode markers on mode block --- packages/opencode/src/config/config.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 4d0a6e453a8..d4cba10d1a1 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -715,7 +715,8 @@ export namespace Config { const message = ConfigMarkdown.FrontmatterError.isInstance(err) ? err.data.message : `Failed to parse mode ${item}` - if (warnings) warnings.push({ path: item, message }) // kilocode_change + // kilocode_change start + if (warnings) warnings.push({ path: item, message }) try { const { Session } = await import("@/session") Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) @@ -724,6 +725,7 @@ export namespace Config { } log.error("failed to load mode", { mode: item, err }) return undefined + // kilocode_change end }) if (!md) continue From 49cabd99c395dcdb85e3dd228c608e6c72c48b4d Mon Sep 17 00:00:00 2001 From: Marius Date: Wed, 8 Apr 2026 10:49:23 +0200 Subject: [PATCH 29/29] feat(agent-manager): add expand/collapse all button to inline diff panel (#8562) --- .../webview-ui/agent-manager/DiffPanel.tsx | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/packages/kilo-vscode/webview-ui/agent-manager/DiffPanel.tsx b/packages/kilo-vscode/webview-ui/agent-manager/DiffPanel.tsx index 3c00d20d5cd..af67780fe9a 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/DiffPanel.tsx +++ b/packages/kilo-vscode/webview-ui/agent-manager/DiffPanel.tsx @@ -307,6 +307,11 @@ export const DiffPanel: Component = (props) => { sendAllToChat() } + const handleExpandAll = () => { + const allOpen = open().length === props.diffs.length + setOpen(allOpen ? [] : props.diffs.map((d) => d.file)) + } + const totals = createMemo(() => ({ files: props.diffs.length, additions: props.diffs.reduce((sum, diff) => sum + diff.additions, 0), @@ -354,6 +359,28 @@ export const DiffPanel: Component = (props) => {
+ 0}> + + + +