poe-code 3.0.30 → 3.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +2 -2
  2. package/dist/cli/commands/mcp.js +1 -0
  3. package/dist/cli/commands/mcp.js.map +1 -1
  4. package/dist/cli/commands/ralph.js +52 -24
  5. package/dist/cli/commands/ralph.js.map +1 -1
  6. package/dist/cli/commands/skill.js +1 -0
  7. package/dist/cli/commands/skill.js.map +1 -1
  8. package/dist/cli/commands/spawn.js +5 -1
  9. package/dist/cli/commands/spawn.js.map +1 -1
  10. package/dist/cli/commands/version.js +8 -4
  11. package/dist/cli/commands/version.js.map +1 -1
  12. package/dist/cli/container.js +6 -2
  13. package/dist/cli/container.js.map +1 -1
  14. package/dist/cli/logger.js +2 -4
  15. package/dist/cli/logger.js.map +1 -1
  16. package/dist/cli/mcp-server.d.ts +1 -1
  17. package/dist/cli/mcp-server.js +1 -1
  18. package/dist/cli/mcp-server.js.map +1 -1
  19. package/dist/cli/program.js +1 -1
  20. package/dist/cli/program.js.map +1 -1
  21. package/dist/index.js +326 -310
  22. package/dist/index.js.map +4 -4
  23. package/dist/providers/claude-code.d.ts +2 -2
  24. package/dist/providers/claude-code.js +4 -49
  25. package/dist/providers/claude-code.js.map +1 -1
  26. package/dist/providers/codex.d.ts +1 -3
  27. package/dist/providers/codex.js +4 -35
  28. package/dist/providers/codex.js.map +1 -1
  29. package/dist/providers/kimi.js +2 -5
  30. package/dist/providers/kimi.js.map +1 -1
  31. package/dist/providers/opencode.js +2 -9
  32. package/dist/providers/opencode.js.map +1 -1
  33. package/dist/providers/spawn-options.d.ts +2 -0
  34. package/dist/sdk/spawn-core.d.ts +3 -0
  35. package/dist/sdk/spawn-core.js +9 -8
  36. package/dist/sdk/spawn-core.js.map +1 -1
  37. package/dist/sdk/spawn.js +4 -0
  38. package/dist/sdk/spawn.js.map +1 -1
  39. package/dist/sdk/types.d.ts +3 -0
  40. package/dist/tools/label-generator.js +2 -1
  41. package/dist/tools/label-generator.js.map +1 -1
  42. package/dist/utils/command-checks.d.ts +4 -0
  43. package/dist/utils/command-checks.js +26 -0
  44. package/dist/utils/command-checks.js.map +1 -1
  45. package/package.json +25 -22
  46. package/packages/tiny-stdio-mcp-test-server/dist/cli.js +26 -0
  47. package/packages/tiny-mcp-test-server/dist/index.js +0 -47
package/dist/index.js CHANGED
@@ -961,7 +961,8 @@ async function applyTemplateMerge(mutation, context, options, formatName) {
961
961
  templateDoc = format.parse(rendered);
962
962
  } catch (error2) {
963
963
  throw new Error(
964
- `Failed to parse rendered template "${mutation.templateId}" as ${formatName.toUpperCase()}: ${error2}`
964
+ `Failed to parse rendered template "${mutation.templateId}" as ${formatName.toUpperCase()}: ${error2}`,
965
+ { cause: error2 }
965
966
  );
966
967
  }
967
968
  const rawContent = await readFileIfExists(context.fs, targetPath);
