From 74b5a2b19e7d91c2270a904c50026544fee984b7 Mon Sep 17 00:00:00 2001 From: sirmacik <127441966+sirmacik@users.noreply.github.com> Date: Wed, 13 May 2026 11:25:31 +0200 Subject: [PATCH] fix(kilo-subagent): align read defaults --- docs/reference/file-formats.md | 4 +++ skills/rulesync/file-formats.md | 4 +++ src/e2e/e2e-subagents.spec.ts | 2 +- src/features/subagents/kilo-subagent.test.ts | 17 ++++----- src/features/subagents/kilo-subagent.ts | 37 +++++++++----------- 5 files changed, 34 insertions(+), 30 deletions(-) diff --git a/docs/reference/file-formats.md b/docs/reference/file-formats.md index b7a0d2705..7064985db 100644 --- a/docs/reference/file-formats.md +++ b/docs/reference/file-formats.md @@ -235,6 +235,8 @@ opencode: # for OpenCode-specific parameters permission: bash: "git diff": allow +kilo: # for Kilo-specific parameters + mode: all # (optional, defaults to "all") use "subagent" for hidden/subagent-only agents takt: # takt specific parameters (optional; emitted under .takt/facets/personas/) name: "renamed-stem" # (optional) override the emitted filename stem (no path separators or "..") --- @@ -248,6 +250,8 @@ Attention, again, you are just the planner, so though you can read any files and > **Gemini CLI note (as of 2026-04-01):** Subagents are generated to `.gemini/agents/`. To enable the agents feature, set `"experimental": { "enableAgents": true }` in your `.gemini/settings.json`. +> **Kilo note (as of 2026-05-13):** Kilo's documented default for user-defined agents is `mode: all`, which makes the agent available both as a top-level pick and as a subagent. Set `kilo.mode: subagent` to opt into hidden/subagent-only behavior. + ## `.rulesync/skills/*/SKILL.md` Example: diff --git a/skills/rulesync/file-formats.md b/skills/rulesync/file-formats.md index b7a0d2705..7064985db 100644 --- a/skills/rulesync/file-formats.md +++ b/skills/rulesync/file-formats.md @@ -235,6 +235,8 @@ opencode: # for OpenCode-specific parameters permission: bash: "git diff": allow +kilo: # for Kilo-specific parameters + mode: all # (optional, defaults to "all") use "subagent" for hidden/subagent-only agents takt: # takt specific parameters (optional; emitted under .takt/facets/personas/) name: "renamed-stem" # (optional) override the emitted filename stem (no path separators or "..") --- @@ -248,6 +250,8 @@ Attention, again, you are just the planner, so though you can read any files and > **Gemini CLI note (as of 2026-04-01):** Subagents are generated to `.gemini/agents/`. To enable the agents feature, set `"experimental": { "enableAgents": true }` in your `.gemini/settings.json`. +> **Kilo note (as of 2026-05-13):** Kilo's documented default for user-defined agents is `mode: all`, which makes the agent available both as a top-level pick and as a subagent. Set `kilo.mode: subagent` to opt into hidden/subagent-only behavior. + ## `.rulesync/skills/*/SKILL.md` Example: diff --git a/src/e2e/e2e-subagents.spec.ts b/src/e2e/e2e-subagents.spec.ts index 9d2ec1a52..5f0a6d862 100644 --- a/src/e2e/e2e-subagents.spec.ts +++ b/src/e2e/e2e-subagents.spec.ts @@ -144,7 +144,7 @@ You are a primary agent. You appear in the Tab rotation. const testDir = getTestDir(); // Kilo's documented default for user-defined agents is `all` - // (https://kilocode.ai/docs/customize/custom-modes). Rulesync must + // (https://kilo.ai/docs/customize/custom-modes). Rulesync must // emit `mode: all` when source frontmatter has no `kilo.mode`, // otherwise generated agents are hidden from Kilo's agent picker. const subagentContent = `--- diff --git a/src/features/subagents/kilo-subagent.test.ts b/src/features/subagents/kilo-subagent.test.ts index 8729953e4..552aaf89c 100644 --- a/src/features/subagents/kilo-subagent.test.ts +++ b/src/features/subagents/kilo-subagent.test.ts @@ -100,7 +100,7 @@ describe("KiloSubagent", () => { it("should build Kilo subagent with default mode 'all' when not specified", () => { // Kilo's documented default for user-defined agents is `all` (available // both as a top-level pick AND as a subagent). See: - // https://kilocode.ai/docs/customize/custom-modes + // https://kilo.ai/docs/customize/custom-modes // Previously rulesync defaulted to "subagent", which hid generated // agents from the Kilo agent picker. const rulesyncSubagent = new RulesyncSubagent({ @@ -165,10 +165,7 @@ describe("KiloSubagent", () => { expect(toolSubagent.getFrontmatter().mode).toBe("subagent"); }); - it("should honour explicit kilo.mode = 'all' override (matches default)", () => { - // Symmetry test: explicit `kilo.mode: all` matches the new default but - // is a valid explicit value. Guards against any future regression - // where the defaulting logic accidentally overrides user intent. + it("should preserve explicit kilo.mode = 'all' with other Kilo-specific fields", () => { const rulesyncSubagent = new RulesyncSubagent({ outputRoot: testDir, relativeDirPath: RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, @@ -179,6 +176,7 @@ describe("KiloSubagent", () => { description: "Explicitly all mode", kilo: { mode: "all", + temperature: 0.4, }, }, body: "Body", @@ -192,7 +190,10 @@ describe("KiloSubagent", () => { relativeDirPath: RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, }) as KiloSubagent; - expect(toolSubagent.getFrontmatter().mode).toBe("all"); + expect(toolSubagent.getFrontmatter()).toMatchObject({ + mode: "all", + temperature: 0.4, + }); }); it("should preserve primary mode for Kilo subagent", () => { @@ -268,7 +269,7 @@ Assist with any tasks`, expect(result.success).toBe(true); }); - it("should apply default mode 'subagent' when mode is omitted", async () => { + it("should apply default mode 'all' when mode is omitted", async () => { const dirPath = join(testDir, ".kilo", "agent"); const filePath = join(dirPath, "no-mode.md"); @@ -285,7 +286,7 @@ Body content`, relativeFilePath: "no-mode.md", }); - expect(subagent.getFrontmatter().mode).toBe("subagent"); + expect(subagent.getFrontmatter().mode).toBe("all"); }); it("should preserve custom mode value when explicitly set", async () => { diff --git a/src/features/subagents/kilo-subagent.ts b/src/features/subagents/kilo-subagent.ts index 65b4de4a7..0b9548d1f 100644 --- a/src/features/subagents/kilo-subagent.ts +++ b/src/features/subagents/kilo-subagent.ts @@ -1,15 +1,12 @@ import { join } from "node:path"; +import { z } from "zod/mini"; + import { ToolTarget } from "../../types/tool-targets.js"; import { formatError } from "../../utils/error.js"; import { readFileContent } from "../../utils/file.js"; import { parseFrontmatter, stringifyFrontmatter } from "../../utils/frontmatter.js"; -import { - OpenCodeStyleSubagent, - OpenCodeStyleSubagentFrontmatter, - OpenCodeStyleSubagentFrontmatterSchema, - OpenCodeStyleSubagentParams, -} from "./opencode-style-subagent.js"; +import { OpenCodeStyleSubagent, OpenCodeStyleSubagentParams } from "./opencode-style-subagent.js"; import { RulesyncSubagent } from "./rulesync-subagent.js"; import { ToolSubagent, @@ -19,8 +16,14 @@ import { ToolSubagentSettablePaths, } from "./tool-subagent.js"; -export const KiloSubagentFrontmatterSchema = OpenCodeStyleSubagentFrontmatterSchema; -export type KiloSubagentFrontmatter = OpenCodeStyleSubagentFrontmatter; +export const KiloSubagentFrontmatterSchema = z.looseObject({ + description: z.optional(z.string()), + // Kilo's documented default for user-defined agents is "all": + // available both as a top-level pick and as a subagent. + mode: z._default(z.string(), "all"), + name: z.optional(z.string()), +}); +export type KiloSubagentFrontmatter = z.infer; export type KiloSubagentParams = OpenCodeStyleSubagentParams; export class KiloSubagent extends OpenCodeStyleSubagent { @@ -47,21 +50,11 @@ export class KiloSubagent extends OpenCodeStyleSubagent { const rulesyncFrontmatter = rulesyncSubagent.getFrontmatter(); const kiloSection = rulesyncFrontmatter.kilo ?? {}; - const kiloFrontmatter: KiloSubagentFrontmatter = { + const kiloFrontmatter: KiloSubagentFrontmatter = KiloSubagentFrontmatterSchema.parse({ ...kiloSection, description: rulesyncFrontmatter.description, - // Kilo CLI's documented default for user-defined agents is "all" - // (available both as a top-level pick AND as a subagent). See - // https://kilocode.ai/docs/customize/custom-modes — "mode" reference: - // all — Available both as a top-level pick and as a subagent - // (default for user-defined agents). - // Previously this defaulted to "subagent", which hid generated - // agents from Kilo's agent picker. The explicit `kilo.mode` override - // in source frontmatter still wins, so users wanting subagent-only - // can opt in with `kilo: { mode: subagent }`. - mode: typeof kiloSection.mode === "string" ? kiloSection.mode : "all", ...(rulesyncFrontmatter.name && { name: rulesyncFrontmatter.name }), - }; + }); const body = rulesyncSubagent.getBody(); const fileContent = stringifyFrontmatter(body, kiloFrontmatter); @@ -118,15 +111,17 @@ export class KiloSubagent extends OpenCodeStyleSubagent { outputRoot = process.cwd(), relativeDirPath, relativeFilePath, + global = false, }: ToolSubagentForDeletionParams): KiloSubagent { return new KiloSubagent({ outputRoot, relativeDirPath, relativeFilePath, - frontmatter: { description: "", mode: "subagent" }, + frontmatter: { description: "", mode: "all" }, body: "", fileContent: "", validate: false, + global, }); } }