poe-code 3.0.31 → 3.0.33

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 (48) hide show
  1. package/dist/cli/commands/mcp.js +1 -0
  2. package/dist/cli/commands/mcp.js.map +1 -1
  3. package/dist/cli/commands/ralph.js +52 -24
  4. package/dist/cli/commands/ralph.js.map +1 -1
  5. package/dist/cli/commands/skill.js +1 -0
  6. package/dist/cli/commands/skill.js.map +1 -1
  7. package/dist/cli/commands/spawn.js +5 -1
  8. package/dist/cli/commands/spawn.js.map +1 -1
  9. package/dist/cli/commands/test.js +20 -9
  10. package/dist/cli/commands/test.js.map +1 -1
  11. package/dist/cli/commands/version.js +8 -4
  12. package/dist/cli/commands/version.js.map +1 -1
  13. package/dist/cli/container.js +6 -2
  14. package/dist/cli/container.js.map +1 -1
  15. package/dist/cli/logger.js +2 -4
  16. package/dist/cli/logger.js.map +1 -1
  17. package/dist/cli/mcp-server.d.ts +1 -1
  18. package/dist/cli/mcp-server.js +1 -1
  19. package/dist/cli/mcp-server.js.map +1 -1
  20. package/dist/cli/program.js +1 -1
  21. package/dist/cli/program.js.map +1 -1
  22. package/dist/index.js +347 -320
  23. package/dist/index.js.map +4 -4
  24. package/dist/providers/claude-code.d.ts +2 -2
  25. package/dist/providers/claude-code.js +4 -49
  26. package/dist/providers/claude-code.js.map +1 -1
  27. package/dist/providers/codex.d.ts +1 -3
  28. package/dist/providers/codex.js +4 -35
  29. package/dist/providers/codex.js.map +1 -1
  30. package/dist/providers/kimi.js +2 -5
  31. package/dist/providers/kimi.js.map +1 -1
  32. package/dist/providers/opencode.js +2 -9
  33. package/dist/providers/opencode.js.map +1 -1
  34. package/dist/providers/spawn-options.d.ts +2 -0
  35. package/dist/sdk/spawn-core.d.ts +3 -0
  36. package/dist/sdk/spawn-core.js +9 -8
  37. package/dist/sdk/spawn-core.js.map +1 -1
  38. package/dist/sdk/spawn.js +4 -0
  39. package/dist/sdk/spawn.js.map +1 -1
  40. package/dist/sdk/types.d.ts +3 -0
  41. package/dist/tools/label-generator.js +2 -1
  42. package/dist/tools/label-generator.js.map +1 -1
  43. package/dist/utils/command-checks.d.ts +4 -0
  44. package/dist/utils/command-checks.js +26 -0
  45. package/dist/utils/command-checks.js.map +1 -1
  46. package/package.json +25 -22
  47. package/packages/tiny-stdio-mcp-test-server/dist/cli.js +26 -0
  48. 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,72 @@ 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