@@ -14722,7 +14723,21 @@ var require_PROMPT_plan = __commonJS({
14722
14723
  // src/templates/ralph/PROMPT_build.md
14723
14724
  var require_PROMPT_build = __commonJS({
14724
14725
  "src/templates/ralph/PROMPT_build.md"(exports, module) {
14725
- module.exports = '# Build\n\nYou are an autonomous coding agent. Your task is to complete the work for exactly one story and record the outcome.\n\n## Paths\n- Plan: {{PLAN_PATH}}\n- Progress Log: {{PROGRESS_PATH}}\n- Guardrails: {{GUARDRAILS_PATH}}\n- Guardrails Reference: {{GUARDRAILS_REF}}\n- Context Reference: {{CONTEXT_REF}}\n- Errors Log: {{ERRORS_LOG_PATH}}\n- Activity Log: {{ACTIVITY_LOG_PATH}}\n- Activity Logger: {{ACTIVITY_CMD}}\n- No-commit: {{NO_COMMIT}}\n- Repo Root: {{REPO_ROOT}}\n- Run ID: {{RUN_ID}}\n- Iteration: {{ITERATION}}\n- Run Log: {{RUN_LOG_PATH}}\n- Run Summary: {{RUN_META_PATH}}\n\n## Global Quality Gates (apply to every story)\n{{QUALITY_GATES}}\n\n## Selected Story (Do not change scope)\nID: {{STORY_ID}}\nTitle: {{STORY_TITLE}}\n\nStory details:\n{{STORY_BLOCK}}\n\nIf the story details are empty or missing, STOP and report that the plan story format could not be parsed.\n\n## Rules (Non-Negotiable)\n- Implement **only** the work required to complete the selected story.\n- Complete all tasks associated with this story (and only this story).\n- Do NOT ask the user questions.\n- Do NOT change unrelated code.\n- Do NOT assume something is unimplemented \u2014 confirm by reading code.\n- Implement completely; no placeholders or stubs.\n- If No-commit is true, do NOT commit or push changes.\n- Do NOT edit the plan file (status is handled by the loop).\n- All changes made during the run must be committed (including updates to progress/logs).\n - Before committing, perform a final **security**, **performance**, and **regression** review of your changes.\n\n## Your Task (Do this in order)\n1. Read {{GUARDRAILS_PATH}} before any code changes.\n2. Read {{ERRORS_LOG_PATH}} for repeated failures to avoid.\n3. Read {{PLAN_PATH}} for global context (do not edit).\n4. Fully audit and read all necessary files to understand the task end-to-end before implementing. Do not assume missing functionality.\n5. Implement only the tasks that belong to {{STORY_ID}}.\n6. Run verification commands listed in the story and the global quality gates.\n7. If the project has a build or dev workflow, run what applies:\n - Build step (e.g., `npm run build`) if defined.\n - Dev server (e.g., `npm run dev`, `wrangler dev`) if it is the normal validation path.\n - Confirm no runtime/build errors in the console.\n8. Perform a brief audit before committing:\n - **Security:** check for obvious vulnerabilities or unsafe handling introduced by your changes.\n - **Performance:** check for avoidable regressions (extra queries, heavy loops, unnecessary re-renders).\n - **Regression:** verify existing behavior that could be impacted still works.\n9. If No-commit is false, commit changes using the `$commit` skill.\n - Stage everything: `git add -A`\n - Confirm a clean working tree after commit: `git status --porcelain` should be empty.\n - After committing, capture the commit hash and subject using:\n `git show -s --format="%h %s" HEAD`.\n10. Append a progress entry to {{PROGRESS_PATH}} with run/commit/test details (format below).\n If No-commit is true, skip committing and note it in the progress entry.\n\n## Progress Entry Format (Append Only)\n```\n## [Date/Time] - {{STORY_ID}}: {{STORY_TITLE}}\nThread: [codex exec session id if available, otherwise leave blank]\nRun: {{RUN_ID}} (iteration {{ITERATION}})\nRun log: {{RUN_LOG_PATH}}\nRun summary: {{RUN_META_PATH}}\n- Guardrails reviewed: yes\n- No-commit run: {{NO_COMMIT}}\n- Commit: <hash> <subject> (or `none` + reason)\n- Post-commit status: `clean` or list remaining files\n- Verification:\n - Command: <exact command> -> PASS/FAIL\n - Command: <exact command> -> PASS/FAIL\n- Files changed:\n - <file path>\n - <file path>\n- What was implemented\n- **Learnings for future iterations:**\n - Patterns discovered\n - Gotchas encountered\n - Useful context\n---\n```\n\n## Completion Signal\nOnly output the completion signal when the **selected story** is fully complete and verified.\nWhen the selected story is complete, output:\n<promise>COMPLETE</promise>\n\nOtherwise, end normally without the signal.\n\n## Additional Guardrails\n- When authoring documentation, capture the why (tests + implementation intent).\n- If you hit repeated errors, log them in {{ERRORS_LOG_PATH}} and add a Sign to {{GUARDRAILS_PATH}} using {{GUARDRAILS_REF}} as the template.\n\n## Activity Logging (Required)\nLog major actions to {{ACTIVITY_LOG_PATH}} using the helper:\n```\n{{ACTIVITY_CMD}} "message"\n```\nLog at least:\n- Start of work on the story\n- After major code changes\n- After tests/verification\n- After updating progress log\n\n## Browser Testing (Required for Frontend Stories)\nIf the selected story changes UI, you MUST verify it in the browser:\n1. Load the `dev-browser` skill.\n2. Navigate to the relevant page.\n3. Verify the UI changes work as expected.\n4. Take a screenshot if helpful for the progress log.\n\nA frontend story is NOT complete until browser verification passes.\n';
14726
+ module.exports = '# Build\n\nYou are an autonomous coding agent. Your task is to complete the work for exactly one story and record the outcome.\n\n## Paths\n\n- Plan: {{PLAN_PATH}}\n- Progress Log: {{PROGRESS_PATH}}\n- Guardrails: {{GUARDRAILS_PATH}}\n- Guardrails Reference: {{GUARDRAILS_REF}}\n- Context Reference: {{CONTEXT_REF}}\n- Errors Log: {{ERRORS_LOG_PATH}}\n- Activity Log: {{ACTIVITY_LOG_PATH}}\n- Activity Logger: {{ACTIVITY_CMD}}\n- No-commit: {{NO_COMMIT}}\n- Repo Root: {{REPO_ROOT}}\n- Run ID: {{RUN_ID}}\n- Iteration: {{ITERATION}}\n- Run Log: {{RUN_LOG_PATH}}\n- Run Summary: {{RUN_META_PATH}}\n\n## Global Quality Gates (apply to every story)\n\n{{QUALITY_GATES}}\n\n## Selected Story (Do not change scope)\n\nID: {{STORY_ID}}\nTitle: {{STORY_TITLE}}\n\nStory details:\n{{STORY_BLOCK}}\n\nIf the story details are empty or missing, STOP and report that the plan story format could not be parsed.\n\n## Rules (Non-Negotiable)\n\n- Implement **only** the work required to complete the selected story.\n- Complete all tasks associated with this story (and only this story).\n- Do NOT ask the user questions.\n- Do NOT change unrelated code.\n- Do NOT assume something is unimplemented \u2014 confirm by reading code.\n- Implement completely; no placeholders or stubs.\n- If No-commit is true, do NOT commit or push changes.\n- Do NOT edit the plan file (status is handled by the loop).\n- All changes in git made during the run must be committed\n- Do NOT commit the progress log ({{PROGRESS_PATH}}). It is gitignored.\n- Before committing, perform a final **security**, **performance**, and **regression** review of your changes.\n\n## Your Task (Do this in order)\n\n1. Read {{GUARDRAILS_PATH}} before any code changes.\n2. Read {{ERRORS_LOG_PATH}} for repeated failures to avoid.\n3. Read {{PLAN_PATH}} for global context (do not edit).\n4. Fully audit and read all necessary files to understand the task end-to-end before implementing. Do not assume missing functionality.\n5. Implement only the tasks that belong to {{STORY_ID}}.\n6. Run verification commands listed in the story and the global quality gates.\n7. If the project has a build or dev workflow, run what applies:\n - Build step (e.g., `npm run build`) if defined.\n - Test step (e.g. `npm run test`) if defined.\n - Confirm no runtime/build errors in the console.\n8. Perform a brief audit before committing:\n - **Security:** check for obvious vulnerabilities or unsafe handling introduced by your changes.\n - **Performance:** check for avoidable regressions (extra queries, heavy loops, unnecessary re-renders).\n - **Regression:** verify existing behavior that could be impacted still works.\n9. If No-commit is false, commit changes.\n - Follow the project\'s commit guidelines\n - Stage only project files you changed\n - After committing, capture the commit hash and subject using:\n `git show -s --format="%h %s" HEAD`.\n10. Append a progress entry to {{PROGRESS_PATH}} with run/commit/test details (format below).\n Do NOT commit this file.\n\n## Progress Entry Format (Append Only)\n\n```\n## [Date/Time] - {{STORY_ID}}: {{STORY_TITLE}}\nRun: {{RUN_ID}} (iteration {{ITERATION}})\nRun log: {{RUN_LOG_PATH}}\nRun summary: {{RUN_META_PATH}}\n- Guardrails reviewed: yes\n- No-commit run: {{NO_COMMIT}}\n- Commit: <hash> <subject> (or `none` + reason)\n- Verification:\n - Command: <exact command> -> PASS/FAIL\n - Command: <exact command> -> PASS/FAIL\n- Files changed:\n - <file path>\n - <file path>\n- What was implemented\n- **Learnings for future iterations:**\n - Patterns discovered\n - Gotchas encountered\n - Useful context\n---\n```\n\n## Completion Signal\n\nOnly output the completion signal when the **selected story** is fully complete and verified.\nWhen the selected story is complete, output:\n<promise>COMPLETE</promise>\n\nOtherwise, end normally without the signal.\n\n## Additional Guardrails\n\n- When authoring documentation, capture the why (tests + implementation intent).\n- If you hit repeated errors, log them in {{ERRORS_LOG_PATH}} and add a Sign to {{GUARDRAILS_PATH}} using {{GUARDRAILS_REF}} as the template.\n\n## Activity Logging (Required)\n\nLog major actions to {{ACTIVITY_LOG_PATH}} using the CLI command:\n\n```\n{{ACTIVITY_CMD}} "message"\n```\n\nLog at least:\n\n- Start of work on the story\n- After major code changes\n- After tests/verification\n';
14727
+ }
14728
+ });
14729
+
14730
+ // src/templates/ralph/references/GUARDRAILS.md
14731
+ var require_GUARDRAILS = __commonJS({
14732
+ "src/templates/ralph/references/GUARDRAILS.md"(exports, module) {
14733
+ module.exports = '# Guardrails Reference ("Signs")\n\nThis document explains how to create and use guardrails in Ralph.\n\n## The Signs Metaphor\n\nFrom Geoffrey Huntley:\n\n> "Ralph is very good at making playgrounds, but he comes home bruised because he fell off the slide, so one then tunes Ralph by adding a sign next to the slide saying \'SLIDE DOWN, DON\'T JUMP, LOOK AROUND,\' and Ralph is more likely to look and see the sign."\n\nSigns are explicit instructions added to prevent known failure modes.\n\n## Anatomy of a Sign\n\n```markdown\n### Sign: [Descriptive Name]\n- **Trigger**: When this situation occurs\n- **Instruction**: What to do instead\n- **Added after**: When/why this was added\n- **Example**: Concrete example if helpful\n```\n\n## Types of Signs\n\n### 1. Preventive Signs\n\nStop problems before they happen:\n\n```markdown\n### Sign: Validate Before Trust\n- **Trigger**: When receiving external input\n- **Instruction**: Always validate and sanitize input before using it\n- **Added after**: Iteration 3 - SQL injection vulnerability\n```\n\n### 2. Corrective Signs\n\nFix recurring mistakes:\n\n```markdown\n### Sign: Check Return Values\n- **Trigger**: When calling functions that can fail\n- **Instruction**: Always check return values and handle errors\n- **Added after**: Iteration 7 - Null pointer exception\n```\n\n### 3. Process Signs\n\nEnforce good practices:\n\n```markdown\n### Sign: Test Before Commit\n- **Trigger**: Before committing changes\n- **Instruction**: Run the test suite and ensure all tests pass\n- **Added after**: Iteration 2 - Broken tests committed\n```\n\n### 4. Architecture Signs\n\nGuide design decisions:\n\n```markdown\n### Sign: Single Responsibility\n- **Trigger**: When a function grows beyond 50 lines\n- **Instruction**: Consider splitting into smaller, focused functions\n- **Added after**: Iteration 12 - Unmaintainable god function\n```\n\n## When to Add Signs\n\nAdd a sign when:\n\n1. **The same mistake happens twice** - Once is learning, twice is a pattern\n2. **A subtle bug is found** - Prevent future occurrences\n3. **A best practice is violated** - Reinforce good habits\n4. **Context-specific knowledge is needed** - Project-specific conventions\n\n## Sign Lifecycle\n\n### Creation\n\n```markdown\n### Sign: [New Sign]\n- **Trigger**: [When it applies]\n- **Instruction**: [What to do]\n- **Added after**: Iteration N - [What happened]\n```\n\n### Refinement\n\nIf a sign isn\'t working:\n- Make the trigger more specific\n- Make the instruction clearer\n- Add examples\n\n### Retirement\n\nSigns can be removed when:\n- The underlying issue is fixed at a deeper level\n- The sign is no longer relevant\n- The sign is causing more problems than it solves\n\n## Example Signs Library\n\n### Security\n\n```markdown\n### Sign: Sanitize All Input\n- **Trigger**: Any user-provided data\n- **Instruction**: Use parameterized queries, escape HTML, validate types\n- **Example**: `db.query("SELECT * FROM users WHERE id = ?", [userId])`\n```\n\n### Error Handling\n\n```markdown\n### Sign: Graceful Degradation\n- **Trigger**: External service calls\n- **Instruction**: Always have a fallback for when services are unavailable\n- **Example**: Cache results, provide default values, show friendly errors\n```\n\n### Testing\n\n```markdown\n### Sign: Test the Unhappy Path\n- **Trigger**: Writing tests for new functionality\n- **Instruction**: Include tests for error cases, edge cases, and invalid input\n```\n\n### Code Quality\n\n```markdown\n### Sign: Explain Why, Not What\n- **Trigger**: Writing comments\n- **Instruction**: Comments should explain reasoning, not describe obvious code\n- **Example**: `// Using retry because API is flaky under load` not `// Call the API`\n```\n\n## Automatic Sign Detection\n\nThe Ralph hooks can automatically detect some patterns and suggest signs:\n\n- **Thrashing**: Same file edited many times \u2192 "Step back and reconsider"\n- **Repeated errors**: Same test failing \u2192 "Check the test assumptions"\n- **Large changes**: Big diffs \u2192 "Consider smaller increments"\n\nThese are logged in `.poe-code-ralph/failures.md` and can be promoted to guardrails.\n\n## Using Signs Effectively\n\n### Do\n\n- Keep signs concise and actionable\n- Include concrete examples\n- Update signs when they\'re not working\n- Remove outdated signs\n\n### Don\'t\n\n- Add signs for every minor issue\n- Make signs too vague ("be careful")\n- Ignore signs that keep triggering\n- Let the guardrails file become overwhelming\n\n## Integration with Ralph\n\nSigns are:\n1. Stored in `.poe-code-ralph/guardrails.md`\n2. Injected into context at the start of each iteration\n3. Referenced when relevant situations arise\n4. Updated based on observed failures\n\nThe goal is a self-improving system where each failure makes future iterations smarter.\n';
14734
+ }
14735
+ });
14736
+
14737
+ // src/templates/ralph/references/CONTEXT_ENGINEERING.md
14738
+ var require_CONTEXT_ENGINEERING = __commonJS({
14739
+ "src/templates/ralph/references/CONTEXT_ENGINEERING.md"(exports, module) {
14740
+ module.exports = "# Context Engineering Reference\n\nThis document explains the malloc/free metaphor for LLM context management that underlies the [Ralph Wiggum technique](https://ghuntley.com/ralph/), originally developed by Geoffrey Huntley.\n\n## The malloc() Metaphor\n\nIn traditional programming, memory management follows a clear pattern: `malloc()` allocates memory, and `free()` releases it. In LLM context windows, **there is no equivalent to `free()`**. Every operation that adds information to context\u2014reading files, executing tools, generating responses\u2014allocates context space permanently until the conversation ends.\n\nThe only way to \"free\" context is to start a new conversation.\n\n## Why This Matters\n\n### Context Pollution\n\nContext pollution occurs when the LLM's context window accumulates information that interferes with current work. This includes failed attempts, unrelated code snippets, error traces, and mixed concerns from multiple tasks.\n\n```\nTask 1: Build authentication \u2192 context contains auth code, JWT docs, security patterns\nTask 2: Build UI components \u2192 context now ALSO contains auth stuff\n\nResult: LLM might suggest auth-related patterns when building UI\n or mix concerns inappropriately\n```\n\n### Autoregressive Failure\n\nWhen context contains pollution, each generated token is influenced by that pollution, creating a feedback loop where the model's outputs become increasingly off-base. This is [autoregressive failure](https://ghuntley.com/gutter/): the model predicts the next token based on a context window that contains increasingly irrelevant or misleading information.\n\n### The Gutter\n\n> \"If the bowling ball is in the gutter, there's no saving it. It's in the gutter.\"\n> \u2014 [Geoffrey Huntley](https://ghuntley.com/gutter/)\n\nThe \"gutter\" is the state where context pollution has reached a point of no return. Like a bowling ball in the gutter, once the agent is stuck in polluted context, there is no recovering without starting fresh.\n\n## Context Health Indicators\n\n### \u{1F7E2} Healthy Context\n- Single focused task\n- Relevant files only\n- Clear progress\n- Under 40% capacity\n\n### \u{1F7E1} Warning Signs\n- Multiple unrelated topics discussed\n- Several failed attempts in history\n- Approaching 40% capacity\n- Repeated similar errors\n\n### \u{1F534} Critical / Gutter\n- Mixed concerns throughout\n- Circular failure patterns\n- Over 40% capacity\n- Model suggesting irrelevant solutions\n\n## Best Practices\n\n### 1. One Task Per Context\n\nHuntley's #1 recommendation: use a context window for one task, and one task only. Don't ask \"fix the auth bug AND add the new feature\". Do them in separate conversations.\n\n### 2. Fresh Start on Topic Change\n\nFinished auth? Start a new conversation for the next feature.\n\n### 3. Don't Redline\n\nThe \"dumb zone\" hits around 40% context utilization. Past that, reasoning degrades. The [Ralph Playbook](https://claytonfarr.github.io/ralph-playbook/) recommends rotating context at 40% of total capacity (e.g., 80k/200k tokens), not at 90%+. This is \"deterministically bad in an undeterministic world\"\u2014it wastes some work on rotation, but guarantees the agent never reaches the gutter.\n\n### 4. Recognize the Gutter\n\nIf you're seeing:\n- Same error 3+ times\n- Solutions that don't match the problem\n- Circular suggestions\n\nStart fresh. Your progress is in the files.\n\n### 5. State in Files, Not Context\n\nReal state lives in files and Git, not in the LLM's context window. Context is treated as ephemeral scratch space that will be discarded at rotation. The next conversation can read your files. Context is ephemeral; files are permanent.\n\n## Ralph's Approach\n\nThe [original Ralph technique](https://ghuntley.com/ralph/) (`while :; do cat PROMPT.md | agent ; done`) naturally implements these principles:\n\n1. **Each iteration is a fresh process** \u2014 Context is freed\n2. **State persists in files** \u2014 Progress survives context resets\n3. **Same prompt each time** \u2014 Focused, single-task context\n4. **Backpressure beats direction** \u2014 Instead of telling the agent what to do, engineer an environment where wrong outputs get rejected automatically\n\nThis implementation aims to bring these benefits while working within Cursor's session model.\n\n## Measuring Context\n\nRough estimates:\n- 1 token \u2248 4 characters\n- Average code file: 500-2000 tokens\n- Large file: 5000+ tokens\n- Conversation history: 100-500 tokens per exchange\n\nTrack allocations in `.poe-code-ralph/context-log.md` to stay aware.\n\n## Theory vs Implementation\n\nThe capacity percentages above (40%, etc.) are **theoretical guidelines** from the original Ralph methodology. In practice, agents like Claude Code and Cursor don't expose context usage programmatically.\n\n### What's Available\n\n| Agent | Context Visibility |\n|-------|-------------------|\n| Claude Code | `/context` command (interactive only) |\n| Cursor | No real-time access; Admin API for team analytics |\n| Codex | No programmatic access |\n\nThere are [open feature requests](https://github.com/anthropics/claude-code/issues/10593) to expose this data, but as of now it's not available for automated rotation.\n\n### What We Use Instead\n\nSince we can't measure context usage directly, this implementation uses proxy signals:\n\n1. **Iteration count** (`maxIterations`) \u2014 Rotate after N iterations regardless of context. Conservative default ensures we never hit the gutter.\n\n2. **Overbaking detection** (`maxFailures`) \u2014 Track consecutive failures per story. If a story fails N times in a row, it's likely we're in the gutter or the story needs to be split.\n\n3. **Stale timeout** (`staleSeconds`) \u2014 Auto-reopen stories stuck in `in_progress` state, handling crashes or abandoned runs.\n\nThe percentages in this document are useful for **humans** making manual decisions about when to start a new conversation. For automated loops, fresh context per iteration sidesteps the problem entirely.\n\n## When to Start Fresh\n\n**Definitely start fresh when:**\n- Switching to unrelated task\n- Context over 40% full\n- Same error 3+ times\n- Model suggestions are off-topic\n\n**Consider starting fresh when:**\n- Significant topic shift within task\n- Feeling \"stuck\"\n- Multiple failed approaches in history\n\n---\n\n## Further Reading\n\n- [Ralph Wiggum as a Software Engineer](https://ghuntley.com/ralph/) \u2014 Original technique by Geoffrey Huntley\n- [Autoregressive Queens of Failure](https://ghuntley.com/gutter/) \u2014 The gutter metaphor explained\n- [The Ralph Playbook](https://claytonfarr.github.io/ralph-playbook/) \u2014 Comprehensive guide by Clayton Farr\n- [how-to-ralph-wiggum](https://github.com/ghuntley/how-to-ralph-wiggum) \u2014 Official methodology guide\n- [ralph](https://github.com/snarktank/ralph) \u2014 Reference implementation of the Ralph autonomous agent loop\n- [From ReAct to Ralph Loop](https://www.alibabacloud.com/blog/from-react-to-ralph-loop-a-continuous-iteration-paradigm-for-ai-agents_602799) \u2014 Comparison with other agent paradigms\n";
14726
14741
  }
14727
14742
  });
14728
14743
 
@@ -15228,7 +15243,8 @@ var dark = {
15228
15243
  success: (text4) => chalk2.green(text4),
15229
15244
  warning: (text4) => chalk2.yellow(text4),
15230
15245
  error: (text4) => chalk2.red(text4),
15231
- info: (text4) => chalk2.magenta(text4)
15246
+ info: (text4) => chalk2.magenta(text4),
15247
+ badge: (text4) => chalk2.bgYellow.black(` ${text4} `)
15232
15248
  };
15233
15249
  var light = {
15234
15250
  header: (text4) => chalk2.hex("#a200ff").bold(text4),
@@ -15243,7 +15259,8 @@ var light = {
15243
15259
  success: (text4) => chalk2.hex("#008800")(text4),
15244
15260
  warning: (text4) => chalk2.hex("#cc6600")(text4),
15245
15261
  error: (text4) => chalk2.hex("#cc0000")(text4),
15246
- info: (text4) => chalk2.hex("#a200ff")(text4)
15262
+ info: (text4) => chalk2.hex("#a200ff")(text4),
15263
+ badge: (text4) => chalk2.bgHex("#cc6600").white(` ${text4} `)
15247
15264
  };
15248
15265
 
15249
15266
  // packages/design-system/src/tokens/typography.ts
@@ -15351,6 +15368,10 @@ var text = {
15351
15368
  muted(content) {
15352
15369
  const theme = getTheme();
15353
15370
  return theme.muted(content);
15371
+ },
15372
+ badge(content) {
15373
+ const theme = getTheme();
15374
+ return theme.badge(content);
15354
15375
  }
15355
15376
  };
15356
15377
 
@@ -15785,7 +15806,7 @@ function createLoggerFactory(emitter, theme) {
15785
15806
  component: scope
15786
15807
  };
15787
15808
  errorLogger.logError(error2, fullContext);
15788
- } else {
15809
+ } else if (!emitter) {
15789
15810
  console.error("Stack trace:", error2.stack);
15790
15811
  }
15791
15812
  },
@@ -15802,7 +15823,7 @@ function createLoggerFactory(emitter, theme) {
15802
15823
  component: scope
15803
15824
  };
15804
15825
  errorLogger.logErrorWithStackTrace(error2, operation, fullContext);
15805
- } else {
15826
+ } else if (!emitter) {
15806
15827
  console.error("Stack trace:", error2.stack);
15807
15828
  }
