Now I have the full picture. The `pi-subagents` package DOES support prompt workflows and the `chain:` frontmatter. Let me analyze the c- files against this implementation:

1. **Invocation**: The c- files say to use `/prompt-workflow c-design-build "add a rate-limiter to the API"`. This matches the registered command in prompt-workflows.ts. ✅

2. **Frontmatter parsing**: The code reads `chain` as a string field via `stringField(frontmatter, "chain")`. The c- files have:
   ```yaml
   chain: p-brainstorm -> p-plan -> p-test -> p-commit
   ```
   This is a string with `->` separators. `splitPromptChain` splits on ` -> `. ✅

3. **async**: The c- files have `async: true`. The code reads `booleanField(frontmatter, ...)` for `worktree`, `inheritContext`, `fork`, `fresh`. It does NOT read `async` from frontmatter! The `async` flag only comes from runtime options `--bg` or `--async`.

   Wait, let me re-check. In `workflowParams`, it uses `...(runtime.bg ? { async: true } : {})`. In the chain branch, it uses `...(runtime.bg ? { async: true } : {})`. So `async: true` in frontmatter is ignored. The c- files say `async: true` in frontmatter, but that won't make the chain run async.

4. **Agent**: The c- files don't specify `subagent:` frontmatter, so `parseAgent` returns `"delegate"`. But the p- files have `subagent: true` and `fork: true` in their frontmatter. Wait, `parseAgent` checks `subagent` field:
   - If `subagent` is missing or `"true"`, returns `"delegate"`.
   - If `subagent` is some other value, returns that value.

   But the p- files have `subagent: true` (boolean true in YAML, which would be string "true" when read). So `parseAgent` returns "delegate".

   However, `parseFrontmatter` might parse `subagent: true` as boolean true, and `stringField` calls `.trim()` on it which would fail for non-strings. Let me check `parseFrontmatter`.

5. **Context/fork**: The p- files have `fork: true` in frontmatter. The code checks `booleanField(frontmatter, "fork") === true` and sets `context: "fork"`. But when the c- workflow invokes the chain, it calls `workflowChainStep(step, args, runtime)` which only takes `params.context` from `runtime.fork` or `runtime.fresh`, NOT from `workflow.context`!

   Wait, let me re-read `workflowParams`:
   ```typescript
   const context = runtime.fork ? "fork" : runtime.fresh ? "fresh" : workflow.context;
   ```
   So it does use `workflow.context`. Good.

   But `workflowChainStep` calls `workflowParams` and gets `params.context`, then includes it:
   ```typescript
   return {
       agent: params.agent ?? "delegate",
       task: params.task,
       ...(params.model ? { model: params.model } : {}),
       ...(params.skill !== undefined ? { skill: params.skill } : {}),
       ...(params.cwd ? { cwd: params.cwd } : {}),
   };
   ```
   Wait, it does NOT include `context` in the returned ChainStep! It spreads model, skill, cwd, but not context. So the `fork: true` from p- files would be lost when used as a chain step.

6. **Model override**: p- files don't have `model`, so irrelevant.

7. **Skill**: p- files don't have `skill` frontmatter, they have `subagent: true` and `fork: true`. But `parseSkill` only checks `skill` field. So no skill is applied.

8. **Substitute args**: The c- files use `${@:-the current task}`. This is shell-style parameter expansion. The code's `substituteArgs` supports:
   - `$ARGUMENTS` → all args
   - `$@` → all args
   - `${N:-fallback}` → positional arg with fallback
   - `$N` → positional arg

   It does NOT support `${@:-default}` or `${@:N:L}`. The c- files use `${@:-...}` which will NOT be substituted correctly! It will be left as-is or partially substituted.

   Wait, the regex is `/\$\{(\d+):-([^}]*)\}/g` which matches `${1:-fallback}` but not `${@:-default}` because `@` is not a digit. So `${@:-the current task}` will remain literally in the expanded prompt.

9. **p- files format**: The p- files have `subagent: true` and `fork: true`. The prompt-workflow parser reads these and uses them. But as I noted, `context` is lost in chain steps. Also, `subagent: true` means agent="delegate".

10. **The actual chain invocation**: When `/prompt-workflow c-design-build "add a rate-limiter"` is run:
    - `name = "c-design-build"`
    - `workflow.chain = "p-brainstorm -> p-plan -> p-test -> p-commit"`
    - It splits into chain names and builds chain steps from each p- workflow.
    - The task for the chain is `runtime.args.join(" ")`, which is the user argument.
    - It runs `run({ chain, task: "add a rate-limiter", ... })`.

So, the c- prompts ARE being picked up by the pi-subagents extension. The question is whether they work CORRECTLY.

Issues found:
1. `${@:-the current task}` argument substitution does NOT work in the pi-subagents prompt-workflow implementation. It only supports `${N:-fallback}` for positional args, not `${@:-fallback}`. The c- files use `${@:-...}` which will be passed literally to the LLM. This is a bug.

2. `async: true` in frontmatter is ignored. To run async, the user must pass `--bg` or `--async` at runtime. The c- files claim `async: true` but it doesn't work.

