diff --git a/skills/dynamic-workflows/SKILL.md b/skills/dynamic-workflows/SKILL.md index 49487cbc..e20273c8 100644 --- a/skills/dynamic-workflows/SKILL.md +++ b/skills/dynamic-workflows/SKILL.md @@ -1,167 +1,105 @@ --- name: dynamic-workflows -description: Orchestrate multi-agent coding workflows via DevSpace Dynamic Workflows (CLI or MCP). +description: Create and run resumable multi-agent orchestration with the DevSpace CLI. Use when work needs programmed fan-out, multiple phases, per-item pipelines, structured aggregation, isolated parallel writers, or recovery after a failed workflow; use a direct subagent for one bounded delegation. --- -# 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 the DevSpace CLI through the host's shell or process tool. Run commands from the project the workflow should operate on. DevSpace scopes runs to the host workspace when supplied, otherwise to the current Git repository or project directory. -## Entry points +Prefer `--json` from an agent harness: it starts or inspects work without holding one tool call open. Retain the returned workflow id and poll explicitly. Use `--follow` only when streaming output is useful and the shell tool supports a long-running process. Do not combine `--json` and `--follow`. -| Host | Surface | -|---|---| -| Coding agent (Claude Code, Codex, pi, …) | CLI + this skill | -| ChatGPT / MCP client | MCP tools `run_workflow` / `workflow_status` / `workflow_cancel` | +## Run and inspect ```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 ls -devspace workflow calls -devspace workflow call -devspace workflow tui [runId] +devspace workflow run --name [--arg key=value]... --json +devspace workflow run --file [--arg key=value]... --json +devspace workflow status --json +devspace workflow calls --json +devspace workflow call --json +devspace workflow cancel --json +devspace workflow ls --json ``` -Project named scripts live under `.devspace/workflows/.js`. +Named workflows live at `.devspace/workflows/.js`. `--script-path` is an alias for `--file`. `--arg key=value` accepts repeated run inputs through the script's `args` value. -## Script shape +Poll `status --json` until the workflow reaches `completed`, `failed`, or `cancelled`. Use `calls` for the compact child-call list and `call` for one call's prompt, result, or error. + +## Write a workflow + +The first executable statement must export literal metadata. The script then uses the provided orchestration primitives and returns a JSON-compatible result. ```js export const meta = { name: 'review-auth', - description: 'Fan-out review of auth changes', + description: 'Review auth changes from two perspectives', phases: [{ title: 'Review' }, { title: 'Synthesize' }], - // optional DevSpace: - // defaultProvider: 'codex', - // concurrency: 4, + concurrency: 2, } phase('Review') const findings = await parallel([ - () => agent('Review for correctness…', { label: 'correctness' }), - () => agent('Review for security…', { label: 'security' }), + () => agent('Review the auth diff for correctness.', { label: 'correctness' }), + () => agent('Review the auth diff for security.', { label: 'security' }), ]) -phase('Synthesize') -const summary = await agent(`Synthesize: ${JSON.stringify(findings)}`) -return { summary, findings } -``` -### Primitives +phase('Synthesize') +const summary = await agent( + `Synthesize these findings: ${JSON.stringify(findings)}`, + { label: 'summary' }, +) -| 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 | +return { findings, summary } +``` -**No `writeMode`.** Teach read-only vs write in the prompt. Use `isolation: 'worktree'` when parallel mutators would conflict (git required). +Available primitives: -### Determinism bans +- `agent(prompt, options?)` delegates one bounded task. Options are `label`, `phase`, `schema`, `profile`, `provider`, `model`, `effort`, and `isolation: 'worktree'`. `profile` and `provider` are mutually exclusive. +- `parallel([thunks])` runs independent tasks concurrently and preserves input order. A failed branch produces `null` in its slot. +- `pipeline(items, ...stages)` processes each item through dependent stages; failed item chains produce `null` without stopping unrelated items. +- `phase(title)` and `log(message)` record meaningful progress. +- `workflow(nameOrRef, args?)` composes another named workflow or `{ scriptPath }` one level deep. +- `args` contains values passed with `--arg`. -`Date.now()`, `Math.random()`, and `new Date()` without args throw. Pass timestamps via `args` if needed. +Use `devspace agents targets --json` before choosing a profile or provider. Prefer profiles for reusable role instructions and defaults. Only pass model or effort overrides when their exact values are already known. -### Schema +Use `schema` when later workflow steps need typed JSON rather than prose: ```js -const out = await agent('Return JSON findings', { +const review = await agent('Return the discovered bugs.', { schema: { type: 'object', - properties: { bugs: { type: 'array', items: { type: 'string' } } }, + 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. +Use `isolation: 'worktree'` for parallel agents that may modify overlapping checkouts. Shared isolation is appropriate for readers or intentionally sequential writers. -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. +Workflow scripts must be replayable: do not use `Date.now()`, `Math.random()`, or `new Date()` without an argument. Pass changing values through `args`. -### Cancel +## Recover a run -`workflow cancel` sets a cooperative flag; worker aborts then hard-kills if needed. +Failed and cancelled runs are terminal. Inspect the prior run, fix or replace its script, then create a resumed run: -## When to use CLI vs MCP - -- **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. - -## Worked mini-examples - -**1. Parallel review** - -```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 status --json +devspace workflow calls --json +devspace workflow call --json +devspace workflow run --resume --json +devspace workflow run --resume --file --json ``` -**2. Pipeline with schema** +Keep completed calls' prompts and options stable when their results should be reused. Resume reuses the unchanged successful prefix and executes from the first call that failed, changed, or cannot be reused. -```js -export const meta = { name: 'pipe', description: 'Find then fix plan' } -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)}`), -) -``` +A completed `isolation: 'worktree'` call cannot be reused because its checkout is not restored. When resume reaches one, that call and every later call execute again, even if their inputs are unchanged. Do not assume mutations from the prior isolated checkout are present in the resumed run. -**3. Isolation for parallel writers** +## Good uses -```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 -``` +- Fan out a change review across correctness, security, and tests, then synthesize it. +- Analyze many files with the same staged pipeline. +- Run parallel implementations in isolated worktrees and compare their results. +- Encode a repeatable migrate, review, and verify sequence. diff --git a/skills/subagents/SKILL.md b/skills/subagents/SKILL.md index caa4faff..53ab44d6 100644 --- a/skills/subagents/SKILL.md +++ b/skills/subagents/SKILL.md @@ -1,58 +1,54 @@ --- name: subagents -description: Delegate focused work to isolated DevSpace coding agents. +description: Delegate focused coding, research, review, or verification work to a bounded DevSpace subagent. Use for one independent task, a specialist perspective, or a follow-up with the same worker; use Dynamic Workflows instead for programmed fan-out or multiple dependent stages. --- -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 + +Use the DevSpace CLI through the host's shell or process tool. Run commands from the project the subagent should work on. DevSpace scopes sessions to the host workspace when supplied, otherwise to the current Git repository or project directory. ## Choose a target -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: +Discover usable targets instead of guessing names: ```bash -devspace agents targets +devspace agents targets --json ``` -Do not guess profile names or provider identifiers. +Prefer a configured profile whose description matches the task. Use a provider target when no profile fits or the user requests that provider. Unavailable providers are omitted. -## Write the brief +Profiles carry their own provider, instructions, model, and effort defaults. Only pass `--model` or `--effort` when the user supplied an exact value or the value is already known to be valid for that target. -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. +## Start work -## Run and continue +Give the child a self-contained brief. Include the objective, relevant paths, constraints, decisions from the parent conversation, and the expected result. A child cannot see the parent conversation or ask the user for missing context. ```bash -devspace agents targets [--json] -devspace agents run "" -devspace agents show -devspace agents run "" -devspace agents ls +devspace agents run "" --json +devspace agents run --model --effort "" --json ``` -`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. - -Do not invoke provider CLIs directly; use `devspace agents` so DevSpace keeps -session and provider handling consistent. - -## Model and effort overrides +The result contains an agent `id` and current status. Execution continues independently, so retain the id. -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. +## Inspect and continue ```bash -devspace agents run --model --effort "" +devspace agents show --json +devspace agents run "" --json +devspace agents ls --json ``` -## Direct subagent or workflow +- `show` returns the current status and includes the response or error when available. +- `run ` continues the same agent session with a new prompt. +- `ls` returns sessions belonging to the current project. + +Poll `show --json` while the status is `starting` or `running`. `idle` means the response is ready; `error` and `stopped` are terminal without a successful response. Use a continuation only when the same context is valuable; start a new subagent for independent work. + +## Good uses + +- Review a change for correctness, security, or test gaps. +- Investigate a bounded part of a codebase and report findings. +- Implement one isolated feature with clear acceptance criteria. +- Run a focused verification pass after another agent's work. -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. +Use a Dynamic Workflow when the task needs several agents, explicit phases, fan-out, pipelines, structured aggregation, or resumable orchestration. diff --git a/skills/subagents/references/claude.md b/skills/subagents/references/claude.md deleted file mode 100644 index d1d50141..00000000 --- a/skills/subagents/references/claude.md +++ /dev/null @@ -1,20 +0,0 @@ -# Claude overrides - -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. - -```bash -devspace agents run claude --model --effort "" -``` diff --git a/skills/subagents/references/codex.md b/skills/subagents/references/codex.md deleted file mode 100644 index ecc97d4c..00000000 --- a/skills/subagents/references/codex.md +++ /dev/null @@ -1,19 +0,0 @@ -# Codex overrides - -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. - -```bash -devspace agents run codex --model --effort "" -``` diff --git a/skills/subagents/references/copilot.md b/skills/subagents/references/copilot.md deleted file mode 100644 index a23a9c55..00000000 --- a/skills/subagents/references/copilot.md +++ /dev/null @@ -1,12 +0,0 @@ -# Copilot overrides - -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. - -```bash -devspace agents run copilot --model --effort "" -``` diff --git a/skills/subagents/references/cursor.md b/skills/subagents/references/cursor.md deleted file mode 100644 index 09a7a167..00000000 --- a/skills/subagents/references/cursor.md +++ /dev/null @@ -1,12 +0,0 @@ -# Cursor overrides - -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. - -```bash -devspace agents run cursor --model --effort "" -``` diff --git a/skills/subagents/references/opencode.md b/skills/subagents/references/opencode.md deleted file mode 100644 index ef0ab01d..00000000 --- a/skills/subagents/references/opencode.md +++ /dev/null @@ -1,12 +0,0 @@ -# OpenCode overrides - -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. - -```bash -devspace agents run opencode --model --effort "" -``` diff --git a/skills/subagents/references/pi.md b/skills/subagents/references/pi.md deleted file mode 100644 index 6953eaf5..00000000 --- a/skills/subagents/references/pi.md +++ /dev/null @@ -1,20 +0,0 @@ -# Pi overrides - -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. - -```bash -devspace agents run pi --model --effort "" -``` diff --git a/src/skills.test.ts b/src/skills.test.ts index 4bc585ea..a287c47c 100644 --- a/src/skills.test.ts +++ b/src/skills.test.ts @@ -207,18 +207,6 @@ try { assert.equal(subagentSkills.some((skill) => skill.name === "subagents"), true); assert.equal(subagentSkills.some((skill) => skill.name === "dynamic-workflows"), false); assert.equal(subagentSkills.some((skill) => skill.name === "subagent-delegation"), false); - const subagentsSkill = subagentSkills.find( - (skill) => skill.name === "subagents", - ); - assert.ok(subagentsSkill); - const codexReference = join(subagentsSkill.baseDir, "references", "codex.md"); - assert.equal(resolveSkillReadPath([subagentsSkill], new Set(), codexReference), undefined); - assert.equal( - resolveSkillReadPath([subagentsSkill], new Set([subagentsSkill.baseDir]), codexReference) - ?.absolutePath, - codexReference, - ); - const workflowsOnlyConfig = loadConfig({ DEVSPACE_ALLOWED_ROOTS: projectRoot, DEVSPACE_AGENT_DIR: agentDir,