15808
15829
  },
@@ -16091,20 +16112,21 @@ var claudeCodeSpawnConfig = {
16091
16112
  promptFlag: "-p",
16092
16113
  modelFlag: "--model",
16093
16114
  defaultArgs: [
16094
- "--allowedTools",
16095
- "Bash,Read",
16096
- "--permission-mode",
16097
- "acceptEdits",
16098
16115
  "--output-format",
16099
16116
  "stream-json",
16100
16117
  "--verbose"
16101
16118
  ],
16119
+ modes: {
16120
+ yolo: ["--dangerously-skip-permissions"],
16121
+ edit: ["--permission-mode", "acceptEdits", "--allowedTools", "Bash,Read,Write,Edit,Glob,Grep,NotebookEdit"],
16122
+ read: ["--permission-mode", "plan"]
16123
+ },
16102
16124
  stdinMode: {
16103
16125
  omitPrompt: true,
16104
16126
  extraArgs: ["--input-format", "text"]
16105
16127
  },
16106
16128
  interactive: {
16107
- defaultArgs: ["--allowedTools", "Bash,Read", "--permission-mode", "acceptEdits"]
16129
+ defaultArgs: []
16108
16130
  },
16109
16131
  resumeCommand: (threadId) => ["--resume", threadId]
16110
16132
  };