3. `fork: true` context on individual p- workflow steps is lost when used in a chain (because `workflowChainStep` doesn't include `context` in the returned ChainStep). Actually wait, let me re-read. The `p-` files have `fork: true`. `loadPromptWorkflow` reads `fork` and sets `context: "fork"`. But when building a chain step via `workflowChainStep`, it calls `workflowParams` which does compute `context = runtime.fork ? "fork" : runtime.fresh ? "fresh" : workflow.context` (so it would be "fork"), but then the returned object only spreads `model`, `skill`, `cwd`. It does NOT include `context`. So the `fork: true` from p- files is silently dropped in chain mode.

4. The `subagent: true` in p- files results in agent="delegate" which is fine.

5. The p- files themselves use shell-style args like `${@:-...}` in their bodies too. For example p-brainstorm has `${@:-the current problem or feature}`. This also won't be substituted correctly.

So the answer is: The c- prompts ARE recognized by pi-subagents' `/prompt-workflow` command, but they have several bugs that prevent them from working correctly:
- Argument substitution uses unsupported `${@:-...}` syntax
- `async: true` frontmatter is ignored
- `fork: true` context is lost in chain steps

Let me verify my understanding of the `parseFrontmatter` function to be sure about boolean/string parsing.


/**
 * Escape regex special characters for use in a RegExp constructor.
 */
function escapeRegex(s: string): string {
	return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

/**
 * Fold a YAML folded block scalar while preserving more-indented lines and
 * every blank-line separator. Trailing whitespace is trimmed.
 */
function foldBlock(block: string): string {
	let folded = "";
	let hasContent = false;
	let previousIsMoreIndented = false;
	let blankLines = 0;

	for (const line of block.split("\n")) {
		const current = line.trimEnd();
		if (current.trim() === "") {
			if (hasContent) blankLines++;
			continue;
		}

		const currentIsMoreIndented = current.length > current.trimStart().length;
		if (hasContent) {
			if (blankLines > 0) {
				folded += "\n".repeat(blankLines + (previousIsMoreIndented || currentIsMoreIndented ? 1 : 0));
			} else {
				folded += previousIsMoreIndented || currentIsMoreIndented ? "\n" : " ";
			}
		}
		folded += current;
		hasContent = true;
		previousIsMoreIndented = currentIsMoreIndented;
		blankLines = 0;
	}

	return folded.trim();
}

/**
 * Normalize a simple-scalar frontmatter list from comma-separated or block-list syntax.
 * Only the standard `- item` marker is removed; ordinary hyphenated values stay intact.
 */
export function parseFrontmatterList(raw: string | undefined): string[] | undefined {
	if (raw === undefined) return undefined;
	return raw
		.split("\n")
		.flatMap((line) => {
			const value = line.trim();
			const listItem = value.match(/^-\s+(.+)$/);
			return (listItem?.[1] ?? value).split(",");
		})
		.map((value) => value.trim())
		.filter(Boolean);
}

/**
 * Parse YAML frontmatter from agent/chain files.
 * Handles both flat (key: value) and nested block (key: \n  sub: val) values.
 * Block values are stored as single strings with embedded newlines.
 * The indentation of the block content is preserved relative to the key.
 */
export function parseFrontmatter(content: string): { frontmatter: Record<string, string>; body: string } {
	const frontmatter: Record<string, string> = {};
	const normalized = content.replace(/\r\n/g, "\n");

	if (!normalized.startsWith("---")) {
		return { frontmatter, body: normalized };
	}

	const endIndex = normalized.indexOf("\n---", 3);
	if (endIndex === -1) {
		return { frontmatter, body: normalized };
	}

	const frontmatterBlock = normalized.slice(4, endIndex);
	const body = normalized.slice(endIndex + 4).trim();

	const lines = frontmatterBlock.split("\n");
	let currentKey: string | null = null;
	let currentBlockLines: string[] | null = null;
	let currentIndent: number | null = null;
	let currentFolded = false;

	for (const line of lines) {
		const indent = line.search(/\S|$/); // position of first non-whitespace char
		const trimmed = line.trim();

		if (currentKey !== null && currentBlockLines !== null && (indent > (currentIndent ?? 0) || (currentFolded && trimmed === ""))) {
			// This line is part of the current block value
			currentBlockLines.push(line);
			continue;
		}

		// Flush any pending block value
		if (currentKey !== null && currentBlockLines !== null) {
			// Strip the common leading whitespace from the block so the
			// serializer can add its own indentation level.

[48 more lines in file. Use offset=101 to continue.]


Let me read the rest of the frontmatter parser to see how boolean values are handled.


			// serializer can add its own indentation level.
			const rawBlock = currentBlockLines.join("\n");
			const leadingSpaces = rawBlock.match(/^[ \t]+(?=\S)/m);
			const prefix = leadingSpaces?.[0] ?? "";
			const stripped = prefix
				? rawBlock.replace(new RegExp(`^${escapeRegex(prefix)}`, "gm"), "").replace(/^\n/, "")
				: rawBlock;
			frontmatter[currentKey] = currentFolded ? foldBlock(stripped) : stripped;
			currentKey = null;
			currentBlockLines = null;
			currentIndent = null;
			currentFolded = false;
		}

		const match = line.match(/^([\w-]+):\s*(.*)$/);
		if (match) {
			const rawValue = match[2].trim();
			const isQuoted = (rawValue.startsWith('"') && rawValue.endsWith('"')) || (rawValue.startsWith("'") && rawValue.endsWith("'"));
			const value = isQuoted ? rawValue.slice(1, -1) : rawValue;
			const isFolded = !isQuoted && (rawValue === ">" || rawValue === ">-");

			if (value === "" || isFolded) {
				// Key with empty value or folded block indicator — defer storing until we see indent
				currentKey = match[1];
				currentBlockLines = [];
				currentIndent = indent;
				currentFolded = isFolded;
			} else {
				// Simple key: value
				frontmatter[match[1]] = value;
			}
		}
		// Lines that don't match a key pattern (e.g., comments, empty lines) are ignored
	}

	// Flush final block value
	if (currentKey !== null && currentBlockLines !== null) {
		const rawBlock = currentBlockLines.join("\n");
		const leadingSpaces = rawBlock.match(/^[ \t]+(?=\S)/m);
		const prefix = leadingSpaces?.[0] ?? "";
		const stripped = prefix
			? rawBlock.replace(new RegExp(`^${escapeRegex(prefix)}`, "gm"), "").replace(/^\n/, "")
			: rawBlock;
		frontmatter[currentKey] = currentFolded ? foldBlock(stripped) : stripped;
	}

	return { frontmatter, body };
}


OK so `parseFrontmatter` returns all values as strings. For `subagent: true`, it stores `"true"` as string. For `async: true`, it stores `"true"`. `booleanField` then checks if the trimmed lowercase value equals "true", "yes", or "1".

But the c- files have `async: true`. `booleanField(frontmatter, "async")` is never called in the prompt-workflow code. So `async` frontmatter is ignored.

Also, `subagent: true` in p- files: `stringField(frontmatter, "subagent")` returns "true". `parseAgent` checks `if (!subagent || subagent === "true") return "delegate"`. So it returns "delegate". Good.

Now let me also check whether the p- files' `fork: true` would work in non-chain mode. In non-chain mode, `workflowParams` includes `...(context ? { context } : {})`. So if you just run `/prompt-workflow p-brainstorm "foo"`, it would pass `context: "fork"`. But in chain mode, `workflowChainStep` loses context.

Also, I should verify whether the `chain` frontmatter in c- files requires the referenced p- files to exist. They do exist in the same directory. `discoverPromptWorkflows` reads from `promptDirs(cwd)` which includes the user prompts dir, so yes they would be found.

Let me also check if the chain task is passed correctly. In the chain branch:
```typescript
await run({ chain, task: runtime.args.join(" "), clarify: false, agentScope: "both", ...(runtime.bg ? { async: true } : {}) }, ctx);
```
So the task is just the user args joined. Each chain step gets its own `task` from `workflowChainStep`, which substitutes args into the workflow body. But the chain-level task is also set. This should be fine.

However, there's another issue: when running a chain, the `task` at the chain level becomes the overall goal, and each step has its own `task`. In pi-subagents, `{previous}` is available in subsequent steps. But the p- files don't use `{previous}`, they just re-state the problem. That's fine for the user's intended workflow.

Now, the biggest issues are:
1. `${@:-...}` not supported in `substituteArgs` - affects both c- and p- files
2. `async: true` frontmatter ignored - affects c- files
3. `context` (fork/fresh) lost in chain steps - affects p- files when chained

Let me also check the README for pi-subagents to see if it documents the supported syntax, so I can be sure about what's expected.


node_modules/yaml/README.md
 80: visit, visitAsync, YAMLMap, YAMLSeq
 98: - [`visitAsync(node, visitor)`](https://eemeli.org/yaml/#finding-and-modifying-nodes)

README.md
 98: | See running work | “Show active async runs.” or “Show the subagent fleet.” |
 247: Background runs keep working after control returns to you. Inspect active runs with `subagent({ action: "status" })`, or a specific run with `subagent({ action: "status", id: "..." })`. `/subagents-fleet` opens a live, inspection-only fleet with current-session foreground work, recent async children, transcript tails, and completed output/session paths. Use `↑`/`↓` or `j`/`k` to select a child, `PgUp`/`PgDn` to scroll its transcript, `r` to refresh immediately, and `Esc` to close. `Ctrl+Alt+F` o...
 249: They also show a compact async widget and send completion notifications. Parallel background runs show per-agent progress instead of fake chain steps. Chains with parallel groups keep their grouped shape in progress and results, so failed or paused agents stay visible next to completed ones. When a child is explicitly allowed to fan out with `tools: subagent`, its nested runs appear under that parent child in the main status tree instead of being hidden inside the child process.
 254: Show me the current async runs.
 257: Async runs also write machine-readable lifecycle artifacts for observability and workflow gates. For a top-level async run, `details.asyncDir` points at a directory containing `status.json`, `events.jsonl`, `output-<index>.log`, and `subagent-log-<runId>.md`; the final summary is written to Pi's subagent results directory as `<runId>.json`. Nested async runs use the same shape under the nested async root and are discoverable through status projections that read the nested-run registry. These fil...
 259: Foreground and async runners share bounded child-protocol handling. A child JSONL line above 4 MiB fails with structured `protocolError` code `protocol_output_limit`, stderr retains only its latest 128 KiB, split UTF-8 and final unterminated JSON events remain valid, and `agent_end.willRetry` defers completion until the child settles. Current Pi builds use `agent_settled` as the terminal watermark; older builds retain the bounded terminal-message fallback.
 261: The stable v1 status/result fields are `lifecycleArtifactVersion`, `runId`/`id`, `sessionId`, `mode`, `state`, `startedAt`, `lastUpdate`, `endedAt`, `durationMs`, `cwd`, `asyncDir`, `sessionFile`, `outputFile`, `workflowGraph`, `steps`, `results`, `totalTokens`, `totalCost`, `model`/`attemptedModels`/`modelAttempts`, `toolCount`, `turnCount`, and nested `children` when a child is allowed to launch subagents. `events.jsonl` records lifecycle transitions such as `subagent.run.started`, `subagent.s...
 267: pi.events.on(`subagents:rpc:v1:reply:${requestId}`, (reply) => {
 279: The v1 methods are `ping`, `status`, `spawn`, `interrupt`, and `stop`. `status` and `interrupt` reuse the normal control actions. `spawn` is async-only: omit `async` or set `async: true`, omit `clarify` or set `clarify: false`, and do not pass management `action` values. It goes through the same executor as the `subagent` tool, so agent discovery, validation, session attribution, configured spawn caps, child-safety depth, artifacts, and async status all behave the same. `stop` targets current-se...
 305: Packaged `planner`, `worker`, and `oracle` default to forked context when a launch omits `context`; pass `context: "fresh"` when you intentionally want a fresh child run.
 307: Child-safety boundaries are enforced at runtime. Spawned child sessions do not receive the bundled `pi-subagents` skill, and forked child context filtering removes parent-only subagent artifacts (including old hidden orchestration-instruction messages, slash/status/control messages, and prior parent `subagent` tool-call/tool-result history) while preserving ordinary prose and unrelated tool calls/results. By default, children do not register the `subagent` tool and receive boundary instructions ...
 528: `subagent({ chain: [...] })` tool API or a saved `.chain.json` for data-driven fan-out.
 572: ### Background and forked runs
 582: Add `--fork` to start each child from a real branched session created from the parent’s current leaf:
 585: /run reviewer "review this diff" --fork
 586: /chain scout "analyze this branch" -> planner "plan next steps" --fork
 587: /parallel scout "audit frontend" -> reviewer "audit backend" --fork
 593: /run reviewer "review this diff" --fork --bg
 594: /run reviewer "review this diff" --bg --fork
 597: Background runs are detached. If the parent agent has other independent work, it should keep working. In an interactive chat, it should normally return control when ready to yield and let Pi deliver the completion notification instead of blocking merely to wait. Override that default and use `subagent_wait` when the current request is run-to-completion — for example, the user asked you to report results back before continuing or a skill cannot return before its work finishes. In a non-interactiv...
 687: | `defaultContext: fork` | Use forked session context when a launch omits `context`; explicit `context: "fresh"` still wins. |
 715: async: true
 753: | `defaultContext` | Optional `fresh` or `fork` launch context default for this agent. |
 759: | `async` | Default a single-agent launch to background (`true`) or foreground (`false`) when the call omits `async`. Explicit call values and `forceTopLevelAsync` win. |
 861: Dynamic fanout is available only through direct `subagent({ chain: [...] })` JSON or saved `.chain.json` files. It expands an array from a prior structured named output, runs one child template per item, and stores the ordered collection under `collect.as`. The source must be structured output; prose is never parsed. `expand.maxItems` is required, over-limit arrays fail, nested fanout and arbitrary expressions are not supported, and `.chain.md` has no dynamic syntax in this release.
 974: - **Delegation patterns**: when to launch which agent, whether to use single, parallel, chain, or async mode, and whether to use fresh or forked context
 1015: The contract uses the established `prompt-template:subagent:*` event transport and the same executor as the `subagent` tool; it does not add another launcher. New integrations must send `version: 1`. Requests are strict and single-agent only. They can set fresh or fork context, model, cwd, timeout, turn and tool-call budgets, skills, output behavior, acceptance, and artifact capture. Unknown or malformed fields return `invalid_request` before execution.
 1019: Delegation requires an active extension context. Emit requests from a supported event callback or queued application step, not by recursively invoking the `subagent` tool inside another tool's `tool_call` hook. The caller selects a configured agent, but agent discovery and effective tools remain package-owned. A request cannot grant arbitrary tools, and tool restrictions are not an operating-system sandbox. The detached RPC remains async-only; this API is foreground-only.
 1044: Child processes do not gain provider tools or extensions automatically. Add `subagent_wait` to the child agent's `tools` allowlist and load each provider through `extensions` or `subagentOnlyExtensions`. The parent's effective `waitTool` setting is serialized through foreground, async, resume, chain, parallel, and fanout launch paths; `PI_SUBAGENT_WAIT_TOOL_ENABLED` keeps precedence.
 1059: // Forked context
 1060: { agent: "worker", task: "continue this thread", context: "fork" }
 1065: { tasks: [{ agent: "scout", task: "audit frontend" }, { agent: "reviewer", task: "audit backend" }], context: "fork" }
 1068: { chain: [
 1076: { chain: [...], async: true }
 1079: { chain: [
 1089: { chain: [
 1106: { chain: [
 1209: | `chain` | array | - | Sequential, static parallel, and dynamic fanout chain steps. Steps and chain parallel tasks support `phase`, `label`, `as`, `outputSchema`, and `acceptance` in addition to the usual execution fields. Dynamic fanout uses `expand`, one child `parallel` template, and `collect`. With `action: "append-step"`, pass exactly one step to append to a running async chain. |
 1210: | `context` | `fresh \| fork` | per-agent default or `fresh` | Explicit `fresh` or `fork` overrides every child. When omitted, each agent uses its own `defaultContext`; `fork` creates real branched sessions from the parent leaf. Packaged `planner`, `worker`, and `oracle` default to `fork`. |
 1216: | `async` | boolean | false | Background execution. For chains, `clarify: true` explicitly keeps the run foreground for the clarify UI. |
 1217: | `timeoutMs` / `maxRuntimeMs` | number | none | Optional run-level max runtime in milliseconds for foreground and async/background runs. |
 1232: `context: "fork"` fails fast when the parent session is not persisted, the current leaf is missing, or the branched child session cannot be created. When the inherited transcript contains signed Anthropic `thinking` / `redacted_thinking` blocks, `pi-subagents` strips those provider-private blocks from the forked child session. It forces thinking `off` only when the child’s effective primary or fallback model resolves through the model registry to the Anthropic provider or `anthropic-messages` AP...
 1254: subagent({ action: "append-step", id: "<run-id>", chain: [{ agent: "worker", task: "Continue from {previous}" }] })
 1258: `status` resolves exact foreground ids, top-level async ids, and nested run ids before falling back to prefix matching. `view: "fleet"` is an optional read-only active-run surface with transcript commands; it does not add steering or stop controls. `view: "transcript"` tails the selected run's live `output-<index>.log` or persisted session transcript, with `lines` capped at 500. Nested status shows the root/parent path, nested children, session/artifact paths when known, and nested control comma...
 1260: `resume` revives a paused, completed, or failed async/foreground child by starting a new child from its stored session file; stopped runs remain non-resumable, and it does not interrupt a live top-level async child. Use `steer` for acknowledged live async guidance. Multi-child async runs and remembered foreground single, parallel, or chain runs can be revived by passing `index` to choose the child. Nested runs can be resumed by nested id when their live route or persisted nested session metadata...
 1262: `stop` ends a current-session top-level async run. It is deliberately stronger than `interrupt`: it is not a resumable pause, stopped runs should be restarted as new runs, foreground and nested targets are rejected, direct id calls execute immediately, and `/subagents-stop` without an id opens a selector with confirmation when a TUI is available. In non-TUI contexts the slash command prints exact `subagent({ action: "stop", id })` and `/subagents-stop <id>` commands. Scheduled jobs can appear in...
 1266: `append-step` accepts exactly one sequential, static parallel, or dynamic fanout chain step for a top-level async chain whose status is still `running`. The step is persisted in the run directory and becomes eligible only after the chain's already-queued steps finish; completed, failed, paused, foreground, single, and top-level parallel runs reject appends.
 1278: { chain: [
 1310: Controls the parent-facing `subagent` tool description registered at startup. `full` is the default. `compact` keeps the execution modes, async/`subagent_wait` guidance, child-safety boundary, management/action split, one-writer review guidance, and artifact/status essentials with less prompt bloat.
 1314: ### `asyncByDefault`


Let me search the README specifically for "prompt workflow" section to see documented behavior.


README.md
 306-
 307- Child-safety boundaries are enforced at runtime. Spawned child sessions do not receive the bundled `pi-subagents` skill, and forked child context filtering removes parent-only subagent artifacts (including old hidden orchestration-instruction messages, slash/status/control messages, and prior parent `subagent` tool-call/tool-result history) while preserving ordinary prose and unrelated tool calls/results. By default, children do not register the `subagent` tool and receive boundary instructions ...
 308-
 309- ## Optional shortcuts
 310-
 311: The package includes reusable prompt templates for common workflows. You do not need them, but they are handy when you want the same shape every time:
 312-
 313- | Prompt | Use it for |
 314- |--------|------------|
 315- | `/parallel-review` | Launch fresh-context reviewers with distinct angles, then synthesize what to fix. |
 316- | `/review-loop` | Run parent-controlled worker, reviewer, and fix-worker cycles until clean or capped. |
 970-
 971- The package bundles a `pi-subagents` skill that is automatically available to the parent agent when the extension is installed. It is for the orchestrating parent only: child subagents never receive it, and their context is explicitly filtered to strip parent-only orchestration instructions.
 972-
 973- What the bundled skill covers:
 974- - **Delegation patterns**: when to launch which agent, whether to use single, parallel, chain, or async mode, and whether to use fresh or forked context
 975: - **Prompt workflow recipes**: how to apply the packaged techniques directly with `subagent(...)` when the user describes the workflow in natural language instead of invoking a slash command. This includes parallel review, review-loop, parallel research, parallel context-build, parallel handoff-plan, gather-context-and-clarify, and parallel cleanup
 976- - **Role-agent prompting guidance**: compact contract prompts instead of long scripts, what to include in role-specific meta prompts, and retrieval budgets for researchers
 977- - **Safety boundaries**: child agents must not run subagents unless their resolved builtin tools explicitly include `subagent`, must not invent intercom targets, and must escalate unapproved decisions
 978- - **Intercom conventions**: when to ask vs send, and how parent-side supervisor/result delivery works through the native channel
 979- - **Control and diagnostics**: attention signals, soft interrupts, status, and the `doctor` action
 980-
 1605-
 1606- The result watcher emits `subagent:async-complete`; `src/extension/index.ts` registers the notification handler that consumes it. Control/attention events are surfaced as visible parent notices and persisted for async runs. Native supervisor requests are delivered only to the exact parent session that spawned the child.
 1607-
 1608- ## Prompt-template integration
 1609-
 1610: `pi-subagents` works standalone through natural language, the `subagent` tool, slash commands, and the packaged prompt shortcuts listed near the top of this README. It also includes a native prompt-workflow adapter for reusable subagent prompt templates, so you do not need `pi-prompt-template-model` for the common subagent workflow path.
 1611-
 1612- Create a prompt in `.pi/prompts/` or `~/.pi/agent/prompts/`:
 1613-
 1614- ```md
 1615- ---
 1622- ```
 1623-
 1624- Then run it through the native adapter:
 1625-
 1626- ```text
 1627: /prompt-workflow take-screenshot https://example.com
 1628- ```
 1629-
 1630- The adapter delegates to the named subagent, applies `model`, `skill`, `cwd`, `worktree`, and fork/fresh context metadata, and supports runtime overrides such as `--subagent reviewer`, `--fork`, `--fresh`, `--worktree`, and `--bg`.
 1631-
 1632- For prompt-template chains, use:
 1630- The adapter delegates to the named subagent, applies `model`, `skill`, `cwd`, `worktree`, and fork/fresh context metadata, and supports runtime overrides such as `--subagent reviewer`, `--fork`, `--fresh`, `--worktree`, and `--bg`.
 1631-
 1632- For prompt-template chains, use:
 1633-
 1634- ```text
 1635: /chain-prompts analyze -> fix -- user arguments here
 1636- ```
 1637-
 1638- Each named prompt becomes a native `subagent` chain step. This is intentionally scoped to subagent workflows; compare-style prompt features such as `/best-of-n` are not part of the built-in adapter.
 1639-
 1640- ## Runtime files


Now let me read the README section around 1614 to see the documented frontmatter format.


## Prompt-template integration

`pi-subagents` works standalone through natural language, the `subagent` tool, slash commands, and the packaged prompt shortcuts listed near the top of this README. It also includes a native prompt-workflow adapter for reusable subagent prompt templates, so you do not need `pi-prompt-template-model` for the common subagent workflow path.

Create a prompt in `.pi/prompts/` or `~/.pi/agent/prompts/`:

```md
---
description: Take a screenshot
model: claude-sonnet-4-20250514
subagent: browser-screenshoter
cwd: /tmp/screenshots
---
Use url in the prompt to take screenshot: $@
```

Then run it through the native adapter:

```text
/prompt-workflow take-screenshot https://example.com
```

The adapter delegates to the named subagent, applies `model`, `skill`, `cwd`, `worktree`, and fork/fresh context metadata, and supports runtime overrides such as `--subagent reviewer`, `--fork`, `--fresh`, `--worktree`, and `--bg`.

For prompt-template chains, use:

```text
/chain-prompts analyze -> fix -- user arguments here
```

Each named prompt becomes a native `subagent` chain step. This is intentionally scoped to subagent workflows; compare-style prompt features such as `/best-of-n` are not part of the built-in adapter.

## Runtime files

The main runtime files are:

| File | Purpose |
|------|---------|
| `src/extension/index.ts` | Extension registration, tool registration, message/render wiring. |
| `src/agents/agents.ts` | Agent and chain discovery, frontmatter parsing. |
| `src/runs/foreground/subagent-executor.ts` | Main execution routing for single, parallel, chain, management, status, interrupt, and doctor actions. |
| `src/runs/foreground/execution.ts` | Core foreground `runSync` handling. |
| `src/runs/background/subagent-runner.ts` | Detached async runner. |
| `src/runs/background/async-execution.ts` | Background launch support. |
| `src/runs/background/async-status.ts` | Status discovery and formatting for async runs. |
| `src/runs/foreground/chain-execution.ts` / `src/agents/chain-serializer.ts` | Chain orchestration and `.chain.md` parsing. |
| `src/shared/settings.ts` | Chain behavior, instructions, and config helpers. |
| `src/runs/shared/worktree.ts` | Git worktree isolation. |
| `src/intercom/intercom-bridge.ts` | Runtime intercom bridge instructions and diagnostics. |
| `src/extension/schemas.ts` / `src/shared/types.ts` | Tool schemas, shared types, and event constants. |

[2 more lines in file. Use offset=1658 to continue.]


Now I have a comprehensive understanding. Let me also check the packaged prompts in pi-subagents to see how they use argument substitution and if they use `${@:-...}` or just `$@`.


gather-context-and-clarify.md
parallel-cleanup.md
parallel-context-build.md
parallel-handoff-plan.md
parallel-research.md
parallel-review.md
review-loop.md


---
description: Parallel subagents review
---

Launch parallel reviewers for an adversarial review of the current work.

Use fresh context, not forked context, unless I explicitly ask for forked context. Reviewers should inspect the repository, relevant instructions, and current diff directly from files and commands. Do not rely on the main conversation history.

Give each reviewer a distinct angle. Generate the angles dynamically from the user's intent, the plan, the implemented code, and the current diff. If I specify angles, use mine. Otherwise, choose the highest-value review angles for this specific work.

These are examples, not fixed defaults:

1. Correctness and regressions
   Check whether the change satisfies the request, preserves existing behavior, handles edge cases, and avoids hidden runtime failures.

2. Tests and validation
   Check whether tests or validation were added at the right layer, whether assertions are meaningful, and whether the chosen verification commands are enough.

3. Simplicity and maintainability
   Check for unnecessary complexity, duplicate structure, single-use wrappers, brittle abstractions, confusing names, verbosity, and cleanup that is clearly worth doing.

Choose or adapt angles when the work calls for it:
- TypeScript-heavy changes: include type safety, source-of-truth types, casts, and error-boundary discipline.
- UI-heavy changes: include UX, accessibility, copy, and visual quality.
- Security-sensitive changes: include unsafe input/output handling, auth boundaries, privacy, and data exposure.
- Docs-heavy changes: include clarity, accuracy, completeness, reader flow, and non-robotic prose.
- Large multi-file changes: consider a fourth reviewer for structural friction, module boundaries, and testability.

Prefer three strong reviewers over many vague reviewers.

Give every reviewer a specific task prompt naming its angle. Ask reviewers to return concise, evidence-backed findings with file/line references and suggested fixes. The response should be review feedback, not a context summary. Reviewers must not edit files unless I explicitly ask for a writer pass.

While reviewers run, do your own narrow inspection if useful. After they return, synthesize the feedback into:
- fixes worth doing now
- optional improvements
- feedback to ignore or defer, with a short reason

Do not blindly apply every reviewer suggestion.

Autofix mode: if the invocation contains the exact word `autofix`, treat it as workflow control, not review scope. Remove it before deciding the review target. After synthesis, apply only fixes worth doing now, validate, and summarize. Do not apply optional improvements unless explicitly requested. If there are no fixes worth doing now, do not edit.

Without autofix mode, ask before applying fixes unless I already told you to address review feedback. When you ask, end with a compact numbered menu so I can respond with a number. Use wording suited to the findings, but include these choices when applicable:

```text
Reply with [1], [2], or further instructions:
[1] Apply only the fixes worth doing now.
[2] Apply the fixes worth doing now plus optional improvements.
```

Additional review target or focus from the slash command invocation:

$@

If the invocation provides a URL, issue link, file path, plan path, or freeform focus, treat it as the primary review scope. Read or fetch that target before assigning reviewer angles, and pass the target explicitly into each reviewer task.


---
description: Review/fix loop until clean
---

Run a parent-orchestrated review loop for the requested work.

Use the `subagent` tool. Keep the parent session as the loop controller and final decision-maker. Child subagents must receive concrete role-specific tasks; they must not run subagents or manage the loop themselves unless the parent intentionally selected an explicit fanout agent whose builtin `tools` includes `subagent` for that assigned fanout.

Default to a maximum of 3 review rounds unless I specify a different cap. Count a review round each time fresh-context reviewers inspect the current diff after a worker pass. Stop early when reviewers find no blockers or fixes worth doing now.

If the invocation includes an implementation request, first launch one async `worker` to implement the approved scope. If the current diff is already the target, start with review. The sequence can be launched up front as an async/background chain when the workflow is already clear, or continued as follow-up subagent runs after each async completion. For an initial chain, pass `async: true` so the main chat is unblocked; do not set `clarify: true` unless I explicitly want the foreground clarify UI. Use only one writer against the active worktree at a time unless I explicitly ask for isolated worktrees.

As a conservative orchestration policy, do not set `turnBudget` or a hard `toolBudget` on implementation or fix workers. A default tool budget blocks read/search tools rather than mutation tools, but count limits still do not measure delivery safety. Give each writer a narrow delivery slice and an outer elapsed deadline with enough margin. Before that deadline, request a checkpoint after the current tool returns with changed files, build/test state, remaining work, and commit or PR state. An elapsed timeout is not a mutation-safe boundary and must not be the checkpoint trigger.

For each review round, launch fresh-context `reviewer` agents in parallel. Reviewers must inspect the repository, relevant instructions, and current diff directly from files and commands. They must not rely on the main conversation history and must not edit files.

Choose review angles from the actual change. Common angles are correctness/regressions, tests/validation, and simplicity/maintainability. Add security, performance, docs/API contracts, or user-flow validation when the work calls for it. Prefer three strong reviewers over many vague reviewers.

After reviewers return, synthesize their feedback into:
- blockers or scope/product/architecture decisions that need user approval;
- fixes worth doing now;
- optional improvements;
- feedback to ignore or defer, with a short reason.

Do not blindly apply every reviewer suggestion. If reviewers surface an unapproved product, scope, or architecture decision, pause and ask me before launching a fix worker.

When an async implementation worker completes, treat its handoff as the transition into review, not as final completion, unless I explicitly asked for worker-only work, review-only output, or to stop after implementation.

When there are fixes worth doing now and the workflow is implementation-authorized, launch one async forked `worker` without hard turn or tool-call caps to apply only those synthesized fixes. Ask it to preserve the approved scope, run focused validation, and report changed files, commands run with exit codes, validation evidence, surprises, and anything left undone.

After a fix worker returns, run another review round only when it made material changes or addressed non-trivial findings. Do not keep looping for optional polish, speculative improvements, or findings already deferred by the parent.

Stop and summarize when one of these is true:
- reviewers find no blockers or fixes worth doing now;
- remaining feedback is optional, speculative, or intentionally deferred;
- reviewers surface an unapproved decision that needs me;
- the max review-round cap is reached.

On completion, inspect the final diff yourself, run or confirm focused validation where appropriate, and summarize the loop: rounds run, fixes applied, validation, remaining deferred items, and why the loop stopped.

Additional target, implementation request, max-iteration cap, or review focus from the slash command invocation:

$@


The packaged prompts use `$@` for all arguments, not `${@:-...}`. They also don't use default values. This confirms that `${@:-default}` is not supported by the prompt-workflow adapter.

Now let me also check if `async: true` frontmatter is documented anywhere. Search README for "async" in frontmatter context. From the docs, `async` is a per-agent default in `.agent.md` files, not in prompt templates. The prompt-workflow adapter supports `--bg` runtime flag.

So the issues are confirmed:
1. `${@:-...}` syntax not supported in prompt bodies (both c- and p- files)
2. `async: true` in frontmatter ignored; use `--bg` or `--async` at invocation
3. `fork: true` context from p- files lost in chain mode (because `workflowChainStep` doesn't propagate `context`)

Wait, I need to double-check #3. In `workflowChainStep`:
```typescript
function workflowChainStep(workflow: PromptWorkflow, args: string[], runtime: ReturnType<typeof parseRuntimeOptions>): ChainStep {
	const params = workflowParams(workflow, args, runtime);
	return {
		agent: params.agent ?? "delegate",
		task: params.task,
		...(params.model ? { model: params.model } : {}),
		...(params.skill !== undefined ? { skill: params.skill } : {}),
		...(params.cwd ? { cwd: params.cwd } : {}),
	};
}
```
Indeed, `context` is missing. The `ChainStep` type likely supports `context`. Let me verify.


src/shared/settings.ts
 116- worktree?: boolean;
 117- cwd?: string;
 118- }
 119-
 120- /** Union type for chain steps */
 121: export type ChainStep = SequentialStep | ParallelStep | DynamicParallelStep;
 122-
 123- // =============================================================================
 124- // Type Guards
 125- // =============================================================================
 126-


src/shared/settings.ts
 29- reads?: string[] | false;
 30- progress?: boolean;
 31- skills?: string[] | false;
 32- model?: string;
 33- }
 34-
 35- function normalizeOutputOverride(output: string | false | undefined): string | false | undefined {
 36- return output === "false" ? false : output;
 37- }
 38-
 39- // =============================================================================
 40- // Chain Step Types
 41- // =============================================================================
 42-
 43- /** Sequential step: single agent execution */
 44: export interface SequentialStep {
 45- agent: string;
 46- task?: string;
 47- phase?: string;
 48- label?: string;
 49- as?: string;
 50- outputSchema?: JsonSchemaObject;
 51- cwd?: string;
 52- output?: string | false;
 53- outputMode?: OutputMode;
 54- reads?: string[] | false;
 55- progress?: boolean;
 56- skill?: string | string[] | false;
 57- model?: string;
 58- toolBudget?: ToolBudgetConfig;
 59- acceptance?: AcceptanceInput;


export interface SequentialStep {
	agent: string;
	task?: string;
	phase?: string;
	label?: string;
	as?: string;
	outputSchema?: JsonSchemaObject;
	cwd?: string;
	output?: string | false;
	outputMode?: OutputMode;
	reads?: string[] | false;
	progress?: boolean;
	skill?: string | string[] | false;
	model?: string;
	toolBudget?: ToolBudgetConfig;
	acceptance?: AcceptanceInput;
}

/** Parallel task item within a parallel step */
export interface ParallelTaskItem {
	agent: string;
	task?: string;
	phase?: string;
	label?: string;
	as?: string;

[383 more lines in file. Use offset=69 to continue.]


Interesting! `SequentialStep` does NOT have a `context` field. So chain steps don't support per-step context. The context is set at the chain level or per-agent default. This means the `fork: true` in p- files is intentionally not applicable when chaining - the chain-level `context` would apply to all steps, or each agent's `defaultContext`.

So point #3 is actually by design in pi-subagents. The `workflowChainStep` correctly doesn't include `context` because `SequentialStep` doesn't have it. The `context` from individual prompt templates is only used for single `/prompt-workflow` invocations, not chains.

This is actually fine then - not a bug per se, but the c- chains will run with default context (fork for packaged agents like delegate/planner/worker/oracle per README line 305). Actually, the c- files chain p- files which use `subagent: true` → agent "delegate". The delegate agent's defaultContext might be fresh or fork. We should check the agent definition.

But the user's question is specifically "do the c- prompts work correctly as prompt-workflow". I should focus on the actual bugs:
1. Argument substitution `${@:-...}` doesn't work
2. `async: true` frontmatter ignored

Let me also check if `argument-hint` frontmatter field is supported by the prompt-workflow adapter. It only reads description, model, subagent, skill, cwd, worktree, fork, fresh, inheritContext, chain. It does NOT read `argument-hint`. So the `argument-hint` in the c- files would be ignored by prompt-workflow, though pi's native prompt template system would use it for autocomplete.

Wait, but the c- files are loaded as prompt templates by pi AND as prompt workflows by pi-subagents. When typing `/`, pi shows autocomplete using its own prompt template parser which reads `description` and `argument-hint`. So `/c-design-build` would show up with the hint. But invoking via `/c-design-build` would use pi's native expansion (not the chain). To use the chain, the user must type `/prompt-workflow c-design-build ...`.

Actually, I need to think about this more carefully. Pi's prompt template system and pi-subagents' prompt-workflow adapter both read the same `.md` files. Pi uses `/name` and expands the body. pi-subagents uses `/prompt-workflow name` and runs it as a subagent. So:
- `/c-design-build` → pi native expansion of the body (just the text "Run the design → plan → test → commit chain for: ${@:-the current task}...", no actual chain)
- `/prompt-workflow c-design-build foo` → pi-subagents adapter, reads `chain:` and runs subagent chain

This is potentially confusing but is how the adapter works.

Now, let me check the delegate agent definition to see its default context, so I can mention whether the chain will run forked or fresh.

bash

find /home/liph/dotfiles/pi/.pi/agent/npm/node_modules/pi-subagents -name "delegate*" -o -name "*.agent.md" | head -20


src/runs/background/completion-batcher.ts
node_modules/yaml/browser/dist/compose/util-map-includes.js
node_modules/yaml/browser/dist/parse/line-counter.js
node_modules/yaml/dist/parse/line-counter.d.ts
node_modules/yaml/dist/parse/line-counter.js

[Query "agents delegate planner worker oracle" produced only weak scattered fuzzy matches. Output capped at 5/20.]


---
name: delegate
description: Lightweight subagent that inherits the parent model with no default reads
systemPromptMode: append
inheritProjectContext: true
tools: read, grep, find, ls, bash, edit, write, contact_supervisor
inheritSkills: false
---

You are a delegated agent. Execute the assigned task using the provided tools. Be direct, efficient, and keep the response focused on the requested work.

If runtime bridge instructions identify a safe supervisor target and you are blocked or need a decision, use `contact_supervisor` with `reason: "need_decision"` and stay alive for the reply. Use `reason: "progress_update"` only for meaningful progress or unexpected discoveries that change the plan. Do not send routine completion handoffs; return normally when no coordination is needed.


Delegate has no `defaultContext`, so it will use the pi-subagents default which is "fresh" unless configured otherwise (README says packaged planner/worker/oracle default to fork, but delegate doesn't). So the c- chains will run with fresh context, not forked. This means they won't inherit the parent conversation context. This might be intentional or not, but the user should be aware.

Actually, wait - the c- files themselves don't specify context, and the p- files have `fork: true` but as we determined, chain steps don't support per-step context. So the chain runs with default context. Since delegate has no `defaultContext`, the effective default is whatever pi-subagents uses when context is omitted. Looking at the tool schema or README line 1210: "When omitted, each agent uses its own defaultContext; fork creates real branched sessions from the parent leaf. Packaged planner, worker, and oracle default to fork." Since delegate has no defaultContext, it probably defaults to "fresh".

But this isn't necessarily a bug - it's the behavior of the system. The main issues are the unsupported argument syntax and ignored async frontmatter.

Now, let me also verify whether `argument-hint` in the c- files causes any issue. The pi-subagents `loadPromptWorkflow` doesn't read `argument-hint`, so it's ignored by the adapter. But it's valid for pi's native prompt templates. No issue there.

Let me also double-check by looking at the `.pi-subagents` artifacts to see if any c- workflow was actually run. The artifacts are in the prompts directory which is unusual - they might be from testing. Let me check one meta file to see what workflow was run.

cat /home/liph/dotfiles/pi/.pi/agent/prompts/.pi-subagents/artifacts/5f390df5_delegate_0_meta.json | head -50


{
  "runId": "5f390df5",
  "agent": "delegate",
  "task": "Brainstorm approaches for: ${@:-the current problem or feature}\n\nStructure your answer as:\n1. **Problem restatement** — one paragraph; what we're solving, what constraints matter, what \"done\" looks like\n2. **Alternatives** — 3 to 5 distinct approaches. For each:\n   - **Summary** — one or two sentences\n   - **How it works** — the key mechanism\n   - **Pros** — what it gets right\n   - **Cons** — what it costs, risks, or breaks\n   - **Best when** — the conditions that would make this the right pick\n3. **Comparison table** — across the alternatives, score or rank on: complexity, time-to-ship, reversibility, performance, fit-with-existing-code\n4. **Recommendation** — which one to pick *given the current codebase*, with the second choice as a fallback\n5. **Unknowns** — questions that, if answered, would change the recommendation\n\nBe opinionated. Surface real trade-offs, not strawmen. If the obvious approach wins decisively, say so and only enumerate 2 alternatives — don't manufacture options for the sake of a full set. If the problem is under-specified, ask clarifying questions before brainstorming.",
  "exitCode": 0,
  "usage": {
    "input": 12542,
    "output": 2362,
    "cacheRead": 0,
    "cacheWrite": 0,
    "cost": 0,
    "turns": 3
  },
  "model": "ollama/minimax-m3:cloud",
  "attemptedModels": [
    "ollama/minimax-m3:cloud"
  ],
  "modelAttempts": [
    {
      "model": "ollama/minimax-m3:cloud",
      "success": true,
      "exitCode": 0,
      "usage": {
        "input": 12542,
        "output": 2362,
        "cacheRead": 0,
        "cacheWrite": 0,
        "cost": 0,
        "turns": 3
      }
    }
  ],
  "durationMs": 33353,
  "toolCount": 2,
  "acceptance": {
    "status": "attested",
    "explicit": false,
    "effectiveAcceptance": {
      "level": "attested",
      "explicit": false,
      "inferredReason": [
        "default lightweight attestation"
      ],
      "criteria": [
        {
          "id": "criterion-1",
          "must": "Return a concise result and residual risks when applicable",
          "evidence": [
            "manual-notes",
            "residual-risks"


This confirms the bug! The task literally contains `${@:-the current problem or feature}` instead of the actual arguments. This means when the c-design-build chain ran p-brainstorm with no user arguments, the substitution didn't happen and the LLM received the placeholder text.

So a c- workflow WAS run (runId 5f390df5), and it demonstrates the argument substitution bug.

Let me check if this was from a chain run by looking at more artifacts or status.

ls /home/liph/dotfiles/pi/.pi/agent/prompts/.pi-subagents | head -20; find /home/liph/dotfiles/pi/.pi/agent/prompts/.pi-subagents -maxdepth 2 -type f -name "*.json" | head -20


Let me check the chain-runs directory to see if there's a status.json for the 5f390df5 chain.

ls /home/liph/dotfiles/pi/.pi/agent/prompts/.pi-subagents/chain-runs | head -20

find /home/liph/dotfiles/pi/.pi/agent/prompts/.pi-subagents/chain-runs -name "status.json" -o -name "*.json" | head -20


5f390df5
68d5d3d2
f23fc33e


(no output)


Let me check the 5f390df5 chain run directory.


total 8
drwxr-xr-x 2 liph liph 4096 Jul 24 17:45 .
drwxr-xr-x 5 liph liph 4096 Jul 24 17:45 ..


The chain-runs directory is empty for that run. The artifacts have the meta files. Let me check the subagent log or run history to understand the chain. But I have enough evidence now.

Let me now prepare the final answer. I should:
1. Confirm that the c- prompts ARE recognized by pi-subagents' prompt-workflow adapter.
2. Point out the bugs that prevent correct operation:
   - `${@:-default}` argument substitution is not supported by the adapter (it only supports `$@`, `$ARGUMENTS`, `$N`, `${N:-fallback}`)
   - `async: true` in frontmatter is ignored; must use `--bg` or `--async` at invocation
   - (Optional) `fork: true` on chained p- steps is not propagated because chain steps don't support per-step context
3. Show evidence from the actual artifact that the placeholder was passed to the LLM.
4. Provide corrected versions of the c- and p- files.

Wait, I should also verify the actual fix. To fix the argument substitution, we need to either:
- Change `${@:-...}` to `$@` in the c- files, but then there's no default text when no args are provided.
- Or extend the `substituteArgs` function in pi-subagents to support `${@:-...}`. But that's a package dependency, not something we should edit directly.
- Or change the prompt-workflow adapter code? No, we shouldn't modify node_modules.

Actually, we could fix the c- files to use `$@` and remove the default fallback. Or, if the user wants defaults, they need to handle that at the pi level or use a different syntax.

Actually wait - the standard pi prompt templates DO support `${@:-default}`. But the pi-subagents adapter has its own simpler substitution. This is the incompatibility.

Let me think about the best fix:
- For the c- files: Use `$@` instead of `${@:-...}`. Or, to preserve a default when no args given, we could write the prompt to handle the empty case with conditional language, but that's harder.
- For the p- files: Same issue. Use `$@` instead of `${@:-...}`.
- For `async: true`: Remove from frontmatter and either:
  - Train the user to pass `--bg` when invoking
  - Or, if they want the c- workflows to always run async, they could create wrapper prompts or modify the invocation pattern
  - Actually, the c- file body says "Invoke with: /prompt-workflow c-design-build ...". We could change that to include `--bg`: `/prompt-workflow c-design-build --bg ...`
- For `fork: true` on p- files: This is actually not a bug in the c- files; it's a limitation of the chain mode. The chain runs with default context. If the user wants forked context, they should pass `--fork` at invocation. But the c- file could include `--fork` in the recommended invocation. However, the p- files have `fork: true` expecting to always run forked. In chain mode, this is ignored. This is worth noting but maybe not a fix we can make in the prompt files.

Actually, I realize there might be another approach: the c- files could use `/chain-prompts` instead of `/prompt-workflow`. The `chain-prompts` command also supports `--bg`, `--fork`, etc. But the c- files have `chain:` frontmatter which only works with `/prompt-workflow`.

Let me now formulate the answer. I should be precise and helpful. I should offer to fix the files.

Actually, I should ask the user what they want to do, or offer to fix them. Since the user asked "can you check if they work correctly", I should report my findings and offer to fix.

Given the instructions, I can use `ask_user_question` if the request is underspecified. But here the user asked a clear question: check if they work. I can answer directly and offer fixes. I don't necessarily need to ask questions first; I can provide the diagnosis and ask if they want me to apply fixes.

Let me also think: is there a chance the user wants me to fix the pi-subagents package? No, that's an installed npm package. We shouldn't modify node_modules. The fix should be in the prompt files themselves.

But wait - the root issue is that pi-subagents' `substituteArgs` doesn't support `${@:-...}`. If the user wants that feature, they could open an issue or PR to pi-subagents, or install a different version. But for now, we should adapt the prompt files to the supported syntax.

Let me also note: the c- files use `argument-hint` which is a pi native prompt template feature, not used by pi-subagents. This is fine, just metadata.

One more thing: the c- files' frontmatter includes `chain: p-brainstorm -> p-plan -> p-test -> p-commit`. The pi-subagents `loadPromptWorkflow` reads `chain` as a string and `splitPromptChain` splits on ` -> `. This works. The referenced p- files are in the same directory and discovered. This part is correct.

So the diagnosis:
- Chain declaration: ✅ correct
- Invocation syntax: ✅ correct
- Argument substitution: ❌ broken due to unsupported `${@:-...}` syntax
- Async flag: ❌ ignored in frontmatter; must use runtime flag
- Fork context in chain steps: ⚠ not supported; chain uses default context
- Discovery: ✅ c- and p- files are in the right place

I should provide this clearly. Let me also check the README's "Optional shortcuts" section to see if there's a similar pattern used by the package itself. We already saw `/parallel-review` and `/review-loop` which don't use frontmatter `chain:` - they are simple prompts that the LLM interprets and uses subagent tool. The c- files are using a more advanced feature.

Actually, I should also verify: does the pi-subagents extension load correctly? The settings show `npm:pi-subagents` is installed. And there are subagent artifacts. So yes, it loads.

OK, let me write the response. I'll structure it:
1. Summary: They are recognized by pi-subagents but have bugs.
2. What works.
3. What doesn't work (with evidence).
4. Recommended fixes.
5. Offer to apply fixes.

I think I should offer to edit the files. But first let me present findings clearly. The user said "can you check" so a report is appropriate. I can then ask if they want me to fix them.

Actually, looking at the available tools, I can use `ask_user_question` to ask if they want fixes. But the instruction says "Use ask_user_question whenever the user's request is underspecified and you cannot proceed without concrete decisions". Here I can proceed with a report. I don't need to ask; I can just provide the report and offer.

However, if I want to actually edit files, I should get confirmation. But I can also just describe the fixes needed.

Let me be concise. The response should not be too long. I'll include file paths.

Actually, I realize I should also run a quick test to verify the behavior empirically. But I can't easily run `/prompt-workflow` from here. The artifacts already show the bug. I have enough evidence.

Let me now write the final response.

I checked the `c-*.md` prompt workflows in `/home/liph/dotfiles/pi/.pi/agent/prompts`. They **are picked up** by the `pi-subagents` package's prompt-workflow adapter (it registers `/prompt-workflow` and `/chain-prompts`), but they have a few bugs that prevent them from working correctly as written.

## What works

- **Discovery & chain declaration** — the `chain: p-brainstorm -> p-plan -> p-test -> p-commit` syntax is valid and parsed by `pi-subagents`.
- **Invocation syntax** — `/prompt-workflow c-design-build "..."` is the right command.
- **Location** — files are in `~/.pi/agent/prompts`, which both pi and `pi-subagents` discover.

## What's broken

### 1. Argument substitution uses unsupported syntax `${@:-...}`
Both the `c-*.md` and `p-*.md` files use shell-style defaults like:

```markdown
Run the design → plan → test → commit chain for: ${@:-the current task}
```

The `pi-subagents` prompt-workflow adapter only supports:

- `$@` / `$ARGUMENTS` — all args
- `$1`, `$2`, … — positional args
- `${1:-fallback}` — positional arg with fallback

It does **not** support `${@:-fallback}`. As a result, the literal placeholder text is passed to the subagent. I can see this in the run artifacts, e.g. `5f390df5_delegate_0_meta.json`:

```json
"task": "Brainstorm approaches for: ${@:-the current problem or feature}\n\nStructure your answer as: ..."
```

So the chain ran, but the LLM never received the actual user argument.

### 2. `async: true` in frontmatter is ignored
The `async: true` field is not read by the adapter. To run a prompt workflow in the background you must pass `--bg` or `--async` at invocation, e.g.:

```text
/prompt-workflow c-design-build --bg "add a rate-limiter"
```

### 3. Per-step `fork: true` from `p-*.md` is dropped in chain mode
The `p-*.md` files have `fork: true`, but `pi-subagents` chain steps do not support per-step `context`. Chain context is determined at the top level or by each agent's `defaultContext`. The `delegate` agent has no `defaultContext`, so these chains run with fresh context unless you pass `--fork`.

## How to fix the files

Replace `${@:-...}` with plain `$@`, and update the invocation examples to use `--bg` and/or `--fork` as desired.

For example, `c-design-build.md` would become:

```markdown
---
description: Brainstorm → plan → test → commit in one background chain
argument-hint: "[task or feature]"
chain: p-brainstorm -> p-plan -> p-test -> p-commit
---
Run the design → plan → test → commit chain for: $@

Invoke with:
/prompt-workflow c-design-build --bg "add a rate-limiter to the API"
```

The same substitution fix is needed in every `p-*.md` body that uses `${@:-...}`.

## Recommendation

Would you like me to apply these fixes to the `c-*.md` and `p-*.md` files? The minimal change is to switch argument substitution to `$@` and update the invocation examples to use `--bg` (and optionally `--fork`) at the command line.


yes please do so. and then run a test to see of the c- prompt work


The user wants me to:
1. Fix the c-*.md and p-*.md files to use supported argument substitution syntax
2. Run a test to verify the c- prompts work

Context-mode is active. I should use ctx_execute_file for reading/editing files since they're in the current project. But I can also use read/edit tools for precise changes. The instructions say "Read/edit files → ctx_execute_file", but the regular read/edit tools are still available and may be simpler for precise edits. However, given the context-mode guidance, I should prefer ctx_execute_file for analysis.

Actually, for making precise edits, the edit tool is still the right tool. ctx_execute_file is for analyzing file content without loading it into context. But I already read the files. Let me use edit to fix them.

Let me first check all p-*.md files for ${@:- patterns and fix them all in one go. Then I'll run a test.

Since I need to check multiple files and fix them, I can use ctx_execute (javascript) to find all files with ${@:- and report them, then use edit to fix each. Or use ffgrep.

Let me use ffgrep to find all occurrences.