From 52352c3986f46e3258db2d3207f686f05d5ecdc6 Mon Sep 17 00:00:00 2001 From: Dushyant Garg Date: Fri, 24 Apr 2026 23:14:19 -0400 Subject: [PATCH] Add OpenWork workspace assets and align runtime specs --- .gitignore | 2 + .opencode/agents/openwork.md | 42 +++ .opencode/commands/get-started.md | 7 + .opencode/commands/learn-files.md | 7 + .opencode/commands/learn-plugins.md | 7 + .opencode/commands/learn-skills.md | 7 + .opencode/skills/agent-creator/.env.example | 1 + .opencode/skills/agent-creator/SKILL.md | 51 +++ .opencode/skills/agent-creator/client.ts | 3 + .opencode/skills/agent-creator/first-call.ts | 10 + .opencode/skills/agent-creator/load-env.ts | 3 + .opencode/skills/command-creator/.env.example | 1 + .../skills/command-creator/.skill.config | 8 + .opencode/skills/command-creator/SKILL.md | 352 ++++++++++++++++++ .opencode/skills/get-started/SKILL.md | 17 + .opencode/skills/plugin-creator/.env.example | 1 + .opencode/skills/plugin-creator/.skill.config | 8 + .opencode/skills/plugin-creator/SKILL.md | 41 ++ .opencode/skills/skill-creator/.env.example | 1 + .opencode/skills/skill-creator/.skill.config | 8 + .opencode/skills/skill-creator/SKILL.md | 101 +++++ .opencode/skills/workspace-guide/SKILL.md | 46 +++ opencode.jsonc | 23 ++ .../.openspec.yaml | 2 + .../design.md | 74 ++++ .../proposal.md | 38 ++ .../specs/agent-authoring-doctrine/spec.md | 19 + .../specs/cli-argument-parsing/spec.md | 50 +++ .../specs/cli-example-templates/spec.md | 16 + .../specs/cli-lifecycle-hooks/spec.md | 59 +++ .../specs/cli-policy-presets/spec.md | 28 ++ .../specs/command-blocks/spec.md | 68 ++++ .../specs/command-help/spec.md | 21 ++ .../specs/testing-kit/spec.md | 28 ++ .../specs/tool-runtime/spec.md | 80 ++++ .../tasks.md | 53 +++ openspec/specs/command-blocks/spec.md | 109 +++++- openspec/specs/tool-runtime/spec.md | 120 ++++-- 38 files changed, 1462 insertions(+), 50 deletions(-) create mode 100644 .opencode/agents/openwork.md create mode 100644 .opencode/commands/get-started.md create mode 100644 .opencode/commands/learn-files.md create mode 100644 .opencode/commands/learn-plugins.md create mode 100644 .opencode/commands/learn-skills.md create mode 100644 .opencode/skills/agent-creator/.env.example create mode 100644 .opencode/skills/agent-creator/SKILL.md create mode 100644 .opencode/skills/agent-creator/client.ts create mode 100644 .opencode/skills/agent-creator/first-call.ts create mode 100644 .opencode/skills/agent-creator/load-env.ts create mode 100644 .opencode/skills/command-creator/.env.example create mode 100644 .opencode/skills/command-creator/.skill.config create mode 100644 .opencode/skills/command-creator/SKILL.md create mode 100644 .opencode/skills/get-started/SKILL.md create mode 100644 .opencode/skills/plugin-creator/.env.example create mode 100644 .opencode/skills/plugin-creator/.skill.config create mode 100644 .opencode/skills/plugin-creator/SKILL.md create mode 100644 .opencode/skills/skill-creator/.env.example create mode 100644 .opencode/skills/skill-creator/.skill.config create mode 100644 .opencode/skills/skill-creator/SKILL.md create mode 100644 .opencode/skills/workspace-guide/SKILL.md create mode 100644 opencode.jsonc create mode 100644 openspec/changes/customizable-cli-runtime-behavior/.openspec.yaml create mode 100644 openspec/changes/customizable-cli-runtime-behavior/design.md create mode 100644 openspec/changes/customizable-cli-runtime-behavior/proposal.md create mode 100644 openspec/changes/customizable-cli-runtime-behavior/specs/agent-authoring-doctrine/spec.md create mode 100644 openspec/changes/customizable-cli-runtime-behavior/specs/cli-argument-parsing/spec.md create mode 100644 openspec/changes/customizable-cli-runtime-behavior/specs/cli-example-templates/spec.md create mode 100644 openspec/changes/customizable-cli-runtime-behavior/specs/cli-lifecycle-hooks/spec.md create mode 100644 openspec/changes/customizable-cli-runtime-behavior/specs/cli-policy-presets/spec.md create mode 100644 openspec/changes/customizable-cli-runtime-behavior/specs/command-blocks/spec.md create mode 100644 openspec/changes/customizable-cli-runtime-behavior/specs/command-help/spec.md create mode 100644 openspec/changes/customizable-cli-runtime-behavior/specs/testing-kit/spec.md create mode 100644 openspec/changes/customizable-cli-runtime-behavior/specs/tool-runtime/spec.md create mode 100644 openspec/changes/customizable-cli-runtime-behavior/tasks.md diff --git a/.gitignore b/.gitignore index 84748a6..177774e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ dist/ .DS_Store npm-debug.log* *.tgz +.opencode/openwork.json +.opencode/.openwork-enterprise-creators diff --git a/.opencode/agents/openwork.md b/.opencode/agents/openwork.md new file mode 100644 index 0000000..84f8384 --- /dev/null +++ b/.opencode/agents/openwork.md @@ -0,0 +1,42 @@ +--- +description: OpenWork default agent (safe, mobile-first, self-referential) +mode: primary +temperature: 0.2 +--- + +You are OpenWork. + +When the user refers to "you", they mean the OpenWork app and the current workspace. + +Your job: +- Help the user work on files safely. +- Automate repeatable work. +- Keep behavior portable and reproducible. + +Memory (two kinds) +1) Behavior memory (shareable, in git) +- ".opencode/skills/**" +- ".opencode/agents/**" +- repo docs + +2) Private memory (never commit) +- Tokens, IDs, credentials +- Local DBs/logs/config files (gitignored) +- Notion pages/databases (if configured via MCP) + +Hard rule: never copy private memory into repo files verbatim. Store only redacted summaries, schemas/templates, and stable pointers. + +Reconstruction-first +- Do not assume env vars or prior setup. +- If required state is missing, ask one targeted question. +- After the user provides it, store it in private memory and continue. + +Verification-first +- If you change code, run the smallest meaningful test or smoke check. +- If you touch UI or remote behavior, validate end-to-end and capture logs on failure. + +Incremental adoption loop +- Do the task once end-to-end. +- If steps repeat, factor them into a skill. +- If the work becomes ongoing, create/refine an agent role. +- If it should run regularly, schedule it and store outputs in private memory. diff --git a/.opencode/commands/get-started.md b/.opencode/commands/get-started.md new file mode 100644 index 0000000..33c28c0 --- /dev/null +++ b/.opencode/commands/get-started.md @@ -0,0 +1,7 @@ +--- +name: get-started +description: Get started +subtask: false +--- + +get started diff --git a/.opencode/commands/learn-files.md b/.opencode/commands/learn-files.md new file mode 100644 index 0000000..5298b79 --- /dev/null +++ b/.opencode/commands/learn-files.md @@ -0,0 +1,7 @@ +--- +name: learn-files +description: Safe, practical file workflows +subtask: false +--- + +Show me how to interact with files in this workspace. Include safe examples for reading, summarizing, and editing. diff --git a/.opencode/commands/learn-plugins.md b/.opencode/commands/learn-plugins.md new file mode 100644 index 0000000..503c818 --- /dev/null +++ b/.opencode/commands/learn-plugins.md @@ -0,0 +1,7 @@ +--- +name: learn-plugins +description: What plugins are and how to install them +subtask: false +--- + +Explain what plugins are and how to install them in this workspace. diff --git a/.opencode/commands/learn-skills.md b/.opencode/commands/learn-skills.md new file mode 100644 index 0000000..0a047f7 --- /dev/null +++ b/.opencode/commands/learn-skills.md @@ -0,0 +1,7 @@ +--- +name: learn-skills +description: How skills work and how to create your own +subtask: false +--- + +Explain what skills are, how to use them, and how to create a new skill for this workspace. diff --git a/.opencode/skills/agent-creator/.env.example b/.opencode/skills/agent-creator/.env.example new file mode 100644 index 0000000..0575799 --- /dev/null +++ b/.opencode/skills/agent-creator/.env.example @@ -0,0 +1 @@ +# No environment variables are required for this skill. diff --git a/.opencode/skills/agent-creator/SKILL.md b/.opencode/skills/agent-creator/SKILL.md new file mode 100644 index 0000000..47f290c --- /dev/null +++ b/.opencode/skills/agent-creator/SKILL.md @@ -0,0 +1,51 @@ +--- +name: agent-creator +description: Create new OpenCode agents with a gpt-5.2-codex default. +--- + +## Quick Usage (Already Configured) + +### Create a project agent +```bash +opencode agent create +``` + +### Agent file locations +- Project agents: `.opencode/agents/.md` +- Global agents: `~/.config/opencode/agents/.md` + +## Default model + +Use `gpt-5.2-codex` as the default model for new agents unless a workflow needs a different model. + +## Minimal agent template + +```markdown +--- +description: One-line description of what the agent does +mode: subagent +model: gpt-5.2-codex +tools: + write: false + edit: false + bash: false +--- +You are a specialized agent. Describe your task, boundaries, and expected output. +``` + +## Notes from OpenCode docs + +- Agent files are markdown with YAML frontmatter. +- The markdown filename becomes the agent name. +- Set `mode` to `primary`, `subagent`, or `all`. +- If no model is specified, subagents inherit the caller model. +- `tools` controls per-agent tool access. + +## Reference + +Follow the official OpenCode agent docs: https://opencode.ai/docs/agents/ + +## First-Time Setup (If Not Configured) + +1. Run `opencode agent create` and choose project scope. +2. Paste in the default template above and adjust tools as needed. diff --git a/.opencode/skills/agent-creator/client.ts b/.opencode/skills/agent-creator/client.ts new file mode 100644 index 0000000..a005513 --- /dev/null +++ b/.opencode/skills/agent-creator/client.ts @@ -0,0 +1,3 @@ +export type AgentCreatorClient = Record; + +export const client: AgentCreatorClient = {}; diff --git a/.opencode/skills/agent-creator/first-call.ts b/.opencode/skills/agent-creator/first-call.ts new file mode 100644 index 0000000..14fd77c --- /dev/null +++ b/.opencode/skills/agent-creator/first-call.ts @@ -0,0 +1,10 @@ +import { config } from "./load-env"; + +async function main() { + void config; + console.log("agent-creator: no credentials required."); +} + +main().catch((error) => { + console.error(error); +}); diff --git a/.opencode/skills/agent-creator/load-env.ts b/.opencode/skills/agent-creator/load-env.ts new file mode 100644 index 0000000..8b8968f --- /dev/null +++ b/.opencode/skills/agent-creator/load-env.ts @@ -0,0 +1,3 @@ +export type AgentCreatorConfig = Record; + +export const config: AgentCreatorConfig = {}; diff --git a/.opencode/skills/command-creator/.env.example b/.opencode/skills/command-creator/.env.example new file mode 100644 index 0000000..0575799 --- /dev/null +++ b/.opencode/skills/command-creator/.env.example @@ -0,0 +1 @@ +# No environment variables are required for this skill. diff --git a/.opencode/skills/command-creator/.skill.config b/.opencode/skills/command-creator/.skill.config new file mode 100644 index 0000000..09c40f1 --- /dev/null +++ b/.opencode/skills/command-creator/.skill.config @@ -0,0 +1,8 @@ +# Required credentials (if any) +# - List the credential name +# - Where to obtain it +# - How to store it locally + +# Example: +# - GITHUB_TOKEN: https://github.com/settings/tokens +# - Store in .env (gitignored) diff --git a/.opencode/skills/command-creator/SKILL.md b/.opencode/skills/command-creator/SKILL.md new file mode 100644 index 0000000..0e91240 --- /dev/null +++ b/.opencode/skills/command-creator/SKILL.md @@ -0,0 +1,352 @@ +--- +name: command-creator +description: Create OpenCode custom commands for repeatable tasks. +--- + +## Quick Usage (Already Configured) + +### Create a new command file +```bash +mkdir -p .opencode/commands +``` + +Create `.opencode/commands/.md` with frontmatter and a prompt template. + +### Command file example +``` +--- +description: Run tests with coverage +agent: build +model: gpt-5.2-codex +--- + +Run the full test suite with coverage report and show any failures. +Focus on the failing tests and suggest fixes. +``` + +## Prompt config essentials + +- Use `$ARGUMENTS` for all arguments, or `$1`, `$2`, `$3` for positional args. +- Use `!\`command\`` to inject shell output into the prompt. +- Use `@path/to/file` to include file contents in the prompt. + +## Notes from OpenCode docs + +- Command files live in `.opencode/commands/` (project) or `~/.config/opencode/commands/` (global). +- The markdown filename becomes the command name (e.g., `test.md` → `/test`). +- JSON config also supports commands in `opencode.json` under `command`. +- Custom commands can override built-ins like `/init`, `/undo`, `/redo`, `/share`, `/help`. + +## Reference + +Follow the official OpenCode command docs: https://opencode.ai/docs/commands/ +Use the docs as the escape hatch when unsure. + +## Docs snapshot + +Skip to content +OpenCode + +Search +⌘ +K +Intro +Config +Providers +Network +Enterprise +Troubleshooting +Migrating to 1.0 +TUI +CLI +Web +IDE +Zen +Share +GitHub +GitLab +Tools +Rules +Agents +Models +Themes +Keybinds +Commands +Formatters +Permissions +LSP Servers +MCP servers +ACP Support +Agent Skills +Custom Tools +SDK +Server +Plugins +Ecosystem +On this page +Overview +Create command files +Configure +JSON +Markdown +Prompt config +Arguments +Shell output +File references +Options +Template +Description +Agent +Subtask +Model +Built-in +Commands +Create custom commands for repetitive tasks. + +Custom commands let you specify a prompt you want to run when that command is executed in the TUI. + +/my-command + +Custom commands are in addition to the built-in commands like /init, /undo, /redo, /share, /help. Learn more. + +Create command files +Create markdown files in the commands/ directory to define custom commands. + +Create .opencode/commands/test.md: + +.opencode/commands/test.md +--- +description: Run tests with coverage +agent: build +model: anthropic/claude-3-sonnet-20241022 +--- + +Run the full test suite with coverage report and show any failures. +Focus on the failing tests and suggest fixes. + +The frontmatter defines command properties. The content becomes the template. + +Use the command by typing / followed by the command name. + +"/test" + +Configure +You can add custom commands through the OpenCode config or by creating markdown files in the commands/ directory. + +JSON +Use the command option in your OpenCode config: + +opencode.jsonc +{ + "$schema": "https://opencode.ai/config.json", + "command": { + // This becomes the name of the command + "test": { + // This is the prompt that will be sent to the LLM + "template": "Run the full test suite with coverage report and show any failures.\nFocus on the failing tests and suggest fixes.", + // This is shown as the description in the TUI + "description": "Run tests with coverage", + "agent": "build", + "model": "anthropic/claude-3-5-sonnet-20241022" + } + } +} + +Now you can run this command in the TUI: + +/test + +Markdown +You can also define commands using markdown files. Place them in: + +Global: ~/.config/opencode/commands/ +Per-project: .opencode/commands/ +~/.config/opencode/commands/test.md +--- +description: Run tests with coverage +agent: build +model: anthropic/claude-3-5-sonnet-20241022 +--- + +Run the full test suite with coverage report and show any failures. +Focus on the failing tests and suggest fixes. + +The markdown file name becomes the command name. For example, test.md lets you run: + +/test + +Prompt config +The prompts for the custom commands support several special placeholders and syntax. + +Arguments +Pass arguments to commands using the $ARGUMENTS placeholder. + +.opencode/commands/component.md +--- +description: Create a new component +--- + +Create a new React component named $ARGUMENTS with TypeScript support. +Include proper typing and basic structure. + +Run the command with arguments: + +/component Button + +And $ARGUMENTS will be replaced with Button. + +You can also access individual arguments using positional parameters: + +$1 - First argument +$2 - Second argument +$3 - Third argument +And so on… +For example: + +.opencode/commands/create-file.md +--- +description: Create a new file with content +--- + +Create a file named $1 in the directory $2 +with the following content: $3 + +Run the command: + +/create-file config.json src "{ \"key\": \"value\" }" + +This replaces: + +$1 with config.json +$2 with src +$3 with { "key": "value" } +Shell output +Use !command to inject bash command output into your prompt. + +For example, to create a custom command that analyzes test coverage: + +.opencode/commands/analyze-coverage.md +--- +description: Analyze test coverage +--- + +Here are the current test results: +!`npm test` + +Based on these results, suggest improvements to increase coverage. + +Or to review recent changes: + +.opencode/commands/review-changes.md +--- +description: Review recent changes +--- + +Recent git commits: +!`git log --oneline -10` + +Review these changes and suggest any improvements. + +Commands run in your project’s root directory and theutput becomes part of the prompt. + +File references +Include files in your command using @ followed by the filename. + +.opencode/commands/review-component.md +--- +description: Review component +--- + +Review the component in @src/components/Button.tsx. +Check for performance issues and suggest improvements. + +The file content gets included in the prompt automatically. + +Options +Let’s look at the configuration options in detail. + +Template +The template option defines the prompt that will be sent to the LLM when the command is executed. + +opencode.json +{ + "command": { + "test": { + "template": "Run the full test suite with coverage report and show any failures.\nFocus on the failing tests and suggest fixes." + } + } +} + +This is a required config option. + +Description +Use the description option to provide a brief description of what the command does. + +opencode.json +{ + "command": { + "test": { + "description": "Run tests with coverage" + } + } +} + +This is shown as the description in the TUI when you type ithe command. + +Agent +Use the agent config to optionally specify which agent should execute this command. If this is a subagent the command will trigger a subagent invocation by default. To disable this behavior, set subtask to false. + +opencode.json +{ + "command": { + "review": { + "agent": "plan" + } + } +} + +This is an optional config option. If not specified, defaults to your current agent. + +Subtask +Use the subtask boolean to force the command to trigger a subagent invocation. This is useful if you want the command to not pollute your primary context and will force the agent to act as a subagent, even if mode is set to primary on the agent configuration. + +opencode.json +{ + "command": { + "analyze": { + "subtask": true + } + } +} + +This is an optional config option. + +Model +Use the model config to override the default model for this command. + +opencode.json +{ + "command": { + "analyze": { + "model": "anthropic/claude-3-5-sonnet-20241022" + } + } +} + +This is an optional config option. + +Built-in +opencode includes several built-in commands like /init, /undo, /redo, /share, /help; learn more. + +Note + +Custom commands can override built-in commands. + +If you define a custom command with the same name, it will override the built-in command. + +Edit this page +Find a bug? Open an issue +Join our Discord community +© Anomaly + +Jan 24, 2026 diff --git a/.opencode/skills/get-started/SKILL.md b/.opencode/skills/get-started/SKILL.md new file mode 100644 index 0000000..8455cdf --- /dev/null +++ b/.opencode/skills/get-started/SKILL.md @@ -0,0 +1,17 @@ +--- +name: get-started +description: Guide users through the get started setup and Chrome DevTools demo. +--- +## When to use +- Always load this skill when the user says "get started". + +## What to do +- Reply with these four lines, exactly and in order: + 1) hey there welcome this is openwork + 2) we've pre-configured you with a couple tools + 3) Get Started + 4) write "hey go on google.com" + +## Then +- If the user writes "go on google.com" (or "hey go on google.com"), use the chrome-devtools MCP to open the site. +- After the navigation completes, reply: "I'm on " where is the final URL or page title they asked for. diff --git a/.opencode/skills/plugin-creator/.env.example b/.opencode/skills/plugin-creator/.env.example new file mode 100644 index 0000000..0575799 --- /dev/null +++ b/.opencode/skills/plugin-creator/.env.example @@ -0,0 +1 @@ +# No environment variables are required for this skill. diff --git a/.opencode/skills/plugin-creator/.skill.config b/.opencode/skills/plugin-creator/.skill.config new file mode 100644 index 0000000..09c40f1 --- /dev/null +++ b/.opencode/skills/plugin-creator/.skill.config @@ -0,0 +1,8 @@ +# Required credentials (if any) +# - List the credential name +# - Where to obtain it +# - How to store it locally + +# Example: +# - GITHUB_TOKEN: https://github.com/settings/tokens +# - Store in .env (gitignored) diff --git a/.opencode/skills/plugin-creator/SKILL.md b/.opencode/skills/plugin-creator/SKILL.md new file mode 100644 index 0000000..50f7ee0 --- /dev/null +++ b/.opencode/skills/plugin-creator/SKILL.md @@ -0,0 +1,41 @@ +--- +name: plugin-creator +description: Create OpenCode plugins and know where to load them. +--- + +## Quick Usage (Already Configured) + +### Where plugins live +- Project plugins: `.opencode/plugins/*.js` or `.opencode/plugins/*.ts` +- Global plugins: `~/.config/opencode/plugins/*.js` or `.ts` + +### Load from npm +Add npm plugin packages in `opencode.json`: +```json +{ + "$schema": "https://opencode.ai/config.json", + "plugin": ["opencode-helicone-session", "opencode-wakatime"] +} +``` + +## Minimal plugin template + +```ts +export const MyPlugin = async ({ project, client, $, directory, worktree }) => { + return { + // Hook implementations go here + } +} +``` + +## Notes from OpenCode docs + +- Plugins are JS/TS modules exporting one or more plugin functions. +- Local plugins are loaded directly from the plugin directory. +- NPM plugins are installed via Bun at startup and cached in `~/.cache/opencode/node_modules/`. +- Load order: global config → project config → global plugins → project plugins. + +## Reference + +Follow the official OpenCode plugin docs: https://opencode.ai/docs/plugins/ +Use the docs as the escape hatch when unsure. diff --git a/.opencode/skills/skill-creator/.env.example b/.opencode/skills/skill-creator/.env.example new file mode 100644 index 0000000..0575799 --- /dev/null +++ b/.opencode/skills/skill-creator/.env.example @@ -0,0 +1 @@ +# No environment variables are required for this skill. diff --git a/.opencode/skills/skill-creator/.skill.config b/.opencode/skills/skill-creator/.skill.config new file mode 100644 index 0000000..09c40f1 --- /dev/null +++ b/.opencode/skills/skill-creator/.skill.config @@ -0,0 +1,8 @@ +# Required credentials (if any) +# - List the credential name +# - Where to obtain it +# - How to store it locally + +# Example: +# - GITHUB_TOKEN: https://github.com/settings/tokens +# - Store in .env (gitignored) diff --git a/.opencode/skills/skill-creator/SKILL.md b/.opencode/skills/skill-creator/SKILL.md new file mode 100644 index 0000000..3a8c3fe --- /dev/null +++ b/.opencode/skills/skill-creator/SKILL.md @@ -0,0 +1,101 @@ +--- +name: skill-creator +description: Create new OpenCode skills with the standard scaffold. +--- + +Skill creator helps create other skills that are self-buildable. + +The best way to use it is after a user already executed a flow and says: create a skill for this. Alternatively, if the user asks for a skill to be created, suggest they do the task first and ask for skill creation at the end. + +This should trigger this scaffold: +- If the user needed to configure things, create a `.env.example` without credentials and include all required variables. +- Ask the user if they want to store credentials. If yes, write them to a `.env` file in the skill, and suggest rotating keys later. +- Always add a `.gitignore` in the skill that ignores `.env`, and verify `.env` is not tracked. +- If the user needed to interact with an API and you created scripts, add reusable scripts under `scripts/`. +- New skills should explain how to use the `scripts/` and that `.env.example` defines the minimum config. +- Skills should state that they infer what they can do from the available config. + +## Trigger phrases (critical) + +The description field is how Claude decides when to use your skill. +Include 2-3 specific phrases that should trigger it. + +Bad example: +"Use when working with content" + +Good examples: +"Use when user mentions 'content pipeline', 'add to content database', or 'schedule a post'" +"Triggers on: 'rotate PDF', 'flip PDF pages', 'change PDF orientation'" + +Quick validation: +- Contains at least one quoted phrase +- Uses "when" or "triggers" +- Longer than ~50 characters + +## Frontmatter template + +```yaml +--- +name: my-skill +description: | + [What it does in one sentence] + + Triggers when user mentions: + - "[specific phrase 1]" + - "[specific phrase 2]" + - "[specific phrase 3]" +--- +``` + +## Quick Usage (Already Configured) + +### Create a new skill folder +```bash +mkdir -p .opencode/skills/ +``` + +### Minimum scaffold files +- `SKILL.md` +- `scripts/` +- `.env` +- `.env.example` (use this to guide the minimum config) +- `.gitignore` (ignore `.env`) + +## .env (credentials + config) + +- Use `.env.example` to document required credentials or external setup. +- Do not include any real credentials in `.env.example`. + +## Minimal skill template + +```markdown +--- +name: skill-name +description: One-line description +--- + +## Quick Usage (Already Configured) + +### Action 1 +```bash +command here +``` + +## Common Gotchas + +- Thing that doesn't work as expected + +## First-Time Setup (If Not Configured) + +1. ... +``` + +## Notes from OpenCode docs + +- Skill folders live in `.opencode/skills//SKILL.md`. +- `name` must be lowercase and match the folder. +- Frontmatter requires `name` and `description`. + +## Reference + +Follow the official OpenCode skills docs: https://opencode.ai/docs/skills/ diff --git a/.opencode/skills/workspace-guide/SKILL.md b/.opencode/skills/workspace-guide/SKILL.md new file mode 100644 index 0000000..12ff718 --- /dev/null +++ b/.opencode/skills/workspace-guide/SKILL.md @@ -0,0 +1,46 @@ +--- +name: workspace-guide +description: Workspace guide to introduce OpenWork and onboard new users. +--- +# Welcome to OpenWork + +Hi, I'm Ben and this is OpenWork. It's an open-source alternative to Claude's cowork. It helps you work on your files with AI and automate the mundane tasks so you don't have to. + +Before we start, use the question tool to ask: +"Are you more technical or non-technical? I'll tailor the explanation." + +## If the person is non-technical +OpenWork feels like a chat app, but it can safely work with the files you allow. Put files in this workspace and I can summarize them, create new ones, or help organize them. + +Try: +- "Summarize the files in this workspace." +- "Create a checklist for my week." +- "Draft a short summary from this document." + +## Skills and plugins (simple) +Skills add new capabilities. Plugins add advanced features like scheduling or browser automation. We can add them later when you're ready. + +## If the person is technical +OpenWork is a GUI for OpenCode. Everything that works in OpenCode works here. + +Most reliable setup today: +1) Install OpenCode from opencode.ai +2) Configure providers there (models and API keys) +3) Come back to OpenWork and start a session + +Skills: +- Install from the Skills tab, or add them to this workspace. +- Docs: https://opencode.ai/docs/skills + +Plugins: +- Configure in opencode.json or use the Plugins tab. +- Docs: https://opencode.ai/docs/plugins/ + +MCP servers: +- Add external tools via opencode.json. +- Docs: https://opencode.ai/docs/mcp-servers/ + +Config reference: +- Docs: https://opencode.ai/docs/config/ + +End with two friendly next actions to try in OpenWork. diff --git a/opencode.jsonc b/opencode.jsonc new file mode 100644 index 0000000..5e57ac7 --- /dev/null +++ b/opencode.jsonc @@ -0,0 +1,23 @@ +{ + "$schema": "https://opencode.ai/config.json", + "default_agent": "openwork", + "mcp": { + "chrome-devtools": { + "command": [ + "npx", + "-y", + "chrome-devtools-mcp@latest" + ], + "type": "local" + }, + "control-chrome": { + "command": [ + "chrome-devtools-mcp" + ], + "type": "local" + } + }, + "plugin": [ + "opencode-scheduler" + ] +} \ No newline at end of file diff --git a/openspec/changes/customizable-cli-runtime-behavior/.openspec.yaml b/openspec/changes/customizable-cli-runtime-behavior/.openspec.yaml new file mode 100644 index 0000000..9323e24 --- /dev/null +++ b/openspec/changes/customizable-cli-runtime-behavior/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-04-24 diff --git a/openspec/changes/customizable-cli-runtime-behavior/design.md b/openspec/changes/customizable-cli-runtime-behavior/design.md new file mode 100644 index 0000000..e33019c --- /dev/null +++ b/openspec/changes/customizable-cli-runtime-behavior/design.md @@ -0,0 +1,74 @@ +## Context + +AgentTK is becoming the runtime layer that downstream agents use to author real CLI tools. The previous runtime-kit work introduced `runToolCli`, built-in help, result helpers, and simple human output, but Microcanvas still needs to keep its own CLI plumbing for strict unknown-command failures, branded `OK`/`ERR` output, typed flags, domain error codes, lock patterns, and full CLI output tests. + +The design should keep AgentTK provider-agnostic: AgentTK owns dispatch, JSON mode, stdout/stderr routing, argument schema parsing, and stable envelopes; downstream tools own domain commands, human voice, brand coloring, and domain-specific error codes. + +## Goals / Non-Goals + +**Goals:** +- Preserve existing runtime behavior by default while allowing strict agent-safe behavior by configuration. +- Add extension hooks that are small, typed, and composable rather than forcing downstream tools to replace the runtime. +- Make typed command arguments available to handlers without adding a parser dependency. +- Strengthen test helpers so agents can assert full CLI behavior through the same runtime path users exercise. +- Document blessed extension patterns for domain errors, custom renderers, strict automation policies, and copyable examples. + +**Non-Goals:** +- Do not build a complete CLI generator. +- Do not add provider-specific Microcanvas logic, colors, or command semantics to AgentTK core. +- Do not replace existing raw-arg access; typed parsing should be additive. +- Do not introduce an external parser or command framework dependency for this scope. + +## Decisions + +### Missing-command and unknown-command policies are runtime configuration + +Add `unknownCommand?: 'help' | 'fail'` and `missingCommand?: 'help' | 'fail'` to `ToolDefinition`, plus `onUnknownCommand` for advanced tools. The defaults remain `help` to preserve current human-friendly behavior. `agentSafeDefaults()` will set both policies to `fail` so strict automation surfaces machine-readable `UNKNOWN_COMMAND` failures whether the caller omits a command or misspells one. + +Alternative considered: always fail missing or unknown commands. That is cleaner for agents but would surprise existing human-first tools that just received help fallback behavior. + +### Render hooks are human-mode only + +Add `presentation.renderHelp`, `presentation.renderSuccess`, and `presentation.renderFailure`. JSON mode always renders the stable envelope/record unchanged. Human mode calls the hook first and uses built-in output as the fallback. Hooks return a string, string array, or void; AgentTK still writes to the appropriate stream. + +Alternative considered: let hooks write directly to streams. Direct writes are possible through `ctx`, but returning text keeps the common path testable and preserves AgentTK stream routing. + +### Typed args wrap raw args, not replace them + +Add `CommandDefinition.args` with `positionals` and a single typed `flags` map. Runtime parsing produces `invocation.args` while retaining `rawArgs`. Parser failures return `INVALID_INPUT` with field-level details before the handler runs. Supported v1 flag types are `boolean`, `string`, and `string[]`, with aliases, defaults, and optional custom parse functions. + +Alternative considered: adopt a parser package. Avoiding a dependency keeps AgentTK small and leaves room to grow only the subset agents repeatedly need. + +### Lifecycle hooks use precise result-mapping semantics + +Add `beforeCommand`, `afterCommand`, and `onError` hooks, plus wrapper helpers such as `withLock` and `withTelemetry`. Hooks receive a normalized lifecycle event with the resolved command, parsed args when available, raw args, context, and result/error where relevant. `beforeCommand` may return a `CommandResult` to abort before the handler. `afterCommand` may return a replacement `CommandResult`. `onError` maps thrown errors from handlers or lifecycle hooks into a structured result, but parser-created `INVALID_INPUT` failures do not flow through `onError`. + +Alternative considered: expose a generic plugin system. That is too broad for this scope and conflicts with AgentTK's framework-not-generator boundary. + +### Testing helpers assert rendered CLI behavior + +Build on `runCli` capture helpers with an `expectCli` fluent helper. It runs the same `ToolRuntime.run` path and exposes assertions for JSON, stdout, stderr, success, failure code, and snapshots where the repo's test runner supports them. + +Alternative considered: process-level CLI spawning. That belongs in downstream integration tests; AgentTK should keep fast in-process tests for framework behavior. + +## Risks / Trade-offs + +- [Risk] Typed parsing could drift into a full parser clone. -> Mitigation: keep v1 grammar limited and document unsupported shell-level parsing, subcommands, and coercions. +- [Risk] Render hooks could make output inconsistent across tools. -> Mitigation: JSON mode remains stable and docs define render hooks as human presentation only. +- [Risk] Middleware can obscure control flow. -> Mitigation: start with lifecycle events plus explicit wrapper helpers instead of a broad plugin system. +- [Risk] Default unknown-command behavior conflicts with archived spec text. -> Mitigation: update the runtime spec to make the default and strict policy explicit, then align tests with that contract. +- [Risk] Example templates can become stale. -> Mitigation: keep the first example set small, copyable, and covered by smoke commands rather than generated output. + +## Migration Plan + +1. Add type definitions and runtime branches behind optional fields, starting with the Microcanvas-first slice. +2. Add tests for default behavior and strict policy behavior before updating implementation. +3. Preserve existing raw-arg handlers and built-in output fallback. +4. Update README and examples to document strict Microcanvas-style configuration with a small example set. +5. Validate with `npm run verify` and `openspec validate customizable-cli-runtime-behavior --strict`. + +Rollback is straightforward because the change is additive: remove optional fields and helpers if tests reveal incompatibility before release. + +## Open Questions + +- Should argument schemas support numeric coercion in v1? This design defers numbers unless a downstream CLI needs them immediately. diff --git a/openspec/changes/customizable-cli-runtime-behavior/proposal.md b/openspec/changes/customizable-cli-runtime-behavior/proposal.md new file mode 100644 index 0000000..60ff3d2 --- /dev/null +++ b/openspec/changes/customizable-cli-runtime-behavior/proposal.md @@ -0,0 +1,38 @@ +## Why + +AgentTK now owns enough CLI runtime behavior that downstream tools such as Microcanvas need explicit extension points instead of forking dispatch, argument parsing, or human presentation. The current runtime can produce useful help and stable result envelopes, but tool-specific CLIs still need machine-friendly unknown-command failures, branded human output, typed args, domain error codes, and golden-path CLI tests. + +## What Changes + +- Add configurable missing-command and unknown-command behavior so tools can choose help fallback or structured `UNKNOWN_COMMAND` failure, with an optional `onUnknownCommand` hook. +- Add presentation render hooks for help, success, and failure while preserving AgentTK ownership of dispatch, JSON mode, stdout, and stderr routing. +- Add first-class typed command argument parsing for positionals and a unified typed `flags` map without introducing an external parser dependency. +- Bless custom domain error-code overrides in result helpers while retaining stable framework/domain categories. +- Add lifecycle and middleware hooks for cross-cutting command behavior such as before/after hooks, error mapping, lock wrappers, and telemetry wrappers. +- Add stronger CLI testing helpers for full runtime behavior, including stdout, stderr, JSON, and result assertions. +- Extend record presentation fields to support nested paths and per-field formatters. +- Add a small set of copyable CLI example templates for the highest-leverage agent-authored CLI shapes rather than a generator. +- Add an agent-safe defaults policy helper and document the upstream extension contract for strict automation behavior. +- No breaking changes: existing tools should continue to compile and keep their current behavior unless they opt into stricter policies. + +## Capabilities + +### New Capabilities +- `cli-argument-parsing`: Typed command argument schemas for positionals, typed flags, aliases, defaults, and validation failures. +- `cli-lifecycle-hooks`: Runtime hooks and composable wrappers for cross-cutting command behavior. +- `cli-policy-presets`: Stable defaults for agent-safe CLI behavior such as unknown-command failure, JSON flag naming, failure output posture, and recovery metadata expectations. +- `cli-example-templates`: Copyable examples for common agent-authored CLI patterns. + +### Modified Capabilities +- `tool-runtime`: Missing-command behavior, unknown-command behavior, command invocation shape, render hook integration, and runtime policy options. +- `command-help`: Help rendering extensibility and help records compatible with custom renderers. +- `command-blocks`: Human output presentation hooks, nested record field presentation, and documented custom domain error-code override patterns. +- `testing-kit`: Full CLI behavior assertions for stdout, stderr, JSON, and result matching. +- `agent-authoring-doctrine`: Extension contract that keeps domain voice, strict automation posture, and domain errors in downstream tools while AgentTK owns runtime plumbing. + +## Impact + +- Affected public APIs: `ToolDefinition`, `CommandDefinition`, `CommandContext`, command invocation objects, presentation types, result helper options, and testing helper exports. +- Affected implementation areas: `src/core/create-tool.ts`, `src/core/define-command.ts`, `src/core/run-tool-cli.ts`, `src/core/types.ts`, `src/blocks/args.ts`, `src/blocks/output.ts`, `src/blocks/results.ts`, and `src/testing/*`. +- Affected docs/examples: README API reference, runtime-kit examples, and new focused examples for minimal, strict rich-output, and mutating locked CLIs. +- No new runtime dependency is expected; argument parsing should stay lightweight and framework-local. diff --git a/openspec/changes/customizable-cli-runtime-behavior/specs/agent-authoring-doctrine/spec.md b/openspec/changes/customizable-cli-runtime-behavior/specs/agent-authoring-doctrine/spec.md new file mode 100644 index 0000000..fd48a0b --- /dev/null +++ b/openspec/changes/customizable-cli-runtime-behavior/specs/agent-authoring-doctrine/spec.md @@ -0,0 +1,19 @@ +## ADDED Requirements + +### Requirement: Upstream extension contract +The system SHALL document the intended extension contract for agent-authored CLIs. + +#### Scenario: Tool needs custom human voice +- **WHEN** a downstream tool needs branded colors, concise status lines, or domain-specific human phrasing +- **THEN** AgentTK documentation directs the tool to use presentation render hooks +- **AND** AgentTK remains responsible for dispatch, JSON mode, and stream routing + +#### Scenario: Tool needs custom domain errors +- **WHEN** a downstream tool needs stable domain error codes +- **THEN** AgentTK documentation directs the tool to use result helper code overrides +- **AND** the tool keeps the standard failure envelope + +#### Scenario: Tool needs strict automation behavior +- **WHEN** a downstream tool must be safe for machine callers +- **THEN** AgentTK documentation directs the tool to configure unknown-command failure or use agent-safe defaults +- **AND** the tool does not need to fork runtime dispatch to get strict behavior diff --git a/openspec/changes/customizable-cli-runtime-behavior/specs/cli-argument-parsing/spec.md b/openspec/changes/customizable-cli-runtime-behavior/specs/cli-argument-parsing/spec.md new file mode 100644 index 0000000..39fe739 --- /dev/null +++ b/openspec/changes/customizable-cli-runtime-behavior/specs/cli-argument-parsing/spec.md @@ -0,0 +1,50 @@ +## ADDED Requirements + +### Requirement: Command argument schema +The system SHALL allow commands to define typed CLI argument schemas. + +#### Scenario: Required positional argument parses +- **WHEN** a command defines a required positional argument +- **AND** the CLI invocation provides that positional value +- **THEN** the command handler receives the value by positional name in parsed args +- **AND** the original raw args remain available + +#### Scenario: Missing required positional fails +- **WHEN** a command defines a required positional argument +- **AND** the CLI invocation omits that value +- **THEN** the runtime returns an `INVALID_INPUT` failure before invoking the handler +- **AND** the failure details identify the missing argument + +#### Scenario: Boolean flag parses aliases +- **WHEN** a command defines a boolean flag with aliases +- **AND** the CLI invocation includes any alias +- **THEN** the command handler receives `true` for that flag name + +#### Scenario: String flag parses value +- **WHEN** a command defines a string flag +- **AND** the CLI invocation includes the flag followed by a value +- **THEN** the command handler receives that value by flag name + +#### Scenario: String array flag parses repeated values +- **WHEN** a command defines a string-array flag +- **AND** the CLI invocation includes the flag more than once +- **THEN** the command handler receives all provided values by flag name + +#### Scenario: Unknown flag fails +- **WHEN** a command has an argument schema +- **AND** the CLI invocation includes a flag not defined by that schema +- **THEN** the runtime returns an `INVALID_INPUT` failure before invoking the handler +- **AND** the failure details identify the unknown flag + +### Requirement: Parser scope boundaries +The system SHALL keep built-in CLI argument parsing intentionally lightweight. + +#### Scenario: Shell parsing remains external +- **WHEN** a command receives argv values +- **THEN** AgentTK parses the provided argv tokens +- **AND** AgentTK does not attempt shell quoting or command-line string tokenization + +#### Scenario: Raw arg helpers remain available +- **WHEN** a command does not define an argument schema +- **THEN** the command continues to receive raw args +- **AND** existing helpers such as `firstPositional` and `hasFlag` remain usable diff --git a/openspec/changes/customizable-cli-runtime-behavior/specs/cli-example-templates/spec.md b/openspec/changes/customizable-cli-runtime-behavior/specs/cli-example-templates/spec.md new file mode 100644 index 0000000..712d676 --- /dev/null +++ b/openspec/changes/customizable-cli-runtime-behavior/specs/cli-example-templates/spec.md @@ -0,0 +1,16 @@ +## ADDED Requirements + +### Requirement: Copyable CLI examples +The system SHALL provide a small set of copyable example CLIs that demonstrate the highest-leverage AgentTK patterns. + +#### Scenario: Minimal CLI example +- **WHEN** a downstream agent needs the smallest useful AgentTK CLI +- **THEN** the examples include a minimal CLI with one command and `runToolCli` + +#### Scenario: Strict rich-output CLI example +- **WHEN** a downstream agent needs strict automation behavior with custom human presentation +- **THEN** the examples include a CLI that demonstrates strict missing-command and unknown-command failure plus help, success, and failure render hooks + +#### Scenario: Mutating locked CLI example +- **WHEN** a downstream agent needs a write command with dry-run behavior and runtime locking +- **THEN** the examples include a mutating CLI that demonstrates dry-run, mutation-safety metadata, and lock handling diff --git a/openspec/changes/customizable-cli-runtime-behavior/specs/cli-lifecycle-hooks/spec.md b/openspec/changes/customizable-cli-runtime-behavior/specs/cli-lifecycle-hooks/spec.md new file mode 100644 index 0000000..f11af26 --- /dev/null +++ b/openspec/changes/customizable-cli-runtime-behavior/specs/cli-lifecycle-hooks/spec.md @@ -0,0 +1,59 @@ +## ADDED Requirements + +### Requirement: Command lifecycle hooks +The system SHALL provide lifecycle hooks for cross-cutting command behavior. + +#### Scenario: Before-command hook runs before handler +- **WHEN** a tool defines a `beforeCommand` hook +- **AND** a known command is invoked +- **THEN** the hook runs after command resolution and argument parsing +- **AND** the command handler runs only after the hook completes successfully + +#### Scenario: Before-command hook aborts with result +- **WHEN** a tool defines a `beforeCommand` hook +- **AND** the hook returns a command result +- **THEN** the command handler is not invoked +- **AND** the runtime renders the hook result through normal output handling + +#### Scenario: After-command hook observes result +- **WHEN** a tool defines an `afterCommand` hook +- **AND** a command handler returns a result +- **THEN** the hook receives the command event and result +- **AND** the runtime renders the final result after the hook completes + +#### Scenario: After-command hook replaces result +- **WHEN** a tool defines an `afterCommand` hook +- **AND** the hook returns a command result +- **THEN** the runtime uses that result as the final command result +- **AND** the replacement result is rendered through normal output handling + +#### Scenario: Error hook maps thrown errors +- **WHEN** a command handler or lifecycle hook throws +- **AND** a tool defines an `onError` hook +- **THEN** the hook can convert the thrown error into a structured command result +- **AND** the runtime renders that result through normal output handling + +#### Scenario: Parser failures bypass error hook +- **WHEN** argument parsing returns a structured `INVALID_INPUT` failure +- **AND** a tool defines an `onError` hook +- **THEN** the error hook is not invoked +- **AND** the parser failure is rendered through normal output handling + +### Requirement: Composable runtime wrappers +The system SHALL provide small wrapper helpers for common lifecycle patterns. + +#### Scenario: Lock wrapper releases on success +- **WHEN** a command is wrapped with `withLock` +- **AND** the wrapped handler succeeds +- **THEN** the configured lock is acquired before execution +- **AND** the lock is released after execution + +#### Scenario: Lock wrapper releases on failure +- **WHEN** a command is wrapped with `withLock` +- **AND** the wrapped handler fails or throws +- **THEN** the lock is released before the result or mapped error is returned + +#### Scenario: Telemetry wrapper observes outcome +- **WHEN** a command is wrapped with `withTelemetry` +- **THEN** the telemetry callback can observe command start and completion +- **AND** the callback receives success or failure state without changing the structured result by default diff --git a/openspec/changes/customizable-cli-runtime-behavior/specs/cli-policy-presets/spec.md b/openspec/changes/customizable-cli-runtime-behavior/specs/cli-policy-presets/spec.md new file mode 100644 index 0000000..da334d5 --- /dev/null +++ b/openspec/changes/customizable-cli-runtime-behavior/specs/cli-policy-presets/spec.md @@ -0,0 +1,28 @@ +## ADDED Requirements + +### Requirement: Agent-safe CLI defaults +The system SHALL provide a stable policy helper for strict agent-safe CLI defaults. + +#### Scenario: Agent-safe defaults configure missing-command failure +- **WHEN** a tool applies `agentSafeDefaults` +- **THEN** the resulting tool configuration uses structured missing-command failure +- **AND** missing commands return code `UNKNOWN_COMMAND` + +#### Scenario: Agent-safe defaults configure unknown-command failure +- **WHEN** a tool applies `agentSafeDefaults` +- **THEN** the resulting tool configuration uses structured unknown-command failure +- **AND** unknown commands return code `UNKNOWN_COMMAND` + +#### Scenario: Agent-safe defaults preserve JSON flag +- **WHEN** a tool applies `agentSafeDefaults` +- **THEN** the resulting tool configuration uses the standard `--json` flag unless explicitly overridden by the tool + +#### Scenario: Agent-safe defaults document failure output posture +- **WHEN** a tool applies `agentSafeDefaults` +- **THEN** the policy documents that human failures are written to stderr +- **AND** JSON mode remains machine-readable on stdout + +#### Scenario: Agent-safe defaults are overridable +- **WHEN** a tool applies `agentSafeDefaults` with explicit overrides +- **THEN** explicit tool settings take precedence over preset defaults +- **AND** the resulting configuration remains a normal AgentTK tool definition diff --git a/openspec/changes/customizable-cli-runtime-behavior/specs/command-blocks/spec.md b/openspec/changes/customizable-cli-runtime-behavior/specs/command-blocks/spec.md new file mode 100644 index 0000000..4201226 --- /dev/null +++ b/openspec/changes/customizable-cli-runtime-behavior/specs/command-blocks/spec.md @@ -0,0 +1,68 @@ +## MODIFIED Requirements + +### Requirement: Output rendering for human mode +The system SHALL render concise human-readable output when JSON mode is disabled and SHALL allow tools to override human presentation through render hooks. + +#### Scenario: Human success output includes receipt details +- **WHEN** a successful result is rendered in human mode +- **THEN** the output includes the result type +- **AND** it includes the id when present +- **AND** it includes the destination when present + +#### Scenario: Human output shows warnings +- **WHEN** a successful result contains warnings +- **THEN** the warnings are surfaced in human mode rather than silently dropped + +#### Scenario: Human failure output is actionable +- **WHEN** a failed result is rendered in human mode +- **THEN** the output includes the error code and message in a concise actionable format + +#### Scenario: Human render hook overrides built-in success output +- **WHEN** a successful result is rendered in human mode +- **AND** the tool defines a success render hook +- **THEN** the hook output is written instead of the built-in human success format +- **AND** warnings and metadata remain available in the result object for the hook to include + +#### Scenario: Human render hook overrides built-in failure output +- **WHEN** a failed result is rendered in human mode +- **AND** the tool defines a failure render hook +- **THEN** the hook output is written instead of the built-in human failure format +- **AND** the runtime still writes failures to stderr + +## ADDED Requirements + +### Requirement: Nested record field presentation +The system SHALL support record presentation fields that read nested values from successful result records. + +#### Scenario: Nested field renders by path +- **WHEN** a successful result record contains nested data +- **AND** presentation record fields include a dotted `path` +- **THEN** human output renders the nested value with the configured label + +#### Scenario: Field formatter renders nested value +- **WHEN** a presentation field defines a formatter +- **THEN** the formatter receives the selected nested value, record, and result +- **AND** the formatted value is rendered in human output + +#### Scenario: Missing nested field is skipped +- **WHEN** a presentation field path does not exist in the result record +- **THEN** the field is omitted from built-in human output +- **AND** rendering continues for other fields + +### Requirement: Custom domain error codes in result helpers +The system SHALL document and support domain-specific error-code overrides in reusable result helpers. + +#### Scenario: Invalid input uses domain code +- **WHEN** a tool creates an invalid-input result with a custom code +- **THEN** the returned failure preserves the custom code +- **AND** the result still uses the stable AgentTK failure envelope + +#### Scenario: Operational failure uses domain code +- **WHEN** a tool creates an operational-failure result with a custom code +- **THEN** the returned failure preserves the custom code +- **AND** the result can still include recovery metadata and details + +#### Scenario: Documentation blesses code overrides +- **WHEN** a downstream agent needs tool-specific failures such as `UNSUPPORTED_CONTENT`, `VERIFY_FAILED`, or `VIEWER_LAUNCH_FAILED` +- **THEN** AgentTK documentation identifies helper code overrides as the preferred path +- **AND** the agent does not need to bypass result helpers to keep domain codes diff --git a/openspec/changes/customizable-cli-runtime-behavior/specs/command-help/spec.md b/openspec/changes/customizable-cli-runtime-behavior/specs/command-help/spec.md new file mode 100644 index 0000000..9708f42 --- /dev/null +++ b/openspec/changes/customizable-cli-runtime-behavior/specs/command-help/spec.md @@ -0,0 +1,21 @@ +## ADDED Requirements + +### Requirement: Custom help rendering +The system SHALL allow tools to customize human help output without replacing AgentTK help records. + +#### Scenario: Tool help uses custom renderer +- **WHEN** a tool help result is rendered in human mode +- **AND** the tool defines `presentation.renderHelp` +- **THEN** the renderer receives the structured tool help record +- **AND** the runtime writes the returned human text to stdout + +#### Scenario: Command help uses custom renderer +- **WHEN** a command help result is rendered in human mode +- **AND** the tool defines `presentation.renderHelp` +- **THEN** the renderer receives the structured command help record +- **AND** the runtime writes the returned human text to stdout + +#### Scenario: Help JSON remains structured +- **WHEN** a help result is rendered in JSON mode +- **THEN** the runtime writes the structured help result as JSON +- **AND** custom human help renderers do not modify the payload diff --git a/openspec/changes/customizable-cli-runtime-behavior/specs/testing-kit/spec.md b/openspec/changes/customizable-cli-runtime-behavior/specs/testing-kit/spec.md new file mode 100644 index 0000000..d7dc21d --- /dev/null +++ b/openspec/changes/customizable-cli-runtime-behavior/specs/testing-kit/spec.md @@ -0,0 +1,28 @@ +## ADDED Requirements + +### Requirement: Golden-path CLI behavior assertions +The system SHALL provide lightweight helpers for asserting full AgentTK CLI behavior in tests. + +#### Scenario: Assert JSON CLI output +- **WHEN** a test runs an AgentTK tool with CLI arguments through `expectCli` +- **THEN** the helper can assert the rendered stdout parses as JSON matching an expected shape +- **AND** the command result remains available for structured assertions + +#### Scenario: Assert stdout output +- **WHEN** a test runs an AgentTK tool with CLI arguments through `expectCli` +- **THEN** the helper can assert the captured stdout exactly or partially matches expected text +- **AND** stderr remains separately available + +#### Scenario: Assert stderr output +- **WHEN** a test runs an AgentTK tool with CLI arguments through `expectCli` +- **THEN** the helper can assert the captured stderr exactly or partially matches expected text +- **AND** stdout remains separately available + +#### Scenario: Assert failure code +- **WHEN** a test runs a failing AgentTK CLI command through `expectCli` +- **THEN** the helper can assert the structured failure code +- **AND** the assertion does not require parsing human output + +#### Scenario: Snapshot-friendly output +- **WHEN** a test framework supports snapshots +- **THEN** the helper exposes stable captured stdout, stderr, JSON, and result values suitable for snapshot assertions diff --git a/openspec/changes/customizable-cli-runtime-behavior/specs/tool-runtime/spec.md b/openspec/changes/customizable-cli-runtime-behavior/specs/tool-runtime/spec.md new file mode 100644 index 0000000..1f4a0de --- /dev/null +++ b/openspec/changes/customizable-cli-runtime-behavior/specs/tool-runtime/spec.md @@ -0,0 +1,80 @@ +## MODIFIED Requirements + +### Requirement: Command dispatch +The system SHALL dispatch execution by command name from CLI arguments and provide configurable missing-command and unknown-command handling. + +#### Scenario: Known command executes +- **WHEN** the tool is run with a registered command name +- **THEN** the matching command handler is invoked +- **AND** the remaining arguments are passed to the command as raw arguments +- **AND** parsed arguments are passed when the command defines an argument schema + +#### Scenario: Alias command executes +- **WHEN** the tool is run with a registered command alias +- **THEN** the matching canonical command handler is invoked +- **AND** the remaining arguments are passed to the command as raw arguments + +#### Scenario: Unknown command shows help by default +- **WHEN** the tool is run with a command name that is not registered +- **AND** the tool has not configured strict unknown-command behavior +- **THEN** the runtime returns the tool help result +- **AND** the output follows the same JSON or human rendering rules as explicit tool help + +#### Scenario: Unknown command fails when configured +- **WHEN** the tool is run with a command name that is not registered +- **AND** the tool configures `unknownCommand` as `fail` +- **THEN** the runtime returns a structured failure with code `UNKNOWN_COMMAND` +- **AND** the failure message identifies the unknown command +- **AND** the failure does not require downstream tools to parse ad hoc strings to determine what happened + +#### Scenario: Unknown command uses custom handler +- **WHEN** the tool is run with a command name that is not registered +- **AND** the tool defines `onUnknownCommand` +- **THEN** the runtime delegates unknown-command result creation to that handler +- **AND** the runtime still owns JSON mode and stdout/stderr rendering for the returned result + +#### Scenario: Missing command shows help by default +- **WHEN** the tool is run without providing a command name +- **AND** the tool has not configured strict missing-command behavior +- **THEN** the runtime returns help for the tool +- **AND** downstream tools can still request stricter automation behavior independently + +#### Scenario: Missing command fails when configured +- **WHEN** the tool is run without providing a command name +- **AND** the tool configures `missingCommand` as `fail` +- **THEN** the runtime returns a structured failure with code `UNKNOWN_COMMAND` +- **AND** the failure message states that no command was provided for the tool + +### Requirement: Command execution context +The system SHALL provide a shared execution context to command handlers and runtime extension hooks. + +#### Scenario: Context is available to handlers +- **WHEN** a command handler runs +- **THEN** it receives a context including the tool name, JSON output mode flag, stdout/stderr streams, presentation hooks, and runtime policy + +#### Scenario: Context is available to render hooks +- **WHEN** a presentation render hook runs +- **THEN** it receives the same command context used by the runtime +- **AND** the hook can inspect JSON mode and tool identity without taking over dispatch + +## ADDED Requirements + +### Requirement: Human render hook integration +The system SHALL allow tools to customize human help, success, and failure presentation while preserving stable runtime behavior. + +#### Scenario: Custom success renderer +- **WHEN** a command succeeds in human mode +- **AND** the tool defines `presentation.renderSuccess` +- **THEN** the runtime uses the rendered text for stdout +- **AND** the structured success result returned from `run` is unchanged + +#### Scenario: Custom failure renderer +- **WHEN** a command fails in human mode +- **AND** the tool defines `presentation.renderFailure` +- **THEN** the runtime uses the rendered text for stderr +- **AND** the structured failure result returned from `run` is unchanged + +#### Scenario: JSON mode bypasses human render hooks +- **WHEN** a command result is rendered in JSON mode +- **THEN** the runtime writes the structured result as JSON +- **AND** human presentation hooks do not alter the JSON payload diff --git a/openspec/changes/customizable-cli-runtime-behavior/tasks.md b/openspec/changes/customizable-cli-runtime-behavior/tasks.md new file mode 100644 index 0000000..d51cbeb --- /dev/null +++ b/openspec/changes/customizable-cli-runtime-behavior/tasks.md @@ -0,0 +1,53 @@ +## 1. Microcanvas-First Runtime Slice + +- [ ] 1.1 Add public types for unknown-command policy, unknown-command hook input, runtime policy, lifecycle hooks, and render hooks. +- [ ] 1.2 Implement `unknownCommand: "help" | "fail"` and `missingCommand: "help" | "fail"` in `createTool`, preserving help fallback as the default. +- [ ] 1.3 Implement `onUnknownCommand` and route its returned result through normal JSON/human rendering. +- [ ] 1.4 Add tests for default unknown help, strict unknown failure, strict missing failure, custom unknown handler, and no-arg tool help. + +## 2. Presentation Hooks And Record Fields + +- [ ] 2.1 Add `presentation.renderHelp`, `presentation.renderSuccess`, and `presentation.renderFailure` support for human mode. +- [ ] 2.2 Ensure JSON mode bypasses human render hooks and preserves structured result payloads. +- [ ] 2.3 Extend record fields to accept dotted `path`, labels, and per-field formatters. +- [ ] 2.4 Add tests for custom help/success/failure renderers, nested record paths, missing nested fields, and formatted fields. + +## 3. Typed Command Args + +- [ ] 3.1 Add command `args` schema types for positionals and a unified typed `flags` map with aliases, defaults, and optional parse hooks. +- [ ] 3.2 Implement lightweight argv parsing in `src/blocks/args.ts` or a focused parser module. +- [ ] 3.3 Pass parsed args to handlers while preserving `rawArgs` and current raw-helper behavior. +- [ ] 3.4 Return structured `INVALID_INPUT` failures for missing positionals, missing flag values, parse failures, and unknown flags. +- [ ] 3.5 Add tests for required positionals, boolean flag aliases, string flags, string-array flags, defaults, unknown flags, and legacy raw handlers. + +## 4. Result Helpers And Policies + +- [ ] 4.1 Update result helper types/docs so `invalidInput`, `operationalFailure`, and related helpers explicitly support custom domain codes. +- [ ] 4.2 Implement `agentSafeDefaults` with strict missing-command and unknown-command failure plus documented output posture. +- [ ] 4.3 Add tests for domain code overrides and policy override precedence. + +## 5. Lifecycle And Wrappers + +- [ ] 5.1 Implement `beforeCommand`, `afterCommand`, and `onError` lifecycle hooks around command execution with explicit result replacement semantics. +- [ ] 5.2 Add `withLock` helper that acquires before handler execution and releases on success, failure, or throw. +- [ ] 5.3 Add `withTelemetry` helper that observes start/completion without mutating results by default. +- [ ] 5.4 Add tests for hook ordering, before-command aborts, after-command result replacement, error mapping, parser failures bypassing `onError`, lock release, and telemetry outcome capture. + +## 6. CLI Test Harness + +- [ ] 6.1 Add `expectCli` helper that runs an AgentTK tool in-process and captures result, stdout, and stderr. +- [ ] 6.2 Add fluent assertions for JSON output, stdout, stderr, success, and failure codes. +- [ ] 6.3 Keep existing `runCli` and assertion helpers backward-compatible. +- [ ] 6.4 Add smoke tests that demonstrate the new harness against a realistic tool definition. + +## 7. Docs And Examples + +- [ ] 7.1 Update README API docs for unknown-command policies, render hooks, typed args, custom domain codes, lifecycle hooks, policy presets, and test harness helpers. +- [ ] 7.2 Add copyable examples for minimal CLI, strict rich-output CLI, and mutating CLI with dry-run plus lock handling. +- [ ] 7.3 Ensure example smoke commands cover new examples without requiring network, credentials, or platform-specific services. + +## 8. Verification + +- [ ] 8.1 Run `openspec validate customizable-cli-runtime-behavior --strict` and fix any spec issues. +- [ ] 8.2 Run `npm run verify` and fix any type, lint, or test failures. +- [ ] 8.3 Review public exports for backwards compatibility and add missing exports before marking the change ready for review. diff --git a/openspec/specs/command-blocks/spec.md b/openspec/specs/command-blocks/spec.md index 9da5c86..239a8a1 100644 --- a/openspec/specs/command-blocks/spec.md +++ b/openspec/specs/command-blocks/spec.md @@ -1,11 +1,45 @@ # command-blocks Specification ## Purpose -TBD - created by archiving change add-agenttk-v0. Update Purpose after archive. +The **command-blocks** specification defines the reusable helper blocks that AgentTK exposes for common CLI command concerns. These blocks cover: + +- raw argument inspection +- validation and corrective guidance +- lookup resolution outcomes +- human and JSON result rendering +- dry-run annotation +- recovery, mutation-safety, and verification presentation +- structured confirmation-required failures for risky commands + +The goal of these blocks is to let downstream tools compose predictable command behavior without rewriting common result, rendering, and guardrail patterns. + ## Requirements + +### Requirement: Raw argument helpers +The system SHALL provide reusable helpers for inspecting raw CLI argument arrays. + +#### Scenario: Detect a flag in raw arguments +- **WHEN** a downstream tool checks raw arguments for a known flag or list of flags +- **THEN** AgentTK can return whether any matching flag is present + +#### Scenario: Read the first positional argument +- **WHEN** a downstream tool needs the first positional value from raw arguments +- **THEN** AgentTK can skip known flags +- **AND** it can skip the value belonging to any known flag that consumes a value +- **AND** it returns the first non-flag positional argument when present + ### Requirement: Validation helpers The system SHALL provide reusable validation helpers for command inputs. +#### Scenario: Schema validation returns parsed input +- **WHEN** a downstream tool validates input with a schema and the input is valid +- **THEN** AgentTK returns the parsed value + +#### Scenario: Schema validation returns structured failure +- **WHEN** a downstream tool validates input with a schema and the input is invalid +- **THEN** AgentTK returns a structured `VALIDATION_ERROR` failure +- **AND** the failure can include recovery metadata + #### Scenario: Config diagnostics reuse structured validation style - **WHEN** a downstream tool validates environment or config inputs before command execution - **THEN** AgentTK can return a structured failure with actionable guidance using the same predictable envelope style as other validation helpers @@ -13,11 +47,39 @@ The system SHALL provide reusable validation helpers for command inputs. ### Requirement: Corrective guidance in validation failures The system SHALL support validation failures that include corrective guidance. +#### Scenario: Validation failures include expected payload guidance +- **WHEN** a downstream tool wants to describe the expected payload shape +- **THEN** AgentTK can include that guidance in the failure message + +#### Scenario: Validation failures include next-step guidance +- **WHEN** a downstream tool wants to point the operator at the next corrective step +- **THEN** AgentTK can include that guidance in the failure message + #### Scenario: Lookup failures point to the next narrowing step - **WHEN** a resolution helper returns a not-found or ambiguous-match outcome - **THEN** AgentTK can surface next-step guidance such as listing records or retrying with an explicit id - **AND** the guidance remains concise in human mode and structured in JSON mode +### Requirement: Lookup resolution helpers +The system SHALL provide reusable helpers for returning lookup resolution outcomes. + +#### Scenario: Resolve by explicit id +- **WHEN** a downstream tool has an explicit id match +- **THEN** AgentTK can return a successful resolution with strategy `id` + +#### Scenario: Resolve by query +- **WHEN** a downstream tool has a single query match +- **THEN** AgentTK can return a successful resolution with strategy `query` + +#### Scenario: Resolve one returns not-found guidance +- **WHEN** a downstream tool attempts to resolve one candidate and no candidates match +- **THEN** AgentTK can return a structured `NOT_FOUND` failure with query and next-step details + +#### Scenario: Resolve one returns ambiguous-match guidance +- **WHEN** a downstream tool attempts to resolve one candidate and multiple candidates match +- **THEN** AgentTK can return a structured `AMBIGUOUS_MATCH` failure +- **AND** the failure can include a compact list of candidate summaries + ### Requirement: Output rendering for JSON mode The system SHALL render command results as structured JSON when JSON mode is enabled. @@ -27,7 +89,7 @@ The system SHALL render command results as structured JSON when JSON mode is ena #### Scenario: Failure renders as JSON - **WHEN** a failed command result is rendered with JSON mode enabled -- **THEN** the exact structured failure envelope is written as formatted JSON +- **THEN** the exact structured failure envelope is written to stdout as formatted JSON - **AND** the output remains machine-readable without requiring downstream parsers to strip presentation text ### Requirement: Output rendering for human mode @@ -39,16 +101,42 @@ The system SHALL render concise human-readable output when JSON mode is disabled - **AND** it includes the id when present - **AND** it includes the destination when present +#### Scenario: Human success output reflects dry-run posture +- **WHEN** a successful result is marked as a dry run +- **THEN** the human output begins with `Dry run` instead of `Saved` + #### Scenario: Human output shows warnings - **WHEN** a successful result contains warnings - **THEN** the warnings are surfaced in human mode rather than silently dropped +#### Scenario: Human output renders simple record fields +- **WHEN** a successful result contains an object record +- **THEN** AgentTK renders primitive record fields in human mode +- **AND** nested object values are omitted by default + +#### Scenario: Human output can use configured record field order +- **WHEN** a tool provides record field presentation settings +- **THEN** AgentTK renders only those configured fields in the declared order +- **AND** it uses configured labels when present + +#### Scenario: Human output can use a record formatter hook +- **WHEN** a tool provides a record formatting function +- **THEN** AgentTK renders the formatter output instead of the default record-field presentation + #### Scenario: Human failure output is actionable - **WHEN** a failed result is rendered in human mode - **THEN** the output includes the error code and message in a concise actionable format +#### Scenario: Human failures render on stderr +- **WHEN** a failed result is rendered in human mode +- **THEN** the rendered output is written to stderr + +#### Scenario: Human successes render on stdout +- **WHEN** a successful result is rendered in human mode +- **THEN** the rendered output is written to stdout + ### Requirement: Dry-run support -The system SHALL provide a reusable dry-run helper for successful mutation results. +The system SHALL provide a reusable dry-run helper for successful results. #### Scenario: Dry-run marks successful result - **WHEN** a successful command result is wrapped as a dry run @@ -59,13 +147,6 @@ The system SHALL provide a reusable dry-run helper for successful mutation resul - **WHEN** a failed command result is passed through the dry-run helper - **THEN** the original failure is returned unchanged -### Requirement: V0 scope boundaries -The system SHALL keep the v0 block set intentionally narrow. - -#### Scenario: V0 excludes non-core blocks -- **WHEN** AgentTK v0 is implemented -- **THEN** it does not require built-in auth doctor, provenance helpers, lookup resolution, plugin loading, or workflow composition to satisfy the v0 contract - ### Requirement: Recovery metadata rendering The system SHALL preserve recovery metadata in both JSON and human rendering modes. @@ -100,5 +181,11 @@ The system SHALL support a structured failure for risky commands that require co #### Scenario: Risky command is not confirmed - **WHEN** a downstream tool requires confirmation for a risky command and confirmation is not present - **THEN** AgentTK can return a structured `CONFIRMATION_REQUIRED` failure -- **AND** the failure can preserve risk level, confirmation posture, and next-step guidance in a reusable format +- **AND** the failure can preserve risk level, confirmation posture, reason, and next-step guidance in a reusable format + +### Requirement: Current block surface +The system SHALL expose a broader reusable block surface than the initial v0-only core helpers. +#### Scenario: Exported blocks include specialized helpers +- **WHEN** a downstream tool imports AgentTK blocks from the package entrypoint +- **THEN** it can use argument, validation, output, dry-run, lookup, risk, adapter, auth, and config helpers from the current public surface diff --git a/openspec/specs/tool-runtime/spec.md b/openspec/specs/tool-runtime/spec.md index a9b394b..c8d9b75 100644 --- a/openspec/specs/tool-runtime/spec.md +++ b/openspec/specs/tool-runtime/spec.md @@ -1,19 +1,29 @@ # tool-runtime Specification ## Purpose -TBD - created by archiving change add-agenttk-v0. Update Purpose after archive. +The **tool-runtime** specification defines a runtime for creating and executing tools with named commands in AgentTK. It provides a structured way to: + +- Define tools and commands with minimal boilerplate. +- Dispatch commands by name from CLI arguments. +- Handle success and failure outcomes with structured envelopes. +- Attach recovery, mutation-safety, verification, and risk metadata to command results. + +This specification ensures that tools are portable, predictable, and interoperable across the AgentTK ecosystem. + ## Requirements + ### Requirement: Tool creation and command registration The system SHALL provide a runtime for defining a named tool with one or more registered commands. #### Scenario: Create a tool with commands -- **WHEN** a developer defines an AgentTK tool with a name and a list of commands -- **THEN** the runtime stores that tool definition -- **AND** each command can be addressed by name during execution +- **WHEN** a developer defines an AgentTK tool with a name, optional description, and a list of commands +- **THEN** the runtime creates a `ToolRuntime` that stores the tool definition +- **AND** each command can be addressed by name or alias during execution #### Scenario: Define a command -- **WHEN** a developer defines a command with a name, optional description, and handler -- **THEN** the command is accepted as part of a tool definition without additional framework boilerplate +- **WHEN** a developer defines a command with a name, optional description, aliases, usage examples, and a handler function +- **THEN** the command is accepted as part of the tool definition without additional framework boilerplate +- **AND** the command can include risk metadata (e.g., `level`, `confirmation`, `reason`) ### Requirement: Command dispatch The system SHALL dispatch execution by command name from CLI arguments. @@ -28,31 +38,45 @@ The system SHALL dispatch execution by command name from CLI arguments. - **THEN** the matching canonical command handler is invoked - **AND** the remaining arguments are passed to the command as raw arguments -#### Scenario: Unknown command fails predictably +#### Scenario: Help request for tool +- **WHEN** the tool is run with `help`, `--help`, or `-h` as the command name +- **THEN** the runtime returns a help result for the tool, listing all available commands + +#### Scenario: Help request for command +- **WHEN** the tool is run with a command name followed by `--help` or `-h` +- **THEN** the runtime returns a help result for the specific command, including usage, examples, and risk metadata + +#### Scenario: Unknown command - **WHEN** the tool is run with a command name that is not registered -- **THEN** the runtime returns a structured failure with code `UNKNOWN_COMMAND` -- **AND** the failure does not require downstream tools to parse ad hoc strings to determine what happened +- **THEN** the runtime returns a help result for the tool, listing all available commands -#### Scenario: Missing command fails predictably +#### Scenario: Missing command - **WHEN** the tool is run without providing a command name -- **THEN** the runtime returns a structured failure with code `UNKNOWN_COMMAND` -- **AND** the message states that no command was provided for the tool +- **THEN** the runtime returns a help result for the tool, listing all available commands ### Requirement: Command execution context The system SHALL provide a shared execution context to command handlers. #### Scenario: Context is available to handlers - **WHEN** a command handler runs -- **THEN** it receives a context including the tool name, JSON output mode flag, and stdout/stderr streams +- **THEN** it receives a context including: + - The `toolName` + - A `json` flag indicating if JSON output mode is enabled + - `stdout` and `stderr` streams for output + - Optional `presentation` settings for formatting command output ### Requirement: Stable success envelope The system SHALL support a standard structured success envelope for command results. #### Scenario: Successful command result - **WHEN** a command succeeds -- **THEN** it can return an envelope containing `ok: true` -- **AND** it includes a `type` string -- **AND** it MAY include `destination`, `id`, `record`, `warnings`, and `dryRun` +- **THEN** it returns an envelope containing: + - `ok: true` + - A `type` string + - Optional fields: `destination`, `id`, `record`, `warnings`, `dryRun` + - Optional recovery metadata: `nextAction`, `classification`, `retryable` + - Optional mutation-safety metadata: `idempotencyKey`, `retrySafety`, `replayRisk`, `partial` + - Optional verification metadata: `verified`, `verificationStatus` #### Scenario: Success envelope is JSON-safe - **WHEN** a success result is rendered in JSON mode @@ -61,54 +85,72 @@ The system SHALL support a standard structured success envelope for command resu ### Requirement: Stable failure envelope The system SHALL support a standard structured failure envelope for command results. +#### Scenario: Command failure +- **WHEN** a command fails +- **THEN** it returns an envelope containing: + - `ok: false` + - An `error` object with `code` and `message` + - Optional `details` for additional context + - Optional recovery metadata: `nextAction`, `classification`, `retryable` + #### Scenario: Adapter failures stay normalized - **WHEN** a downstream command converts an adapter-layer failure into an AgentTK failure result -- **THEN** the failure envelope can preserve category and retryability hints without leaking raw provider response formats into the runtime contract +- **THEN** the failure envelope preserves category and retryability hints without leaking raw provider response formats into the runtime contract ### Requirement: Separate package consumption The system SHALL be usable as a dependency from separate TypeScript CLI repositories. #### Scenario: Import AgentTK from another repo - **WHEN** a developer installs AgentTK in another TypeScript CLI repository -- **THEN** they can import the public runtime primitives from the package entrypoint -- **AND** they do not need to copy internal files from the AgentTK repo to build a tool +- **THEN** they can import the public runtime primitives (e.g., `createTool`, `runToolCli`) from the package entrypoint +- **AND** they can define and run tools without copying internal files from the AgentTK repo ### Requirement: First-class recovery metadata on command results The system SHALL support first-class recovery metadata on AgentTK command result envelopes. -#### Scenario: Failure includes recovery metadata -- **WHEN** a downstream tool returns a failure outcome with recovery hints -- **THEN** the top-level failure envelope can include `nextAction`, `classification`, and `retryable` -- **AND** downstream automation does not need to scrape provider-specific details to understand the intended recovery lane - -#### Scenario: Success can include follow-up recovery metadata -- **WHEN** a downstream tool returns a successful outcome that still requires a follow-up step -- **THEN** the success envelope can include the same recovery metadata fields without breaking the standard success contract +#### Scenario: Command result includes recovery metadata +- **WHEN** a command returns a result (success or failure) +- **THEN** the result envelope can include: + - `nextAction`: The suggested next action (e.g., `fix_input`, `retry`, `confirm`, `abort`) + - `classification`: The category of the result (e.g., `user_action_required`, `transient`, `unknown`) + - `retryable`: A boolean indicating if the command can be retried ### Requirement: Mutation-safety metadata on command results The system SHALL support first-class mutation-safety metadata on AgentTK command result envelopes. -#### Scenario: Write result includes replay-safety cues -- **WHEN** a downstream tool returns the result of a mutation command -- **THEN** the result can include `idempotencyKey`, `retrySafety`, `replayRisk`, and `partial` -- **AND** those fields remain available to downstream automation without parsing human text +#### Scenario: Mutation result includes safety metadata +- **WHEN** a command returns the result of a mutation +- **THEN** the result can include: + - `idempotencyKey`: A unique key for idempotency + - `retrySafety`: The safety level of retrying the command + - `replayRisk`: The risk level of replaying the command + - `partial`: A boolean indicating if the mutation was partial + - `verified`: A boolean indicating if the mutation was verified + - `verificationStatus`: The status of verification (e.g., `verified`, `unverified`) ### Requirement: Verification metadata on command results -The system SHALL support first-class verification metadata on AgentTK command result envelopes. +The system SHALL support first-class verification metadata on AgentTK command result envelopes for mutation commands. #### Scenario: Successful mutation result is unverified -- **WHEN** a downstream tool returns a mutation result that has not yet been read back and confirmed -- **THEN** the result can include `verified: false` and `verificationStatus: unverified` -- **AND** the recovery layer can point the agent toward `verify_state` +- **WHEN** a command returns a mutation result that has not yet been read back and confirmed +- **THEN** the result includes `verified: false` and `verificationStatus: unverified` +- **AND** the `nextAction` can be set to `verify_state` #### Scenario: Successful mutation result is verified -- **WHEN** a downstream tool confirms the intended post-mutation state -- **THEN** the result can include `verified: true` and `verificationStatus: verified` +- **WHEN** a command confirms the intended post-mutation state +- **THEN** the result includes `verified: true` and `verificationStatus: verified` ### Requirement: Command risk metadata The system SHALL support provider-agnostic risk metadata on AgentTK command definitions. +#### Scenario: Define command with risk metadata +- **WHEN** a developer defines a command with risk metadata +- **THEN** the command can include: + - `level`: The risk level (e.g., `low`, `medium`, `high`) + - `confirmation`: The confirmation requirement (e.g., `none`, `recommended`, `required`) + - `reason`: The reason for the risk level + #### Scenario: Risk metadata appears in command help -- **WHEN** a downstream tool defines a command with risk metadata -- **THEN** the generated help records preserve that metadata so operators and agents can inspect the command posture before execution +- **WHEN** a tool generates help for a command with risk metadata +- **THEN** the help output includes the risk level, confirmation requirement, and reason