@@ -16117,7 +16139,12 @@ var codexSpawnConfig = {
16117
16139
  adapter: "codex",
16118
16140
  promptFlag: "exec",
16119
16141
  modelFlag: "--model",
16120
- defaultArgs: ["--full-auto", "--skip-git-repo-check", "--json"],
16142
+ defaultArgs: ["--skip-git-repo-check", "--json"],
16143
+ modes: {
16144
+ yolo: ["-s", "danger-full-access"],
16145
+ edit: ["-s", "workspace-write"],
16146
+ read: ["-s", "read-only"]
16147
+ },
16121
16148
  stdinMode: {
16122
16149
  omitPrompt: true,
16123
16150
  extraArgs: ["-"]
@@ -16139,6 +16166,11 @@ var openCodeSpawnConfig = {
16139
16166
  promptFlag: "run",
16140
16167
  modelFlag: "--model",
16141
16168
  defaultArgs: ["--format", "json"],
16169
+ modes: {
16170
+ yolo: [],
16171
+ edit: [],
16172
+ read: ["--agent", "plan"]
16173
+ },
16142
16174
  interactive: {
16143
16175
  defaultArgs: [],
16144
16176
  promptFlag: "--prompt"
@@ -16156,6 +16188,11 @@ var kimiSpawnConfig = {
16156
16188
  adapter: "kimi",
16157
16189
  promptFlag: "-p",
16158
16190
  defaultArgs: ["--print", "--output-format", "stream-json"],
16191
+ modes: {
16192
+ yolo: ["--yolo"],
16193
+ edit: [],
16194
+ read: []
16195
+ },
16159
16196
  stdinMode: {
16160
16197
  omitPrompt: true,
16161
16198
  extraArgs: ["--input-format", "stream-json"]
@@ -16204,44 +16241,71 @@ function resolveConfig(agentId) {
16204
16241
  return { agentId: resolvedAgentId, binaryName, spawnConfig };
16205
16242
  }
16206
16243
 
16244
+ // packages/agent-spawn/src/model-utils.ts
16245
+ function stripModelNamespace2(model) {
16246
+ const slashIndex = model.indexOf("/");
16247
+ return slashIndex === -1 ? model : model.slice(slashIndex + 1);
16248
+ }
16249
+
16207
16250
  // packages/agent-spawn/src/spawn.ts
16208
- async function spawn2(agentId, options, _context) {
16251
+ function resolveCliConfig(agentId) {
16209
16252
  const resolved = resolveConfig(agentId);
16210
- const spawnConfig = resolved.spawnConfig;
16211
- if (!spawnConfig) {
16253
+ if (!resolved.spawnConfig) {
16212
16254
  throw new Error(`Agent "${resolved.agentId}" has no spawn config.`);
16213
16255
  }
16214
- if (spawnConfig.kind !== "cli") {
16256
+ if (resolved.spawnConfig.kind !== "cli") {
16215
16257
  throw new Error(`Agent "${resolved.agentId}" does not support CLI spawn.`);
16216
16258
  }
16217
16259
  if (!resolved.binaryName) {
16218
16260
  throw new Error(`Agent "${resolved.agentId}" has no binaryName.`);
16219
16261
  }
16220
- const stdinMode = options.useStdin && spawnConfig.stdinMode ? spawnConfig.stdinMode : void 0;
16262
+ return {
16263
+ agentId: resolved.agentId,
16264
+ binaryName: resolved.binaryName,
16265
+ spawnConfig: resolved.spawnConfig
16266
+ };
16267
+ }
16268
+ function buildCliArgs(config2, options, stdinMode) {
16221
16269
  const args = stdinMode ? [
16222
- spawnConfig.promptFlag,
16270
+ config2.promptFlag,
16223
16271
  ...stdinMode.omitPrompt ? [] : [options.prompt],
16224
16272
  ...stdinMode.extraArgs
16225
- ] : [spawnConfig.promptFlag, options.prompt];
16226
- if (options.model && spawnConfig.modelFlag) {
16227
- args.push(spawnConfig.modelFlag, options.model);
16273
+ ] : [config2.promptFlag, options.prompt];
16274
+ if (options.model && config2.modelFlag) {
16275
+ args.push(config2.modelFlag, stripModelNamespace2(options.model));
16228
16276
  }
16229
- args.push(...spawnConfig.defaultArgs);
16277
+ args.push(...config2.defaultArgs);
16278
+ args.push(...config2.modes[options.mode ?? "yolo"]);
16230
16279
  if (options.args && options.args.length > 0) {
16231
16280
  args.push(...options.args);
16232
16281
  }
16233
- const child = spawnChildProcess(resolved.binaryName, args, {
16282
+ return args;
16283
+ }
16284
+ function buildSpawnArgs(agentId, options) {
16285
+ const { binaryName, spawnConfig } = resolveCliConfig(agentId);
16286
+ return { binaryName, args: buildCliArgs(spawnConfig, options) };
16287
+ }
16288
+ async function spawn2(agentId, options, context) {
16289
+ const { agentId: resolvedId, binaryName, spawnConfig } = resolveCliConfig(agentId);
16290
+ const stdinMode = options.useStdin && spawnConfig.stdinMode ? spawnConfig.stdinMode : void 0;
16291
+ const spawnArgs = buildCliArgs(spawnConfig, options, stdinMode);
16292
+ if (context?.dryRun) {
16293
+ const rendered = [binaryName, ...spawnArgs].join(" ");
16294
+ context.logger?.dryRun(rendered);
16295
+ return { stdout: "", stderr: "", exitCode: 0 };
16296
+ }
16297
+ const child = spawnChildProcess(binaryName, spawnArgs, {
16234
16298
  cwd: options.cwd,
16235
16299
  stdio: [stdinMode ? "pipe" : "inherit", "pipe", "pipe"]
16236
16300
  });
16237
16301
  if (!child.stdout || !child.stderr) {
16238
- throw new Error(`Failed to spawn "${resolved.agentId}": missing stdio pipes.`);
16302
+ throw new Error(`Failed to spawn "${resolvedId}": missing stdio pipes.`);
16239
16303
  }
16240
16304
  const stdoutStream = child.stdout;
16241
16305
  const stderrStream = child.stderr;
16242
16306
  if (stdinMode) {
16243
16307
  if (!child.stdin) {
16244
- throw new Error(`Failed to spawn "${resolved.agentId}": missing stdin pipe.`);
16308
+ throw new Error(`Failed to spawn "${resolvedId}": missing stdin pipe.`);
16245
16309
  }
16246
16310
  child.stdin.setDefaultEncoding("utf8");
16247
16311
  child.stdin.write(options.prompt);
@@ -16300,9 +16364,11 @@ async function spawnInteractive(agentId, options) {
16300
16364
  }
16301
16365
  }
16302
16366
  if (options.model && spawnConfig.modelFlag) {
16303
- args.push(spawnConfig.modelFlag, options.model);
16367
+ args.push(spawnConfig.modelFlag, stripModelNamespace2(options.model));
16304
16368
  }
16305
16369
  args.push(...interactive.defaultArgs);
16370
+ const mode = options.mode ?? "yolo";
16371
+ args.push(...spawnConfig.modes[mode]);
16306
16372
  if (options.args && options.args.length > 0) {
16307
16373
  args.push(...options.args);
16308
16374
  }
@@ -16515,7 +16581,7 @@ async function* adaptClaude(lines) {
16515
16581
  if (blockType !== "tool_result") continue;
16516
16582
  const kind = toolKindsById.get(item.tool_use_id);
16517
16583
  toolKindsById.delete(item.tool_use_id);
16518
- let path17 = "";
16584
+ let path17;
16519
16585
  if (typeof item.content === "string") {
16520
16586
  path17 = item.content;
16521
16587
  } else {
@@ -16874,9 +16940,11 @@ function spawnStreaming(options) {
16874
16940
  args.push(options.prompt);
16875
16941
  }
16876
16942
  if (options.model && spawnConfig.modelFlag) {
16877
- args.push(spawnConfig.modelFlag, options.model);
16943
+ args.push(spawnConfig.modelFlag, stripModelNamespace2(options.model));
16878
16944
  }
16879
16945
  args.push(...spawnConfig.defaultArgs);
16946
+ const mode = options.mode ?? "yolo";
16947
+ args.push(...spawnConfig.modes[mode]);
16880
16948
  if (useStdin) {
16881
16949
  args.push(...spawnConfig.stdinMode.extraArgs);
16882
16950
  }
@@ -16935,50 +17003,6 @@ ${stdout}
16935
17003
  stderr:
16936
17004
  ${stderr}`;
16937
17005
  }
16938
- function describeCommandExpectation(command, args, expectedOutput) {
16939
- return `${renderCommandLine(command, args)} (expecting "${expectedOutput}")`;
16940
- }
16941
- function createCommandExpectationCheck(options) {
16942
- return {
16943
- id: options.id,
16944
- description: describeCommandExpectation(
16945
- options.command,
16946
- options.args,
16947
- options.expectedOutput
16948
- ),
16949
- async run(context) {
16950
- await runAndMatchOutput(context, options);
16951
- }
16952
- };
16953
- }
16954
- async function runAndMatchOutput(context, options) {
16955
- const rendered = renderCommandLine(options.command, options.args);
16956
- if (options.skipOnDryRun !== false && context.isDryRun) {
16957
- if (context.logDryRun) {
16958
- context.logDryRun(
16959
- `Dry run: ${rendered} (expecting "${options.expectedOutput}")`
16960
- );
16961
- }
16962
- return;
16963
- }
16964
- const result = await context.runCommand(options.command, options.args);
16965
- if (result.exitCode !== 0) {
16966
- const detail = formatCommandRunnerResult(result);
16967
- throw new Error(
16968
- [`Command ${rendered} failed with exit code ${result.exitCode}.`, detail].join("\n")
16969
- );
16970
- }
16971
- if (!stdoutMatchesExpected(result.stdout, options.expectedOutput)) {
16972
- const detail = formatCommandRunnerResult(result);
16973
- const received = result.stdout.trim();
16974
- throw new Error(
16975
- [
16976
- `Command ${rendered} failed: expected "${options.expectedOutput}" but received "${received}".`,
16977
- detail
16978
- ].join("\n")
16979
- );
16980
- }
16981
- }
16982
17006
  function stdoutMatchesExpected(stdout, expected) {
16983
17007
  const trimmed = stdout.trim();
16984
17008
  if (trimmed === expected) {
@@ -16986,20 +17010,38 @@ function stdoutMatchesExpected(stdout, expected) {
16986
17010
  }
16987
17011
  return stdout.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0).some((line) => line === expected);
16988
17012
  }
16989
- function renderCommandLine(command, args) {
16990
- return [command, ...args].map(quoteIfNeeded).join(" ").trim();
16991
- }
16992
- function quoteIfNeeded(value) {
16993
- if (value.length === 0) {
16994
- return '""';
16995
- }
16996
- if (needsQuoting(value)) {
16997
- return `"${value.replaceAll('"', '\\"')}"`;
16998
- }
16999
- return value;
17000
- }
17001
- function needsQuoting(value) {
17002
- return value.includes(" ") || value.includes(" ") || value.includes("\n");
17013
+ function createSpawnHealthCheck(agentId, options) {
17014
+ const prompt = `Output exactly: ${options.expectedOutput}`;
17015
+ const { binaryName, args } = buildSpawnArgs(agentId, {
17016
+ prompt,
17017
+ model: options.model,
17018
+ mode: "yolo"
17019
+ });
17020
+ return {
17021
+ id: `${agentId}-cli-health`,
17022
+ description: `spawn ${agentId} (expecting "${options.expectedOutput}")`,
17023
+ async run(context) {
17024
+ if (context.isDryRun) {
17025
+ context.logDryRun?.(
17026
+ `Dry run: ${[binaryName, ...args].join(" ")} (expecting "${options.expectedOutput}")`
17027
+ );
17028
+ return;
17029
+ }
17030
+ const result = await context.runCommand(binaryName, args);
17031
+ if (result.exitCode !== 0) {
17032
+ throw new Error(
17033
+ `spawn ${agentId} failed with exit code ${result.exitCode}.
17034
+ ${formatCommandRunnerResult(result)}`
17035
+ );
17036
+ }
17037
+ if (!result.stdout.includes(options.expectedOutput)) {
17038
+ throw new Error(
17039
+ `spawn ${agentId}: expected "${options.expectedOutput}" in stdout.
17040
+ ${formatCommandRunnerResult(result)}`
17041
+ );
17042
+ }
17043
+ }
17044
+ };
17003
17045
  }
17004
17046
  function createBinaryExistsCheck(binaryName, id, description) {
17005
17047
  return {
@@ -17092,9 +17134,9 @@ function describeInstallCommand(step) {
17092
17134
  return `[${step.id}] ${formatCommand2(step.command, step.args)}`;
17093
17135
  }
17094
17136
  function formatCommand2(command, args) {
17095
- return [command, ...args.map(quoteIfNeeded2)].join(" ");
17137
+ return [command, ...args.map(quoteIfNeeded)].join(" ");
17096
17138
  }
17097
- function quoteIfNeeded2(value) {
17139
+ function quoteIfNeeded(value) {
17098
17140
  if (value.length === 0) {
17099
17141
  return '""';
17100
17142
  }
@@ -17227,28 +17269,6 @@ var CLAUDE_CODE_INSTALL_DEFINITION = {
17227
17269
  ],
17228
17270
  successMessage: "Installed Claude CLI."
17229
17271
  };
17230
- var CLAUDE_SPAWN_DEFAULTS = [
17231
- "--allowedTools",
17232
- "Bash,Read",
17233
- "--permission-mode",
17234
- "acceptEdits",
17235
- "--output-format",
17236
- "text"
17237
- ];
17238
- function buildClaudeArgs(prompt, extraArgs, model) {
17239
- const modelArgs = model ? ["--model", model] : [];
17240
- if (prompt == null) {
17241
- return [
17242
- "-p",
17243
- "--input-format",
17244
- "text",
17245
- ...modelArgs,
17246
- ...CLAUDE_SPAWN_DEFAULTS,
17247
- ...extraArgs ?? []
17248
- ];
17249
- }
17250
- return ["-p", prompt, ...modelArgs, ...CLAUDE_SPAWN_DEFAULTS, ...extraArgs ?? []];
17251
- }
17252
17272
  var claudeCodeService = createProvider({
17253
17273
  ...claudeCodeAgent,
17254
17274
  supportsStdinPrompt: true,
@@ -17282,14 +17302,8 @@ var claudeCodeService = createProvider({
17282
17302
  },
17283
17303
  test(context) {
17284
17304
  return context.runCheck(
17285
- createCommandExpectationCheck({
17286
- id: "claude-cli-health",
17287
- command: "claude",
17288
- args: buildClaudeArgs(
17289
- "Output exactly: CLAUDE_CODE_OK",
17290
- void 0,
17291
- stripModelNamespace(DEFAULT_CLAUDE_CODE_MODEL)
17292
- ),
17305
+ createSpawnHealthCheck("claude-code", {
17306
+ model: DEFAULT_CLAUDE_CODE_MODEL,
17293
17307
  expectedOutput: "CLAUDE_CODE_OK"
17294
17308
  })
17295
17309
  );
@@ -17327,36 +17341,7 @@ var claudeCodeService = createProvider({
17327
17341
  })
17328
17342
  ]
17329
17343
  },
17330
- install: CLAUDE_CODE_INSTALL_DEFINITION,
17331
- spawn(context, options) {
17332
- const shouldUseStdin = Boolean(options.useStdin);
17333
- const args = buildClaudeArgs(
17334
- shouldUseStdin ? void 0 : options.prompt,
17335
- options.args,
17336
- options.model
17337
- );
17338
- if (shouldUseStdin) {
17339
- if (options.cwd) {
17340
- return context.command.runCommand(
17341
- "poe-code",
17342
- ["wrap", "claude-code", ...args],
17343
- {
17344
- cwd: options.cwd,
17345
- stdin: options.prompt
17346
- }
17347
- );
17348
- }
17349
- return context.command.runCommand("poe-code", ["wrap", "claude-code", ...args], {
17350
- stdin: options.prompt
17351
- });
17352
- }
17353
- if (options.cwd) {
17354
- return context.command.runCommand("poe-code", ["wrap", "claude-code", ...args], {
17355
- cwd: options.cwd
17356
- });
17357
- }
17358
- return context.command.runCommand("poe-code", ["wrap", "claude-code", ...args]);
17359
- }
17344
+ install: CLAUDE_CODE_INSTALL_DEFINITION
17360
17345
  });
17361
17346
 
17362
17347
  // src/providers/codex.ts
@@ -17406,14 +17391,6 @@ function stripCodexConfiguration(document) {
17406
17391
  function isTableEmpty(value) {
17407
17392
  return isConfigObject4(value) && Object.keys(value).length === 0;
17408
17393
  }
17409
- var CODEX_DEFAULT_EXEC_ARGS = [
17410
- "--full-auto",
17411
- "--skip-git-repo-check"
17412
- ];
17413
- function buildCodexExecArgs(prompt, extraArgs = [], model) {
17414
- const modelArgs = model ? ["--model", model] : [];
17415
- return [...modelArgs, "exec", prompt, ...CODEX_DEFAULT_EXEC_ARGS, ...extraArgs];
17416
- }
17417
17394
  var codexService = createProvider({
17418
17395
  ...codexAgent,
17419
17396
  supportsStdinPrompt: true,
@@ -17441,14 +17418,8 @@ var codexService = createProvider({
17441
17418
  },
17442
17419
  test(context) {
17443
17420
  return context.runCheck(
17444
- createCommandExpectationCheck({
17445
- id: "codex-cli-health",
17446
- command: "codex",
17447
- args: buildCodexExecArgs(
17448
- "Output exactly: CODEX_OK",
17449
- [],
17450
- stripModelNamespace(DEFAULT_CODEX_MODEL)
17451
- ),
17421
+ createSpawnHealthCheck("codex", {
17422
+ model: DEFAULT_CODEX_MODEL,
17452
17423
  expectedOutput: "CODEX_OK"
17453
17424
  })
17454
17425
  );
@@ -17487,32 +17458,7 @@ var codexService = createProvider({
17487
17458
  })
17488
17459
  ]
17489
17460
  },
17490
- install: CODEX_INSTALL_DEFINITION,
17491
- spawn(context, options) {
17492
- const shouldUseStdin = Boolean(options.useStdin);
17493
- const args = buildCodexExecArgs(
17494
- shouldUseStdin ? "-" : options.prompt,
17495
- options.args,
17496
- options.model
17497
- );
17498
- if (shouldUseStdin) {
17499
- if (options.cwd) {
17500
- return context.command.runCommand("poe-code", ["wrap", "codex", ...args], {
17501
- cwd: options.cwd,
17502
- stdin: options.prompt
17503
- });
17504
- }
17505
- return context.command.runCommand("poe-code", ["wrap", "codex", ...args], {
17506
- stdin: options.prompt
17507
- });
17508
- }
17509
- if (options.cwd) {
17510
- return context.command.runCommand("poe-code", ["wrap", "codex", ...args], {
17511
- cwd: options.cwd
17512
- });
17513
- }
17514
- return context.command.runCommand("poe-code", ["wrap", "codex", ...args]);
17515
- }
17461
+ install: CODEX_INSTALL_DEFINITION
17516
17462
  });
17517
17463
 
17518
17464
  // src/providers/opencode.ts
@@ -17609,14 +17555,7 @@ var openCodeService = createProvider({
17609
17555
  install: OPEN_CODE_INSTALL_DEFINITION,
17610
17556
  test(context) {
17611
17557
  return context.runCheck(
17612
- createCommandExpectationCheck({
17613
- id: "opencode-cli-health",
17614
- command: "opencode",
17615
- args: [
17616
- ...getModelArgs(DEFAULT_FRONTIER_MODEL),
17617
- "run",
17618
- "Output exactly: OPEN_CODE_OK"
17619
- ],
17558
+ createSpawnHealthCheck("opencode", {
17620
17559
  expectedOutput: "OPEN_CODE_OK"
17621
17560
  })
17622
17561
  );
@@ -17689,10 +17628,7 @@ var kimiService = createProvider({
17689
17628
  },
17690
17629
  test(context) {
17691
17630
  return context.runCheck(
17692
- createCommandExpectationCheck({
17693
- id: "kimi-cli-health",
17694
- command: "kimi",
17695
- args: buildKimiArgs("Output exactly: KIMI_OK"),
17631
+ createSpawnHealthCheck("kimi", {
17696
17632
  expectedOutput: "KIMI_OK"
17697
17633
  })
17698
17634
  );
@@ -17908,7 +17844,9 @@ function createCliContainer(dependencies) {
17908
17844
  logDir: environment.logDir,
17909
17845
  logToStderr: true
17910
17846
  });
17911
- loggerFactory.setErrorLogger(errorLogger);
17847
+ if (!dependencies.logger) {
17848
+ loggerFactory.setErrorLogger(errorLogger);
17849
+ }
17912
17850
  const contextFactory = createCommandContextFactory({
17913
17851
  fs: dependencies.fs
17914
17852
  });
@@ -18199,6 +18137,7 @@ async function spawnCore(container, service, options, flags = { dryRun: false, v
18199
18137
  prompt: options.prompt,
18200
18138
  args: options.args,
18201
18139
  model: options.model,
18140
+ mode: options.mode,
18202
18141
  cwd: cwdOverride,
18203
18142
  useStdin: options.useStdin ?? false
18204
18143
  };
@@ -18206,21 +18145,12 @@ async function spawnCore(container, service, options, flags = { dryRun: false, v
18206
18145
  if (!adapter) {
18207
18146
  throw new Error(`Unknown service "${service}".`);
18208
18147
  }
18209
- if (typeof adapter.spawn !== "function") {
18210
- throw new Error(`${adapter.label} does not support spawn.`);
18211
- }
18212
- if (spawnOptions.useStdin && !adapter.supportsStdinPrompt) {
18213
- throw new Error(
18214
- `${adapter.label} does not support stdin prompts. Use a different service (e.g. "codex") or pass the prompt as an argument.`
18215
- );
18216
- }
18217
18148
  const commandFlags = { dryRun: flags.dryRun, assumeYes: true, verbose: flags.verbose };
18218
18149
  const resources = createExecutionResources(
18219
18150
  container,
18220
18151
  commandFlags,
18221
18152
  `spawn:${service}`
18222
18153
  );
18223
- const providerContext = buildProviderContext(container, adapter, resources);
18224
18154
  if (flags.dryRun) {
18225
18155
  const summary = formatSpawnDryRunMessage(adapter.label, spawnOptions);
18226
18156
  resources.logger.dryRun(summary);
@@ -18230,6 +18160,15 @@ async function spawnCore(container, service, options, flags = { dryRun: false, v
18230
18160
  exitCode: 0
18231
18161
  };
18232
18162
  }
18163
+ if (typeof adapter.spawn !== "function") {
18164
+ throw new Error(`${adapter.label} does not support spawn.`);
18165
+ }
18166
+ if (spawnOptions.useStdin && !adapter.supportsStdinPrompt) {
18167
+ throw new Error(
18168
+ `${adapter.label} does not support stdin prompts. Use a different service (e.g. "codex") or pass the prompt as an argument.`
18169
+ );
18170
+ }
18171
+ const providerContext = buildProviderContext(container, adapter, resources);
18233
18172
  const result = await container.registry.invoke(
18234
18173
  adapter.name,
18235
18174
  "spawn",
@@ -18456,6 +18395,7 @@ function spawn3(service, promptOrOptions, maybeOptions) {
18456
18395
  prompt: options.prompt,
18457
18396
  cwd: options.cwd,
18458
18397
  model: options.model,
18398
+ mode: options.mode,
18459
18399
  args: options.args
18460
18400
  });
18461
18401
  return {
@@ -18472,6 +18412,7 @@ function spawn3(service, promptOrOptions, maybeOptions) {
18472
18412
  prompt: options.prompt,
18473
18413
  cwd: options.cwd,
18474
18414
  model: options.model,
18415
+ mode: options.mode,
18475
18416
  args: options.args,
18476
18417
  useStdin: false
18477
18418
  });
@@ -18491,6 +18432,7 @@ function spawn3(service, promptOrOptions, maybeOptions) {
18491
18432
  prompt: options.prompt,
18492
18433
  cwd: options.cwd,
18493
18434
  model: options.model,
18435
+ mode: options.mode,
18494
18436
  args: options.args,
18495
18437
  useStdin: false
18496
18438
  });
@@ -18501,6 +18443,7 @@ function spawn3(service, promptOrOptions, maybeOptions) {
18501
18443
  prompt: options.prompt,
18502
18444
  cwd: options.cwd,
18503
18445
  model: options.model,
18446
+ mode: options.mode,
18504
18447
  args: options.args,
18505
18448
  useStdin: false
18506
18449
  });
@@ -18514,11 +18457,11 @@ function spawn3(service, promptOrOptions, maybeOptions) {
18514
18457
 
18515
18458
  // src/cli/commands/spawn.ts
18516
18459
  function registerSpawnCommand(program, container, options = {}) {
18517
- const spawnServices = container.registry.list().filter((service) => typeof service.spawn === "function").map((service) => service.name);
18460
+ const spawnServices = container.registry.list().filter((service) => typeof service.spawn === "function" || getSpawnConfig(service.name)).map((service) => service.name);
18518
18461
  const extraServices = options.extraServices ?? [];
18519
18462
  const serviceList = [...spawnServices, ...extraServices];
18520
18463
  const serviceDescription = `Agent to spawn${formatServiceList(serviceList)}`;
18521
- program.command("spawn").description("Run a single prompt through a configured agent CLI.").option("--model <model>", "Model identifier override passed to the agent CLI").option("-C, --cwd <path>", "Working directory for the agent CLI").option("--stdin", "Read the prompt from stdin").option("-i, --interactive", "Launch the agent in interactive TUI mode").argument(
18464
+ program.command("spawn").description("Run a single prompt through a configured agent CLI.").option("--model <model>", "Model identifier override passed to the agent CLI").option("-C, --cwd <path>", "Working directory for the agent CLI").option("--stdin", "Read the prompt from stdin").option("-i, --interactive", "Launch the agent in interactive TUI mode").option("--mode <mode>", "Permission mode: yolo | edit | read (default: yolo)").argument(
18522
18465
  "<agent>",
18523
18466
  serviceDescription
18524
18467
  ).argument("[prompt]", "Prompt text to send (or '-' / stdin)").argument(
@@ -18546,6 +18489,7 @@ function registerSpawnCommand(program, container, options = {}) {
18546
18489
  prompt: promptText ?? "",
18547
18490
  args: forwardedArgs,
18548
18491
  model: commandOptions.model,
18492
+ mode: commandOptions.mode,
18549
18493
  cwd: cwdOverride
18550
18494
  });
18551
18495
  process.exitCode = result.exitCode;
@@ -18565,6 +18509,7 @@ function registerSpawnCommand(program, container, options = {}) {
18565
18509
  prompt: promptText,
18566
18510
  args: forwardedArgs,
18567
18511
  model: commandOptions.model,
18512
+ mode: commandOptions.mode,
18568
18513
  cwd: cwdOverride,
18569
18514
  useStdin: shouldReadFromStdin
18570
18515
  };
@@ -18621,6 +18566,7 @@ function registerSpawnCommand(program, container, options = {}) {
18621
18566
  prompt: spawnOptions.prompt,
18622
18567
  args: spawnOptions.args,
18623
18568
  model: spawnOptions.model,
18569
+ mode: spawnOptions.mode,
18624
18570
  cwd: spawnOptions.cwd
18625
18571
  });
18626
18572
  await renderAcpStream(events);
@@ -19810,10 +19756,10 @@ function resolveOutputPath(filename, cwd) {
19810
19756
  // src/cli/commands/mcp.ts
19811
19757
  init_credentials();
19812
19758
 
19813
- // packages/tiny-mcp-server/src/server.ts
19759
+ // packages/tiny-stdio-mcp-server/src/server.ts
19814
19760
  import * as readline from "readline";
19815
19761
 
19816
- // packages/tiny-mcp-server/src/types.ts
19762
+ // packages/tiny-stdio-mcp-server/src/types.ts
19817
19763
  var JSON_RPC_ERROR_CODES = {
19818
19764
  PARSE_ERROR: -32700,
19819
19765
  INVALID_REQUEST: -32600,
@@ -19822,7 +19768,7 @@ var JSON_RPC_ERROR_CODES = {
19822
19768
  INTERNAL_ERROR: -32603
19823
19769
  };
19824
19770
 
19825
- // packages/tiny-mcp-server/src/jsonrpc.ts
19771
+ // packages/tiny-stdio-mcp-server/src/jsonrpc.ts
19826
19772
  function parseMessage(line) {
19827
19773
  let parsed;
19828
19774
  try {
@@ -19919,7 +19865,7 @@ function formatErrorResponse(id, error2) {
19919
19865
  return JSON.stringify(response);
19920
19866
  }
19921
19867
 
19922
- // packages/tiny-file-type/src/file-type.ts
19868
+ // packages/tiny-stdio-mcp-server/src/content/file-type.ts
19923
19869
  function fileTypeFromBuffer(data) {
19924
19870
  if (data.length < 12) {
19925
19871
  return void 0;
@@ -19957,7 +19903,7 @@ function fileTypeFromBuffer(data) {
19957
19903
  return void 0;
19958
19904
  }
19959
19905
 
19960
- // packages/tiny-mcp-server/src/content/image.ts
19906
+ // packages/tiny-stdio-mcp-server/src/content/image.ts
19961
19907
  var SUPPORTED_IMAGE_MIMES = /* @__PURE__ */ new Set([
19962
19908
  "image/png",
19963
19909
  "image/jpeg",
@@ -20017,7 +19963,7 @@ var Image = class _Image {
20017
19963
  }
20018
19964
  };
20019
19965
 
20020
- // packages/tiny-mcp-server/src/content/audio.ts
19966
+ // packages/tiny-stdio-mcp-server/src/content/audio.ts
20021
19967
  var SUPPORTED_AUDIO_MIMES = /* @__PURE__ */ new Set([
20022
19968
  "audio/mpeg",
20023
19969
  "audio/wav",
@@ -20088,7 +20034,7 @@ var Audio = class _Audio {
20088
20034
  }
20089
20035
  };
20090
20036
 
20091
- // packages/tiny-mcp-server/src/content/file.ts
20037
+ // packages/tiny-stdio-mcp-server/src/content/file.ts
20092
20038
  function isTextMimeType(mimeType) {
20093
20039
  return mimeType.startsWith("text/") || mimeType === "application/json" || mimeType === "application/xml" || mimeType === "application/javascript" || mimeType === "application/typescript";
20094
20040
  }
@@ -20170,7 +20116,7 @@ var File2 = class _File {
20170
20116
  }
20171
20117
  };
20172
20118
 
20173
- // packages/tiny-mcp-server/src/content/convert.ts
20119
+ // packages/tiny-stdio-mcp-server/src/content/convert.ts
20174
20120
  function convertSingleValue(value) {
20175
20121
  if (typeof value === "string") {
20176
20122
  return { type: "text", text: value };
@@ -20193,7 +20139,7 @@ function toContentBlocks(result) {
20193
20139
  return [convertSingleValue(result)];
20194
20140
  }
20195
20141
 
20196
- // packages/tiny-mcp-server/src/server.ts
20142
+ // packages/tiny-stdio-mcp-server/src/server.ts
20197
20143
  var PROTOCOL_VERSION = "2025-11-25";
20198
20144
  function createServer(options) {
20199
20145
  const tools = /* @__PURE__ */ new Map();
@@ -20394,7 +20340,7 @@ function createServer(options) {
20394
20340
  return server;
20395
20341
  }
20396
20342
 
20397
- // packages/tiny-mcp-server/src/schema.ts
20343
+ // packages/tiny-stdio-mcp-server/src/schema.ts
20398
20344
  function defineSchema(definition) {
20399
20345
  const properties = {};
20400
20346
  const required2 = [];
@@ -33308,7 +33254,7 @@ function buildHelpText() {
33308
33254
  return lines.join("\n");
33309
33255
  }
33310
33256
  function registerMcpCommand(program, container) {
33311
- const mcp = program.command("mcp").description("MCP server commands").addHelpText("after", buildHelpText()).action(function() {
33257
+ const mcp = program.command("mcp").description("MCP server commands").addHelpText("after", buildHelpText()).allowExcessArguments().action(function() {
33312
33258
  if (this.args.length > 0) {
33313
33259
  throwCommandNotFound({
33314
33260
  container,
@@ -33626,7 +33572,7 @@ function buildHelpText2() {
33626
33572
  ].join("\n");
33627
33573
  }
33628
33574
  function registerSkillCommand(program, container) {
33629
- const skill = program.command("skill").description("Skill directory commands").addHelpText("after", buildHelpText2()).action(function() {
33575
+ const skill = program.command("skill").description("Skill directory commands").addHelpText("after", buildHelpText2()).allowExcessArguments().action(function() {
33630
33576
  if (this.args.length > 0) {
33631
33577
  throwCommandNotFound({
33632
33578
  container,
@@ -33883,17 +33829,18 @@ async function displayVersion(container, currentVersion) {
33883
33829
  verbose: false,
33884
33830
  scope: "version"
33885
33831
  });
33886
- logger2.info(`poe-code ${currentVersion}`);
33832
+ logger2.intro("version");
33833
+ const versionValue = currentVersion === "0.0.0-dev" ? `${currentVersion} ${text.badge("local build")}` : currentVersion;
33834
+ logger2.resolved("poe-code", versionValue);
33887
33835
  const result = await checkForUpdate({
33888
33836
  currentVersion,
33889
33837
  httpClient
33890
33838
  });
33891
33839
  if (result?.updateAvailable) {
33892
- logger2.info("");
33893
- logger2.info(
33840
+ logger2.warn(
33894
33841
  `Update available: ${result.currentVersion} -> ${result.latestVersion}`
33895
33842
  );
33896
- logger2.info("Run: npm install -g poe-code@latest");
33843
+ logger2.resolved("Update", `npm install -g poe-code@latest`);
33897
33844
  }
33898
33845
  }
33899
33846
 
@@ -34235,7 +34182,7 @@ function parsePlan(yamlContent) {
34235
34182
  doc = parse7(yamlContent);
34236
34183
  } catch (error2) {
34237
34184
  const message = error2 instanceof Error ? error2.message : String(error2);
34238
- throw new Error(`Invalid plan YAML: ${message}`);
34185
+ throw new Error(`Invalid plan YAML: ${message}`, { cause: error2 });
34239
34186
  }
34240
34187
  if (!isRecord3(doc)) {
34241
34188
  throw new Error("Invalid plan YAML: expected top-level object");
@@ -34539,7 +34486,7 @@ var OverbakingDetector = class {
34539
34486
  consecutiveFailures: 0,
34540
34487
  warned: false
34541
34488
  };
34542
- if (status !== "failure") {
34489
+ if (status === "success") {
34543
34490
  if (existing.consecutiveFailures !== 0 || existing.warned) {
34544
34491
  this.failuresByStoryId.set(storyId, { consecutiveFailures: 0, warned: false });
34545
34492
  }
@@ -34574,6 +34521,7 @@ async function defaultStreamingSpawn(agentId, options) {
34574
34521
  agentId,
34575
34522
  prompt: options.prompt,
34576
34523
  cwd: options.cwd,
34524
+ mode: options.mode,
34577
34525
  useStdin: options.useStdin
34578
34526
  });
34579
34527
  let agentText = "";
@@ -34632,7 +34580,7 @@ function formatAgentSetupHint(agentId) {
34632
34580
  async function appendToErrorsLog(fs3, errorsLogPath, message) {
34633
34581
  const next = message.endsWith("\n") ? message : `${message}
34634
34582
  `;
34635
- let previous = "";
34583
+ let previous;
34636
34584
  try {
34637
34585
  previous = await fs3.readFile(errorsLogPath, "utf8");
34638
34586
  } catch (error2) {
@@ -34845,8 +34793,15 @@ async function buildLoop(options) {
34845
34793
  const activityLogPath = absPath(cwd, options.activityLogPath ?? ".poe-code-ralph/activity.log");
34846
34794
  const guardrailsRef = absPath(cwd, ".agents/poe-code-ralph/references/GUARDRAILS.md");
34847
34795
  const contextRef = absPath(cwd, ".agents/poe-code-ralph/references/CONTEXT_ENGINEERING.md");
34848
- const activityCmd = absPath(cwd, ".agents/poe-code-ralph/log-activity.sh");
34796
+ const activityCmd = "poe-code ralph agent log";
34849
34797
  const promptTemplatePath = absPath(cwd, ".agents/poe-code-ralph/PROMPT_build.md");
34798
+ try {
34799
+ await fs3.readFile(promptTemplatePath, "utf8");
34800
+ } catch {
34801
+ throw new Error(
34802
+ `PROMPT_build.md not found at ${promptTemplatePath}. Run "poe-code ralph install" to set up Ralph templates.`
34803
+ );
34804
+ }
34850
34805
  const runsDir = absPath(cwd, ".poe-code-ralph/runs");
34851
34806
  const runId = options.deps?.runId ?? createRunId(nowFn());
34852
34807
  const overbaking = new OverbakingDetector({ threshold: options.maxFailures });
@@ -34903,14 +34858,15 @@ async function buildLoop(options) {
34903
34858
  });
34904
34859
  await fs3.mkdir(dirname6(renderedPromptPath), { recursive: true });
34905
34860
  await fs3.writeFile(renderedPromptPath, prompt, { encoding: "utf8" });
34906
- let status = "failure";
34907
- let combinedOutput = "";
34908
- let stderrForErrorsLog = "";
34861
+ let status;
34862
+ let combinedOutput;
34863
+ let stderrForErrorsLog;
34909
34864
  let overbakeAction = null;
34910
34865
  try {
34911
34866
  const result = await spawn5(options.agent, {
34912
34867
  prompt,
34913
34868
  cwd,
34869
+ mode: "yolo",
34914
34870
  useStdin: true
34915
34871
  });
34916
34872
  const agentStdout = result.stdout ?? "";
@@ -34952,6 +34908,8 @@ ${agentStderr}` : ""
34952
34908
  });
34953
34909
  stderr.write(warning);
34954
34910
  await appendToErrorsLog(fs3, errorsLogPath, warning);
34911
+ }
34912
+ if (overbakeEvent.overbaked) {
34955
34913
  if (options.pauseOnOverbake) {
34956
34914
  overbakeAction = await promptOverbake({
34957
34915
  storyId: story.id,
@@ -34960,7 +34918,7 @@ ${agentStderr}` : ""
34960
34918
  threshold: overbakeEvent.threshold
34961
34919
  });
34962
34920
  } else {
34963
- overbakeAction = "continue";
34921
+ overbakeAction = "skip";
34964
34922
  }
34965
34923
  if (overbakeAction === "skip") {
34966
34924
  skippedStoryIds.add(story.id);
@@ -35137,7 +35095,8 @@ async function resolvePlanPath(options) {
35137
35095
  } catch (error2) {
35138
35096
  if (isNotFound(error2)) {
35139
35097
  throw new Error(
35140
- `Plan not found at "${provided}". Provide --plan <path> to an existing plan file.`
35098
+ `Plan not found at "${provided}". Provide --plan <path> to an existing plan file.`,
35099
+ { cause: error2 }
35141
35100
  );
35142
35101
  }
35143
35102
  throw error2;
@@ -35151,9 +35110,6 @@ async function resolvePlanPath(options) {
35151
35110
  );
35152
35111
  return null;
35153
35112
  }
35154
- if (candidates.length === 1) {
35155
- return candidates[0].path;
35156
- }
35157
35113
  const selection = await select2({
35158
35114
  message: "Select a plan file to use for this Ralph run",
35159
35115
  options: candidates.map((candidate) => ({
@@ -35255,7 +35211,7 @@ async function logActivity(path17, message, options = {}) {
35255
35211
  await fs3.appendFile(path17, entry, { encoding: "utf8" });
35256
35212
  } catch (error2) {
35257
35213
  const detail = error2 instanceof Error ? error2.message : `Unknown error: ${String(error2)}`;
35258
- throw new Error(`Failed to append activity log entry: ${detail}`);
35214
+ throw new Error(`Failed to append activity log entry: ${detail}`, { cause: error2 });
35259
35215
  }
35260
35216
  }
35261
35217
 
@@ -35295,9 +35251,7 @@ function pickOptionalPositiveInt(config2, key, options) {
35295
35251
  }
35296
35252
  return value;
35297
35253
  }
35298
- async function loadConfig(cwd, deps) {
35299
- const fs3 = deps?.fs ?? fsPromises9;
35300
- const configDir = path14.join(cwd, ".agents", "poe-code-ralph");
35254
+ async function loadSingleConfig(configDir, fs3) {
35301
35255
  const yamlPath = path14.join(configDir, "config.yaml");
35302
35256
  const jsonPath = path14.join(configDir, "config.json");
35303
35257
  let raw = null;
@@ -35324,40 +35278,69 @@ async function loadConfig(cwd, deps) {
35324
35278
  }
35325
35279
  }
35326
35280
  if (raw == null || format == null || sourcePath == null) {
35327
- return {};
35281
+ return null;
35328
35282
  }
35329
35283
  let parsed;
35330
35284
  try {
35331
35285
  parsed = format === "yaml" ? YAML.parse(raw) : JSON.parse(raw);
35332
35286
  } catch (error2) {
35333
35287
  const detail = error2 instanceof Error ? error2.message : String(error2);
35334
- throw new Error(`Invalid Ralph config ${format.toUpperCase()} at ${sourcePath}: ${detail}`);
35288
+ throw new Error(`Invalid Ralph config ${format.toUpperCase()} at ${sourcePath}: ${detail}`, { cause: error2 });
35335
35289
  }
35336
35290
  if (!isPlainObject2(parsed)) {
35337
35291
  throw new Error(`Invalid Ralph config at ${sourcePath}: expected an object.`);
35338
35292
  }
35339
- const config2 = parsed;
35340
- const result = {};
35341
- const planPath = pickOptionalString(config2, "planPath");
35342
- if (planPath) result.planPath = planPath;
35343
- const progressPath = pickOptionalString(config2, "progressPath");
35344
- if (progressPath) result.progressPath = progressPath;
35345
- const guardrailsPath = pickOptionalString(config2, "guardrailsPath");
35346
- if (guardrailsPath) result.guardrailsPath = guardrailsPath;
35347
- const errorsLogPath = pickOptionalString(config2, "errorsLogPath");
35348
- if (errorsLogPath) result.errorsLogPath = errorsLogPath;
35349
- const activityLogPath = pickOptionalString(config2, "activityLogPath");
35350
- if (activityLogPath) result.activityLogPath = activityLogPath;
35351
- const agent = pickOptionalString(config2, "agent");
35352
- if (agent) result.agent = agent;
35353
- const maxIterations = pickOptionalPositiveInt(config2, "maxIterations", { min: 1 });
35354
- if (maxIterations != null) result.maxIterations = maxIterations;
35355
- const staleSeconds = pickOptionalPositiveInt(config2, "staleSeconds", { min: 0 });
35356
- if (staleSeconds != null) result.staleSeconds = staleSeconds;
35357
- const noCommit = pickOptionalBoolean(config2, "noCommit");
35358
- if (noCommit != null) result.noCommit = noCommit;
35293
+ const rawConfig = parsed;
35294
+ const config2 = {};
35295
+ const planPath = pickOptionalString(rawConfig, "planPath");
35296
+ if (planPath) config2.planPath = planPath;
35297
+ const progressPath = pickOptionalString(rawConfig, "progressPath");
35298
+ if (progressPath) config2.progressPath = progressPath;
35299
+ const guardrailsPath = pickOptionalString(rawConfig, "guardrailsPath");
35300
+ if (guardrailsPath) config2.guardrailsPath = guardrailsPath;
35301
+ const errorsLogPath = pickOptionalString(rawConfig, "errorsLogPath");
35302
+ if (errorsLogPath) config2.errorsLogPath = errorsLogPath;
35303
+ const activityLogPath = pickOptionalString(rawConfig, "activityLogPath");
35304
+ if (activityLogPath) config2.activityLogPath = activityLogPath;
35305
+ const agent = pickOptionalString(rawConfig, "agent");
35306
+ if (agent) config2.agent = agent;
35307
+ const maxIterations = pickOptionalPositiveInt(rawConfig, "maxIterations", { min: 1 });
35308
+ if (maxIterations != null) config2.maxIterations = maxIterations;
35309
+ const staleSeconds = pickOptionalPositiveInt(rawConfig, "staleSeconds", { min: 0 });
35310
+ if (staleSeconds != null) config2.staleSeconds = staleSeconds;
35311
+ const noCommit = pickOptionalBoolean(rawConfig, "noCommit");
35312
+ if (noCommit != null) config2.noCommit = noCommit;
35313
+ return { config: config2, sourcePath };
35314
+ }
35315
+ function mergeConfigs(base, override) {
35316
+ const result = { ...base };
35317
+ for (const key of Object.keys(override)) {
35318
+ if (override[key] !== void 0) {
35319
+ result[key] = override[key];
35320
+ }
35321
+ }
35359
35322
  return result;
35360
35323
  }
35324
+ async function loadConfig(cwd, deps) {
35325
+ const fs3 = deps?.fs ?? fsPromises9;
35326
+ const sources = [];
35327
+ let merged = {};
35328
+ if (deps?.homeDir) {
35329
+ const globalDir = path14.join(deps.homeDir, ".poe-code", "ralph");
35330
+ const globalResult = await loadSingleConfig(globalDir, fs3);
35331
+ if (globalResult) {
35332
+ merged = globalResult.config;
35333
+ sources.push({ path: globalResult.sourcePath, scope: "global" });
35334
+ }
35335
+ }
35336
+ const localDir = path14.join(cwd, ".agents", "poe-code-ralph");
35337
+ const localResult = await loadSingleConfig(localDir, fs3);
35338
+ if (localResult) {
35339
+ merged = mergeConfigs(merged, localResult.config);
35340
+ sources.push({ path: localResult.sourcePath, scope: "local" });
35341
+ }
35342
+ return { config: merged, sources };
35343
+ }
35361
35344
 
35362
35345
  // packages/ralph/src/index.ts
35363
35346
  async function ralphBuild(options) {
@@ -35445,6 +35428,8 @@ var templateImports2 = {
35445
35428
  skillPlan: () => Promise.resolve().then(() => __toESM(require_SKILL_plan(), 1)),
35446
35429
  promptPlan: () => Promise.resolve().then(() => __toESM(require_PROMPT_plan(), 1)),
35447
35430
  promptBuild: () => Promise.resolve().then(() => __toESM(require_PROMPT_build(), 1)),
35431
+ refGuardrails: () => Promise.resolve().then(() => __toESM(require_GUARDRAILS(), 1)),
35432
+ refContextEngineering: () => Promise.resolve().then(() => __toESM(require_CONTEXT_ENGINEERING(), 1)),
35448
35433
  stateProgress: () => Promise.resolve().then(() => __toESM(require_progress(), 1)),
35449
35434
  stateGuardrails: () => Promise.resolve().then(() => __toESM(require_guardrails(), 1)),
35450
35435
  stateErrors: () => Promise.resolve().then(() => __toESM(require_errors3(), 1)),
@@ -35456,6 +35441,8 @@ async function loadRalphTemplates() {
35456
35441
  skillPlan,
35457
35442
  promptPlan,
35458
35443
  promptBuild,
35444
+ refGuardrails,
35445
+ refContextEngineering,
35459
35446
  stateProgress,
35460
35447
  stateGuardrails,
35461
35448
  stateErrors,
@@ -35465,6 +35452,8 @@ async function loadRalphTemplates() {
35465
35452
  templateImports2.skillPlan(),
35466
35453
  templateImports2.promptPlan(),
35467
35454
  templateImports2.promptBuild(),
35455
+ templateImports2.refGuardrails(),
35456
+ templateImports2.refContextEngineering(),
35468
35457
  templateImports2.stateProgress(),
35469
35458
  templateImports2.stateGuardrails(),
35470
35459
  templateImports2.stateErrors(),
@@ -35475,6 +35464,8 @@ async function loadRalphTemplates() {
35475
35464
  skillPlan: skillPlan.default,
35476
35465
  promptPlan: promptPlan.default,
35477
35466
  promptBuild: promptBuild.default,
35467
+ refGuardrails: refGuardrails.default,
35468
+ refContextEngineering: refContextEngineering.default,
35478
35469
  stateProgress: stateProgress.default,
35479
35470
  stateGuardrails: stateGuardrails.default,
35480
35471
  stateErrors: stateErrors.default,
@@ -35567,6 +35558,16 @@ async function installRalphTemplates(args) {
35567
35558
  targetPath: path16.join(cwd, ".agents", "poe-code-ralph", "PROMPT_build.md"),
35568
35559
  displayPath: ".agents/poe-code-ralph/PROMPT_build.md",
35569
35560
  contents: templates.promptBuild
35561
+ },
35562
+ {
35563
+ targetPath: path16.join(cwd, ".agents", "poe-code-ralph", "references", "GUARDRAILS.md"),
35564
+ displayPath: ".agents/poe-code-ralph/references/GUARDRAILS.md",
35565
+ contents: templates.refGuardrails
35566
+ },
35567
+ {
35568
+ targetPath: path16.join(cwd, ".agents", "poe-code-ralph", "references", "CONTEXT_ENGINEERING.md"),
35569
+ displayPath: ".agents/poe-code-ralph/references/CONTEXT_ENGINEERING.md",
35570
+ contents: templates.refContextEngineering
35570
35571
  }
35571
35572
  ];
35572
35573
  for (const entry of templateWrites) {
@@ -35634,8 +35635,11 @@ function registerRalphCommand(program, container) {
35634
35635
  }
35635
35636
  let configActivityLogPath;
35636
35637
  try {
35637
- const config2 = await loadConfig(container.env.cwd, { fs: container.fs });
35638
- configActivityLogPath = config2.activityLogPath;
35638
+ const configResult = await loadConfig(container.env.cwd, {
35639
+ fs: container.fs,
35640
+ homeDir: container.env.homeDir
35641
+ });
35642
+ configActivityLogPath = configResult.config.activityLogPath;
35639
35643
  } catch (error2) {
35640
35644
  const message2 = error2 instanceof Error ? error2.message : String(error2);
35641
35645
  throw new ValidationError(message2);
@@ -35740,13 +35744,36 @@ function registerRalphCommand(program, container) {
35740
35744
  "ralph build does not support --dry-run. Use --no-commit instead."
35741
35745
  );
35742
35746
  }
35743
- let config2;
35747
+ let configResult;
35744
35748
  try {
35745
- config2 = await loadConfig(container.env.cwd, { fs: container.fs });
35749
+ configResult = await loadConfig(container.env.cwd, {
35750
+ fs: container.fs,
35751
+ homeDir: container.env.homeDir
35752
+ });
35746
35753
  } catch (error2) {
35747
35754
  const message = error2 instanceof Error ? error2.message : String(error2);
35748
35755
  throw new ValidationError(message);
35749
35756
  }
35757
+ const config2 = configResult.config;
35758
+ const maxIterations = typeof iterations === "string" ? resolveIterations(iterations) : config2.maxIterations ?? 25;
35759
+ const agent2 = options.agent?.trim() ? options.agent.trim() : config2.agent ?? "codex";
35760
+ const noCommit = options.commit === false ? true : config2.noCommit ?? false;
35761
+ const staleSeconds = config2.staleSeconds ?? 60;
35762
+ const maxFailures = typeof options.maxFailures === "string" ? resolveMaxFailures(options.maxFailures) : void 0;
35763
+ const pauseOnOverbake = Boolean(options.pauseOnOverbake);
35764
+ const worktreeEnabled = Boolean(options.worktree);
35765
+ const worktree = worktreeEnabled ? { enabled: true, name: options.worktreeName?.trim() || void 0 } : void 0;
35766
+ const cwd = container.env.cwd;
35767
+ const configLines = [
35768
+ `Agent: ${agent2}`,
35769
+ `Iterations: ${maxIterations}`
35770
+ ];
35771
+ if (noCommit) configLines.push("No-commit: true");
35772
+ if (worktree) configLines.push(`Worktree: ${worktree.name ?? "(auto)"}`);
35773
+ for (const source of configResult.sources) {
35774
+ configLines.push(`${source.scope}: ${source.path}`);
35775
+ }
35776
+ resources.logger.resolved("Config", configLines.join("\n "));
35750
35777
  let planPath;
35751
35778
  try {
35752
35779
  planPath = await resolvePlanPath({
@@ -35761,20 +35788,6 @@ function registerRalphCommand(program, container) {
35761
35788
  if (!planPath) {
35762
35789
  return;
35763
35790
  }
35764
- const maxIterations = typeof iterations === "string" ? resolveIterations(iterations) : config2.maxIterations ?? 25;
35765
- const agent2 = options.agent?.trim() ? options.agent.trim() : config2.agent ?? "codex";
35766
- const noCommit = options.commit === false ? true : config2.noCommit ?? false;
35767
- const staleSeconds = config2.staleSeconds ?? 60;
35768
- const maxFailures = typeof options.maxFailures === "string" ? resolveMaxFailures(options.maxFailures) : void 0;
35769
- const pauseOnOverbake = Boolean(options.pauseOnOverbake);
35770
- const worktreeEnabled = Boolean(options.worktree);
35771
- const worktree = worktreeEnabled ? { enabled: true, name: options.worktreeName?.trim() || void 0 } : void 0;
35772
- const cwd = container.env.cwd;
35773
- resources.logger.info(`Plan: ${planPath}`);
35774
- resources.logger.info(`Agent: ${agent2}`);
35775
- resources.logger.info(`Iterations: ${maxIterations}`);
35776
- if (noCommit) resources.logger.info("No-commit: true");
35777
- if (worktree) resources.logger.info(`Worktree: ${worktree.name ?? "(auto)"}`);
35778
35791
  try {
35779
35792
  const planContent = await container.fs.readFile(
35780
35793
  path16.resolve(cwd, planPath),
@@ -35785,7 +35798,7 @@ function registerRalphCommand(program, container) {
35785
35798
  const done = plan.stories.filter((s) => s.status === "done").length;
35786
35799
  const inProgress = plan.stories.filter((s) => s.status === "in_progress").length;
35787
35800
  const open = total - done - inProgress;
35788
- resources.logger.info(`Stories: ${done}/${total} done${inProgress ? `, ${inProgress} in progress` : ""}${open ? `, ${open} open` : ""}`);
35801
+ resources.logger.resolved("Stories", `${done}/${total} done${inProgress ? `, ${inProgress} in progress` : ""}${open ? `, ${open} open` : ""}`);
35789
35802
  } catch {
35790
35803
  }
35791
35804
  await ralphBuild({
@@ -36201,7 +36214,7 @@ function registerModelsCommand(program, container) {
36201
36214
  // package.json
36202
36215
  var package_default = {
36203
36216
  name: "poe-code",
36204
- version: "3.0.30",
36217
+ version: "3.0.32",
36205
36218
  description: "CLI tool to configure Poe API for developer workflows.",
36206
36219
  type: "module",
36207
36220
  workspaces: [
@@ -36244,7 +36257,10 @@ var package_default = {
36244
36257
  "e2e:logs:rotate": "tsx packages/e2e-docker-test-runner/scripts/logs.ts --rotate",
36245
36258
  "e2e:cache:clear": "rm -rf ~/.cache/poe-e2e",
36246
36259
  prepack: "npm run build",
36247
- prepare: "husky"
36260
+ prepare: "husky",
36261
+ "install-local-package": "npm pack --pack-destination /tmp && npm install -g /tmp/poe-code-*.tgz && rm /tmp/poe-code-*.tgz",
36262
+ smoke: "tsx scripts/smoke-test.ts",
36263
+ "smoke:verbose": "tsx scripts/smoke-test.ts --verbose"
36248
36264
  },
36249
36265
  bin: {
36250
36266
  "poe-code": "dist/bin.cjs",
@@ -36252,7 +36268,7 @@ var package_default = {
36252
36268
  "poe-claude": "dist/bin/poe-claude.js",
36253
36269
  "poe-codex": "dist/bin/poe-codex.js",
36254
36270
  "poe-opencode": "dist/bin/poe-opencode.js",
36255
- "tiny-mcp-test-server": "packages/tiny-mcp-test-server/dist/index.js"
36271
+ "tiny-stdio-mcp-test-server": "packages/tiny-stdio-mcp-test-server/dist/cli.js"
36256
36272
  },
36257
36273
  files: [
36258
36274
  "dist"
@@ -36262,41 +36278,41 @@ var package_default = {
36262
36278
  },
36263
36279
  packageManager: "npm@10.9.2",
36264
36280
  dependencies: {
36265
- "@clack/prompts": "^0.11.0",
36266
- chalk: "^5.3.0",
36267
- commander: "^11.1.0",
36281
+ "@clack/prompts": "^1.0.0",
36282
+ chalk: "^5.6.2",
36283
+ commander: "^14.0.3",
36268
36284
  "console-table-printer": "^2.15.0",
36269
36285
  diff: ">=8.0.3",
36270
36286
  "jsonc-parser": "^3.3.1",
36271
36287
  mustache: "^4.2.0",
36272
- semver: "^7.7.3",
36273
- "smol-toml": "^1.3.0",
36288
+ semver: "^7.7.4",
36289
+ "smol-toml": "^1.6.0",
36274
36290
  yaml: "^2.8.2"
36275
36291
  },
36276
36292
  devDependencies: {
36277
- "@modelcontextprotocol/sdk": "^1.25.3",
36293
+ "@eslint/js": "^9.0.0",
36294
+ "@modelcontextprotocol/sdk": "^1.26.0",
36278
36295
  "@poe-code/agent-spawn": "*",
36279
36296
  "@poe-code/config-mutations": "*",
36280
36297
  "@poe-code/design-system": "*",
36281
36298
  "@poe-code/e2e-docker-test-runner": "*",
36282
36299
  "@poe-code/freeze-cli": "*",
36283
- "@poe-code/tiny-mcp-server": "*",
36284
- "@poe-code/tiny-mcp-test-server": "*",
36300
+ "tiny-stdio-mcp-server": "*",
36301
+ "tiny-stdio-mcp-test-server": "*",
36285
36302
  "@types/mustache": "^4.2.6",
36286
- "@types/node": "^20.12.7",
36303
+ "@types/node": "^25.2.2",
36287
36304
  "@types/semver": "^7.7.1",
36288
- "@typescript-eslint/eslint-plugin": "^8.46.2",
36289
- "@typescript-eslint/parser": "^8.46.2",
36290
- eslint: "^9.38.0",
36291
- "eslint-config-prettier": "^9.1.0",
36305
+ eslint: "^9.0.0",
36306
+ "eslint-config-prettier": "^10.1.8",
36307
+ globals: "^17.3.0",
36292
36308
  husky: "^9.1.7",
36293
- memfs: "^4.7.3",
36294
- prettier: "^3.3.3",
36309
+ memfs: "^4.56.10",
36310
+ prettier: "^3.8.1",
36295
36311
  tsx: ">=4.21.0",
36296
- turbo: "^2.5.0",
36297
- typescript: "^5.4.5",
36298
- "typescript-eslint": "^8.46.2",
36299
- vitest: ">=4.0.17"
36312
+ turbo: "^2.8.3",
36313
+ typescript: "^5.9.3",
36314
+ "typescript-eslint": "^8.54.0",
36315
+ vitest: "^4.0.18"
36300
36316
  },
36301
36317
  repository: {
36302
36318
  type: "git",
@@ -36523,7 +36539,7 @@ function bootstrapProgram(container) {
36523
36539
  registerRalphCommand(program, container);
36524
36540
  registerUsageCommand(program, container);
36525
36541
  registerModelsCommand(program, container);
36526
- program.action(function() {
36542
+ program.allowExcessArguments().action(function() {
36527
36543
  const args = this.args;
36528
36544
  if (args.length > 0) {
36529
36545
  throwCommandNotFound({