diff --git a/skills/dynamic-workflows/SKILL.md b/skills/dynamic-workflows/SKILL.md index 49487cbc..103b288f 100644 --- a/skills/dynamic-workflows/SKILL.md +++ b/skills/dynamic-workflows/SKILL.md @@ -1,167 +1,108 @@ --- name: dynamic-workflows -description: Orchestrate multi-agent coding workflows via DevSpace Dynamic Workflows (CLI or MCP). +description: Run programmable multi-agent workflows through the DevSpace CLI. --- -# Dynamic Workflows +# DevSpace Dynamic Workflows -Use this skill when the user wants multi-step, multi-agent orchestration — fan-out -review, migrate-and-verify, research panels — **not** a single subagent turn. +Use a workflow when the work benefits from a repeatable program: parallel +reviews, fan-out research, staged implementation, per-file pipelines, or a +review-and-fix loop. Use a direct subagent for one focused delegation. -## Entry points +This skill is CLI-only. A coding harness should create or select a workflow +script and invoke it with `devspace workflow`. -| Host | Surface | -|---|---| -| Coding agent (Claude Code, Codex, pi, …) | CLI + this skill | -| ChatGPT / MCP client | MCP tools `run_workflow` / `workflow_status` / `workflow_cancel` | +## CLI ```bash -devspace workflow run --file path/to/script.js [--arg k=v]... [--follow] -devspace workflow run --script-path path/to/script.js [--resume ] [--follow] -devspace workflow run --name review-auth [--follow] -devspace workflow run --resume -devspace workflow status [--follow] -devspace workflow cancel +devspace workflow run --file path/to/workflow.js [--arg key=value]... [--follow] +devspace workflow run --script-path path/to/workflow.js [--resume ] [--follow] +devspace workflow run --name [--arg key=value]... [--follow] +devspace workflow status [--follow] +devspace workflow cancel devspace workflow ls -devspace workflow calls -devspace workflow call -devspace workflow tui [runId] +devspace workflow calls +devspace workflow call ``` -Project named scripts live under `.devspace/workflows/.js`. +Named scripts are stored in the project’s `.devspace/workflows/` directory. +Workflow commands are scoped to the current Git checkout (or the current +directory when it is not a Git project). Use `--follow` for a live terminal +handoff; otherwise poll with `status`. -## Script shape +`--arg key=value` passes JSON values when the value is valid JSON and otherwise +passes a string. A failed or cancelled run can be started again with +`workflow run --resume ` after reviewing its status and call results. -```js -export const meta = { - name: 'review-auth', - description: 'Fan-out review of auth changes', - phases: [{ title: 'Review' }, { title: 'Synthesize' }], - // optional DevSpace: - // defaultProvider: 'codex', - // concurrency: 4, -} +## Script capabilities -phase('Review') -const findings = await parallel([ - () => agent('Review for correctness…', { label: 'correctness' }), - () => agent('Review for security…', { label: 'security' }), -]) -phase('Synthesize') -const summary = await agent(`Synthesize: ${JSON.stringify(findings)}`) -return { summary, findings } -``` - -### Primitives +Workflow scripts are JavaScript modules with a metadata export and an async +body. The orchestration API includes: -| API | Notes | -|---|---| -| `agent(prompt, opts?)` | Throws on failure. `opts`: `label`, `phase`, `schema`, `model`, `effort`, `profile` or `provider`, `isolation: 'worktree'` | -| `parallel(thunks)` | Barrier; throw → `null` slot | -| `pipeline(items, ...stages)` | Per-item chains; no cross-item barrier | -| `phase(title)` / `log(msg)` | Progress; journaled | -| `args` | Run input (object preferred) | -| `workflow(name\|{scriptPath}, args?)` | Nested, depth 1, shared call index | +| Capability | Use | +| --- | --- | +| `agent(prompt, options?)` | Ask one configured profile or provider to perform a unit of work. | +| `parallel(thunks)` | Run independent units together and collect their results. | +| `pipeline(items, ...stages)` | Apply the same sequence of agent stages to each item. | +| `phase(title)` | Group later work under a named stage. | +| `log(message)` | Emit progress text for the supervising harness. | +| `args` | Read values passed with `--arg`. | +| `workflow(nameOrPath, args?)` | Compose a named or project workflow as a step. | -**No `writeMode`.** Teach read-only vs write in the prompt. Use `isolation: 'worktree'` when parallel mutators would conflict (git required). +An `agent` can select `profile` or `provider`, and can set `label`, `phase`, +`schema`, `model`, `effort`, or `isolation: 'worktree'`. Use a profile when one +is configured; use `provider` only when the target is intentional. `profile` +and `provider` are alternatives, not a combination. -### Determinism bans +The optional `schema` describes a JSON result, which is useful when later +stages consume structured findings. Prompts should say whether a child may +change files and what it should return. -`Date.now()`, `Math.random()`, and `new Date()` without args throw. Pass timestamps via `args` if needed. - -### Schema +## Basic script ```js -const out = await agent('Return JSON findings', { - schema: { - type: 'object', - properties: { bugs: { type: 'array', items: { type: 'string' } } }, - required: ['bugs'], - }, -}) -// out is validated object; engine retries ≤2 on invalid JSON -// codex/claude: native structured output first, then prompt repair; others: prompt+Ajv -``` - -### Providers - -Profiles exposed by `open_workspace` may be selected with `opts.profile`. The -profile supplies instructions, provider, model, and effort defaults; per-call -`model` and `effort` override those defaults. `profile` and `provider` are -mutually exclusive. - -Without a profile, default provider resolution is `opts.provider` → -`meta.defaultProvider` → first currently available provider. - -### Resume - -Failed and cancelled runs are terminal. Recovery creates a **new** run: - -1. Inspect the prior run with `workflow status`, `workflow calls`, and - `workflow call`. -2. Edit the persisted `scriptPath` reported by the run, or pass a different - `--script-path`. -3. Keep prompts and agent options stable for completed calls whose return values - should be reused. -4. Run `devspace workflow run --resume ` (optionally with - `--script-path `). - -Replay walks the prior run in call-index order and reuses the longest unchanged -prefix. The first failed, interrupted, changed, missing, corrupt, or unavailable -result executes live and closes replay for every later call, even when a later -cache key happens to match. Exact return values are stored separately from -bounded UI previews. - -Replay restores an agent's **return value**, not its execution. Shared-checkout -calls assume their existing filesystem effects are still present. Worktree calls -are never reused unless their exact worktree can be restored, so they currently -end the reusable prefix and run live. - -Return values must fit the replay budget (~1 MiB JSON). Oversized returns fail -the `agent()` call with `result_too_large` — prefer summaries or paths to large -artifacts on disk. - -### Cancel - -`workflow cancel` sets a cooperative flag; worker aborts then hard-kills if needed. - -## When to use CLI vs MCP +export const meta = { + name: 'review-changes', + description: 'Independent correctness and security review', +} -- **CLI**: host agent can shell; prefer for long runs + `--follow`. -- **TUI**: `devspace workflow tui` opens a read-only live view for workflows associated with the current working directory. -- **MCP**: ChatGPT plans; call `run_workflow`, then `workflow_status` until terminal. With full widgets enabled, workflow tool cards and the `open_workspace` dashboard show read-only live activity, including workflows launched through the CLI. Disconnecting MCP does **not** kill the worker. +phase('Review') +const [correctness, security] = await parallel([ + () => agent('Review the diff for correctness bugs. Return file paths and concrete findings.', { label: 'correctness' }), + () => agent('Review the diff for security issues. Return file paths, severity, and evidence.', { label: 'security' }), +]) -## Worked mini-examples +return { correctness, security } +``` -**1. Parallel review** +Run it with: -```js -export const meta = { name: 'p-review', description: 'Two reviewers' } -const [a, b] = await parallel([ - () => agent('Correctness review of the diff', { label: 'corr' }), - () => agent('Security review of the diff', { label: 'sec' }), -]) -return { a, b } +```bash +devspace workflow run --file .devspace/workflows/review-changes.js --follow ``` -**2. Pipeline with schema** +## Structured pipeline ```js -export const meta = { name: 'pipe', description: 'Find then fix plan' } +export const meta = { name: 'test-plan', description: 'Find and prioritize test gaps' } + return await pipeline( args.files, - (file) => agent(`List bugs in ${file}`, { schema: { type: 'object', properties: { bugs: { type: 'array', items: { type: 'string' } } }, required: ['bugs'] } }), - (findings, file) => agent(`Plan fixes for ${file}: ${JSON.stringify(findings)}`), + (file) => agent(`Find test gaps in ${file}`, { + schema: { + type: 'object', + properties: { gaps: { type: 'array', items: { type: 'string' } } }, + required: ['gaps'], + }, + }), + (findings, file) => agent(`Prioritize these gaps for ${file}: ${JSON.stringify(findings)}`), ) ``` -**3. Isolation for parallel writers** - -```js -export const meta = { name: 'iso', description: 'Parallel mutators' } -await parallel([ - () => agent('Implement feature A in isolation', { isolation: 'worktree', label: 'a' }), - () => agent('Implement feature B in isolation', { isolation: 'worktree', label: 'b' }), -]) -// dirty worktrees preserved; compose via return text / shared follow-up +```bash +devspace workflow run --name test-plan --arg files='["src/parser.ts","src/parser.test.ts"]' --follow ``` + +For parallel writers, request `isolation: 'worktree'` and make the prompt +describe how the result should be handed back. For sequential edits that must +see one another’s files, keep the stages in a pipeline or ordinary sequence. diff --git a/skills/subagents/SKILL.md b/skills/subagents/SKILL.md index caa4faff..2dfd1218 100644 --- a/skills/subagents/SKILL.md +++ b/skills/subagents/SKILL.md @@ -1,58 +1,87 @@ --- name: subagents -description: Delegate focused work to isolated DevSpace coding agents. +description: Delegate focused coding work to DevSpace subagents from a shell. --- -Each subagent is headless, has its own context window, cannot see the parent conversation, cannot ask the user, and cannot spawn subagents or workflows. Give every child a self-contained prompt with paths, constraints, and the expected report. +# DevSpace subagents -## Choose a target +Use a subagent for one focused piece of work: a second opinion, a narrow +investigation, a test plan, or an isolated implementation. Use a dynamic +workflow when the task needs several stages or programmable fan-out. -Prefer a configured profile that matches the task. Use a raw provider when the -user explicitly names that harness or no profile fits. Use target information -already available in the current host. When the choices are not known, run: +This skill uses the DevSpace CLI from any coding harness that can run shell +commands. + +## Discover available targets + +Run this before choosing a profile or provider when the available targets are +not already known: ```bash devspace agents targets +devspace agents targets --json ``` -Do not guess profile names or provider identifiers. +Configured profiles are preferred because they provide a reusable description +and defaults. A raw provider is useful when the user names a specific harness +or no matching profile exists. Do not guess a profile name. -## Write the brief +## Start and inspect work + +```bash +devspace agents run "" +devspace agents show +devspace agents ls +``` -Describe the task directly. Include decisions and constraints that exist only -in the parent conversation. Mention relevant paths or scope when useful. Do not -repeat project instructions that the child can discover from the repository. +The `run` command returns an agent id immediately. Use `show` to wait for the +final response or to read a later update. `ls` lists sessions for the current +project scope. Running the command from a subdirectory uses the enclosing Git +checkout; a non-Git directory uses the current directory. -## Run and continue +To continue the same session, use its id as the target: ```bash -devspace agents targets [--json] -devspace agents run "" -devspace agents show -devspace agents run "" -devspace agents ls +devspace agents run "Follow up by checking the failing test and report the cause." ``` -`targets` lists currently usable profiles and providers. `run` with a profile -or provider starts a child and returns its id. `show` reads its latest status -and response. `run` with an existing id continues the same child session. `ls` -lists sessions for the current project. +## Write a useful brief + +Give the child everything it needs without relying on the parent conversation: -Do not invoke provider CLIs directly; use `devspace agents` so DevSpace keeps -session and provider handling consistent. +- the exact goal and expected output; +- relevant files, commands, or boundaries; +- whether it may modify files; +- the checks it should run before reporting back. -## Model and effort overrides +The child’s final response is the handoff. Ask for concise findings, paths, or +patch-ready changes rather than a broad narrative. -Normally omit `--model` and `--effort`. When an exact override is needed, read -`references/.md` first. Do not guess values or transfer an effort -name between providers merely because both use the same word. +## Optional model controls + +Profiles normally supply model and effort defaults. When an exact override is +needed, pass: ```bash -devspace agents run --model --effort "" +devspace agents run --model --effort "" ``` -## Direct subagent or workflow +Only use values supplied by the user, a configured profile, or the target +catalog. Omit overrides when the provider’s accepted values are unknown. + +## Common uses + +```bash +# Ask for an independent security review. +devspace agents run reviewer "Review the authentication changes for vulnerabilities. Return findings with file paths and severity." + +# Delegate a small implementation and ask for verification. +devspace agents run implementer "Add a regression test for the parser bug. Run the focused test and report the result." + +# Continue after the parent has inspected the first response. +devspace agents run agt_1234abcd "The test still fails on Windows. Investigate only the path handling and report a fix." +``` -Use a direct subagent for one focused delegation or a follow-up with the same -child. Use a dynamic workflow when the task needs programmed fan-out, stages, -branching, nesting, or replay. +Keep direct delegation to one focused child at a time. For independent +reviewers, staged implementation, or repeatable fan-out, use the +`dynamic-workflows` skill. diff --git a/skills/subagents/references/claude.md b/skills/subagents/references/claude.md index d1d50141..c8c2b016 100644 --- a/skills/subagents/references/claude.md +++ b/skills/subagents/references/claude.md @@ -1,20 +1,9 @@ -# Claude overrides +# Claude target options -DevSpace passes `--model` to the Claude Agent SDK. When `--effort` is present, -DevSpace passes the SDK effort value with adaptive thinking enabled. - -The SDK effort vocabulary is: - -- `low` -- `medium` -- `high` -- `xhigh` -- `max` - -Support is model-dependent. Some Claude models expose only part of this set or -do not support the effort option. Prefer configured defaults and omit an -override when the selected model's capability is unknown. +Use the configured profile defaults whenever possible. Model and effort values +are installation- and model-dependent; pass an override only when the user or +the target catalog provides the exact value. ```bash -devspace agents run claude --model --effort "" +devspace agents run claude --model --effort "" ``` diff --git a/skills/subagents/references/codex.md b/skills/subagents/references/codex.md index ecc97d4c..0f5f24a1 100644 --- a/skills/subagents/references/codex.md +++ b/skills/subagents/references/codex.md @@ -1,19 +1,9 @@ -# Codex overrides +# Codex target options -DevSpace passes `--model` to the Codex SDK and maps `--effort` to model -reasoning effort. - -The SDK accepts these effort labels: - -- `minimal` -- `low` -- `medium` -- `high` -- `xhigh` - -The selected model may support only a subset. Prefer the profile or provider -default. Omit `--effort` when the exact model capability is unknown. +Use the configured profile defaults whenever possible. Model and effort values +are installation- and model-dependent; pass an override only when the user or +the target catalog provides the exact value. ```bash -devspace agents run codex --model --effort "" +devspace agents run codex --model --effort "" ``` diff --git a/skills/subagents/references/copilot.md b/skills/subagents/references/copilot.md index a23a9c55..e5a1772f 100644 --- a/skills/subagents/references/copilot.md +++ b/skills/subagents/references/copilot.md @@ -1,12 +1,9 @@ -# Copilot overrides +# Copilot target options -DevSpace connects to Copilot through ACP. `--model` selects the ACP `model` -option and `--effort` selects the ACP `thought_level` option. - -Both option sets are announced by the running Copilot ACP session and may vary -by version or account. Do not invent a value. Omit the override unless the user -provided an exact value known to that Copilot installation. +Use the configured profile defaults whenever possible. Model and effort values +are installation- and account-dependent; pass an override only when the user +or the target catalog provides the exact value. ```bash -devspace agents run copilot --model --effort "" +devspace agents run copilot --model --effort "" ``` diff --git a/skills/subagents/references/cursor.md b/skills/subagents/references/cursor.md index 09a7a167..824b9435 100644 --- a/skills/subagents/references/cursor.md +++ b/skills/subagents/references/cursor.md @@ -1,12 +1,9 @@ -# Cursor overrides +# Cursor target options -DevSpace connects to Cursor through ACP. `--model` selects the ACP `model` -option and `--effort` selects the ACP `thought_level` option. - -Both option sets are announced by the running Cursor ACP session and may vary -by version or account. Do not invent a value. Omit the override unless the user -provided an exact value known to that Cursor installation. +Use the configured profile defaults whenever possible. Model and effort values +are installation- and account-dependent; pass an override only when the user +or the target catalog provides the exact value. ```bash -devspace agents run cursor --model --effort "" +devspace agents run cursor --model --effort "" ``` diff --git a/skills/subagents/references/opencode.md b/skills/subagents/references/opencode.md index ef0ab01d..d3ce01e0 100644 --- a/skills/subagents/references/opencode.md +++ b/skills/subagents/references/opencode.md @@ -1,12 +1,9 @@ -# OpenCode overrides +# OpenCode target options -DevSpace passes `--model` to OpenCode. A model may be written as -`/` when the OpenCode provider id is needed. - -DevSpace maps `--effort` to the OpenCode model `variant` field. Variant names -are model-specific; there is no safe global effort list. Omit `--effort` unless -the exact variant is already known from the user's configuration or request. +Use the configured profile defaults whenever possible. Model and effort values +are installation- and model-dependent; pass an override only when the user or +the target catalog provides the exact value. ```bash -devspace agents run opencode --model --effort "" +devspace agents run opencode --model --effort "" ``` diff --git a/skills/subagents/references/pi.md b/skills/subagents/references/pi.md index 6953eaf5..4690f689 100644 --- a/skills/subagents/references/pi.md +++ b/skills/subagents/references/pi.md @@ -1,20 +1,9 @@ -# Pi overrides +# Pi target options -DevSpace passes `--model` to Pi and maps `--effort` to Pi's native -`--thinking` option. - -Pi accepts these thinking labels: - -- `off` -- `minimal` -- `low` -- `medium` -- `high` -- `xhigh` - -Pi applies model-specific capability rules, so a selected model may expose or -honor only a subset. Prefer the profile or provider default when uncertain. +Use the configured profile defaults whenever possible. Model and effort values +are installation- and model-dependent; pass an override only when the user or +the target catalog provides the exact value. ```bash -devspace agents run pi --model --effort "" +devspace agents run pi --model --effort "" ``` diff --git a/src/cli.ts b/src/cli.ts index 9fb69a77..d3ad8ccf 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -15,6 +15,7 @@ import { loadConfig } from "./config.js"; import { runLocalAgentProvider } from "./local-agent-adapters.js"; import { buildLocalAgentCatalog, + compactLocalAgentCatalog, formatLocalAgentCatalog, } from "./local-agent-catalog.js"; import { @@ -417,7 +418,7 @@ async function runAgentsTargets(args: string[]): Promise { ); console.log( args.includes("--json") - ? JSON.stringify(catalog, null, 2) + ? JSON.stringify(compactLocalAgentCatalog(catalog), null, 2) : formatLocalAgentCatalog(catalog), ); } diff --git a/src/local-agent-catalog.test.ts b/src/local-agent-catalog.test.ts index ad4bd4ad..ee5b9f50 100644 --- a/src/local-agent-catalog.test.ts +++ b/src/local-agent-catalog.test.ts @@ -1,6 +1,7 @@ import assert from "node:assert/strict"; import { buildLocalAgentCatalog, + compactLocalAgentCatalog, formatLocalAgentCatalog, } from "./local-agent-catalog.js"; import type { LocalAgentProfile } from "./local-agent-profiles.js"; @@ -29,6 +30,9 @@ const catalog = buildLocalAgentCatalog(profiles, [ { name: "claude", available: false, reason: "missing" }, ]); +assert.deepEqual(compactLocalAgentCatalog(catalog).providers, [{ name: "codex" }]); +assert.equal("model" in compactLocalAgentCatalog(catalog).providers[0]!, false); + assert.deepEqual(catalog.providers.map((provider) => provider.name), ["codex"]); assert.deepEqual(catalog.profiles.map((profile) => profile.name), ["reviewer"]); assert.equal(catalog.providers[0]?.effort.semantics, "reasoning_effort"); diff --git a/src/local-agent-catalog.ts b/src/local-agent-catalog.ts index da8775f2..a4985c35 100644 --- a/src/local-agent-catalog.ts +++ b/src/local-agent-catalog.ts @@ -16,6 +16,19 @@ export interface LocalAgentCatalog { profiles: ReturnType[]; } +export interface LocalAgentTargetCatalog { + providers: Array<{ name: string }>; + profiles: ReturnType[]; +} + +/** Keep model-facing target discovery focused on selectable values. */ +export function compactLocalAgentCatalog(catalog: LocalAgentCatalog): LocalAgentTargetCatalog { + return { + providers: catalog.providers.map(({ name }) => ({ name })), + profiles: catalog.profiles, + }; +} + export function formatLocalAgentCatalog(catalog: LocalAgentCatalog): string { const profileLines = catalog.profiles.length > 0 ? [ diff --git a/src/skills.test.ts b/src/skills.test.ts index 4bc585ea..f4ecb526 100644 --- a/src/skills.test.ts +++ b/src/skills.test.ts @@ -1,4 +1,4 @@ -import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises"; +import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import assert from "node:assert/strict"; @@ -279,3 +279,11 @@ try { else process.env.USERPROFILE = originalUserProfile; await rm(root, { recursive: true, force: true }); } + +const subagentsGuide = await readFile(new URL("../skills/subagents/SKILL.md", import.meta.url), "utf8"); +const workflowsGuide = await readFile(new URL("../skills/dynamic-workflows/SKILL.md", import.meta.url), "utf8"); +assert.match(subagentsGuide, /devspace agents run/); +assert.doesNotMatch(subagentsGuide, /MCP/i); +assert.match(workflowsGuide, /devspace workflow run/); +assert.doesNotMatch(workflowsGuide, /workflow tui/i); +assert.doesNotMatch(workflowsGuide, /MCP|replay walks|determinism bans/i);