+ const stdinMode = options.useStdin && spawnConfig.stdinMode ? spawnConfig.stdinMode : void 0;
16287
+ return { binaryName, args: buildCliArgs(spawnConfig, options, stdinMode) };
16288
+ }
16289
+ async function spawn2(agentId, options, context) {
16290
+ const { agentId: resolvedId, binaryName, spawnConfig } = resolveCliConfig(agentId);
16291
+ const stdinMode = options.useStdin && spawnConfig.stdinMode ? spawnConfig.stdinMode : void 0;
16292
+ const spawnArgs = buildCliArgs(spawnConfig, options, stdinMode);
16293
+ if (context?.dryRun) {
16294
+ const rendered = [binaryName, ...spawnArgs].join(" ");
16295
+ context.logger?.dryRun(rendered);
16296
+ return { stdout: "", stderr: "", exitCode: 0 };
16297
+ }
16298
+ const child = spawnChildProcess(binaryName, spawnArgs, {
16234
16299
  cwd: options.cwd,
16235
16300
  stdio: [stdinMode ? "pipe" : "inherit", "pipe", "pipe"]
16236
16301
  });
16237
16302
  if (!child.stdout || !child.stderr) {
16238
- throw new Error(`Failed to spawn "${resolved.agentId}": missing stdio pipes.`);
16303
+ throw new Error(`Failed to spawn "${resolvedId}": missing stdio pipes.`);
16239
16304
  }
16240
16305
  const stdoutStream = child.stdout;
16241
16306
  const stderrStream = child.stderr;
16242
16307
  if (stdinMode) {
16243
16308
  if (!child.stdin) {
16244
- throw new Error(`Failed to spawn "${resolved.agentId}": missing stdin pipe.`);
16309
+ throw new Error(`Failed to spawn "${resolvedId}": missing stdin pipe.`);
16245
16310
  }
16246
16311
  child.stdin.setDefaultEncoding("utf8");
16247
16312
  child.stdin.write(options.prompt);
@@ -16300,9 +16365,11 @@ async function spawnInteractive(agentId, options) {
16300
16365
  }
16301
16366
  }
16302
16367
  if (options.model && spawnConfig.modelFlag) {
16303
- args.push(spawnConfig.modelFlag, options.model);
16368
+ args.push(spawnConfig.modelFlag, stripModelNamespace2(options.model));
16304
16369
  }
16305
16370
  args.push(...interactive.defaultArgs);
16371
+ const mode = options.mode ?? "yolo";
16372
+ args.push(...spawnConfig.modes[mode]);
16306
16373
  if (options.args && options.args.length > 0) {
16307
16374
  args.push(...options.args);
16308
16375
  }
@@ -16515,7 +16582,7 @@ async function* adaptClaude(lines) {
16515
16582
  if (blockType !== "tool_result") continue;
16516
16583
  const kind = toolKindsById.get(item.tool_use_id);
16517
16584
  toolKindsById.delete(item.tool_use_id);
16518
- let path17 = "";
16585
+ let path17;
16519
16586
  if (typeof item.content === "string") {
16520
16587
  path17 = item.content;
16521
16588
  } else {
@@ -16874,9 +16941,11 @@ function spawnStreaming(options) {
16874
16941
  args.push(options.prompt);
16875
16942
  }
16876
16943
  if (options.model && spawnConfig.modelFlag) {
16877
- args.push(spawnConfig.modelFlag, options.model);
16944
+ args.push(spawnConfig.modelFlag, stripModelNamespace2(options.model));
16878
16945
  }
16879
16946
  args.push(...spawnConfig.defaultArgs);
16947
+ const mode = options.mode ?? "yolo";
16948
+ args.push(...spawnConfig.modes[mode]);
16880
16949
  if (useStdin) {
16881
16950
  args.push(...spawnConfig.stdinMode.extraArgs);
16882
16951
  }
@@ -16935,50 +17004,6 @@ ${stdout}
16935
17004
  stderr:
16936
17005
  ${stderr}`;
16937
17006
  }
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
17007
  function stdoutMatchesExpected(stdout, expected) {
16983
17008
  const trimmed = stdout.trim();
16984
17009
  if (trimmed === expected) {
@@ -16986,20 +17011,38 @@ function stdoutMatchesExpected(stdout, expected) {
16986
17011
  }
16987
17012
  return stdout.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0).some((line) => line === expected);
16988
17013
  }
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");
17014
+ function createSpawnHealthCheck(agentId, options) {
17015
+ const prompt = `Output exactly: ${options.expectedOutput}`;
17016
+ const { binaryName, args } = buildSpawnArgs(agentId, {
17017
+ prompt,
17018
+ model: options.model,
17019
+ mode: "yolo"
17020
+ });
17021
+ return {
17022
+ id: `${agentId}-cli-health`,
17023
+ description: `spawn ${agentId} (expecting "${options.expectedOutput}")`,
17024
+ async run(context) {
17025
+ if (context.isDryRun) {
17026
+ context.logDryRun?.(
17027
+ `Dry run: ${[binaryName, ...args].join(" ")} (expecting "${options.expectedOutput}")`
17028
+ );
17029
+ return;
17030
+ }
17031
+ const result = await context.runCommand(binaryName, args);
17032
+ if (result.exitCode !== 0) {
17033
+ throw new Error(
17034
+ `spawn ${agentId} failed with exit code ${result.exitCode}.
17035
+ ${formatCommandRunnerResult(result)}`
17036
+ );
17037
+ }
17038
+ if (!result.stdout.includes(options.expectedOutput)) {
17039
+ throw new Error(
17040
+ `spawn ${agentId}: expected "${options.expectedOutput}" in stdout.
17041
+ ${formatCommandRunnerResult(result)}`
17042
+ );
17043
+ }
17044
+ }
17045
+ };
17003
17046
  }
17004
17047
  function createBinaryExistsCheck(binaryName, id, description) {
17005
17048
  return {
@@ -17092,9 +17135,9 @@ function describeInstallCommand(step) {
17092
17135
  return `[${step.id}] ${formatCommand2(step.command, step.args)}`;
17093
17136
  }
17094
17137
  function formatCommand2(command, args) {
17095
- return [command, ...args.map(quoteIfNeeded2)].join(" ");
17138
+ return [command, ...args.map(quoteIfNeeded)].join(" ");
17096
17139
  }
17097
- function quoteIfNeeded2(value) {
17140
+ function quoteIfNeeded(value) {
17098
17141
  if (value.length === 0) {
17099
17142
  return '""';
17100
17143
  }
@@ -17227,28 +17270,6 @@ var CLAUDE_CODE_INSTALL_DEFINITION = {
17227
17270
  ],
17228
17271
  successMessage: "Installed Claude CLI."
17229
17272
  };
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
17273
  var claudeCodeService = createProvider({
17253
17274
  ...claudeCodeAgent,
17254
17275
  supportsStdinPrompt: true,
@@ -17282,14 +17303,8 @@ var claudeCodeService = createProvider({
17282
17303
  },
17283
17304
  test(context) {
17284
17305
  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
- ),
17306
+ createSpawnHealthCheck("claude-code", {
17307
+ model: DEFAULT_CLAUDE_CODE_MODEL,
17293
17308
  expectedOutput: "CLAUDE_CODE_OK"
17294
17309
  })
17295
17310
  );
@@ -17327,36 +17342,7 @@ var claudeCodeService = createProvider({
17327
17342
  })
17328
17343
  ]
17329
17344
  },
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 ? stripModelNamespace(options.model) : void 0
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
- }
17345
+ install: CLAUDE_CODE_INSTALL_DEFINITION
17360
17346
  });
17361
17347
 
17362
17348
  // src/providers/codex.ts
@@ -17406,14 +17392,6 @@ function stripCodexConfiguration(document) {
17406
17392
  function isTableEmpty(value) {
17407
17393
  return isConfigObject4(value) && Object.keys(value).length === 0;
17408
17394
  }
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
17395
  var codexService = createProvider({
17418
17396
  ...codexAgent,
17419
17397
  supportsStdinPrompt: true,
@@ -17441,14 +17419,8 @@ var codexService = createProvider({
17441
17419
  },
17442
17420
  test(context) {
17443
17421
  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
- ),
17422
+ createSpawnHealthCheck("codex", {
17423
+ model: DEFAULT_CODEX_MODEL,
17452
17424
  expectedOutput: "CODEX_OK"
17453
17425
  })
17454
17426
  );
@@ -17487,32 +17459,7 @@ var codexService = createProvider({
17487
17459
  })
17488
17460
  ]
17489
17461
  },
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 ? stripModelNamespace(options.model) : void 0
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
- }
17462
+ install: CODEX_INSTALL_DEFINITION
17516
17463
  });
17517
17464
 
17518
17465
  // src/providers/opencode.ts
@@ -17609,14 +17556,7 @@ var openCodeService = createProvider({
17609
17556
  install: OPEN_CODE_INSTALL_DEFINITION,
17610
17557
  test(context) {
17611
17558
  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
- ],
17559
+ createSpawnHealthCheck("opencode", {
17620
17560
  expectedOutput: "OPEN_CODE_OK"
17621
17561
  })
17622
17562
  );
@@ -17689,10 +17629,7 @@ var kimiService = createProvider({
17689
17629
  },
17690
17630
  test(context) {
17691
17631
  return context.runCheck(
17692
- createCommandExpectationCheck({
17693
- id: "kimi-cli-health",
17694
- command: "kimi",
17695
- args: buildKimiArgs("Output exactly: KIMI_OK"),
17632
+ createSpawnHealthCheck("kimi", {
17696
17633
  expectedOutput: "KIMI_OK"
17697
17634
  })
17698
17635
  );
@@ -17908,7 +17845,9 @@ function createCliContainer(dependencies) {
17908
17845
  logDir: environment.logDir,
17909
17846
  logToStderr: true
17910
17847
  });
17911
- loggerFactory.setErrorLogger(errorLogger);
17848
+ if (!dependencies.logger) {
17849
+ loggerFactory.setErrorLogger(errorLogger);
17850
+ }
17912
17851
  const contextFactory = createCommandContextFactory({
17913
17852
  fs: dependencies.fs
17914
17853
  });
@@ -18199,6 +18138,7 @@ async function spawnCore(container, service, options, flags = { dryRun: false, v
18199
18138
  prompt: options.prompt,
18200
18139
  args: options.args,
18201
18140
  model: options.model,
18141
+ mode: options.mode,
18202
18142
  cwd: cwdOverride,
18203
18143
  useStdin: options.useStdin ?? false
18204
18144
  };
@@ -18206,21 +18146,12 @@ async function spawnCore(container, service, options, flags = { dryRun: false, v
18206
18146
  if (!adapter) {
18207
18147
  throw new Error(`Unknown service "${service}".`);
18208
18148
  }
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
18149
  const commandFlags = { dryRun: flags.dryRun, assumeYes: true, verbose: flags.verbose };
18218
18150
  const resources = createExecutionResources(
18219
18151
  container,
18220
18152
  commandFlags,
18221
18153
  `spawn:${service}`
18222
18154
  );
18223
- const providerContext = buildProviderContext(container, adapter, resources);
18224
18155
  if (flags.dryRun) {
18225
18156
  const summary = formatSpawnDryRunMessage(adapter.label, spawnOptions);
18226
18157
  resources.logger.dryRun(summary);
@@ -18230,6 +18161,15 @@ async function spawnCore(container, service, options, flags = { dryRun: false, v
18230
18161
  exitCode: 0
18231
18162
  };
18232
18163
  }
18164
+ if (typeof adapter.spawn !== "function") {
18165
+ throw new Error(`${adapter.label} does not support spawn.`);
18166
+ }
18167
+ if (spawnOptions.useStdin && !adapter.supportsStdinPrompt) {
18168
+ throw new Error(
18169
+ `${adapter.label} does not support stdin prompts. Use a different service (e.g. "codex") or pass the prompt as an argument.`
18170
+ );
18171
+ }
18172
+ const providerContext = buildProviderContext(container, adapter, resources);
18233
18173
  const result = await container.registry.invoke(
18234
18174
  adapter.name,
18235
18175
  "spawn",
@@ -18456,6 +18396,7 @@ function spawn3(service, promptOrOptions, maybeOptions) {
18456
18396
  prompt: options.prompt,
18457
18397
  cwd: options.cwd,
18458
18398
  model: options.model,
18399
+ mode: options.mode,
18459
18400
  args: options.args
18460
18401
  });
18461
18402
  return {
@@ -18472,6 +18413,7 @@ function spawn3(service, promptOrOptions, maybeOptions) {
18472
18413
  prompt: options.prompt,
18473
18414
  cwd: options.cwd,
18474
18415
  model: options.model,
18416
+ mode: options.mode,
18475
18417
  args: options.args,
18476
18418
  useStdin: false
18477
18419
  });
@@ -18491,6 +18433,7 @@ function spawn3(service, promptOrOptions, maybeOptions) {
18491
18433
  prompt: options.prompt,
18492
18434
  cwd: options.cwd,
18493
18435
  model: options.model,
18436
+ mode: options.mode,
18494
18437
  args: options.args,
18495
18438
  useStdin: false
18496
18439
  });
@@ -18501,6 +18444,7 @@ function spawn3(service, promptOrOptions, maybeOptions) {
18501
18444
  prompt: options.prompt,
18502
18445
  cwd: options.cwd,
18503
18446
  model: options.model,
18447
+ mode: options.mode,
18504
18448
  args: options.args,
18505
18449
  useStdin: false
18506
18450
  });
@@ -18514,11 +18458,11 @@ function spawn3(service, promptOrOptions, maybeOptions) {
18514
18458
 
18515
18459
  // src/cli/commands/spawn.ts
18516
18460
  function registerSpawnCommand(program, container, options = {}) {
18517
- const spawnServices = container.registry.list().filter((service) => typeof service.spawn === "function").map((service) => service.name);
18461
+ const spawnServices = container.registry.list().filter((service) => typeof service.spawn === "function" || getSpawnConfig(service.name)).map((service) => service.name);
18518
18462
  const extraServices = options.extraServices ?? [];
18519
18463
  const serviceList = [...spawnServices, ...extraServices];
18520
18464
  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(
18465
+ 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
18466
  "<agent>",
18523
18467
  serviceDescription
18524
18468
  ).argument("[prompt]", "Prompt text to send (or '-' / stdin)").argument(
@@ -18546,6 +18490,7 @@ function registerSpawnCommand(program, container, options = {}) {
18546
18490
  prompt: promptText ?? "",
18547
18491
  args: forwardedArgs,
18548
18492
  model: commandOptions.model,
18493
+ mode: commandOptions.mode,
18549
18494
  cwd: cwdOverride
18550
18495
  });
18551
18496
  process.exitCode = result.exitCode;
@@ -18565,6 +18510,7 @@ function registerSpawnCommand(program, container, options = {}) {
18565
18510
  prompt: promptText,
18566
18511
  args: forwardedArgs,
18567
18512
  model: commandOptions.model,
18513
+ mode: commandOptions.mode,
18568
18514
  cwd: cwdOverride,
18569
18515
  useStdin: shouldReadFromStdin
18570
18516
  };
@@ -18621,6 +18567,7 @@ function registerSpawnCommand(program, container, options = {}) {
18621
18567
  prompt: spawnOptions.prompt,
18622
18568
  args: spawnOptions.args,
18623
18569
  model: spawnOptions.model,
18570
+ mode: spawnOptions.mode,
18624
18571
  cwd: spawnOptions.cwd
18625
18572
  });
18626
18573
  await renderAcpStream(events);
@@ -19210,21 +19157,31 @@ async function executeTest(program, container, service, options = {}) {
19210
19157
  }
19211
19158
  const expectedOutput = "STDIN_OK";
19212
19159
  const prompt = `Output exactly: ${expectedOutput}`;
19213
- const result = await container.registry.invoke(
19214
- canonicalService,
19215
- "spawn",
19216
- async (entry) => {
19217
- if (!entry.spawn) {
19218
- throw new Error(`Agent "${canonicalService}" does not support spawn.`);
19219
- }
19220
- const output = await entry.spawn(providerContext, {
19160
+ const result = await (async () => {
19161
+ const spawnConfig = getSpawnConfig(canonicalService);
19162
+ if (spawnConfig) {
19163
+ return spawn2(canonicalService, {
19221
19164
  prompt,
19222
19165
  useStdin: true,
19223
19166
  model: options.model
19224
19167
  });
19225
- return output;
19226
19168
  }
19227
- );
19169
+ return container.registry.invoke(
19170
+ canonicalService,
19171
+ "spawn",
19172
+ async (entry) => {
19173
+ if (!entry.spawn) {
19174
+ throw new Error(`Agent "${canonicalService}" does not support spawn.`);
19175
+ }
19176
+ const output = await entry.spawn(providerContext, {
19177
+ prompt,
19178
+ useStdin: true,
19179
+ model: options.model
19180
+ });
19181
+ return output;
19182
+ }
19183
+ );
19184
+ })();
19228
19185
  if (!result) {
19229
19186
  throw new Error(
19230
19187
  `Stdin spawn test for ${adapter.label} did not return command output.`
@@ -19810,10 +19767,10 @@ function resolveOutputPath(filename, cwd) {
19810
19767
  // src/cli/commands/mcp.ts
19811
19768
  init_credentials();
19812
19769
 
19813
- // packages/tiny-mcp-server/src/server.ts
19770
+ // packages/tiny-stdio-mcp-server/src/server.ts
19814
19771
  import * as readline from "readline";
19815
19772
 
19816
- // packages/tiny-mcp-server/src/types.ts
19773
+ // packages/tiny-stdio-mcp-server/src/types.ts
19817
19774
  var JSON_RPC_ERROR_CODES = {
19818
19775
  PARSE_ERROR: -32700,
19819
19776
  INVALID_REQUEST: -32600,
@@ -19822,7 +19779,7 @@ var JSON_RPC_ERROR_CODES = {
19822
19779
  INTERNAL_ERROR: -32603
19823
19780
  };
19824
19781
 
19825
- // packages/tiny-mcp-server/src/jsonrpc.ts
19782
+ // packages/tiny-stdio-mcp-server/src/jsonrpc.ts
19826
19783
  function parseMessage(line) {
19827
19784
  let parsed;
19828
19785
  try {
@@ -19919,7 +19876,7 @@ function formatErrorResponse(id, error2) {
19919
19876
  return JSON.stringify(response);
19920
19877
  }
19921
19878
 
19922
- // packages/tiny-file-type/src/file-type.ts
19879
+ // packages/tiny-stdio-mcp-server/src/content/file-type.ts
19923
19880
  function fileTypeFromBuffer(data) {
19924
19881
  if (data.length < 12) {
19925
19882
  return void 0;
@@ -19957,7 +19914,7 @@ function fileTypeFromBuffer(data) {
19957
19914
  return void 0;
19958
19915
  }
19959
19916
 
19960
- // packages/tiny-mcp-server/src/content/image.ts
19917
+ // packages/tiny-stdio-mcp-server/src/content/image.ts
19961
19918
  var SUPPORTED_IMAGE_MIMES = /* @__PURE__ */ new Set([
19962
19919
  "image/png",
19963
19920
  "image/jpeg",
@@ -20017,7 +19974,7 @@ var Image = class _Image {
20017
19974
  }
20018
19975
  };
20019
19976
 
20020
- // packages/tiny-mcp-server/src/content/audio.ts
19977
+ // packages/tiny-stdio-mcp-server/src/content/audio.ts
20021
19978
  var SUPPORTED_AUDIO_MIMES = /* @__PURE__ */ new Set([
20022
19979
  "audio/mpeg",
20023
19980
  "audio/wav",
@@ -20088,7 +20045,7 @@ var Audio = class _Audio {
20088
20045
  }
20089
20046
  };
20090
20047
 
20091
- // packages/tiny-mcp-server/src/content/file.ts
20048
+ // packages/tiny-stdio-mcp-server/src/content/file.ts
20092
20049
  function isTextMimeType(mimeType) {
20093
20050
  return mimeType.startsWith("text/") || mimeType === "application/json" || mimeType === "application/xml" || mimeType === "application/javascript" || mimeType === "application/typescript";
20094
20051
  }
@@ -20170,7 +20127,7 @@ var File2 = class _File {
20170
20127
  }
20171
20128
  };
20172
20129
 
20173
- // packages/tiny-mcp-server/src/content/convert.ts
20130
+ // packages/tiny-stdio-mcp-server/src/content/convert.ts
20174
20131
  function convertSingleValue(value) {
20175
20132
  if (typeof value === "string") {
20176
20133
  return { type: "text", text: value };
@@ -20193,7 +20150,7 @@ function toContentBlocks(result) {
20193
20150
  return [convertSingleValue(result)];
20194
20151
  }
20195
20152
 
20196
- // packages/tiny-mcp-server/src/server.ts
20153
+ // packages/tiny-stdio-mcp-server/src/server.ts
20197
20154
  var PROTOCOL_VERSION = "2025-11-25";
20198
20155
  function createServer(options) {
20199
20156
  const tools = /* @__PURE__ */ new Map();
@@ -20394,7 +20351,7 @@ function createServer(options) {
20394
20351
  return server;
20395
20352
  }
20396
20353
 
20397
- // packages/tiny-mcp-server/src/schema.ts
20354
+ // packages/tiny-stdio-mcp-server/src/schema.ts
20398
20355
  function defineSchema(definition) {
20399
20356
  const properties = {};
20400
20357
  const required2 = [];
@@ -33308,7 +33265,7 @@ function buildHelpText() {
33308
33265
  return lines.join("\n");
33309
33266
  }
33310
33267
  function registerMcpCommand(program, container) {
33311
- const mcp = program.command("mcp").description("MCP server commands").addHelpText("after", buildHelpText()).action(function() {
33268
+ const mcp = program.command("mcp").description("MCP server commands").addHelpText("after", buildHelpText()).allowExcessArguments().action(function() {
33312
33269
  if (this.args.length > 0) {
33313
33270
  throwCommandNotFound({
33314
33271
  container,
@@ -33626,7 +33583,7 @@ function buildHelpText2() {
33626
33583
  ].join("\n");
33627
33584
  }
33628
33585
  function registerSkillCommand(program, container) {
33629
- const skill = program.command("skill").description("Skill directory commands").addHelpText("after", buildHelpText2()).action(function() {
33586
+ const skill = program.command("skill").description("Skill directory commands").addHelpText("after", buildHelpText2()).allowExcessArguments().action(function() {
33630
33587
  if (this.args.length > 0) {
33631
33588
  throwCommandNotFound({
33632
33589
  container,
@@ -33883,17 +33840,18 @@ async function displayVersion(container, currentVersion) {
33883
33840
  verbose: false,
33884
33841
  scope: "version"
33885
33842
  });
33886
- logger2.info(`poe-code ${currentVersion}`);
33843
+ logger2.intro("version");
33844
+ const versionValue = currentVersion === "0.0.0-dev" ? `${currentVersion} ${text.badge("local build")}` : currentVersion;
33845
+ logger2.resolved("poe-code", versionValue);
33887
33846
  const result = await checkForUpdate({
33888
33847
  currentVersion,
33889
33848
  httpClient
33890
33849
  });
33891
33850
  if (result?.updateAvailable) {
33892
- logger2.info("");
33893
- logger2.info(
33851
+ logger2.warn(
33894
33852
  `Update available: ${result.currentVersion} -> ${result.latestVersion}`
33895
33853
  );
33896
- logger2.info("Run: npm install -g poe-code@latest");
33854
+ logger2.resolved("Update", `npm install -g poe-code@latest`);
33897
33855
  }
33898
33856
  }
33899
33857
 
@@ -34235,7 +34193,7 @@ function parsePlan(yamlContent) {
34235
34193
  doc = parse7(yamlContent);
34236
34194
  } catch (error2) {
34237
34195
  const message = error2 instanceof Error ? error2.message : String(error2);
34238
- throw new Error(`Invalid plan YAML: ${message}`);
34196
+ throw new Error(`Invalid plan YAML: ${message}`, { cause: error2 });
34239
34197
  }
34240
34198
  if (!isRecord3(doc)) {
34241
34199
  throw new Error("Invalid plan YAML: expected top-level object");
@@ -34539,7 +34497,7 @@ var OverbakingDetector = class {
34539
34497
  consecutiveFailures: 0,
34540
34498
  warned: false
34541
34499
  };
34542
- if (status !== "failure") {
34500
+ if (status === "success") {
34543
34501
  if (existing.consecutiveFailures !== 0 || existing.warned) {
34544
34502
  this.failuresByStoryId.set(storyId, { consecutiveFailures: 0, warned: false });
34545
34503
  }
@@ -34574,6 +34532,7 @@ async function defaultStreamingSpawn(agentId, options) {
34574
34532
  agentId,
34575
34533
  prompt: options.prompt,
34576
34534
  cwd: options.cwd,
34535
+ mode: options.mode,
34577
34536
  useStdin: options.useStdin
34578
34537
  });
34579
34538
  let agentText = "";
@@ -34632,7 +34591,7 @@ function formatAgentSetupHint(agentId) {
34632
34591
  async function appendToErrorsLog(fs3, errorsLogPath, message) {
34633
34592
  const next = message.endsWith("\n") ? message : `${message}
34634
34593
  `;
34635
- let previous = "";
34594
+ let previous;
34636
34595
  try {
34637
34596
  previous = await fs3.readFile(errorsLogPath, "utf8");
34638
34597
  } catch (error2) {
@@ -34845,8 +34804,15 @@ async function buildLoop(options) {
34845
34804
  const activityLogPath = absPath(cwd, options.activityLogPath ?? ".poe-code-ralph/activity.log");
34846
34805
  const guardrailsRef = absPath(cwd, ".agents/poe-code-ralph/references/GUARDRAILS.md");
34847
34806
  const contextRef = absPath(cwd, ".agents/poe-code-ralph/references/CONTEXT_ENGINEERING.md");
34848
- const activityCmd = absPath(cwd, ".agents/poe-code-ralph/log-activity.sh");
34807
+ const activityCmd = "poe-code ralph agent log";
34849
34808
  const promptTemplatePath = absPath(cwd, ".agents/poe-code-ralph/PROMPT_build.md");
34809
+ try {
34810
+ await fs3.readFile(promptTemplatePath, "utf8");
34811
+ } catch {
34812
+ throw new Error(
34813
+ `PROMPT_build.md not found at ${promptTemplatePath}. Run "poe-code ralph install" to set up Ralph templates.`
34814
+ );
34815
+ }
34850
34816
  const runsDir = absPath(cwd, ".poe-code-ralph/runs");
34851
34817
  const runId = options.deps?.runId ?? createRunId(nowFn());
34852
34818
  const overbaking = new OverbakingDetector({ threshold: options.maxFailures });
@@ -34903,14 +34869,15 @@ async function buildLoop(options) {
34903
34869
  });
34904
34870
  await fs3.mkdir(dirname6(renderedPromptPath), { recursive: true });
34905
34871
  await fs3.writeFile(renderedPromptPath, prompt, { encoding: "utf8" });
34906
- let status = "failure";
34907
- let combinedOutput = "";
34908
- let stderrForErrorsLog = "";
34872
+ let status;
34873
+ let combinedOutput;
34874
+ let stderrForErrorsLog;
34909
34875
  let overbakeAction = null;
34910
34876
  try {
34911
34877
  const result = await spawn5(options.agent, {
34912
34878
  prompt,
34913
34879
  cwd,
34880
+ mode: "yolo",
34914
34881
  useStdin: true
34915
34882
  });
34916
34883
  const agentStdout = result.stdout ?? "";
@@ -34952,6 +34919,8 @@ ${agentStderr}` : ""
34952
34919
  });
34953
34920
  stderr.write(warning);
34954
34921
  await appendToErrorsLog(fs3, errorsLogPath, warning);
34922
+ }
34923
+ if (overbakeEvent.overbaked) {
34955
34924
  if (options.pauseOnOverbake) {
34956
34925
  overbakeAction = await promptOverbake({
34957
34926
  storyId: story.id,
@@ -34960,7 +34929,7 @@ ${agentStderr}` : ""
34960
34929
  threshold: overbakeEvent.threshold
34961
34930
  });
34962
34931
  } else {
34963
- overbakeAction = "continue";
34932
+ overbakeAction = "skip";
34964
34933
  }
34965
34934
  if (overbakeAction === "skip") {
34966
34935
  skippedStoryIds.add(story.id);
@@ -35137,7 +35106,8 @@ async function resolvePlanPath(options) {
35137
35106
  } catch (error2) {
35138
35107
  if (isNotFound(error2)) {
35139
35108
  throw new Error(
35140
- `Plan not found at "${provided}". Provide --plan <path> to an existing plan file.`
35109
+ `Plan not found at "${provided}". Provide --plan <path> to an existing plan file.`,
35110
+ { cause: error2 }
35141
35111
  );
35142
35112
  }
35143
35113
  throw error2;
@@ -35151,9 +35121,6 @@ async function resolvePlanPath(options) {
35151
35121
  );
35152
35122
  return null;
35153
35123
  }
35154
- if (candidates.length === 1) {
35155
- return candidates[0].path;
35156
- }
35157
35124
  const selection = await select2({
35158
35125
  message: "Select a plan file to use for this Ralph run",
35159
35126
  options: candidates.map((candidate) => ({
@@ -35255,7 +35222,7 @@ async function logActivity(path17, message, options = {}) {
35255
35222
  await fs3.appendFile(path17, entry, { encoding: "utf8" });
35256
35223
  } catch (error2) {
35257
35224
  const detail = error2 instanceof Error ? error2.message : `Unknown error: ${String(error2)}`;
35258
- throw new Error(`Failed to append activity log entry: ${detail}`);
35225
+ throw new Error(`Failed to append activity log entry: ${detail}`, { cause: error2 });
35259
35226
  }
35260
35227
  }
35261
35228
 
@@ -35295,9 +35262,7 @@ function pickOptionalPositiveInt(config2, key, options) {
35295
35262
  }
35296
35263
  return value;
35297
35264
  }
35298
- async function loadConfig(cwd, deps) {
35299
- const fs3 = deps?.fs ?? fsPromises9;
35300
- const configDir = path14.join(cwd, ".agents", "poe-code-ralph");
35265
+ async function loadSingleConfig(configDir, fs3) {
35301
35266
  const yamlPath = path14.join(configDir, "config.yaml");
35302
35267
  const jsonPath = path14.join(configDir, "config.json");
35303
35268
  let raw = null;
@@ -35324,40 +35289,69 @@ async function loadConfig(cwd, deps) {
35324
35289
  }
35325
35290
  }
35326
35291
  if (raw == null || format == null || sourcePath == null) {
35327
- return {};
35292
+ return null;
35328
35293
  }
35329
35294
  let parsed;
35330
35295
  try {
35331
35296
  parsed = format === "yaml" ? YAML.parse(raw) : JSON.parse(raw);
35332
35297
  } catch (error2) {
35333
35298
  const detail = error2 instanceof Error ? error2.message : String(error2);
35334
- throw new Error(`Invalid Ralph config ${format.toUpperCase()} at ${sourcePath}: ${detail}`);
35299
+ throw new Error(`Invalid Ralph config ${format.toUpperCase()} at ${sourcePath}: ${detail}`, { cause: error2 });
35335
35300
  }
35336
35301
  if (!isPlainObject2(parsed)) {
35337
35302
  throw new Error(`Invalid Ralph config at ${sourcePath}: expected an object.`);
35338
35303
  }
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;
35304
+ const rawConfig = parsed;
35305
+ const config2 = {};
35306
+ const planPath = pickOptionalString(rawConfig, "planPath");
35307
+ if (planPath) config2.planPath = planPath;
35308
+ const progressPath = pickOptionalString(rawConfig, "progressPath");
35309
+ if (progressPath) config2.progressPath = progressPath;
35310
+ const guardrailsPath = pickOptionalString(rawConfig, "guardrailsPath");
35311
+ if (guardrailsPath) config2.guardrailsPath = guardrailsPath;
35312
+ const errorsLogPath = pickOptionalString(rawConfig, "errorsLogPath");
35313
+ if (errorsLogPath) config2.errorsLogPath = errorsLogPath;
35314
+ const activityLogPath = pickOptionalString(rawConfig, "activityLogPath");
35315
+ if (activityLogPath) config2.activityLogPath = activityLogPath;
35316
+ const agent = pickOptionalString(rawConfig, "agent");
35317
+ if (agent) config2.agent = agent;
35318
+ const maxIterations = pickOptionalPositiveInt(rawConfig, "maxIterations", { min: 1 });
35319
+ if (maxIterations != null) config2.maxIterations = maxIterations;
35320
+ const staleSeconds = pickOptionalPositiveInt(rawConfig, "staleSeconds", { min: 0 });
35321
+ if (staleSeconds != null) config2.staleSeconds = staleSeconds;
35322
+ const noCommit = pickOptionalBoolean(rawConfig, "noCommit");
35323
+ if (noCommit != null) config2.noCommit = noCommit;
35324
+ return { config: config2, sourcePath };
35325
+ }
35326
+ function mergeConfigs(base, override) {
35327
+ const result = { ...base };
35328
+ for (const key of Object.keys(override)) {
35329
+ if (override[key] !== void 0) {
35330
+ result[key] = override[key];
35331
+ }
35332
+ }
35359
35333
  return result;
35360
35334
  }
35335
+ async function loadConfig(cwd, deps) {
35336
+ const fs3 = deps?.fs ?? fsPromises9;
35337
+ const sources = [];
35338
+ let merged = {};
35339
+ if (deps?.homeDir) {
35340
+ const globalDir = path14.join(deps.homeDir, ".poe-code", "ralph");
35341
+ const globalResult = await loadSingleConfig(globalDir, fs3);
35342
+ if (globalResult) {
35343
+ merged = globalResult.config;
35344
+ sources.push({ path: globalResult.sourcePath, scope: "global" });
35345
+ }
35346
+ }
35347
+ const localDir = path14.join(cwd, ".agents", "poe-code-ralph");
35348
+ const localResult = await loadSingleConfig(localDir, fs3);
35349
+ if (localResult) {
35350
+ merged = mergeConfigs(merged, localResult.config);
35351
+ sources.push({ path: localResult.sourcePath, scope: "local" });
35352
+ }
35353
+ return { config: merged, sources };
35354
+ }
35361
35355
 
35362
35356
  // packages/ralph/src/index.ts
35363
35357
  async function ralphBuild(options) {
@@ -35445,6 +35439,8 @@ var templateImports2 = {
35445
35439
  skillPlan: () => Promise.resolve().then(() => __toESM(require_SKILL_plan(), 1)),
35446
35440
  promptPlan: () => Promise.resolve().then(() => __toESM(require_PROMPT_plan(), 1)),
35447
35441
  promptBuild: () => Promise.resolve().then(() => __toESM(require_PROMPT_build(), 1)),
35442
+ refGuardrails: () => Promise.resolve().then(() => __toESM(require_GUARDRAILS(), 1)),
35443
+ refContextEngineering: () => Promise.resolve().then(() => __toESM(require_CONTEXT_ENGINEERING(), 1)),
35448
35444
  stateProgress: () => Promise.resolve().then(() => __toESM(require_progress(), 1)),
35449
35445
  stateGuardrails: () => Promise.resolve().then(() => __toESM(require_guardrails(), 1)),
35450
35446
  stateErrors: () => Promise.resolve().then(() => __toESM(require_errors3(), 1)),
@@ -35456,6 +35452,8 @@ async function loadRalphTemplates() {
35456
35452
  skillPlan,
35457
35453
  promptPlan,
35458
35454
  promptBuild,
35455
+ refGuardrails,
35456
+ refContextEngineering,
35459
35457
  stateProgress,
35460
35458
  stateGuardrails,
35461
35459
  stateErrors,
@@ -35465,6 +35463,8 @@ async function loadRalphTemplates() {
35465
35463
  templateImports2.skillPlan(),
35466
35464
  templateImports2.promptPlan(),
35467
35465
  templateImports2.promptBuild(),
35466
+ templateImports2.refGuardrails(),
35467
+ templateImports2.refContextEngineering(),
35468
35468
  templateImports2.stateProgress(),
35469
35469
  templateImports2.stateGuardrails(),
35470
35470
  templateImports2.stateErrors(),
@@ -35475,6 +35475,8 @@ async function loadRalphTemplates() {
35475
35475
  skillPlan: skillPlan.default,
35476
35476
  promptPlan: promptPlan.default,
35477
35477
  promptBuild: promptBuild.default,
35478
+ refGuardrails: refGuardrails.default,
35479
+ refContextEngineering: refContextEngineering.default,
35478
35480
  stateProgress: stateProgress.default,
35479
35481
  stateGuardrails: stateGuardrails.default,
35480
35482
  stateErrors: stateErrors.default,
@@ -35567,6 +35569,16 @@ async function installRalphTemplates(args) {
35567
35569
  targetPath: path16.join(cwd, ".agents", "poe-code-ralph", "PROMPT_build.md"),
35568
35570
  displayPath: ".agents/poe-code-ralph/PROMPT_build.md",
35569
35571
  contents: templates.promptBuild
35572
+ },
35573
+ {
35574
+ targetPath: path16.join(cwd, ".agents", "poe-code-ralph", "references", "GUARDRAILS.md"),
35575
+ displayPath: ".agents/poe-code-ralph/references/GUARDRAILS.md",
35576
+ contents: templates.refGuardrails
35577
+ },
35578
+ {
35579
+ targetPath: path16.join(cwd, ".agents", "poe-code-ralph", "references", "CONTEXT_ENGINEERING.md"),
35580
+ displayPath: ".agents/poe-code-ralph/references/CONTEXT_ENGINEERING.md",
35581
+ contents: templates.refContextEngineering
35570
35582
  }
35571
35583
  ];
35572
35584
  for (const entry of templateWrites) {
@@ -35634,8 +35646,11 @@ function registerRalphCommand(program, container) {
35634
35646
  }
35635
35647
  let configActivityLogPath;
35636
35648
  try {
35637
- const config2 = await loadConfig(container.env.cwd, { fs: container.fs });
35638
- configActivityLogPath = config2.activityLogPath;
35649
+ const configResult = await loadConfig(container.env.cwd, {
35650
+ fs: container.fs,
35651
+ homeDir: container.env.homeDir
35652
+ });
35653
+ configActivityLogPath = configResult.config.activityLogPath;
35639
35654
  } catch (error2) {
35640
35655
  const message2 = error2 instanceof Error ? error2.message : String(error2);
35641
35656
  throw new ValidationError(message2);
@@ -35740,13 +35755,36 @@ function registerRalphCommand(program, container) {
35740
35755
  "ralph build does not support --dry-run. Use --no-commit instead."
35741
35756
  );
35742
35757
  }
35743
- let config2;
35758
+ let configResult;
35744
35759
  try {
35745
- config2 = await loadConfig(container.env.cwd, { fs: container.fs });
35760
+ configResult = await loadConfig(container.env.cwd, {
35761
+ fs: container.fs,
35762
+ homeDir: container.env.homeDir
35763
+ });
35746
35764
  } catch (error2) {
35747
35765
  const message = error2 instanceof Error ? error2.message : String(error2);
35748
35766
  throw new ValidationError(message);
35749
35767
  }
35768
+ const config2 = configResult.config;
35769
+ const maxIterations = typeof iterations === "string" ? resolveIterations(iterations) : config2.maxIterations ?? 25;
35770
+ const agent2 = options.agent?.trim() ? options.agent.trim() : config2.agent ?? "codex";
35771
+ const noCommit = options.commit === false ? true : config2.noCommit ?? false;
35772
+ const staleSeconds = config2.staleSeconds ?? 60;
35773
+ const maxFailures = typeof options.maxFailures === "string" ? resolveMaxFailures(options.maxFailures) : void 0;
35774
+ const pauseOnOverbake = Boolean(options.pauseOnOverbake);
35775
+ const worktreeEnabled = Boolean(options.worktree);
35776
+ const worktree = worktreeEnabled ? { enabled: true, name: options.worktreeName?.trim() || void 0 } : void 0;
35777
+ const cwd = container.env.cwd;
35778
+ const configLines = [
35779
+ `Agent: ${agent2}`,
35780
+ `Iterations: ${maxIterations}`
35781
+ ];
35782
+ if (noCommit) configLines.push("No-commit: true");
35783
+ if (worktree) configLines.push(`Worktree: ${worktree.name ?? "(auto)"}`);
35784
+ for (const source of configResult.sources) {
35785
+ configLines.push(`${source.scope}: ${source.path}`);
35786
+ }
35787
+ resources.logger.resolved("Config", configLines.join("\n "));
35750
35788
  let planPath;
35751
35789
  try {
35752
35790
  planPath = await resolvePlanPath({
@@ -35761,20 +35799,6 @@ function registerRalphCommand(program, container) {
35761
35799
  if (!planPath) {
35762
35800
  return;
35763
35801
  }
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
35802
  try {
35779
35803
  const planContent = await container.fs.readFile(
35780
35804
  path16.resolve(cwd, planPath),
@@ -35785,7 +35809,7 @@ function registerRalphCommand(program, container) {
35785
35809
  const done = plan.stories.filter((s) => s.status === "done").length;
35786
35810
  const inProgress = plan.stories.filter((s) => s.status === "in_progress").length;
35787
35811
  const open = total - done - inProgress;
35788
- resources.logger.info(`Stories: ${done}/${total} done${inProgress ? `, ${inProgress} in progress` : ""}${open ? `, ${open} open` : ""}`);
35812
+ resources.logger.resolved("Stories", `${done}/${total} done${inProgress ? `, ${inProgress} in progress` : ""}${open ? `, ${open} open` : ""}`);
35789
35813
  } catch {
35790
35814
  }
35791
35815
  await ralphBuild({
@@ -36201,7 +36225,7 @@ function registerModelsCommand(program, container) {
36201
36225
  // package.json
36202
36226
  var package_default = {
36203
36227
  name: "poe-code",
36204
- version: "3.0.31",
36228
+ version: "3.0.33",
36205
36229
  description: "CLI tool to configure Poe API for developer workflows.",
36206
36230
  type: "module",
36207
36231
  workspaces: [
@@ -36244,7 +36268,10 @@ var package_default = {
36244
36268
  "e2e:logs:rotate": "tsx packages/e2e-docker-test-runner/scripts/logs.ts --rotate",
36245
36269
  "e2e:cache:clear": "rm -rf ~/.cache/poe-e2e",
36246
36270
  prepack: "npm run build",
36247
- prepare: "husky"
36271
+ prepare: "husky",
36272
+ "install-local-package": "npm pack --pack-destination /tmp && npm install -g /tmp/poe-code-*.tgz && rm /tmp/poe-code-*.tgz",
36273
+ smoke: "tsx scripts/smoke-test.ts",
36274
+ "smoke:verbose": "tsx scripts/smoke-test.ts --verbose"
36248
36275
  },
36249
36276
  bin: {
36250
36277
  "poe-code": "dist/bin.cjs",
@@ -36252,7 +36279,7 @@ var package_default = {
36252
36279
  "poe-claude": "dist/bin/poe-claude.js",
36253
36280
  "poe-codex": "dist/bin/poe-codex.js",
36254
36281
  "poe-opencode": "dist/bin/poe-opencode.js",
36255
- "tiny-mcp-test-server": "packages/tiny-mcp-test-server/dist/index.js"
36282
+ "tiny-stdio-mcp-test-server": "packages/tiny-stdio-mcp-test-server/dist/cli.js"
36256
36283
  },
36257
36284
  files: [
36258
36285
  "dist"
@@ -36262,41 +36289,41 @@ var package_default = {
36262
36289
  },
36263
36290
  packageManager: "npm@10.9.2",
36264
36291
  dependencies: {
36265
- "@clack/prompts": "^0.11.0",
36266
- chalk: "^5.3.0",
36267
- commander: "^11.1.0",
36292
+ "@clack/prompts": "^1.0.0",
36293
+ chalk: "^5.6.2",
36294
+ commander: "^14.0.3",
36268
36295
  "console-table-printer": "^2.15.0",
36269
36296
  diff: ">=8.0.3",
36270
36297
  "jsonc-parser": "^3.3.1",
36271
36298
  mustache: "^4.2.0",
36272
- semver: "^7.7.3",
36273
- "smol-toml": "^1.3.0",
36299
+ semver: "^7.7.4",
36300
+ "smol-toml": "^1.6.0",
36274
36301
  yaml: "^2.8.2"
36275
36302
  },
36276
36303
  devDependencies: {
36277
- "@modelcontextprotocol/sdk": "^1.25.3",
36304
+ "@eslint/js": "^9.0.0",
36305
+ "@modelcontextprotocol/sdk": "^1.26.0",
36278
36306
  "@poe-code/agent-spawn": "*",
36279
36307
  "@poe-code/config-mutations": "*",
36280
36308
  "@poe-code/design-system": "*",
36281
36309
  "@poe-code/e2e-docker-test-runner": "*",
36282
36310
  "@poe-code/freeze-cli": "*",
36283
- "@poe-code/tiny-mcp-server": "*",
36284
- "@poe-code/tiny-mcp-test-server": "*",
36311
+ "tiny-stdio-mcp-server": "*",
36312
+ "tiny-stdio-mcp-test-server": "*",
36285
36313
  "@types/mustache": "^4.2.6",
36286
- "@types/node": "^20.12.7",
36314
+ "@types/node": "^25.2.2",
36287
36315
  "@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",
36316
+ eslint: "^9.0.0",
36317
+ "eslint-config-prettier": "^10.1.8",
36318
+ globals: "^17.3.0",
36292
36319
  husky: "^9.1.7",
36293
- memfs: "^4.7.3",
36294
- prettier: "^3.3.3",
36320
+ memfs: "^4.56.10",
36321
+ prettier: "^3.8.1",
36295
36322
  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"
36323
+ turbo: "^2.8.3",
36324
+ typescript: "^5.9.3",
36325
+ "typescript-eslint": "^8.54.0",
36326
+ vitest: "^4.0.18"
36300
36327
  },
36301
36328
  repository: {
36302
36329
  type: "git",
@@ -36523,7 +36550,7 @@ function bootstrapProgram(container) {
36523
36550
  registerRalphCommand(program, container);
36524
36551
  registerUsageCommand(program, container);
36525
36552
  registerModelsCommand(program, container);
36526
- program.action(function() {
36553
+ program.allowExcessArguments().action(function() {
36527
36554
  const args = this.args;
36528
36555
  if (args.length > 0) {
36529
36556
  throwCommandNotFound({