diff --git a/AGENTS.md b/AGENTS.md index d627c16..517700e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -64,9 +64,10 @@ packages/opencode/.opencode/ # Generated OpenCode output for review - For navigator-style commands, separate context loading, blocker checks, delegated execution, and final reporting into distinct workflow subsections so the control flow is easy to follow - Prefer explicit subsection names like `### Load ... Context`, `### Check Blockers`, `### Delegate ...`, and `### Mark Complete And Loop` when the command coordinates multiple phases or subagents - Treat loader tools and provided attachments as the source of truth for orchestration inputs; avoid extra exploratory commands when an existing tool result already answers the question -- Before dispatching a same-session command step, say what result should be stored and whether the workflow must stop, pause, or continue based on that result -- Use literal `` tags when the workflow must queue exact text through `session_command`; `agent` and `command` are required, and the block body is the exact rendered body to send for that command -- Do not use `` blocks in command docs; author navigator delegation with `` blocks only +- Before dispatching a same-session command step, say what delegated result should be stored and whether the workflow must stop, pause, or continue based on that result +- Use literal `` tags when the workflow must delegate exact text through `command_expansion`; `agent` and `command` are required, and the block body is the exact rendered body to send for that command +- Do not use `` blocks in command docs; author navigator delegation with `` blocks only +- Do not restate `command_expansion` or `task` mechanics inside command docs; navigator owns that execution flow - When a command can pause for approval or loop over repeated work, describe the resume condition and the exact cases that must STOP without mutating state - Use `## Additional Context` for instructions about how optional guidance, related tickets, focus areas, or other stored context should influence analysis and response formatting - Use `### Output` as the final workflow step to define the exact user-facing response shape, including placeholders for generated values @@ -107,24 +108,25 @@ $ARGUMENTS ### Delegate Planning - + Task: Task context: Additional context: - + -- Store the result as `` +- Store the delegated result as `` - STOP if planning is blocked or unusable ### Delegate Implementation - + Plan: Constraints: - + +- Store the delegated result as `` - STOP if implementation is blocked or incomplete ### Output @@ -136,13 +138,13 @@ Constraints: Example delegation rule: ```text -Before dispatching, write the exact `...` block, say what queue acknowledgement should be stored, and whether the workflow should continue or STOP based on that acknowledgement. +Before delegation, write the exact `...` block, say what delegated result should be stored, and whether the workflow should continue or STOP based on that result. ``` Example literal session command rule: ```text -Before literal command forwarding, write the exact `...` block, then call `session_command` with the rendered body, exact `agent`, and exact `command`, and say what queue acknowledgement should be stored and whether the workflow should continue or STOP based on that acknowledgement. +Before literal command forwarding, write the exact `...` block, then let navigator expand and execute it, and say what delegated result should be stored and whether the workflow should continue or STOP based on that result. ``` ## Component Authoring diff --git a/README.md b/README.md index 99235eb..2d2dce8 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Kompass keeps AI coding agents on course with token-efficient, composable workfl - Commands cover direct work (`/ask`, `/commit`, `/merge`), orchestration (`/dev`, `/ship`, `/todo`), ticket planning/sync, and PR review/shipping flows. - Agents are intentionally narrow: `worker` is generic, `planner` is no-edit planning, `navigator` owns multi-step orchestration, and `reviewer` is a no-edit review specialist. -- Structured tools keep workflows grounded in repo and GitHub state: `changes_load`, `session_command` (resolve a slash command and queue it into the current session), `pr_load`, `pr_sync`, `ticket_load`, `ticket_sync`. +- Structured tools keep workflows grounded in repo and GitHub state: `changes_load`, `command_expansion` (resolve a slash command and return the expanded prompt for immediate delegation), `pr_load`, `pr_sync`, `ticket_load`, `ticket_sync`. - Reusable command-template components live in `packages/core/components/` and are documented in the components reference. ## Installation diff --git a/kompass.jsonc b/kompass.jsonc index 3c1ebf8..8b8a333 100644 --- a/kompass.jsonc +++ b/kompass.jsonc @@ -41,7 +41,7 @@ "tools": { "changes_load": { "enabled": true }, - "session_command": { "enabled": true }, + "command_expansion": { "enabled": true }, "pr_load": { "enabled": true }, "pr_sync": { "enabled": true }, "ticket_load": { "enabled": true }, diff --git a/kompass.schema.json b/kompass.schema.json index 60cbee5..aed7d8d 100644 --- a/kompass.schema.json +++ b/kompass.schema.json @@ -184,7 +184,7 @@ "changes_load": { "$ref": "#/$defs/toolConfig" }, - "session_command": { + "command_expansion": { "$ref": "#/$defs/toolConfig" }, "pr_load": { @@ -361,7 +361,7 @@ "type": "string", "enum": [ "changes_load", - "session_command", + "command_expansion", "pr_load", "pr_sync", "ticket_sync", diff --git a/packages/core/agents/navigator.md b/packages/core/agents/navigator.md index d8db957..3028d2d 100644 --- a/packages/core/agents/navigator.md +++ b/packages/core/agents/navigator.md @@ -8,14 +8,22 @@ You coordinate structured, multi-step workflows. - Execute required user-interaction steps exactly as the command defines them; if a required interaction tool is unavailable, use the command's non-interactive fallback. - If a step is blocked, incomplete, or fails, stop and report it clearly. -## Session Commands - -- Treat each `...` block as literal input. -- Render variables, then call `session_command` with `command` set to the tag value, `body` set to the rendered block body, and `agent` set to the tag value. -- `session_command` queues the next same-session user turn and returns immediately; it does not wait for the queued command result. -- Do not rewrite or interpret the block body; preserve line breaks and ordering. -- Run `session_command` blocks sequentially unless the workflow clearly makes them independent. -- If a `session_command` block is malformed, report it as invalid and continue with remaining valid blocks when safe. +## Delegation + +When you see a `...` block, you MUST make TWO tool calls in sequence: + +1. **Expand**: Call `command_expansion` with `command` from the tag and `body` set to the rendered block content +2. **Delegate**: IMMEDIATELY call `task` with `subagent_type: AGENT_NAME` and `prompt` set to the expanded text from step 1 + +**CRITICAL RULES:** +- These are TWO SEPARATE tool calls. You must call BOTH. +- DO NOT execute the expanded content yourself. Your job is to DELEGATE via `task`. +- The `task` result IS the delegated result. Use it as the source of truth. +- If you don't call `task`, the delegation is incomplete and will fail. + +- Treat each `` block as literal input; do not rewrite or interpret before expansion. +- Run `` blocks sequentially unless the workflow clearly makes them independent. +- If a `` block is malformed, the expansion fails, or the delegated `task` fails, stop and report it clearly. ## Output diff --git a/packages/core/commands/branch.md b/packages/core/commands/branch.md index c3f0e51..34908d6 100644 --- a/packages/core/commands/branch.md +++ b/packages/core/commands/branch.md @@ -28,7 +28,7 @@ $ARGUMENTS ### Check Blockers - If `` contains no files, STOP and report that there is nothing to branch from -- If `` already starts with a conventional work-branch category such as `feature/`, `fix/`, `refactor/`, `docs/`, `test/`, `chore/`, `feat/`, `bugfix/`, `hotfix/`, `perf/`, `build/`, or `ci/`, STOP and report that branching is being skipped because the current branch already looks like a work branch +- If `` already starts with a conventional work-branch category such as `feature/`, `fix/`, `refactor/`, `docs/`, `test/`, `chore/`, `feat/`, `bugfix/`, `hotfix/`, `perf/`, `build/`, or `ci/`, STOP and report that branching was skipped because the current branch already looks like a work branch ### Create Branch @@ -51,7 +51,9 @@ No additional steps are required. If branching is skipped because the current branch already looks like a work branch, display: ``` -Already on work branch: +Branching skipped because the current branch already looks like a work branch. + +Current branch: No additional steps are required. ``` diff --git a/packages/core/commands/ship.md b/packages/core/commands/ship.md index fa511f1..edae165 100644 --- a/packages/core/commands/ship.md +++ b/packages/core/commands/ship.md @@ -4,7 +4,7 @@ Ship the current work by dispatching branch creation, commit creation, and PR cr ## Additional Context -Use `` to steer dispatched branch naming. Use `` to refine the dispatched commit and PR summaries. Pass `` through to PR creation when it was provided. This command is session-command-first: send each `` body literally through `session_command` and use the result as the source of truth for the next step. +Use `` to steer delegated branch naming. Use `` to refine the delegated commit and PR summaries. Pass `` through to PR creation when it was provided. ## Workflow @@ -21,38 +21,37 @@ $ARGUMENTS - If the trimmed `` is only a branch reference (for example `main` or `origin/develop`), store it as `` and leave the context fields empty - Otherwise, store `` as both `` and `` -### Ensure Feature Branch +### Delegate Branch Creation - + Branch naming guidance: - + -- Store the dispatch result as `` +- Store the delegated result as `` +- If `` says there was nothing to branch from, continue on the current branch +- If `` says branching was skipped because the current branch already looks like a work branch, continue on the current branch - If `` is blocked or incomplete, STOP and report the branch blocker -- If `` says there was nothing to branch from, continue without changing branches - Otherwise, continue with the created branch ### Delegate Commit - + Additional context: - - -- Store the dispatch result as `` + +- Store the delegated result as `` - If `` says there was nothing to commit, continue without creating a new commit - If `` is blocked or incomplete, STOP and report the commit blocker - Otherwise, continue with the created commit ### Delegate PR Creation - + Base branch: Additional context: - - -- Store the dispatch result as `` + +- Store the delegated result as `` - If `` is blocked or incomplete, STOP and report the PR blocker - If `` says there is nothing to include in a PR, STOP and report that there is nothing to ship - Otherwise, continue with the created or existing PR diff --git a/packages/core/commands/ticket/dev.md b/packages/core/commands/ticket/dev.md index 007290a..a5d6e6e 100644 --- a/packages/core/commands/ticket/dev.md +++ b/packages/core/commands/ticket/dev.md @@ -4,7 +4,7 @@ Implement a ticket by orchestrating development, branching, commit-and-push, and ## Additional Context -Use `` to refine scope, sequencing, and tradeoffs across the dispatched `/dev`, `/branch`, `/commit-and-push`, and `/pr/create` steps. +Use `` to refine scope, sequencing, and tradeoffs across the delegated `/dev`, `/branch`, `/commit-and-push`, and `/pr/create` steps. ## Workflow @@ -31,55 +31,47 @@ $ARGUMENTS ### Delegate Implementation -- Before continuing, send the exact `session_command` block below through `session_command` - - + Ticket reference: Ticket context: Additional context: - + -- Store the result as `` +- Store the delegated result as `` - If `` is blocked or incomplete, STOP and report the implementation blocker ### Delegate Branch Creation -- Before continuing, send the exact `session_command` block below through `session_command` - - + Branch naming guidance: Additional context: - + -- Store the result as `` +- Store the delegated result as `` - If `` is blocked or incomplete, STOP and report the branch blocker - If `` says branching was skipped because the current branch already looks like a work branch, continue ### Delegate Commit And Push -- Before continuing, send the exact `session_command` block below through `session_command` - - + Ticket reference: Ticket summary: Additional context: - + -- Store the result as `` +- Store the delegated result as `` - If `` is blocked or incomplete, STOP and report the commit or push blocker - If `` says there was nothing to commit or push, continue to PR creation so already-committed branch work can still be shipped ### Delegate PR Creation -- Before continuing, send the exact `session_command` block below through `session_command` - - + Ticket reference: Ticket context: Additional context: - + -- Store the result as `` +- Store the delegated result as `` - If `` is blocked or incomplete, STOP and report the PR blocker - Otherwise, continue and store the resulting PR URL as `` diff --git a/packages/core/commands/todo.md b/packages/core/commands/todo.md index f4b68f2..5692348 100644 --- a/packages/core/commands/todo.md +++ b/packages/core/commands/todo.md @@ -8,6 +8,7 @@ Work through a todo file one pending item at a time by planning, getting approva - Do not merge separate todo items unless the file explicitly frames them as one task - If implementation reveals scope that materially changes the approved plan, pause and re-plan before marking the task complete - Use `` to prioritize tradeoffs, constraints, or validation expectations during planning and implementation +- Delegate planner and worker steps through literal `` blocks and use the delegated results as the source of truth for planning, implementation, and commit steps. ## Workflow @@ -41,14 +42,13 @@ $ARGUMENTS ### Delegate Planning - + Task: Task context: Additional context: - + -- Ask the planner for a concise implementation plan with clear scope, risks, and validation steps -- Store the result as `` +- Store the delegated result as `` - If the planner is blocked or cannot produce a usable plan, store the blocker as ``, then STOP and report that planning blocker ### Review Plan With User @@ -62,39 +62,42 @@ Additional context: - `Revise` - update the plan based on feedback - custom answers enabled so the user can provide specific plan changes - If the user requests changes, store that feedback as `` - +- Only run the revised planning block below when the user requests changes +- If the user approves the current plan, skip the revised planning block and continue to implementation + + Task: Task context: Current plan: Plan feedback: Additional context: - + -- Store the revised result as `` and continue the review loop +- Store the revised delegated result as `` and continue the review loop - If the revised planner result is blocked or unusable, store that blocker as ``, then STOP and report it before continuing the review loop - Repeat this review step until the user approves or stops - If the user does not approve implementation, store `plan approval not granted` as ``, then STOP without changing `` ### Delegate Implementation - + Plan: Task: Task context: Additional context: - + -- Store the dispatch result as `` +- Store the delegated result as `` - If `` is incomplete, blocked, or fails validation, store the issue as ``, then STOP and report it without marking the task complete ### Delegate Commit - + Task: Additional context: - + -- Store the dispatch result as `` +- Store the delegated result as `` - If `` does not succeed, store the commit status as ``, then STOP and report it without marking the task complete ### Mark Complete And Loop diff --git a/packages/core/index.ts b/packages/core/index.ts index 8518e3c..a91d9da 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -17,8 +17,8 @@ export type { } from "./lib/config.ts"; export { createTools } from "./tools/index.ts"; export { createChangesLoadTool } from "./tools/changes-load.ts"; -export { createSessionCommandTool, resolveSessionCommand } from "./tools/dispatch.ts"; -export type { SessionCommandResolution } from "./tools/dispatch.ts"; +export { createCommandExpansionTool, resolveCommandExpansion } from "./tools/dispatch.ts"; +export type { CommandExpansion } from "./tools/dispatch.ts"; export { createPrLoadTool } from "./tools/pr-load.ts"; export { createPrSyncTool } from "./tools/pr-sync.ts"; export { createTicketLoadTool } from "./tools/ticket-load.ts"; diff --git a/packages/core/kompass.jsonc b/packages/core/kompass.jsonc index 3c1ebf8..8b8a333 100644 --- a/packages/core/kompass.jsonc +++ b/packages/core/kompass.jsonc @@ -41,7 +41,7 @@ "tools": { "changes_load": { "enabled": true }, - "session_command": { "enabled": true }, + "command_expansion": { "enabled": true }, "pr_load": { "enabled": true }, "pr_sync": { "enabled": true }, "ticket_load": { "enabled": true }, diff --git a/packages/core/lib/config.ts b/packages/core/lib/config.ts index 9e2332f..b07e7a1 100644 --- a/packages/core/lib/config.ts +++ b/packages/core/lib/config.ts @@ -14,7 +14,7 @@ export interface AgentDefinition { export const DEFAULT_TOOL_NAMES = [ "changes_load", - "session_command", + "command_expansion", "pr_load", "pr_sync", "ticket_sync", @@ -117,7 +117,7 @@ export interface KompassConfig { }; tools?: { changes_load?: ToolConfig; - session_command?: ToolConfig; + command_expansion?: ToolConfig; pr_load?: ToolConfig; pr_sync?: ToolConfig; ticket_sync?: ToolConfig; @@ -165,7 +165,7 @@ export interface MergedKompassConfig { }; tools: { changes_load: ToolConfig; - session_command: ToolConfig; + command_expansion: ToolConfig; pr_load: ToolConfig; pr_sync: ToolConfig; ticket_sync: ToolConfig; @@ -448,7 +448,7 @@ const defaultComponentPaths: Record = { const defaultToolConfig: Record = { changes_load: { enabled: true }, - session_command: { enabled: true }, + command_expansion: { enabled: true }, pr_load: { enabled: true }, pr_sync: { enabled: true }, ticket_sync: { enabled: true }, @@ -566,9 +566,9 @@ export function mergeWithDefaults( }, tools: { changes_load: { ...defaultToolConfig.changes_load, ...config?.tools?.changes_load }, - session_command: { - ...defaultToolConfig.session_command, - ...config?.tools?.session_command, + command_expansion: { + ...defaultToolConfig.command_expansion, + ...config?.tools?.command_expansion, }, pr_load: { ...defaultToolConfig.pr_load, ...config?.tools?.pr_load }, pr_sync: { ...defaultToolConfig.pr_sync, ...config?.tools?.pr_sync }, diff --git a/packages/core/test/dispatch.test.ts b/packages/core/test/dispatch.test.ts index 35bbdbd..0500872 100644 --- a/packages/core/test/dispatch.test.ts +++ b/packages/core/test/dispatch.test.ts @@ -3,49 +3,45 @@ import assert from "node:assert/strict"; import os from "node:os"; import path from "node:path"; -import { resolveSessionCommand } from "../index.ts"; +import { resolveCommandExpansion } from "../index.ts"; process.env.HOME = path.join(os.tmpdir(), `kompass-test-home-${process.pid}-core-dispatch`); -describe("resolveSessionCommand", () => { - test("expands known slash commands and infers their default agent", async () => { - const result = await resolveSessionCommand(process.cwd(), { command: "review", body: "auth bug" }); +describe("resolveCommandExpansion", () => { + test("expands known slash commands", async () => { + const result = await resolveCommandExpansion(process.cwd(), { command: "review", body: "auth bug" }); - assert.equal(result.agent, "reviewer"); assert.equal(result.command, "review"); assert.equal(result.body, "auth bug"); assert.equal(result.expanded, true); assert.match(result.prompt, /\s*auth bug\s*<\/arguments>/); }); - test("preserves explicit agent routing when present", async () => { - const result = await resolveSessionCommand(process.cwd(), { - agent: "planner", + test("expands planner commands without carrying routing state", async () => { + const result = await resolveCommandExpansion(process.cwd(), { command: "ticket/plan", body: "auth bug", }); - assert.equal(result.agent, "planner"); assert.equal(result.command, "ticket/plan"); assert.equal(result.body, "auth bug"); assert.equal(result.expanded, true); }); - test("allows a dispatch-tag agent override at tool level", async () => { - const { createSessionCommandTool } = await import("../index.ts"); - const tool = createSessionCommandTool(process.cwd()); + test("returns the expanded prompt at tool level", async () => { + const { createCommandExpansionTool } = await import("../index.ts"); + const tool = createCommandExpansionTool(process.cwd()); const output = await tool.execute( - { command: "review", body: "auth bug", agent: "worker" }, + { command: "review", body: "auth bug" }, { worktree: process.cwd(), directory: process.cwd() }, ); - const result = JSON.parse(output); - assert.equal(result.agent, "worker"); - assert.equal(result.command, "review"); + assert.match(output, /## Goal/); + assert.match(output, /auth bug/); }); test("keeps unknown slash commands dispatchable without expansion", async () => { - const result = await resolveSessionCommand(process.cwd(), { command: "unknown", body: "auth bug" }); + const result = await resolveCommandExpansion(process.cwd(), { command: "unknown", body: "auth bug" }); assert.deepEqual(result, { command: "unknown", @@ -57,7 +53,7 @@ describe("resolveSessionCommand", () => { test("rejects missing commands", async () => { await assert.rejects( - resolveSessionCommand(process.cwd(), { command: " ", body: "auth bug" }), + resolveCommandExpansion(process.cwd(), { command: " ", body: "auth bug" }), /requires a command/, ); }); diff --git a/packages/core/tools/dispatch.ts b/packages/core/tools/dispatch.ts index 0a3d531..db37a27 100644 --- a/packages/core/tools/dispatch.ts +++ b/packages/core/tools/dispatch.ts @@ -1,22 +1,20 @@ import { resolveCommands } from "../commands/index.ts"; -import { stringifyJson, type ToolDefinition, type ToolExecutionContext } from "./shared.ts"; +import type { ToolDefinition, ToolExecutionContext } from "./shared.ts"; -export type SessionCommandResolution = { - agent?: string; +export type CommandExpansion = { command: string; body: string; prompt: string; expanded: boolean; }; -type ResolveSessionCommandOptions = { +type ResolveCommandExpansionOptions = { rewriteBody?: (body: string) => string; }; -type SessionCommandInput = { +type CommandExpansionInput = { command: string; body?: string; - agent?: string; }; function renderSlashCommand(command: string, body: string) { @@ -37,16 +35,16 @@ function expandCommandTemplate(template: string, commandBody: string) { return expandedTemplate; } -export async function resolveSessionCommand( +export async function resolveCommandExpansion( projectRoot: string, - input: SessionCommandInput, - options?: ResolveSessionCommandOptions, -): Promise { + input: CommandExpansionInput, + options?: ResolveCommandExpansionOptions, +): Promise { const normalizedCommand = input.command.trim(); const normalizedBody = input.body?.trim() ?? ""; if (!normalizedCommand) { - throw new Error("session_command requires a command"); + throw new Error("command_expansion requires a command"); } const commands = await resolveCommands(projectRoot); @@ -54,7 +52,6 @@ export async function resolveSessionCommand( if (!definition) { return { - ...(input.agent?.trim() ? { agent: input.agent.trim() } : {}), command: normalizedCommand, body: normalizedBody, prompt: renderSlashCommand(normalizedCommand, normalizedBody), @@ -69,7 +66,6 @@ export async function resolveSessionCommand( } return { - agent: input.agent?.trim() || definition.agent, command: normalizedCommand, body: normalizedBody, prompt, @@ -77,12 +73,12 @@ export async function resolveSessionCommand( }; } -export function createSessionCommandTool( +export function createCommandExpansionTool( projectRoot: string, - options?: ResolveSessionCommandOptions, + options?: ResolveCommandExpansionOptions, ) { return { - description: "Resolve a command and body for same-session queuing", + description: "Expand a delegated command body into a runnable prompt", args: { command: { type: "string", @@ -91,19 +87,15 @@ export function createSessionCommandTool( body: { type: "string", optional: true, - description: "Literal body content from the session_command block", - }, - agent: { - type: "string", - optional: true, - description: "Optional agent override from the session_command tag", + description: "Literal body content from the delegate block", }, }, async execute( - args: { command: string; body?: string; agent?: string }, + args: { command: string; body?: string }, _ctx: ToolExecutionContext, ) { - return stringifyJson(await resolveSessionCommand(projectRoot, args, options)); + const resolved = await resolveCommandExpansion(projectRoot, args, options); + return resolved.prompt; }, - } satisfies ToolDefinition<{ command: string; body?: string; agent?: string }>; + } satisfies ToolDefinition<{ command: string; body?: string }>; } diff --git a/packages/core/tools/index.ts b/packages/core/tools/index.ts index ffaf5b3..e61fe87 100644 --- a/packages/core/tools/index.ts +++ b/packages/core/tools/index.ts @@ -5,7 +5,7 @@ import { mergeWithDefaults, } from "../lib/config.ts"; import { createChangesLoadTool } from "./changes-load.ts"; -import { createSessionCommandTool } from "./dispatch.ts"; +import { createCommandExpansionTool } from "./dispatch.ts"; import { createPrLoadTool } from "./pr-load.ts"; import { createPrSyncTool } from "./pr-sync.ts"; import { createTicketLoadTool } from "./ticket-load.ts"; @@ -14,7 +14,7 @@ import type { Shell, ToolDefinition } from "./shared.ts"; const toolCreators: Record ToolDefinition> = { changes_load: ($) => createChangesLoadTool($), - session_command: (_, projectRoot) => createSessionCommandTool(projectRoot), + command_expansion: (_, projectRoot) => createCommandExpansionTool(projectRoot), pr_load: ($) => createPrLoadTool($), pr_sync: ($) => createPrSyncTool($), ticket_sync: ($) => createTicketSyncTool($), diff --git a/packages/opencode/.opencode/agents/navigator.md b/packages/opencode/.opencode/agents/navigator.md index 285c431..61b5af0 100644 --- a/packages/opencode/.opencode/agents/navigator.md +++ b/packages/opencode/.opencode/agents/navigator.md @@ -18,14 +18,22 @@ You coordinate structured, multi-step workflows. - Execute required user-interaction steps exactly as the command defines them; if a required interaction tool is unavailable, use the command's non-interactive fallback. - If a step is blocked, incomplete, or fails, stop and report it clearly. -## Session Commands - -- Treat each `...` block as literal input. -- Render variables, then call `kompass_session_command` with `command` set to the tag value, `body` set to the rendered block body, and `agent` set to the tag value. -- `kompass_session_command` queues the next same-session user turn and returns immediately; it does not wait for the queued command result. -- Do not rewrite or interpret the block body; preserve line breaks and ordering. -- Run `kompass_session_command` blocks sequentially unless the workflow clearly makes them independent. -- If a `kompass_session_command` block is malformed, report it as invalid and continue with remaining valid blocks when safe. +## Delegation + +When you see a `...` block, you MUST make TWO tool calls in sequence: + +1. **Expand**: Call `kompass_command_expansion` with `command` from the tag and `body` set to the rendered block content +2. **Delegate**: IMMEDIATELY call `task` with `subagent_type: AGENT_NAME` and `prompt` set to the expanded text from step 1 + +**CRITICAL RULES:** +- These are TWO SEPARATE tool calls. You must call BOTH. +- DO NOT execute the expanded content yourself. Your job is to DELEGATE via `task`. +- The `task` result IS the delegated result. Use it as the source of truth. +- If you don't call `task`, the delegation is incomplete and will fail. + +- Treat each `` block as literal input; do not rewrite or interpret before expansion. +- Run `` blocks sequentially unless the workflow clearly makes them independent. +- If a `` block is malformed, the expansion fails, or the delegated `task` fails, stop and report it clearly. ## Output diff --git a/packages/opencode/.opencode/commands/branch.md b/packages/opencode/.opencode/commands/branch.md index 239459a..9d4b7ac 100644 --- a/packages/opencode/.opencode/commands/branch.md +++ b/packages/opencode/.opencode/commands/branch.md @@ -46,7 +46,7 @@ $ARGUMENTS ### Check Blockers - If `` contains no files, STOP and report that there is nothing to branch from -- If `` already starts with a conventional work-branch category such as `feature/`, `fix/`, `refactor/`, `docs/`, `test/`, `chore/`, `feat/`, `bugfix/`, `hotfix/`, `perf/`, `build/`, or `ci/`, STOP and report that branching is being skipped because the current branch already looks like a work branch +- If `` already starts with a conventional work-branch category such as `feature/`, `fix/`, `refactor/`, `docs/`, `test/`, `chore/`, `feat/`, `bugfix/`, `hotfix/`, `perf/`, `build/`, or `ci/`, STOP and report that branching was skipped because the current branch already looks like a work branch ### Create Branch @@ -69,7 +69,9 @@ No additional steps are required. If branching is skipped because the current branch already looks like a work branch, display: ``` -Already on work branch: +Branching skipped because the current branch already looks like a work branch. + +Current branch: No additional steps are required. ``` diff --git a/packages/opencode/.opencode/commands/ship.md b/packages/opencode/.opencode/commands/ship.md index 99aad77..dee2dd6 100644 --- a/packages/opencode/.opencode/commands/ship.md +++ b/packages/opencode/.opencode/commands/ship.md @@ -9,7 +9,7 @@ Ship the current work by dispatching branch creation, commit creation, and PR cr ## Additional Context -Use `` to steer dispatched branch naming. Use `` to refine the dispatched commit and PR summaries. Pass `` through to PR creation when it was provided. This command is session-command-first: send each `` body literally through `kompass_session_command` and use the result as the source of truth for the next step. +Use `` to steer delegated branch naming. Use `` to refine the delegated commit and PR summaries. Pass `` through to PR creation when it was provided. ## Workflow @@ -26,38 +26,37 @@ $ARGUMENTS - If the trimmed `` is only a branch reference (for example `main` or `origin/develop`), store it as `` and leave the context fields empty - Otherwise, store `` as both `` and `` -### Ensure Feature Branch +### Delegate Branch Creation - + Branch naming guidance: - + -- Store the dispatch result as `` +- Store the delegated result as `` +- If `` says there was nothing to branch from, continue on the current branch +- If `` says branching was skipped because the current branch already looks like a work branch, continue on the current branch - If `` is blocked or incomplete, STOP and report the branch blocker -- If `` says there was nothing to branch from, continue without changing branches - Otherwise, continue with the created branch ### Delegate Commit - + Additional context: - - -- Store the dispatch result as `` + +- Store the delegated result as `` - If `` says there was nothing to commit, continue without creating a new commit - If `` is blocked or incomplete, STOP and report the commit blocker - Otherwise, continue with the created commit ### Delegate PR Creation - + Base branch: Additional context: - - -- Store the dispatch result as `` + +- Store the delegated result as `` - If `` is blocked or incomplete, STOP and report the PR blocker - If `` says there is nothing to include in a PR, STOP and report that there is nothing to ship - Otherwise, continue with the created or existing PR diff --git a/packages/opencode/.opencode/commands/ticket/dev.md b/packages/opencode/.opencode/commands/ticket/dev.md index ad7d220..41eb77f 100644 --- a/packages/opencode/.opencode/commands/ticket/dev.md +++ b/packages/opencode/.opencode/commands/ticket/dev.md @@ -9,7 +9,7 @@ Implement a ticket by orchestrating development, branching, commit-and-push, and ## Additional Context -Use `` to refine scope, sequencing, and tradeoffs across the dispatched `/dev`, `/branch`, `/commit-and-push`, and `/pr/create` steps. +Use `` to refine scope, sequencing, and tradeoffs across the delegated `/dev`, `/branch`, `/commit-and-push`, and `/pr/create` steps. ## Workflow @@ -46,55 +46,47 @@ $ARGUMENTS ### Delegate Implementation -- Before continuing, send the exact `kompass_session_command` block below through `kompass_session_command` - - + Ticket reference: Ticket context: Additional context: - + -- Store the result as `` +- Store the delegated result as `` - If `` is blocked or incomplete, STOP and report the implementation blocker ### Delegate Branch Creation -- Before continuing, send the exact `kompass_session_command` block below through `kompass_session_command` - - + Branch naming guidance: Additional context: - + -- Store the result as `` +- Store the delegated result as `` - If `` is blocked or incomplete, STOP and report the branch blocker - If `` says branching was skipped because the current branch already looks like a work branch, continue ### Delegate Commit And Push -- Before continuing, send the exact `kompass_session_command` block below through `kompass_session_command` - - + Ticket reference: Ticket summary: Additional context: - + -- Store the result as `` +- Store the delegated result as `` - If `` is blocked or incomplete, STOP and report the commit or push blocker - If `` says there was nothing to commit or push, continue to PR creation so already-committed branch work can still be shipped ### Delegate PR Creation -- Before continuing, send the exact `kompass_session_command` block below through `kompass_session_command` - - + Ticket reference: Ticket context: Additional context: - + -- Store the result as `` +- Store the delegated result as `` - If `` is blocked or incomplete, STOP and report the PR blocker - Otherwise, continue and store the resulting PR URL as `` diff --git a/packages/opencode/.opencode/commands/todo.md b/packages/opencode/.opencode/commands/todo.md index 62274b1..b315678 100644 --- a/packages/opencode/.opencode/commands/todo.md +++ b/packages/opencode/.opencode/commands/todo.md @@ -13,6 +13,7 @@ Work through a todo file one pending item at a time by planning, getting approva - Do not merge separate todo items unless the file explicitly frames them as one task - If implementation reveals scope that materially changes the approved plan, pause and re-plan before marking the task complete - Use `` to prioritize tradeoffs, constraints, or validation expectations during planning and implementation +- Delegate planner and worker steps through literal `` blocks and use the delegated results as the source of truth for planning, implementation, and commit steps. ## Workflow @@ -46,14 +47,13 @@ $ARGUMENTS ### Delegate Planning - + Task: Task context: Additional context: - + -- Ask the planner for a concise implementation plan with clear scope, risks, and validation steps -- Store the result as `` +- Store the delegated result as `` - If the planner is blocked or cannot produce a usable plan, store the blocker as ``, then STOP and report that planning blocker ### Review Plan With User @@ -67,39 +67,42 @@ Additional context: - `Revise` - update the plan based on feedback - custom answers enabled so the user can provide specific plan changes - If the user requests changes, store that feedback as `` - +- Only run the revised planning block below when the user requests changes +- If the user approves the current plan, skip the revised planning block and continue to implementation + + Task: Task context: Current plan: Plan feedback: Additional context: - + -- Store the revised result as `` and continue the review loop +- Store the revised delegated result as `` and continue the review loop - If the revised planner result is blocked or unusable, store that blocker as ``, then STOP and report it before continuing the review loop - Repeat this review step until the user approves or stops - If the user does not approve implementation, store `plan approval not granted` as ``, then STOP without changing `` ### Delegate Implementation - + Plan: Task: Task context: Additional context: - + -- Store the dispatch result as `` +- Store the delegated result as `` - If `` is incomplete, blocked, or fails validation, store the issue as ``, then STOP and report it without marking the task complete ### Delegate Commit - + Task: Additional context: - + -- Store the dispatch result as `` +- Store the delegated result as `` - If `` does not succeed, store the commit status as ``, then STOP and report it without marking the task complete ### Mark Complete And Loop diff --git a/packages/opencode/.opencode/kompass.jsonc b/packages/opencode/.opencode/kompass.jsonc index bbbd650..f24c886 100644 --- a/packages/opencode/.opencode/kompass.jsonc +++ b/packages/opencode/.opencode/kompass.jsonc @@ -83,7 +83,7 @@ "changes_load": { "enabled": true }, - "session_command": { + "command_expansion": { "enabled": true }, "pr_load": { diff --git a/packages/opencode/README.md b/packages/opencode/README.md index 99235eb..2d2dce8 100644 --- a/packages/opencode/README.md +++ b/packages/opencode/README.md @@ -20,7 +20,7 @@ Kompass keeps AI coding agents on course with token-efficient, composable workfl - Commands cover direct work (`/ask`, `/commit`, `/merge`), orchestration (`/dev`, `/ship`, `/todo`), ticket planning/sync, and PR review/shipping flows. - Agents are intentionally narrow: `worker` is generic, `planner` is no-edit planning, `navigator` owns multi-step orchestration, and `reviewer` is a no-edit review specialist. -- Structured tools keep workflows grounded in repo and GitHub state: `changes_load`, `session_command` (resolve a slash command and queue it into the current session), `pr_load`, `pr_sync`, `ticket_load`, `ticket_sync`. +- Structured tools keep workflows grounded in repo and GitHub state: `changes_load`, `command_expansion` (resolve a slash command and return the expanded prompt for immediate delegation), `pr_load`, `pr_sync`, `ticket_load`, `ticket_sync`. - Reusable command-template components live in `packages/core/components/` and are documented in the components reference. ## Installation diff --git a/packages/opencode/index.ts b/packages/opencode/index.ts index 70d4504..f32f1a7 100644 --- a/packages/opencode/index.ts +++ b/packages/opencode/index.ts @@ -3,7 +3,7 @@ import { tool, type ToolDefinition } from "@opencode-ai/plugin/tool"; import { createChangesLoadTool, - createSessionCommandTool, + createCommandExpansionTool, createPrLoadTool, createPrSyncTool, createTicketLoadTool, @@ -72,37 +72,6 @@ async function logObservedFailure( }); } -function getSessionError(result: unknown) { - if (!result || typeof result !== "object") return undefined; - if (!("error" in result)) return undefined; - return (result as { error?: unknown }).error; -} - -async function executeSessionCommand( - client: PluginInput["client"], - context: { sessionID: string; directory: string }, - sessionCommand: { agent?: string; command: string; body: string; prompt: string; expanded: boolean }, -) { - const result = await client.session.promptAsync({ - path: { id: context.sessionID }, - query: { directory: context.directory }, - body: { - ...(sessionCommand.agent ? { agent: sessionCommand.agent } : {}), - // Queue the delegated turn without surfacing the prompt as a normal user message. - parts: [{ type: "text", text: sessionCommand.prompt, synthetic: true }], - }, - }); - - const error = getSessionError(result); - if (error) { - throw new Error(`Session command enqueue failed: ${JSON.stringify(error)}`); - } - - return { - mode: "prompt_async", - }; -} - export async function getTaskToolExecution( input: ToolExecuteBeforeInput, output: ToolExecuteBeforeOutput, @@ -174,47 +143,32 @@ const opencodeToolCreators: Record = { execute: (args, context) => definition.execute(args, context), }); }, - session_command(_: PluginInput["$"], client: PluginInput["client"], config: MergedKompassConfig, projectRoot: string) { + command_expansion(_: PluginInput["$"], _client: PluginInput["client"], config: MergedKompassConfig, projectRoot: string) { const configuredToolNames = Object.fromEntries( getEnabledToolNames(config.tools).map((toolName) => [ toolName, getConfiguredOpenCodeToolName(toolName, config.tools[toolName].name), ]), ); - const definition = createSessionCommandTool(projectRoot, { + const definition = createCommandExpansionTool(projectRoot, { rewriteBody: (body) => prefixKompassToolReferences(body, configuredToolNames), }); return tool({ - description: "Resolve a command and body and queue it in the current session", + description: "Expand a delegated command body into a runnable prompt for immediate task execution.", args: { command: tool.schema.string().describe("Command name to execute, without the leading slash"), - body: tool.schema.string().describe("Literal body content from the session_command block").optional(), - agent: tool.schema.string().describe("Optional agent override from the session_command tag").optional(), + body: tool.schema.string().describe("Literal body content from the delegate block").optional(), }, execute: async (args, context) => { context.metadata({ title: `Command /${args.command.trim()}`, metadata: { command: args.command, - agent: args.agent, }, }); - const resolved = JSON.parse(await definition.execute(args, context)) as { - agent?: string; - command: string; - body: string; - prompt: string; - expanded: boolean; - }; - const dispatched = await executeSessionCommand(client, context, resolved); - - return JSON.stringify({ - ...resolved, - queued: true, - mode: dispatched.mode, - }); + return definition.execute(args, context); }, }); }, diff --git a/packages/opencode/kompass.jsonc b/packages/opencode/kompass.jsonc index 3c1ebf8..8b8a333 100644 --- a/packages/opencode/kompass.jsonc +++ b/packages/opencode/kompass.jsonc @@ -41,7 +41,7 @@ "tools": { "changes_load": { "enabled": true }, - "session_command": { "enabled": true }, + "command_expansion": { "enabled": true }, "pr_load": { "enabled": true }, "pr_sync": { "enabled": true }, "ticket_load": { "enabled": true }, diff --git a/packages/opencode/test/agents-config.test.ts b/packages/opencode/test/agents-config.test.ts index cd84790..82945fc 100644 --- a/packages/opencode/test/agents-config.test.ts +++ b/packages/opencode/test/agents-config.test.ts @@ -55,9 +55,9 @@ describe("applyAgentsConfig", () => { }); assert.equal(cfg.agent.worker?.prompt, undefined); assert.match(cfg.agent.navigator?.prompt ?? "", /structured, multi-step workflows/i); - assert.match(cfg.agent.navigator?.prompt ?? "", /call `?kompass_session_command`?/i); + assert.match(cfg.agent.navigator?.prompt ?? "", /call `?kompass_command_expansion`?/i); assert.match(cfg.agent.navigator?.prompt ?? "", /manage step order/i); - assert.match(cfg.agent.navigator?.prompt ?? "", / { assert.ok(cfg.command!["review"]?.template); }); - test("embeds literal session_command blocks in ship command", async () => { + test("embeds literal delegate blocks in ship command", async () => { delete process.env.CI; const cfg: { command?: Record } = {}; @@ -434,16 +434,17 @@ describe("applyCommandsConfig", () => { assert.match(shipTemplate, /## Goal/); assert.match(shipTemplate, /Ship the current work by dispatching/); - assert.match(shipTemplate, /Ensure Feature Branch/); - assert.match(shipTemplate, //); - assert.match(shipTemplate, /\nBranch naming guidance: \n<\/kompass_session_command>/); - assert.match(shipTemplate, /Store the dispatch result as ``/); - assert.match(shipTemplate, /Store the dispatch result as ``/); - assert.match(shipTemplate, //); - assert.match(shipTemplate, /\nAdditional context: \n<\/kompass_session_command>/); - assert.match(shipTemplate, /Store the dispatch result as ``/); - assert.match(shipTemplate, //); - assert.match(shipTemplate, /\nBase branch: \nAdditional context: \n<\/kompass_session_command>/); + assert.match(shipTemplate, /Delegate Branch Creation/); + assert.match(shipTemplate, //); + assert.match(shipTemplate, /\nBranch naming guidance: \n<\/delegate>/); + assert.match(shipTemplate, /Store the delegated result as ``/); + assert.match(shipTemplate, //); + assert.match(shipTemplate, /\nAdditional context: \n<\/delegate>/); + assert.match(shipTemplate, /Store the delegated result as ``/); + assert.match(shipTemplate, //); + assert.match(shipTemplate, /\nBase branch: \nAdditional context: \n<\/delegate>/); + assert.match(shipTemplate, /Store the delegated result as ``/); + assert.match(shipTemplate, /Ship flow complete/); assert.doesNotMatch(shipTemplate, /<%/); }); @@ -514,20 +515,24 @@ describe("applyCommandsConfig", () => { // PR Author content is now inline in pr/create, not embedded here assert.match(ticketDevTemplate, /## Goal/); assert.match(ticketDevTemplate, /Implement a ticket/); - assert.match(ticketDevTemplate, //); - assert.match(ticketDevTemplate, /\nTicket reference: \nTicket context: \nAdditional context: \n<\/kompass_session_command>/); - assert.match(ticketDevTemplate, //); - assert.match(ticketDevTemplate, /\nBranch naming guidance: \nAdditional context: \n<\/kompass_session_command>/); - assert.match(ticketDevTemplate, //); - assert.match(ticketDevTemplate, /\nTicket reference: \nTicket summary: \nAdditional context: \n<\/kompass_session_command>/); - assert.match(ticketDevTemplate, //); - assert.match(ticketDevTemplate, /\nTicket reference: \nTicket context: \nAdditional context: \n<\/kompass_session_command>/); + assert.match(ticketDevTemplate, //); + assert.match(ticketDevTemplate, /\nTicket reference: \nTicket context: \nAdditional context: \n<\/delegate>/); + assert.match(ticketDevTemplate, /Store the delegated result as ``/); + assert.match(ticketDevTemplate, //); + assert.match(ticketDevTemplate, /\nBranch naming guidance: \nAdditional context: \n<\/delegate>/); + assert.match(ticketDevTemplate, /Store the delegated result as ``/); + assert.match(ticketDevTemplate, //); + assert.match(ticketDevTemplate, /\nTicket reference: \nTicket summary: \nAdditional context: \n<\/delegate>/); + assert.match(ticketDevTemplate, /Store the delegated result as ``/); + assert.match(ticketDevTemplate, //); + assert.match(ticketDevTemplate, /\nTicket reference: \nTicket context: \nAdditional context: \n<\/delegate>/); + assert.match(ticketDevTemplate, /Store the delegated result as ``/); assert.doesNotMatch(ticketDevTemplate, / { + test("embeds literal delegate blocks in todo command", async () => { delete process.env.CI; const cfg: { command?: Record } = {}; @@ -537,14 +542,19 @@ describe("applyCommandsConfig", () => { const todoTemplate = cfg.command!["todo"].template; assert.match(todoTemplate, /## Goal/); - assert.match(todoTemplate, /Work through a todo file one pending item at a time/); - assert.match(todoTemplate, //); - assert.match(todoTemplate, /\nTask: \nTask context: \nAdditional context: \n<\/kompass_session_command>/); - assert.match(todoTemplate, //); + assert.match(todoTemplate, /Work through a todo file one pending item at a time by planning/); + assert.match(todoTemplate, //); + assert.match(todoTemplate, /\nTask: \nTask context: \nAdditional context: \n<\/delegate>/); + assert.match(todoTemplate, /Store the delegated result as ``/); + assert.match(todoTemplate, //); assert.match(todoTemplate, /Current plan: \nPlan feedback: /); - assert.match(todoTemplate, /\nPlan: \nTask: \nTask context: \nAdditional context: \n<\/kompass_session_command>/); - assert.match(todoTemplate, //); - assert.match(todoTemplate, /\nTask: \nAdditional context: \n<\/kompass_session_command>/); + assert.match(todoTemplate, /\nPlan: \nTask: \nTask context: \nAdditional context: \n<\/delegate>/); + assert.match(todoTemplate, /Store the revised delegated result as ``/); + assert.match(todoTemplate, //); + assert.match(todoTemplate, /\nTask: \nAdditional context: \n<\/delegate>/); + assert.match(todoTemplate, /Store the delegated result as ``/); + assert.match(todoTemplate, /Store the delegated result as ``/); + assert.match(todoTemplate, /Todo: /); assert.doesNotMatch(todoTemplate, / { }) as never, createMockClient() as never, process.cwd()); assert.ok(tools.kompass_changes_load); - assert.ok(tools.kompass_session_command); + assert.ok(tools.kompass_command_expansion); assert.ok(tools.kompass_pr_load); assert.ok(tools.kompass_pr_sync); assert.ok(tools.kompass_ticket_load); assert.ok(tools.kompass_ticket_sync); assert.equal(tools.changes_load, undefined); - assert.equal(tools.session_command, undefined); + assert.equal(tools.command_expansion, undefined); assert.equal(tools.pr_load, undefined); assert.equal(tools.pr_sync, undefined); assert.equal(tools.ticket_load, undefined); @@ -138,7 +138,7 @@ describe("createOpenCodeTools", () => { `{ "tools": { "changes_load": { "enabled": false }, - "session_command": { "enabled": false }, + "command_expansion": { "enabled": false }, "pr_load": { "enabled": false }, "pr_sync": { "enabled": false }, "ticket_sync": { @@ -174,9 +174,9 @@ describe("createOpenCodeTools", () => { `{ // jsonc config should work "tools": { - "session_command": { - "enabled": false - }, + "command_expansion": { + "enabled": false + }, "pr_load": { "enabled": true, "name": "pull_request_context", @@ -256,14 +256,14 @@ describe("createOpenCodeTools", () => { }); }); - test("session_command queues expanded commands into the current session", async () => { + test("command_expansion returns expanded prompts for delegated task execution", async () => { await withTempHome(async () => { const client = createMockClient(); const tools = await createOpenCodeTools((() => { throw new Error("not implemented"); }) as never, client as never, process.cwd()); - const output = await (tools.kompass_session_command as any).execute( + const output = await (tools.kompass_command_expansion as any).execute( { command: "review", body: "auth bug" }, { sessionID: "session-1", @@ -277,26 +277,11 @@ describe("createOpenCodeTools", () => { }, ); - const result = JSON.parse(output); - - assert.equal(result.agent, "reviewer"); - assert.equal(result.command, "review"); - assert.equal(result.body, "auth bug"); - assert.equal(result.expanded, true); - assert.equal(result.queued, true); - assert.equal(result.mode, "prompt_async"); - assert.match(result.prompt, /kompass_changes_load/); - assert.doesNotMatch(result.prompt, /`changes_load`/); assert.equal(client.sessionCommands.length, 0); - assert.equal(client.sessionPromptAsyncs.length, 1); - assert.deepEqual(client.sessionPromptAsyncs[0], { - path: { id: "session-1" }, - query: { directory: process.cwd() }, - body: { - agent: "reviewer", - parts: [{ type: "text", text: result.prompt, synthetic: true }], - }, - }); + assert.equal(client.sessionPromptAsyncs.length, 0); + assert.match(String(output), /kompass_changes_load/); + assert.doesNotMatch(String(output), /`changes_load`/); + assert.match(String(output), /auth bug/); assert.equal(client.sessionPrompts.length, 0); }); }); @@ -309,8 +294,8 @@ describe("createOpenCodeTools", () => { }) as never, client as never, process.cwd()); await assert.rejects( - (tools.kompass_session_command as any).execute( - { command: " ", body: "Investigate the redirect bug", agent: "worker" }, + (tools.kompass_command_expansion as any).execute( + { command: " ", body: "Investigate the redirect bug" }, { sessionID: "session-2", messageID: "message-2", diff --git a/packages/web/src/components/CommandShowcase.astro b/packages/web/src/components/CommandShowcase.astro index 1a29070..64979c3 100644 --- a/packages/web/src/components/CommandShowcase.astro +++ b/packages/web/src/components/CommandShowcase.astro @@ -212,10 +212,10 @@ const scenarios: CommandScenario[] = [ { id: 'thinking', phase: 'thinking', content: 'Load the ticket, then orchestrate implementation, branch creation, commit-and-push, and PR creation in order.' }, { id: 'tools', phase: 'tool_calls', toolCalls: [ { tool: 'ticket_load', args: '#42', status: 'complete' }, - { tool: 'session_command', args: 'worker · dev', status: 'complete' }, - { tool: 'session_command', args: 'worker · branch', status: 'complete' }, - { tool: 'session_command', args: 'worker · commit-and-push', status: 'complete' }, - { tool: 'session_command', args: 'worker · pr/create', status: 'complete' } + { tool: 'command_expansion', args: 'worker · dev', status: 'complete' }, + { tool: 'command_expansion', args: 'worker · branch', status: 'complete' }, + { tool: 'command_expansion', args: 'worker · commit-and-push', status: 'complete' }, + { tool: 'command_expansion', args: 'worker · pr/create', status: 'complete' } ] }, { id: 'output', phase: 'output', output: [ 'Implemented ticket: refresh command docs', @@ -381,9 +381,9 @@ const scenarios: CommandScenario[] = [ steps: [ { id: 'thinking', phase: 'thinking', content: 'Ensure a work branch, delegate commit creation, then delegate PR creation and stop on blockers.' }, { id: 'tools', phase: 'tool_calls', toolCalls: [ - { tool: 'session_command', args: 'worker · branch', status: 'complete' }, - { tool: 'session_command', args: 'worker · commit', status: 'complete' }, - { tool: 'session_command', args: 'worker · pr/create', status: 'complete' } + { tool: 'command_expansion', args: 'worker · branch', status: 'complete' }, + { tool: 'command_expansion', args: 'worker · commit', status: 'complete' }, + { tool: 'command_expansion', args: 'worker · pr/create', status: 'complete' } ] }, { id: 'output', phase: 'output', output: [ 'Ship flow complete', @@ -403,10 +403,10 @@ const scenarios: CommandScenario[] = [ { id: 'thinking', phase: 'thinking', content: 'Take the first unchecked item only, plan it, get approval, implement it, commit it, mark it complete, and loop.' }, { id: 'tools', phase: 'tool_calls', toolCalls: [ { tool: 'read', args: '@TODO.md', status: 'complete' }, - { tool: 'session_command', args: 'planner · ticket/plan', status: 'complete' }, + { tool: 'command_expansion', args: 'planner · ticket/plan', status: 'complete' }, { tool: 'question', args: 'Plan Review', status: 'complete' }, - { tool: 'session_command', args: 'worker · dev', status: 'complete' }, - { tool: 'session_command', args: 'worker · commit', status: 'complete' } + { tool: 'command_expansion', args: 'worker · dev', status: 'complete' }, + { tool: 'command_expansion', args: 'worker · commit', status: 'complete' } ] }, { id: 'output', phase: 'output', output: [ 'Todo complete: @TODO.md', @@ -575,16 +575,16 @@ const orchestrationScenarios = scenarios.filter(s => s.group === 'orchestration'
- {step.toolCalls.some(t => t.tool === 'session_command') ? 'Queued' : 'Explored'} + {step.toolCalls.some(t => t.tool === 'command_expansion') ? 'Delegated' : 'Explored'} - {step.toolCalls.length} {step.toolCalls.some(t => t.tool === 'session_command') ? 'session commands' : 'reads, 1 search'} + {step.toolCalls.length} {step.toolCalls.some(t => t.tool === 'command_expansion') ? 'delegations' : 'reads, 1 search'}
{step.toolCalls.map((tool) => ( -
- {tool.tool === 'session_command' ? ( +
+ {tool.tool === 'command_expansion' ? ( <> {tool.args} diff --git a/packages/web/src/content/docs/docs/adapters/opencode.mdx b/packages/web/src/content/docs/docs/adapters/opencode.mdx index 9c855e2..d894b5b 100644 --- a/packages/web/src/content/docs/docs/adapters/opencode.mdx +++ b/packages/web/src/content/docs/docs/adapters/opencode.mdx @@ -35,7 +35,7 @@ Most command execution runs on the bundled `worker` agent. Kompass also provides the `worker`, `navigator`, `planner`, and `reviewer` agent roles for structured workflow execution and review-specific execution. -OpenCode also exposes the `session_command` tool, which resolves explicit command-plus-body inputs and queues the rendered prompt back into the current session. +OpenCode also exposes the `command_expansion` tool, which resolves explicit command-plus-body inputs into the rendered prompt that a navigator can delegate immediately through `task`. ## Useful OpenCode notes diff --git a/packages/web/src/content/docs/docs/reference/agents/index.mdx b/packages/web/src/content/docs/docs/reference/agents/index.mdx index 4277546..b5e009d 100644 --- a/packages/web/src/content/docs/docs/reference/agents/index.mdx +++ b/packages/web/src/content/docs/docs/reference/agents/index.mdx @@ -15,7 +15,7 @@ Generic worker role with minimal built-in behavior. It is the default execution ### `navigator` -Owns structured multi-step workflows locally, preserves state and stop conditions, and forwards literal `` blocks through the `session_command` tool when commands such as `/ship`, `/todo`, and `/ticket/dev` require queued same-session work. +Owns structured multi-step workflows locally, preserves state and stop conditions, and forwards literal `` blocks through the `command_expansion` tool when commands such as `/ship`, `/todo`, and `/ticket/dev` require delegated step execution. ### `planner` diff --git a/packages/web/src/content/docs/docs/reference/agents/navigator.mdx b/packages/web/src/content/docs/docs/reference/agents/navigator.mdx index 71b9563..8b405e0 100644 --- a/packages/web/src/content/docs/docs/reference/agents/navigator.mdx +++ b/packages/web/src/content/docs/docs/reference/agents/navigator.mdx @@ -11,11 +11,11 @@ description: Orchestrator agent for structured multi-step workflows. - pause-and-resume workflows - stepwise orchestration such as `/ship`, `/todo`, and `/ticket/dev` -- commands that mix local state handling with queued `session_command` leaf steps +- commands that mix local state handling with delegated leaf steps ## Key behavior -- forwards literal `...` bodies exactly after variable substitution -- calls `session_command` with explicit `command`, `body`, and `agent` -- queues the next same-session turn instead of waiting for the queued command result +- forwards literal `...` bodies exactly after variable substitution +- calls `command_expansion` with explicit `command` and `body` +- immediately delegates the returned prompt through `task` and uses that task result as the step result - stops and reports blockers when the workflow itself cannot continue safely diff --git a/packages/web/src/content/docs/docs/reference/commands/index.mdx b/packages/web/src/content/docs/docs/reference/commands/index.mdx index ae11b17..0eea5d9 100644 --- a/packages/web/src/content/docs/docs/reference/commands/index.mdx +++ b/packages/web/src/content/docs/docs/reference/commands/index.mdx @@ -16,7 +16,7 @@ Kompass ships workflow-oriented commands authored as explicit templates in `pack - each command is a documented workflow, not an opaque prompt - placeholders like ``, ``, and `` are normalized before execution -- navigator workflows orchestrate locally and queue focused leaf work with literal `` blocks +- navigator workflows orchestrate locally and delegate focused leaf work with literal `` blocks - outputs are deterministic, reviewable, and usually terminal ## Core workflows @@ -101,7 +101,7 @@ Implements a ticket through a navigator-led orchestration of `/dev`, `/branch`, - usage: `/ticket/dev ` - arguments: ticket reference, URL, file path, or raw request -- expected tools: `ticket_load`, `session_command(dev)`, `session_command(branch)`, `session_command(commit-and-push)`, `session_command(pr/create)` +- expected tools: `ticket_load`, `command_expansion(dev)`, `command_expansion(branch)`, `command_expansion(commit-and-push)`, `command_expansion(pr/create)` ### `/ticket/plan` @@ -169,7 +169,7 @@ Ships current work by delegating branch creation, commit creation, and PR creati - usage: `/ship [context]` - arguments: optional base branch, branch naming guidance, or additional shipping context -- expected tools: `session_command(branch)`, `session_command(commit)`, `session_command(pr/create)` +- expected tools: `command_expansion(branch)`, `command_expansion(commit)`, `command_expansion(pr/create)` ### `/todo` @@ -177,4 +177,4 @@ Works through a todo file one pending item at a time with planning, approval, im - usage: `/todo [@todo-file]` - arguments: optional `@file` reference and execution guidance; defaults to `@TODO.md` -- expected tools: `read`, `question`, `session_command(ticket/plan)`, `session_command(dev)`, `session_command(commit)` +- expected tools: `read`, `question`, `command_expansion(ticket/plan)`, `command_expansion(dev)`, `command_expansion(commit)` diff --git a/packages/web/src/content/docs/docs/reference/commands/ship.mdx b/packages/web/src/content/docs/docs/reference/commands/ship.mdx index 6890e35..d16a483 100644 --- a/packages/web/src/content/docs/docs/reference/commands/ship.mdx +++ b/packages/web/src/content/docs/docs/reference/commands/ship.mdx @@ -5,7 +5,7 @@ description: Move current work through branch, commit, and PR creation as a fast ## Purpose -Use `/ship` when you want a fast path from current work to a PR through local orchestration and queued same-session command steps. +Use `/ship` when you want a fast path from current work to a PR through local orchestration and delegated command steps. ## Usage @@ -16,11 +16,11 @@ Use `/ship` when you want a fast path from current work to a PR through local or ## Typical flow - ensure there is a suitable work branch -- queue `branch`, `commit`, and `pr/create` through `session_command` +- expand and delegate `branch`, `commit`, and `pr/create` - stop on blockers and report `Nothing to ship` when PR creation finds no shippable work ## Common tools -- `session_command(worker, branch)` -- `session_command(worker, commit)` -- `session_command(worker, pr/create)` +- `command_expansion(worker, branch)` +- `command_expansion(worker, commit)` +- `command_expansion(worker, pr/create)` diff --git a/packages/web/src/content/docs/docs/reference/commands/ticket-dev.mdx b/packages/web/src/content/docs/docs/reference/commands/ticket-dev.mdx index 8160309..07fe3f4 100644 --- a/packages/web/src/content/docs/docs/reference/commands/ticket-dev.mdx +++ b/packages/web/src/content/docs/docs/reference/commands/ticket-dev.mdx @@ -5,7 +5,7 @@ description: Implement a ticket by orchestrating development, branching, commit- ## Purpose -Use `/ticket/dev` when implementation starts from a tracked ticket and should run through a navigator-led shipping flow with queued same-session command steps. +Use `/ticket/dev` when implementation starts from a tracked ticket and should run through a navigator-led shipping flow with delegated command steps. ## Usage @@ -16,15 +16,15 @@ Use `/ticket/dev` when implementation starts from a tracked ticket and should ru ## Typical flow - load the ticket and its surrounding context -- queue `dev` for implementation and validation -- queue `branch` for branch naming from the ticket summary -- queue `commit-and-push` -- queue `pr/create` as the next shipping step +- expand and delegate `dev` for implementation and validation +- expand and delegate `branch` for branch naming from the ticket summary +- expand and delegate `commit-and-push` +- expand and delegate `pr/create` as the next shipping step ## Common tools - `ticket_load` -- `session_command(worker, dev)` -- `session_command(worker, branch)` -- `session_command(worker, commit-and-push)` -- `session_command(worker, pr/create)` +- `command_expansion(worker, dev)` +- `command_expansion(worker, branch)` +- `command_expansion(worker, commit-and-push)` +- `command_expansion(worker, pr/create)` diff --git a/packages/web/src/content/docs/docs/reference/commands/todo.mdx b/packages/web/src/content/docs/docs/reference/commands/todo.mdx index 04de854..7b59520 100644 --- a/packages/web/src/content/docs/docs/reference/commands/todo.mdx +++ b/packages/web/src/content/docs/docs/reference/commands/todo.mdx @@ -5,7 +5,7 @@ description: Work through a todo file one item at a time with planning, approval ## Purpose -Use `/todo` for structured pause-and-resume work from a checklist file with explicit planning, approval, and queued implementation steps. +Use `/todo` for structured pause-and-resume work from a checklist file with explicit planning, approval, and delegated implementation steps. ## Usage @@ -17,14 +17,14 @@ Use `/todo` for structured pause-and-resume work from a checklist file with expl - load `@TODO.md` by default or a provided `@file` - take the first unchecked item only -- queue `ticket/plan`, show the plan, and require explicit approval before implementation -- queue `dev`, then `commit`, then mark the checklist item complete +- expand and delegate `ticket/plan`, show the plan, and require explicit approval before implementation +- expand and delegate `dev`, then `commit`, then mark the checklist item complete - repeat until there are no pending tasks left ## Common tools - `read` - `question` -- `session_command(planner, ticket/plan)` -- `session_command(worker, dev)` -- `session_command(worker, commit)` +- `command_expansion(planner, ticket/plan)` +- `command_expansion(worker, dev)` +- `command_expansion(worker, commit)` diff --git a/packages/web/src/content/docs/docs/reference/tools/command-expansion.mdx b/packages/web/src/content/docs/docs/reference/tools/command-expansion.mdx new file mode 100644 index 0000000..7b3e73c --- /dev/null +++ b/packages/web/src/content/docs/docs/reference/tools/command-expansion.mdx @@ -0,0 +1,30 @@ +--- +title: command_expansion +description: Resolve an explicit command plus body into a runnable delegated prompt. +--- + +## Purpose + +Use `command_expansion` when a workflow should expand another Kompass command into a concrete prompt before delegating it through `task`. + +## Inputs + +- `command`: command name without the leading slash +- `body`: literal body content from the `` block + +## Behavior + +- resolves the command template from the bundled command set +- substitutes `$ARGUMENTS` and positional placeholders from `body` +- prefixes Kompass tool references for the active adapter when needed +- returns the fully expanded prompt text for the delegated command + +## Typical use + +```xml + +auth bug + +``` + +This expands the equivalent of the `/review` command with `auth bug` as the body content so a navigator can pass the returned prompt into `task`. The `` tag's `agent` value remains the routing source of truth. diff --git a/packages/web/src/content/docs/docs/reference/tools/index.mdx b/packages/web/src/content/docs/docs/reference/tools/index.mdx index 51f3862..99fde34 100644 --- a/packages/web/src/content/docs/docs/reference/tools/index.mdx +++ b/packages/web/src/content/docs/docs/reference/tools/index.mdx @@ -8,7 +8,7 @@ Kompass uses structured tools to keep workflows grounded in repository and GitHu Current built-in tools: - `changes_load` -- `session_command` +- `command_expansion` - `pr_load` - `pr_sync` - `ticket_load` @@ -20,9 +20,9 @@ Current built-in tools: Loads either uncommitted worktree changes or a base/head git comparison, returning structured file diffs and optional commit metadata. -### `session_command` +### `command_expansion` -Resolves an explicit command plus body into a rendered prompt and queues that prompt back into the current session. +Resolves an explicit command plus body into a rendered prompt so a navigator can delegate it immediately through `task`. ### `pr_load` diff --git a/packages/web/src/content/docs/docs/reference/tools/session-command.mdx b/packages/web/src/content/docs/docs/reference/tools/session-command.mdx deleted file mode 100644 index 64f07a5..0000000 --- a/packages/web/src/content/docs/docs/reference/tools/session-command.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: session_command -description: Resolve an explicit command plus body and queue it back into the current session. ---- - -## Purpose - -Use `session_command` when a workflow should queue another Kompass command into the same session instead of spawning separate subagent execution. - -## Inputs - -- `command`: command name without the leading slash -- `body`: literal body content from the `` block -- `agent`: optional target agent override - -## Behavior - -- resolves the command template from the bundled command set -- substitutes `$ARGUMENTS` and positional placeholders from `body` -- prefixes Kompass tool references for the active adapter when needed -- queues the rendered prompt with the adapter's same-session async prompt API -- returns a queue acknowledgement immediately instead of waiting for the queued command result - -## Typical use - -```xml - -auth bug - -``` - -This queues the equivalent of the `/review` command with `auth bug` as the body content.