From 5589889631e2713edc701dbcb30b15ee31947505 Mon Sep 17 00:00:00 2001 From: KeyInfo Bot Date: Wed, 23 Sep 2026 00:44:17 +0800 Subject: [PATCH] Sync third-party and MCP marketplace plugins Constraint: Public skills are published only by explicit administrator action unless they are tracked third-party market sources. Confidence: high Scope-risk: narrow Directive: Keep private/internal skills out of the public marketplace and preserve normal incremental market Git history. Tested: Marketplace validation passed. --- .agents/plugins/marketplace.json | 36 - config/external-sources.json | 121 +- config/external-sources.lock.json | 37 +- .../codex/.agents/plugins/marketplace.json | 36 - .../.codex-plugin/plugin.json | 2 +- .../THIRD_PARTY_SOURCE.json | 6 +- .../skills/artemis-automation/SKILL.md | 2 +- .../plugins/mcp-playwright/MCP_SOURCE.json | 2 +- .../next-skills/THIRD_PARTY_SOURCE.json | 4 +- plugins/codex/plugins/oh-my-codex/.app.json | 3 - .../oh-my-codex/.codex-plugin/plugin.json | 44 - plugins/codex/plugins/oh-my-codex/.mcp.json | 52 - .../oh-my-codex/THIRD_PARTY_SOURCE.json | 9 - .../oh-my-codex/hooks/codex-native-hook.mjs | 657 ---------- .../plugins/oh-my-codex/hooks/hooks.json | 76 -- .../skills/ai-slop-cleaner/SKILL.md | 72 -- .../oh-my-codex/skills/analyze/SKILL.md | 70 - .../plugins/oh-my-codex/skills/ask/SKILL.md | 58 - .../oh-my-codex/skills/autopilot/SKILL.md | 128 -- .../oh-my-codex/skills/autoresearch/SKILL.md | 72 -- .../skills/best-practice-research/SKILL.md | 88 -- .../oh-my-codex/skills/cancel/SKILL.md | 40 - .../oh-my-codex/skills/code-review/SKILL.md | 118 -- .../skills/configure-notifications/SKILL.md | 101 -- .../skills/deep-interview/SKILL.md | 571 -------- .../oh-my-codex/skills/design/SKILL.md | 80 -- .../oh-my-codex/skills/doctor/SKILL.md | 118 -- .../plugins/oh-my-codex/skills/hud/SKILL.md | 98 -- .../oh-my-codex/skills/omx-setup/SKILL.md | 59 - .../skills/performance-goal/SKILL.md | 68 - .../plugins/oh-my-codex/skills/plan/SKILL.md | 94 -- .../oh-my-codex/skills/ralplan/SKILL.md | 149 --- .../plugins/oh-my-codex/skills/team/SKILL.md | 54 - .../oh-my-codex/skills/ultragoal/SKILL.md | 77 -- .../oh-my-codex/skills/ultraqa/SKILL.md | 86 -- .../oh-my-codex/skills/visual-ralph/SKILL.md | 83 -- .../plugins/oh-my-codex/skills/wiki/SKILL.md | 57 - .../oh-my-codex/skills/worker/SKILL.md | 47 - .../superpowers-zh/.codex-plugin/plugin.json | 48 - .../plugins/superpowers-zh/CODE_OF_CONDUCT.md | 128 -- plugins/codex/plugins/superpowers-zh/LICENSE | 21 - .../codex/plugins/superpowers-zh/README.md | 378 ------ .../superpowers-zh/THIRD_PARTY_SOURCE.json | 9 - .../superpowers-zh/assets/app-icon.png | Bin 48231 -> 0 bytes .../assets/superpowers-small.svg | 1 - .../skills/brainstorming/SKILL.md | 203 --- .../brainstorming/scripts/frame-template.html | 213 --- .../skills/brainstorming/scripts/helper.js | 167 --- .../skills/brainstorming/scripts/server.cjs | 723 ----------- .../brainstorming/scripts/start-server.sh | 209 --- .../brainstorming/scripts/stop-server.sh | 120 -- .../spec-document-reviewer-prompt.md | 48 - .../skills/brainstorming/visual-companion.md | 288 ----- .../dispatching-parallel-agents/SKILL.md | 167 --- .../skills/executing-plans/SKILL.md | 64 - .../finishing-a-development-branch/SKILL.md | 213 --- .../skills/receiving-code-review/SKILL.md | 203 --- .../skills/requesting-code-review/SKILL.md | 95 -- .../requesting-code-review/code-reviewer.md | 174 --- .../subagent-driven-development/SKILL.md | 403 ------ .../implementer-prompt.md | 141 -- .../re-review-prompt.md | 101 -- .../scripts/review-package | 46 - .../scripts/sdd-workspace | 40 - .../scripts/task-brief | 41 - .../task-reviewer-prompt.md | 163 --- .../systematic-debugging/CREATION-LOG.md | 119 -- .../skills/systematic-debugging/SKILL.md | 278 ---- .../condition-based-waiting-example.ts | 158 --- .../condition-based-waiting.md | 113 -- .../systematic-debugging/defense-in-depth.md | 121 -- .../systematic-debugging/find-polluter.sh | 72 -- .../root-cause-tracing.md | 167 --- .../systematic-debugging/test-academic.md | 14 - .../systematic-debugging/test-pressure-1.md | 58 - .../systematic-debugging/test-pressure-2.md | 68 - .../systematic-debugging/test-pressure-3.md | 69 - .../skills/test-driven-development/SKILL.md | 320 ----- .../writing-good-tests.md | 173 --- .../skills/using-git-worktrees/SKILL.md | 174 --- .../skills/using-superpowers/SKILL.md | 64 - .../references/antigravity-tools.md | 23 - .../references/codex-tools.md | 59 - .../references/gemini-tools.md | 63 - .../references/hermes-tools.md | 54 - .../using-superpowers/references/pi-tools.md | 16 - .../verification-before-completion/SKILL.md | 118 -- .../skills/writing-plans/SKILL.md | 160 --- .../plan-document-reviewer-prompt.md | 49 - .../skills/writing-skills/SKILL.md | 665 ---------- .../anthropic-best-practices.md | 1129 ---------------- .../examples/CLAUDE_MD_TESTING.md | 187 --- .../writing-skills/graphviz-conventions.dot | 172 --- .../writing-skills/persuasion-principles.md | 186 --- .../skills/writing-skills/render-graphs.js | 169 --- .../testing-skills-with-subagents.md | 380 ------ .../superpowers/.codex-plugin/plugin.json | 48 - .../plugins/superpowers/CODE_OF_CONDUCT.md | 130 -- plugins/codex/plugins/superpowers/LICENSE | 21 - plugins/codex/plugins/superpowers/README.md | 399 ------ .../superpowers/THIRD_PARTY_SOURCE.json | 9 - .../plugins/superpowers/assets/app-icon.png | Bin 48231 -> 0 bytes .../superpowers/assets/superpowers-small.svg | 1 - .../superpowers/skills/brainstorming/SKILL.md | 285 ---- .../brainstorming/scripts/frame-template.html | 213 --- .../skills/brainstorming/scripts/helper.js | 167 --- .../skills/brainstorming/scripts/server.cjs | 723 ----------- .../brainstorming/scripts/start-server.sh | 209 --- .../brainstorming/scripts/stop-server.sh | 120 -- .../spec-document-reviewer-prompt.md | 49 - .../skills/brainstorming/visual-companion.md | 299 ----- .../skills/diagnosing-superpowers/SKILL.md | 120 -- .../prompts/analyst-common.md | 38 - .../prompts/cost-and-time.md | 28 - .../prompts/plan-adherence.md | 29 - .../prompts/quality-evidence.md | 26 - .../prompts/repeated-work.md | 30 - .../prompts/request-conflicts.md | 20 - .../prompts/scrub-audit.md | 33 - .../diagnosing-superpowers/prompts/scrub.md | 29 - .../prompts/similar-session.md | 38 - .../prompts/skill-timeline.md | 30 - .../prompts/stumbles.md | 28 - .../references/context-safety.md | 22 - .../references/github-issues.md | 47 - .../references/redaction-policy.md | 34 - .../references/session-discovery.md | 31 - .../templates/bundle-README.md | 77 -- .../diagnosing-superpowers/templates/case.md | 64 - .../diagnosing-superpowers/templates/issue.md | 51 - .../templates/report.md | 82 -- .../dispatching-parallel-agents/SKILL.md | 167 --- .../skills/executing-plans/SKILL.md | 373 ------ .../skills/executing-plans/scripts/task-done | 52 - .../skills/executing-plans/scripts/task-start | 28 - .../finishing-a-development-branch/SKILL.md | 225 ---- .../skills/receiving-code-review/SKILL.md | 205 --- .../skills/requesting-code-review/SKILL.md | 95 -- .../requesting-code-review/code-reviewer.md | 198 --- .../subagent-driven-development/SKILL.md | 568 -------- .../implementer-prompt.md | 154 --- .../re-review-prompt.md | 115 -- .../scripts/review-package | 53 - .../scripts/sdd-workspace | 82 -- .../scripts/task-brief | 43 - .../task-reviewer-prompt.md | 207 --- .../systematic-debugging/CREATION-LOG.md | 119 -- .../skills/systematic-debugging/SKILL.md | 283 ---- .../condition-based-waiting-example.ts | 158 --- .../condition-based-waiting.md | 115 -- .../systematic-debugging/defense-in-depth.md | 122 -- .../systematic-debugging/find-polluter.sh | 72 -- .../root-cause-tracing.md | 169 --- .../systematic-debugging/test-academic.md | 14 - .../systematic-debugging/test-pressure-1.md | 58 - .../systematic-debugging/test-pressure-2.md | 68 - .../systematic-debugging/test-pressure-3.md | 69 - .../skills/test-driven-development/SKILL.md | 330 ----- .../writing-good-tests.md | 198 --- .../skills/using-git-worktrees/SKILL.md | 167 --- .../skills/using-superpowers/SKILL.md | 65 - .../references/antigravity-tools.md | 23 - .../references/claude-code-tools.md | 29 - .../references/codex-tools.md | 108 -- .../references/gemini-tools.md | 63 - .../references/hermes-tools.md | 56 - .../references/muse-tools.md | 35 - .../using-superpowers/references/pi-tools.md | 16 - .../verification-before-completion/SKILL.md | 120 -- .../superpowers/skills/writing-plans/SKILL.md | 192 --- .../plan-document-reviewer-prompt.md | 49 - .../skills/writing-skills/SKILL.md | 681 ---------- .../anthropic-best-practices.md | 1150 ----------------- .../examples/CLAUDE_MD_TESTING.md | 189 --- .../writing-skills/graphviz-conventions.dot | 172 --- .../writing-skills/persuasion-principles.md | 187 --- .../skills/writing-skills/render-graphs.js | 169 --- .../testing-skills-with-subagents.md | 384 ------ scripts/sync_external_plugins.ps1 | 47 +- 179 files changed, 51 insertions(+), 25001 deletions(-) delete mode 100644 plugins/codex/plugins/oh-my-codex/.app.json delete mode 100644 plugins/codex/plugins/oh-my-codex/.codex-plugin/plugin.json delete mode 100644 plugins/codex/plugins/oh-my-codex/.mcp.json delete mode 100644 plugins/codex/plugins/oh-my-codex/THIRD_PARTY_SOURCE.json delete mode 100644 plugins/codex/plugins/oh-my-codex/hooks/codex-native-hook.mjs delete mode 100644 plugins/codex/plugins/oh-my-codex/hooks/hooks.json delete mode 100644 plugins/codex/plugins/oh-my-codex/skills/ai-slop-cleaner/SKILL.md delete mode 100644 plugins/codex/plugins/oh-my-codex/skills/analyze/SKILL.md delete mode 100644 plugins/codex/plugins/oh-my-codex/skills/ask/SKILL.md delete mode 100644 plugins/codex/plugins/oh-my-codex/skills/autopilot/SKILL.md delete mode 100644 plugins/codex/plugins/oh-my-codex/skills/autoresearch/SKILL.md delete mode 100644 plugins/codex/plugins/oh-my-codex/skills/best-practice-research/SKILL.md delete mode 100644 plugins/codex/plugins/oh-my-codex/skills/cancel/SKILL.md delete mode 100644 plugins/codex/plugins/oh-my-codex/skills/code-review/SKILL.md delete mode 100644 plugins/codex/plugins/oh-my-codex/skills/configure-notifications/SKILL.md delete mode 100644 plugins/codex/plugins/oh-my-codex/skills/deep-interview/SKILL.md delete mode 100644 plugins/codex/plugins/oh-my-codex/skills/design/SKILL.md delete mode 100644 plugins/codex/plugins/oh-my-codex/skills/doctor/SKILL.md delete mode 100644 plugins/codex/plugins/oh-my-codex/skills/hud/SKILL.md delete mode 100644 plugins/codex/plugins/oh-my-codex/skills/omx-setup/SKILL.md delete mode 100644 plugins/codex/plugins/oh-my-codex/skills/performance-goal/SKILL.md delete mode 100644 plugins/codex/plugins/oh-my-codex/skills/plan/SKILL.md delete mode 100644 plugins/codex/plugins/oh-my-codex/skills/ralplan/SKILL.md delete mode 100644 plugins/codex/plugins/oh-my-codex/skills/team/SKILL.md delete mode 100644 plugins/codex/plugins/oh-my-codex/skills/ultragoal/SKILL.md delete mode 100644 plugins/codex/plugins/oh-my-codex/skills/ultraqa/SKILL.md delete mode 100644 plugins/codex/plugins/oh-my-codex/skills/visual-ralph/SKILL.md delete mode 100644 plugins/codex/plugins/oh-my-codex/skills/wiki/SKILL.md delete mode 100644 plugins/codex/plugins/oh-my-codex/skills/worker/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers-zh/.codex-plugin/plugin.json delete mode 100644 plugins/codex/plugins/superpowers-zh/CODE_OF_CONDUCT.md delete mode 100644 plugins/codex/plugins/superpowers-zh/LICENSE delete mode 100644 plugins/codex/plugins/superpowers-zh/README.md delete mode 100644 plugins/codex/plugins/superpowers-zh/THIRD_PARTY_SOURCE.json delete mode 100644 plugins/codex/plugins/superpowers-zh/assets/app-icon.png delete mode 100644 plugins/codex/plugins/superpowers-zh/assets/superpowers-small.svg delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/brainstorming/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/brainstorming/scripts/frame-template.html delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/brainstorming/scripts/helper.js delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/brainstorming/scripts/server.cjs delete mode 100755 plugins/codex/plugins/superpowers-zh/skills/brainstorming/scripts/start-server.sh delete mode 100755 plugins/codex/plugins/superpowers-zh/skills/brainstorming/scripts/stop-server.sh delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/brainstorming/spec-document-reviewer-prompt.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/brainstorming/visual-companion.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/dispatching-parallel-agents/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/executing-plans/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/finishing-a-development-branch/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/receiving-code-review/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/requesting-code-review/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/requesting-code-review/code-reviewer.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/subagent-driven-development/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/subagent-driven-development/implementer-prompt.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/subagent-driven-development/re-review-prompt.md delete mode 100755 plugins/codex/plugins/superpowers-zh/skills/subagent-driven-development/scripts/review-package delete mode 100755 plugins/codex/plugins/superpowers-zh/skills/subagent-driven-development/scripts/sdd-workspace delete mode 100755 plugins/codex/plugins/superpowers-zh/skills/subagent-driven-development/scripts/task-brief delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/subagent-driven-development/task-reviewer-prompt.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/systematic-debugging/CREATION-LOG.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/systematic-debugging/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/systematic-debugging/condition-based-waiting-example.ts delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/systematic-debugging/condition-based-waiting.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/systematic-debugging/defense-in-depth.md delete mode 100755 plugins/codex/plugins/superpowers-zh/skills/systematic-debugging/find-polluter.sh delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/systematic-debugging/root-cause-tracing.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/systematic-debugging/test-academic.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/systematic-debugging/test-pressure-1.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/systematic-debugging/test-pressure-2.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/systematic-debugging/test-pressure-3.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/test-driven-development/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/test-driven-development/writing-good-tests.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/using-git-worktrees/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/using-superpowers/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/using-superpowers/references/antigravity-tools.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/using-superpowers/references/codex-tools.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/using-superpowers/references/gemini-tools.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/using-superpowers/references/hermes-tools.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/using-superpowers/references/pi-tools.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/verification-before-completion/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/writing-plans/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/writing-plans/plan-document-reviewer-prompt.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/writing-skills/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/writing-skills/anthropic-best-practices.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/writing-skills/examples/CLAUDE_MD_TESTING.md delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/writing-skills/graphviz-conventions.dot delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/writing-skills/persuasion-principles.md delete mode 100755 plugins/codex/plugins/superpowers-zh/skills/writing-skills/render-graphs.js delete mode 100644 plugins/codex/plugins/superpowers-zh/skills/writing-skills/testing-skills-with-subagents.md delete mode 100644 plugins/codex/plugins/superpowers/.codex-plugin/plugin.json delete mode 100644 plugins/codex/plugins/superpowers/CODE_OF_CONDUCT.md delete mode 100644 plugins/codex/plugins/superpowers/LICENSE delete mode 100644 plugins/codex/plugins/superpowers/README.md delete mode 100644 plugins/codex/plugins/superpowers/THIRD_PARTY_SOURCE.json delete mode 100644 plugins/codex/plugins/superpowers/assets/app-icon.png delete mode 100644 plugins/codex/plugins/superpowers/assets/superpowers-small.svg delete mode 100644 plugins/codex/plugins/superpowers/skills/brainstorming/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers/skills/brainstorming/scripts/frame-template.html delete mode 100644 plugins/codex/plugins/superpowers/skills/brainstorming/scripts/helper.js delete mode 100644 plugins/codex/plugins/superpowers/skills/brainstorming/scripts/server.cjs delete mode 100755 plugins/codex/plugins/superpowers/skills/brainstorming/scripts/start-server.sh delete mode 100755 plugins/codex/plugins/superpowers/skills/brainstorming/scripts/stop-server.sh delete mode 100644 plugins/codex/plugins/superpowers/skills/brainstorming/spec-document-reviewer-prompt.md delete mode 100644 plugins/codex/plugins/superpowers/skills/brainstorming/visual-companion.md delete mode 100644 plugins/codex/plugins/superpowers/skills/diagnosing-superpowers/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers/skills/diagnosing-superpowers/prompts/analyst-common.md delete mode 100644 plugins/codex/plugins/superpowers/skills/diagnosing-superpowers/prompts/cost-and-time.md delete mode 100644 plugins/codex/plugins/superpowers/skills/diagnosing-superpowers/prompts/plan-adherence.md delete mode 100644 plugins/codex/plugins/superpowers/skills/diagnosing-superpowers/prompts/quality-evidence.md delete mode 100644 plugins/codex/plugins/superpowers/skills/diagnosing-superpowers/prompts/repeated-work.md delete mode 100644 plugins/codex/plugins/superpowers/skills/diagnosing-superpowers/prompts/request-conflicts.md delete mode 100644 plugins/codex/plugins/superpowers/skills/diagnosing-superpowers/prompts/scrub-audit.md delete mode 100644 plugins/codex/plugins/superpowers/skills/diagnosing-superpowers/prompts/scrub.md delete mode 100644 plugins/codex/plugins/superpowers/skills/diagnosing-superpowers/prompts/similar-session.md delete mode 100644 plugins/codex/plugins/superpowers/skills/diagnosing-superpowers/prompts/skill-timeline.md delete mode 100644 plugins/codex/plugins/superpowers/skills/diagnosing-superpowers/prompts/stumbles.md delete mode 100644 plugins/codex/plugins/superpowers/skills/diagnosing-superpowers/references/context-safety.md delete mode 100644 plugins/codex/plugins/superpowers/skills/diagnosing-superpowers/references/github-issues.md delete mode 100644 plugins/codex/plugins/superpowers/skills/diagnosing-superpowers/references/redaction-policy.md delete mode 100644 plugins/codex/plugins/superpowers/skills/diagnosing-superpowers/references/session-discovery.md delete mode 100644 plugins/codex/plugins/superpowers/skills/diagnosing-superpowers/templates/bundle-README.md delete mode 100644 plugins/codex/plugins/superpowers/skills/diagnosing-superpowers/templates/case.md delete mode 100644 plugins/codex/plugins/superpowers/skills/diagnosing-superpowers/templates/issue.md delete mode 100644 plugins/codex/plugins/superpowers/skills/diagnosing-superpowers/templates/report.md delete mode 100644 plugins/codex/plugins/superpowers/skills/dispatching-parallel-agents/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers/skills/executing-plans/SKILL.md delete mode 100755 plugins/codex/plugins/superpowers/skills/executing-plans/scripts/task-done delete mode 100755 plugins/codex/plugins/superpowers/skills/executing-plans/scripts/task-start delete mode 100644 plugins/codex/plugins/superpowers/skills/finishing-a-development-branch/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers/skills/receiving-code-review/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers/skills/requesting-code-review/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers/skills/requesting-code-review/code-reviewer.md delete mode 100644 plugins/codex/plugins/superpowers/skills/subagent-driven-development/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers/skills/subagent-driven-development/implementer-prompt.md delete mode 100644 plugins/codex/plugins/superpowers/skills/subagent-driven-development/re-review-prompt.md delete mode 100755 plugins/codex/plugins/superpowers/skills/subagent-driven-development/scripts/review-package delete mode 100755 plugins/codex/plugins/superpowers/skills/subagent-driven-development/scripts/sdd-workspace delete mode 100755 plugins/codex/plugins/superpowers/skills/subagent-driven-development/scripts/task-brief delete mode 100644 plugins/codex/plugins/superpowers/skills/subagent-driven-development/task-reviewer-prompt.md delete mode 100644 plugins/codex/plugins/superpowers/skills/systematic-debugging/CREATION-LOG.md delete mode 100644 plugins/codex/plugins/superpowers/skills/systematic-debugging/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers/skills/systematic-debugging/condition-based-waiting-example.ts delete mode 100644 plugins/codex/plugins/superpowers/skills/systematic-debugging/condition-based-waiting.md delete mode 100644 plugins/codex/plugins/superpowers/skills/systematic-debugging/defense-in-depth.md delete mode 100755 plugins/codex/plugins/superpowers/skills/systematic-debugging/find-polluter.sh delete mode 100644 plugins/codex/plugins/superpowers/skills/systematic-debugging/root-cause-tracing.md delete mode 100644 plugins/codex/plugins/superpowers/skills/systematic-debugging/test-academic.md delete mode 100644 plugins/codex/plugins/superpowers/skills/systematic-debugging/test-pressure-1.md delete mode 100644 plugins/codex/plugins/superpowers/skills/systematic-debugging/test-pressure-2.md delete mode 100644 plugins/codex/plugins/superpowers/skills/systematic-debugging/test-pressure-3.md delete mode 100644 plugins/codex/plugins/superpowers/skills/test-driven-development/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers/skills/test-driven-development/writing-good-tests.md delete mode 100644 plugins/codex/plugins/superpowers/skills/using-git-worktrees/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers/skills/using-superpowers/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers/skills/using-superpowers/references/antigravity-tools.md delete mode 100644 plugins/codex/plugins/superpowers/skills/using-superpowers/references/claude-code-tools.md delete mode 100644 plugins/codex/plugins/superpowers/skills/using-superpowers/references/codex-tools.md delete mode 100644 plugins/codex/plugins/superpowers/skills/using-superpowers/references/gemini-tools.md delete mode 100644 plugins/codex/plugins/superpowers/skills/using-superpowers/references/hermes-tools.md delete mode 100644 plugins/codex/plugins/superpowers/skills/using-superpowers/references/muse-tools.md delete mode 100644 plugins/codex/plugins/superpowers/skills/using-superpowers/references/pi-tools.md delete mode 100644 plugins/codex/plugins/superpowers/skills/verification-before-completion/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers/skills/writing-plans/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers/skills/writing-plans/plan-document-reviewer-prompt.md delete mode 100644 plugins/codex/plugins/superpowers/skills/writing-skills/SKILL.md delete mode 100644 plugins/codex/plugins/superpowers/skills/writing-skills/anthropic-best-practices.md delete mode 100644 plugins/codex/plugins/superpowers/skills/writing-skills/examples/CLAUDE_MD_TESTING.md delete mode 100644 plugins/codex/plugins/superpowers/skills/writing-skills/graphviz-conventions.dot delete mode 100644 plugins/codex/plugins/superpowers/skills/writing-skills/persuasion-principles.md delete mode 100755 plugins/codex/plugins/superpowers/skills/writing-skills/render-graphs.js delete mode 100644 plugins/codex/plugins/superpowers/skills/writing-skills/testing-skills-with-subagents.md diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json index 614995c3..b648de7a 100644 --- a/.agents/plugins/marketplace.json +++ b/.agents/plugins/marketplace.json @@ -292,18 +292,6 @@ }, "category": "Developer Tools" }, - { - "name": "oh-my-codex", - "source": { - "source": "local", - "path": "./plugins/codex/plugins/oh-my-codex" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, { "name": "shadcn", "source": { @@ -316,30 +304,6 @@ }, "category": "Developer Tools" }, - { - "name": "superpowers", - "source": { - "source": "local", - "path": "./plugins/codex/plugins/superpowers" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "superpowers-zh", - "source": { - "source": "local", - "path": "./plugins/codex/plugins/superpowers-zh" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, { "name": "updating-gitea-repositories", "source": { diff --git a/config/external-sources.json b/config/external-sources.json index 0868bb10..27867b45 100644 --- a/config/external-sources.json +++ b/config/external-sources.json @@ -27,122 +27,6 @@ } } }, - { - "id": "superpowers", - "repo": "https://github.com/obra/superpowers.git", - "ref": "main", - "adapter": "codex-plugin", - "pluginName": "superpowers", - "category": "Developer Tools", - "sourcePath": ".", - "include": [ - ".codex-plugin", - "skills", - "assets", - "README.md", - "LICENSE", - "CODE_OF_CONDUCT.md" - ], - "manifestOverrides": { - "description": "面向编码 Agent 的规划、TDD、调试、代码评审和交付工作流集合。", - "interface": { - "displayName": "Superpowers", - "shortDescription": "规划、TDD、调试、代码评审和交付工作流。", - "longDescription": "Superpowers 提供面向编码 Agent 的结构化工作流,覆盖需求澄清、计划编写、测试驱动开发、系统化调试、并行执行、代码评审和交付收尾。", - "category": "Developer Tools", - "defaultPrompt": [ - "使用 Superpowers 帮我规划这个开发任务。", - "使用 Superpowers 帮我系统调试这个问题。" - ] - } - }, - "skillDescriptions": { - "brainstorming": "在开始创造性工作前澄清意图、约束和可行方向;适合功能设计、组件设计和行为变更。", - "dispatching-parallel-agents": "将两个或更多互不依赖的子任务分派给并行 Agent,以提升吞吐并保持边界清晰。", - "executing-plans": "按已经写好的实现计划逐步执行,并在关键节点进行检查和复核。", - "finishing-a-development-branch": "实现完成且验证通过后,用于判断如何整理、提交和集成开发分支。", - "receiving-code-review": "处理代码评审反馈时使用,先理解反馈和技术依据,再决定是否修改。", - "requesting-code-review": "完成较大改动后请求代码评审,重点检查需求满足、风险、测试和回归。", - "subagent-driven-development": "按计划把独立实现任务交给子 Agent,并由主 Agent 负责整合和验证。", - "systematic-debugging": "遇到 bug、测试失败或异常行为时使用,通过复现、定位根因和验证修复来调试。", - "test-driven-development": "实现功能或修复缺陷前使用,先写失败测试,再实现最小改动并重构。", - "using-git-worktrees": "开始需要隔离的功能开发时使用,通过 git worktree 建立独立工作区。", - "using-superpowers": "开始对话时了解如何发现和调用 Superpowers 技能。", - "verification-before-completion": "在声称任务完成、修复成功或测试通过前使用,确保有实际验证证据。", - "writing-plans": "在动手实现前编写多步骤计划,明确目标、约束、文件范围和验证方式。", - "writing-skills": "创建、修改或验证 Agent skill 时使用,确保 skill 可触发、可维护、可测试。" - } - }, - { - "id": "superpowers-zh", - "repo": "https://github.com/AreChen/superpowers-zh.git", - "ref": "main", - "adapter": "codex-plugin", - "pluginName": "superpowers-zh", - "category": "Developer Tools", - "sourcePath": ".", - "include": [ - ".codex-plugin", - "skills", - "assets", - "README.md", - "LICENSE", - "CODE_OF_CONDUCT.md" - ], - "manifestOverrides": { - "name": "superpowers-zh", - "description": "Superpowers 的简体中文本地化版本,提供规划、TDD、系统化调试、代码评审和交付工作流。", - "interface": { - "displayName": "Superpowers-zh", - "shortDescription": "Superpowers 简体中文本地化版本。", - "longDescription": "Superpowers-zh 是官方 Superpowers 的简体中文本地化版本,保留规划、测试驱动开发、系统化调试、并行执行、代码评审和分支收尾工作流。", - "developerName": "AreChen / Jesse Vincent", - "category": "Developer Tools" - } - } - }, - { - "id": "oh-my-codex", - "repo": "https://github.com/Yeachan-Heo/oh-my-codex.git", - "ref": "main", - "adapter": "codex-plugin", - "pluginName": "oh-my-codex", - "category": "Developer Tools", - "sourcePath": "plugins/oh-my-codex", - "sparseCheckout": [ - "plugins/oh-my-codex" - ], - "include": [ - ".codex-plugin", - "skills", - "hooks", - ".mcp.json", - ".app.json" - ], - "manifestOverrides": { - "description": "oh-my-codex 是 Codex CLI 的多 Agent 编排、结构化工作流、插件级 hooks、MCP 和 HUD 扩展插件。", - "interface": { - "displayName": "oh-my-codex", - "shortDescription": "为 Codex CLI 提供 OMX 工作流、hooks、MCP 和多 Agent 编排。", - "longDescription": "oh-my-codex 将 OMX 的规划、调试、代码评审、多 Agent 协作、目标执行、质量验证、wiki 和 HUD 工作流打包为 Codex 插件。插件包含官方支持的 plugin-scoped hooks、MCP 配置和多个 workflow skills;完整运行需要用户已安装 oh-my-codex CLI 并完成 omx setup。", - "developerName": "Yeachan Heo", - "category": "Developer Tools", - "capabilities": [ - "Interactive", - "Read", - "Write" - ], - "defaultPrompt": [ - "使用 oh-my-codex 帮我选择合适的 OMX 工作流。", - "使用 oh-my-codex 帮我规划、执行并验证这个开发任务。" - ], - "websiteURL": "https://oh-my-codex.dev", - "privacyPolicyURL": "https://github.com/Yeachan-Heo/oh-my-codex", - "termsOfServiceURL": "https://github.com/Yeachan-Heo/oh-my-codex", - "brandColor": "#111827" - } - } - }, { "id": "ui-ux-pro-max", "repo": "https://github.com/nextlevelbuilder/ui-ux-pro-max-skill.git", @@ -866,8 +750,8 @@ { "id": "artemis-automation", "repo": "https://git.playones.com/chenmingxuan/Artemis-Eapil.git", - "repoProvider": "gitea", - "ref": "main", + "releaseChannel": "latest-stable", + "releaseProvider": "gitea", "adapter": "claude-skill", "pluginName": "artemis-automation", "category": "Developer Tools", @@ -878,7 +762,6 @@ ], "brandColor": "#2563EB", "manifestOverrides": { - "version": "0.1.0", "description": "使用 ARTEMIS MCP 完成 Android 自动化测试、批量执行、失败诊断、历史检索和报告交付。", "author": { "name": "chenmingxuan", diff --git a/config/external-sources.lock.json b/config/external-sources.lock.json index f2148187..25a5c64f 100644 --- a/config/external-sources.lock.json +++ b/config/external-sources.lock.json @@ -9,33 +9,6 @@ "commit": "70323e1486e64b1ca823fa2bd9c48b9b07f88519", "syncedAt": "2026-09-16T15:59:59Z" }, - { - "id": "superpowers", - "pluginName": "superpowers", - "repo": "https://github.com/obra/superpowers.git", - "ref": "main", - "adapter": "codex-plugin", - "commit": "5bf4e78011075bcfc0dc295f0724994cd123ee71", - "syncedAt": "2026-09-22T16:08:55Z" - }, - { - "id": "superpowers-zh", - "pluginName": "superpowers-zh", - "repo": "https://github.com/AreChen/superpowers-zh.git", - "ref": "main", - "adapter": "codex-plugin", - "commit": "e6ae7ddf5da1b5f7ec0388ce053c894d23a64857", - "syncedAt": "2026-08-21T06:41:32Z" - }, - { - "id": "oh-my-codex", - "pluginName": "oh-my-codex", - "repo": "https://github.com/Yeachan-Heo/oh-my-codex.git", - "ref": "main", - "adapter": "codex-plugin", - "commit": "cdc24a71408ebd6bd0362f52170f0d7998f77007", - "syncedAt": "2026-09-22T16:08:55Z" - }, { "id": "ui-ux-pro-max", "pluginName": "ui-ux-pro-max", @@ -148,10 +121,10 @@ "id": "artemis-automation", "pluginName": "artemis-automation", "repo": "https://git.playones.com/chenmingxuan/Artemis-Eapil.git", - "ref": "main", + "ref": "v1.0.0", "adapter": "claude-skill", - "commit": "8e757d462615fa211cafcb85ab56a4ad6cd73a28", - "syncedAt": "2026-09-22T16:08:55Z" + "commit": "2ec563c07943d4d651a5a2875314a2e4f3ca1126", + "syncedAt": "2026-09-22T16:42:07Z" }, { "id": "embedded-app-pc-debug", @@ -186,8 +159,8 @@ "repo": "https://github.com/vercel/next.js.git", "ref": "canary", "adapter": "skill-collection", - "commit": "3204d86c4494dec07f8927378084e10f92a58a37", - "syncedAt": "2026-09-22T16:08:55Z" + "commit": "bf53f53c7da622c7ee366cef50aa9ba4bb8143db", + "syncedAt": "2026-09-22T16:42:07Z" }, { "id": "shuorenhua", diff --git a/plugins/codex/.agents/plugins/marketplace.json b/plugins/codex/.agents/plugins/marketplace.json index adf6fdb4..48dda127 100644 --- a/plugins/codex/.agents/plugins/marketplace.json +++ b/plugins/codex/.agents/plugins/marketplace.json @@ -292,18 +292,6 @@ }, "category": "Developer Tools" }, - { - "name": "oh-my-codex", - "source": { - "source": "local", - "path": "./plugins/oh-my-codex" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, { "name": "shadcn", "source": { @@ -316,30 +304,6 @@ }, "category": "Developer Tools" }, - { - "name": "superpowers", - "source": { - "source": "local", - "path": "./plugins/superpowers" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "superpowers-zh", - "source": { - "source": "local", - "path": "./plugins/superpowers-zh" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, { "name": "updating-gitea-repositories", "source": { diff --git a/plugins/codex/plugins/artemis-automation/.codex-plugin/plugin.json b/plugins/codex/plugins/artemis-automation/.codex-plugin/plugin.json index a860bf3f..ffd0fd64 100644 --- a/plugins/codex/plugins/artemis-automation/.codex-plugin/plugin.json +++ b/plugins/codex/plugins/artemis-automation/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "artemis-automation", - "version": "0.1.0", + "version": "1.0.0", "description": "使用 ARTEMIS MCP 完成 Android 自动化测试、批量执行、失败诊断、历史检索和报告交付。", "author": { "name": "chenmingxuan", diff --git a/plugins/codex/plugins/artemis-automation/THIRD_PARTY_SOURCE.json b/plugins/codex/plugins/artemis-automation/THIRD_PARTY_SOURCE.json index f977c81b..c2131f51 100644 --- a/plugins/codex/plugins/artemis-automation/THIRD_PARTY_SOURCE.json +++ b/plugins/codex/plugins/artemis-automation/THIRD_PARTY_SOURCE.json @@ -1,9 +1,9 @@ { "sourceId": "artemis-automation", "repo": "https://git.playones.com/chenmingxuan/Artemis-Eapil.git", - "ref": "main", - "commit": "8e757d462615fa211cafcb85ab56a4ad6cd73a28", + "ref": "v1.0.0", + "commit": "2ec563c07943d4d651a5a2875314a2e4f3ca1126", "adapter": "claude-skill", "sourcePath": "skills/artemis-automation", - "syncedAt": "2026-09-22T16:08:55Z" + "syncedAt": "2026-09-22T16:42:07Z" } diff --git a/plugins/codex/plugins/artemis-automation/skills/artemis-automation/SKILL.md b/plugins/codex/plugins/artemis-automation/skills/artemis-automation/SKILL.md index fea30d35..441b0162 100644 --- a/plugins/codex/plugins/artemis-automation/skills/artemis-automation/SKILL.md +++ b/plugins/codex/plugins/artemis-automation/skills/artemis-automation/SKILL.md @@ -27,7 +27,7 @@ description: 使用 ARTEMIS MCP 在 Android 真机或模拟器上完成自然语 ## 配置和恢复边界 - `mobile_config` 读取非敏感设置。只有用户明确要求保存或修改默认配置时,才传 `changes` 与 `persist_defaults=true`;这些设置影响后续任务,不能为一次临时需求擅自改变以后所有任务。 -- 模型名称支持预设 `gpt-5.6-luna`、`mimo-v2.6-flash` 和自定义名称,取决于用户配置的 Responses 网关。默认思考强度 `high`、单次请求 300 秒、失败后最多重试 5 次、固定间隔 10 秒;默认关闭视频分析与录屏。不根据模型名称猜测网关能力。 +- 模型名称支持预设 `gpt-5.6-luna`、`mimo-v2.6-flash` 和自定义名称,取决于用户配置的 Responses 网关。思考强度最高支持 `max`,默认 `high`、单次请求 300 秒、失败后最多重试 5 次、固定间隔 10 秒;默认关闭视频分析与录屏。不根据模型名称猜测网关能力。 - 强度选 `default` 表示不发送该参数,和 `none`(显式要求关闭)不同。接口拒绝强度参数时明确呈现失败,先询问用户是否改用 `default`,不能静默降级或换模型。 - API Key 只通过网页或 `.env` 配置。不要请求用户把 key 发到聊天中,不读取/回显已有 key。保存后新任务生效;直接编辑环境文件后需重启相应服务,运行中的任务保留自己的配置。 - 当前视频开关仅提供保存的全局设置,没有单任务覆盖或独立视频文件分析接口。用户只要求临时分析且默认关闭时,明确说明这个限制;不能用“先开全局、执行后再关”影响同期任务。只有用户改为要求保存启用后,才修改配置并提交新任务。 diff --git a/plugins/codex/plugins/mcp-playwright/MCP_SOURCE.json b/plugins/codex/plugins/mcp-playwright/MCP_SOURCE.json index ac6bf9ea..b179bf27 100644 --- a/plugins/codex/plugins/mcp-playwright/MCP_SOURCE.json +++ b/plugins/codex/plugins/mcp-playwright/MCP_SOURCE.json @@ -3,5 +3,5 @@ "name": "playwright浏览器自动化操作", "version": "20260605", "keySource": "none", - "syncedAt": "2026-09-22T16:12:43Z" + "syncedAt": "2026-09-22T16:44:16Z" } diff --git a/plugins/codex/plugins/next-skills/THIRD_PARTY_SOURCE.json b/plugins/codex/plugins/next-skills/THIRD_PARTY_SOURCE.json index 17511728..4fde42b1 100644 --- a/plugins/codex/plugins/next-skills/THIRD_PARTY_SOURCE.json +++ b/plugins/codex/plugins/next-skills/THIRD_PARTY_SOURCE.json @@ -2,8 +2,8 @@ "sourceId": "next-skills", "repo": "https://github.com/vercel/next.js.git", "ref": "canary", - "commit": "3204d86c4494dec07f8927378084e10f92a58a37", + "commit": "bf53f53c7da622c7ee366cef50aa9ba4bb8143db", "adapter": "skill-collection", "sourcePath": "skills", - "syncedAt": "2026-09-22T16:08:55Z" + "syncedAt": "2026-09-22T16:42:07Z" } diff --git a/plugins/codex/plugins/oh-my-codex/.app.json b/plugins/codex/plugins/oh-my-codex/.app.json deleted file mode 100644 index e3afa4c2..00000000 --- a/plugins/codex/plugins/oh-my-codex/.app.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "apps": {} -} diff --git a/plugins/codex/plugins/oh-my-codex/.codex-plugin/plugin.json b/plugins/codex/plugins/oh-my-codex/.codex-plugin/plugin.json deleted file mode 100644 index 14583158..00000000 --- a/plugins/codex/plugins/oh-my-codex/.codex-plugin/plugin.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "oh-my-codex", - "version": "0.21.6", - "description": "oh-my-codex 是 Codex CLI 的多 Agent 编排、结构化工作流、插件级 hooks、MCP 和 HUD 扩展插件。", - "author": { - "name": "Yeachan Heo", - "url": "https://github.com/Yeachan-Heo" - }, - "homepage": "https://yeachan-heo.github.io/oh-my-codex", - "repository": "https://github.com/Yeachan-Heo/oh-my-codex.git", - "license": "MIT", - "keywords": [ - "codex", - "openai", - "cli", - "agents", - "orchestration", - "multi-agent" - ], - "skills": "./skills/", - "mcpServers": "./.mcp.json", - "apps": "./.app.json", - "interface": { - "displayName": "oh-my-codex", - "shortDescription": "为 Codex CLI 提供 OMX 工作流、hooks、MCP 和多 Agent 编排。", - "longDescription": "oh-my-codex 将 OMX 的规划、调试、代码评审、多 Agent 协作、目标执行、质量验证、wiki 和 HUD 工作流打包为 Codex 插件。插件包含官方支持的 plugin-scoped hooks、MCP 配置和多个 workflow skills;完整运行需要用户已安装 oh-my-codex CLI 并完成 omx setup。", - "developerName": "Yeachan Heo", - "category": "Developer Tools", - "capabilities": [ - "Interactive", - "Read", - "Write" - ], - "defaultPrompt": [ - "使用 oh-my-codex 帮我选择合适的 OMX 工作流。", - "使用 oh-my-codex 帮我规划、执行并验证这个开发任务。" - ], - "websiteURL": "https://oh-my-codex.dev", - "privacyPolicyURL": "https://github.com/Yeachan-Heo/oh-my-codex", - "termsOfServiceURL": "https://github.com/Yeachan-Heo/oh-my-codex", - "brandColor": "#111827" - }, - "hooks": "./hooks/hooks.json" -} diff --git a/plugins/codex/plugins/oh-my-codex/.mcp.json b/plugins/codex/plugins/oh-my-codex/.mcp.json deleted file mode 100644 index 85d3148e..00000000 --- a/plugins/codex/plugins/oh-my-codex/.mcp.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "mcpServers": { - "omx_state": { - "command": "omx", - "args": [ - "mcp-serve", - "state" - ], - "enabled": false - }, - "omx_memory": { - "command": "omx", - "args": [ - "mcp-serve", - "memory" - ], - "enabled": false - }, - "omx_code_intel": { - "command": "omx", - "args": [ - "mcp-serve", - "code-intel" - ], - "enabled": false - }, - "omx_trace": { - "command": "omx", - "args": [ - "mcp-serve", - "trace" - ], - "enabled": false - }, - "omx_wiki": { - "command": "omx", - "args": [ - "mcp-serve", - "wiki" - ], - "enabled": false - }, - "omx_hermes": { - "command": "omx", - "args": [ - "mcp-serve", - "hermes" - ], - "enabled": false - } - } -} diff --git a/plugins/codex/plugins/oh-my-codex/THIRD_PARTY_SOURCE.json b/plugins/codex/plugins/oh-my-codex/THIRD_PARTY_SOURCE.json deleted file mode 100644 index d4a1e160..00000000 --- a/plugins/codex/plugins/oh-my-codex/THIRD_PARTY_SOURCE.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "sourceId": "oh-my-codex", - "repo": "https://github.com/Yeachan-Heo/oh-my-codex.git", - "ref": "main", - "commit": "cdc24a71408ebd6bd0362f52170f0d7998f77007", - "adapter": "codex-plugin", - "sourcePath": "plugins/oh-my-codex", - "syncedAt": "2026-09-22T16:08:55Z" -} diff --git a/plugins/codex/plugins/oh-my-codex/hooks/codex-native-hook.mjs b/plugins/codex/plugins/oh-my-codex/hooks/codex-native-hook.mjs deleted file mode 100644 index b15e6e9e..00000000 --- a/plugins/codex/plugins/oh-my-codex/hooks/codex-native-hook.mjs +++ /dev/null @@ -1,657 +0,0 @@ -#!/usr/bin/env node -import { spawn } from 'node:child_process'; -import { extname } from 'node:path'; -import { closeSync, constants as fsConstants, existsSync, fstatSync, lstatSync, mkdirSync, openSync, readFileSync, readSync, realpathSync, writeFileSync } from 'node:fs'; -import { dirname, join, resolve } from 'node:path'; -import { fileURLToPath } from 'node:url'; - -const hookDir = dirname(fileURLToPath(import.meta.url)); -// sync-plugin-mirror verifies this stable marker; runtime behavior is tested separately. -const OMX_PLUGIN_HOOK_LAUNCHER_CONTRACT_MARKER = 'omx-plugin-hook-launcher:v1'; -const OMX_PLUGIN_HOOK_ROUTING_ONLY_MARKER = 'omx-plugin-hook-routing-only:v1'; -const MAX_WRAPPER_STDIN_BYTES = 1024 * 1024; -const RAW_EVENT_SCAN_BYTES = 64 * 1024; -const MAX_STOP_STDOUT_BYTES = 1024 * 1024; -const MAX_ROUTING_RECORD_BYTES = 4 * 1024; -const CODEX_HOOK_EVENT_NAMES = new Set([ - 'SessionStart', - 'PreToolUse', - 'PostToolUse', - 'UserPromptSubmit', - 'PreCompact', - 'PostCompact', - 'Stop', -]); - -function skipJsonWhitespace(raw, index) { - while (index < raw.length && /\s/.test(raw[index] ?? '')) index += 1; - return index; -} - -function readJsonStringLiteral(raw, quoteIndex) { - if (raw[quoteIndex] !== '"') return null; - let value = ''; - for (let index = quoteIndex + 1; index < raw.length; index += 1) { - const char = raw[index]; - if (char === '"') return { value, endIndex: index + 1 }; - if (char !== '\\') { - value += char; - continue; - } - - index += 1; - if (index >= raw.length) return null; - const escaped = raw[index]; - switch (escaped) { - case '"': - case '\\': - case '/': - value += escaped; - break; - case 'b': - value += '\b'; - break; - case 'f': - value += '\f'; - break; - case 'n': - value += '\n'; - break; - case 'r': - value += '\r'; - break; - case 't': - value += '\t'; - break; - case 'u': { - const hex = raw.slice(index + 1, index + 5); - if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null; - value += String.fromCharCode(Number.parseInt(hex, 16)); - index += 4; - break; - } - default: - return null; - } - } - return null; -} - -function extractTopLevelStringField(rawInput, fieldNames) { - const raw = rawInput.slice(0, RAW_EVENT_SCAN_BYTES); - const wanted = new Set(fieldNames); - let depth = 0; - let index = 0; - - while (index < raw.length) { - const char = raw[index]; - if (char === '"') { - const key = readJsonStringLiteral(raw, index); - if (!key) return null; - index = key.endIndex; - const afterKey = skipJsonWhitespace(raw, index); - if (depth === 1 && raw[afterKey] === ':' && wanted.has(key.value)) { - const valueStart = skipJsonWhitespace(raw, afterKey + 1); - const value = readJsonStringLiteral(raw, valueStart); - return value?.value ?? null; - } - continue; - } - if (char === '{') depth += 1; - else if (char === '}') depth = Math.max(0, depth - 1); - index += 1; - } - - return null; -} - -function extractTopLevelHookEventName(rawInput) { - const eventName = extractTopLevelStringField(rawInput, ['hook_event_name', 'hookEventName', 'event', 'name']); - return CODEX_HOOK_EVENT_NAMES.has(eventName) ? eventName : null; -} - -function detectStopHookInput(input) { - const text = input.toString('utf8'); - try { - const parsed = JSON.parse(text); - const eventName = parsed?.hook_event_name ?? parsed?.hookEventName ?? parsed?.event ?? parsed?.name; - return eventName === 'Stop'; - } catch { - return extractTopLevelHookEventName(text) === 'Stop'; - } -} - -function detectCompactHookInput(input) { - const text = input.toString('utf8'); - try { - const parsed = JSON.parse(text); - const eventName = parsed?.hook_event_name ?? parsed?.hookEventName ?? parsed?.event ?? parsed?.name; - return eventName === 'PreCompact' || eventName === 'PostCompact'; - } catch { - const eventName = extractTopLevelHookEventName(text); - return eventName === 'PreCompact' || eventName === 'PostCompact'; - } -} - -function parseHookPayload(input) { - try { - const parsed = JSON.parse(input.toString('utf8')); - return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {}; - } catch { - return {}; - } -} - -function sanitizeLaunchId(value) { - return String(value ?? '').trim().replace(/[^a-zA-Z0-9._-]/g, '_').slice(0, 128); -} - -function resolveLaunchRoutingPath(payload, launchId) { - const cwd = typeof payload.cwd === 'string' && payload.cwd.trim() ? payload.cwd : process.cwd(); - const stateRoot = typeof process.env.OMX_ROOT === 'string' && process.env.OMX_ROOT.trim() - ? process.env.OMX_ROOT.trim() - : join(cwd, '.omx'); - return join(stateRoot, 'state', 'plugin-hook-routing', `${sanitizeLaunchId(launchId)}.json`); -} - -function hookPayloadSessionId(input, payload) { - const candidate = payload?.session_id ?? payload?.sessionId; - const parsedSessionId = typeof candidate === 'string' ? candidate.trim() : ''; - if (parsedSessionId) return parsedSessionId; - return input && typeof input.toString === 'function' - ? extractTopLevelStringField(input.toString('utf8'), ['session_id', 'sessionId'])?.trim() ?? '' - : ''; -} - -function isSafeRoutingSessionId(value) { - return typeof value === 'string' && value.length > 0 && value.length <= 256; -} - -function isChildRoutingSessionStart(payload, ownerSessionId) { - if ((payload?.hook_event_name ?? payload?.hookEventName) !== 'SessionStart' || !ownerSessionId) return false; - const childSessionId = hookPayloadSessionId(null, payload); - const transcriptCandidate = payload?.transcript_path ?? payload?.transcriptPath; - const transcriptPath = typeof transcriptCandidate === 'string' ? transcriptCandidate.trim() : ''; - if (!childSessionId || !transcriptPath) return false; - let fd; - try { - fd = openSync(transcriptPath, 'r'); - const buffer = Buffer.alloc(64 * 1024); - const bytes = readSync(fd, buffer, 0, buffer.length, 0); - const newline = buffer.subarray(0, bytes).indexOf(0x0a); - const firstLine = buffer.subarray(0, newline >= 0 ? newline : bytes).toString('utf8').trim(); - const record = JSON.parse(firstLine); - const metadata = record?.type === 'session_meta' && record?.payload && typeof record.payload === 'object' ? record.payload : null; - const spawn = metadata?.source?.subagent?.thread_spawn; - const metadataIds = [metadata?.id, metadata?.session_id].filter((value) => value !== undefined); - return metadataIds.length > 0 - && metadataIds.every((value) => value === childSessionId) - && spawn && typeof spawn === 'object' - && spawn.parent_thread_id === ownerSessionId; - } catch { - return false; - } finally { - if (fd !== undefined) try { closeSync(fd); } catch {} - } -} - -function readPinnedRoutingRecord(routingPath) { - let fd; - try { - const initial = lstatSync(routingPath); - if (!initial.isFile() || initial.isSymbolicLink() || initial.nlink !== 1 || initial.size <= 0 || initial.size > MAX_ROUTING_RECORD_BYTES) return null; - fd = openSync(routingPath, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0)); - const opened = fstatSync(fd); - if (!opened.isFile() || opened.nlink !== 1 || opened.size !== initial.size || opened.dev !== initial.dev || opened.ino !== initial.ino) return null; - const buffer = Buffer.alloc(opened.size); - if (readSync(fd, buffer, 0, buffer.length, 0) !== buffer.length) return null; - const current = lstatSync(routingPath); - const final = fstatSync(fd); - if (!current.isFile() || current.isSymbolicLink() || current.nlink !== 1 || current.size !== opened.size || current.dev !== opened.dev || current.ino !== opened.ino || final.nlink !== 1 || final.size !== opened.size) return null; - const routing = JSON.parse(buffer.toString('utf8')); - return routing && typeof routing === 'object' && !Array.isArray(routing) ? routing : null; - } catch { - return null; - } finally { - if (fd !== undefined) try { closeSync(fd); } catch {} - } -} - -// This is routing correlation only. It is intentionally unauthenticated and must -// never be used as authorization or proof that a session is OMX-owned. -function isPluginHookRoutingSession(input, payload) { - const launchId = sanitizeLaunchId(process.env.OMX_CODEX_LAUNCH_ID); - const entryPath = process.env.OMX_ENTRY_PATH?.trim(); - const sessionId = hookPayloadSessionId(input, payload); - if (!launchId || !entryPath || !isSafeRoutingSessionId(sessionId)) return false; - - const routingPath = resolveLaunchRoutingPath(payload, launchId); - try { - if (existsSync(routingPath)) { - const routing = readPinnedRoutingRecord(routingPath); - if (!routing) return false; - const ownerSessionId = routing.routing === OMX_PLUGIN_HOOK_ROUTING_ONLY_MARKER && isSafeRoutingSessionId(routing.ownerSessionId) - ? routing.ownerSessionId - : ''; - return ownerSessionId === sessionId || isChildRoutingSessionStart(payload, ownerSessionId); - } - mkdirSync(dirname(routingPath), { recursive: true }); - writeFileSync(routingPath, `${JSON.stringify({ routing: OMX_PLUGIN_HOOK_ROUTING_ONLY_MARKER, ownerSessionId: sessionId })}\n`, { encoding: 'utf8', mode: 0o600, flag: 'wx' }); - return true; - } catch { - return false; - } -} - -function writePlainCodexNoop(isStop) { - if (isStop) process.stdout.write('{}\n'); - process.exitCode = 0; -} - -async function readBoundedStdin({ drainOversized = false } = {}) { - const chunks = []; - let totalBytes = 0; - let storedBytes = 0; - let oversized = false; - for await (const rawChunk of process.stdin) { - const chunk = Buffer.isBuffer(rawChunk) ? rawChunk : Buffer.from(rawChunk); - totalBytes += chunk.length; - - if (oversized) continue; - - const remaining = MAX_WRAPPER_STDIN_BYTES - storedBytes; - if (chunk.length > remaining) { - if (remaining > 0) chunks.push(chunk.subarray(0, remaining)); - storedBytes += Math.max(remaining, 0); - oversized = true; - if (!drainOversized) return { input: Buffer.concat(chunks), oversized: true, totalBytes }; - continue; - } - chunks.push(chunk); - storedBytes += chunk.length; - } - return { input: Buffer.concat(chunks), oversized, totalBytes }; -} - -function stopFallbackOutput(stopReason, detail) { - const reason = 'OMX plugin Stop hook launcher failed before valid native Stop JSON could be produced. Continue once, preserve runtime state, inspect hook launcher diagnostics, and retry.'; - return { - decision: 'block', - reason, - stopReason, - systemMessage: detail ? `${reason} Failure: ${detail}` : reason, - }; -} - -function writeStopFallback(stopReason, detail) { - process.stdout.write(`${JSON.stringify(stopFallbackOutput(stopReason, detail))}\n`); - process.exitCode = 0; -} - -function writeOversizedStopFallback(stopReason, detail) { - const reason = - 'OMX plugin Stop rejected oversized stdin before launcher delegation; continue once with a smaller valid Stop JSON response.'; - process.stdout.write(`${JSON.stringify({ - decision: 'block', - reason, - stopReason, - systemMessage: detail ? `${reason} Failure: ${detail}` : reason, - })}\n`); - process.exitCode = 0; -} - -function writeOversizedStopNoop() { - process.stdout.write('{}\n'); - process.exitCode = 0; -} - -function writeOversizedToolHookOutput(eventName) { - const systemMessage = 'OMX native hook rejected oversized stdin JSON before parsing; maxBytes=1048576.'; - const output = eventName === 'PreToolUse' - ? { - systemMessage, - hookSpecificOutput: { - hookEventName: 'PreToolUse', - permissionDecision: 'deny', - permissionDecisionReason: systemMessage, - }, - } - : { - continue: false, - stopReason: 'native_hook_stdin_oversized', - systemMessage, - }; - process.stdout.write(`${JSON.stringify(output)}\n`); - process.exitCode = 0; -} -function writeCompactFallback() { - process.exitCode = 0; -} - -function failLauncher(error, isStop, isCompact, stopReason = 'plugin_stop_hook_launcher_failure') { - const detail = error instanceof Error ? error.message : String(error); - console.error(`[oh-my-codex] ${detail}`); - if (isStop) { - writeStopFallback(stopReason, detail); - return; - } - if (isCompact) { - writeCompactFallback(); - return; - } - process.exitCode = 1; -} - -function readPinnedLauncher() { - const launcherPath = join(hookDir, 'omx-command.json'); - try { - const raw = JSON.parse(readFileSync(launcherPath, 'utf8')); - if (typeof raw.command !== 'string' || raw.command.trim() === '') { - throw new Error('missing non-empty command'); - } - const argsPrefix = Array.isArray(raw.argsPrefix) ? raw.argsPrefix : []; - if (!argsPrefix.every((arg) => typeof arg === 'string')) { - throw new Error('argsPrefix must contain only strings'); - } - return { command: raw.command, argsPrefix }; - } catch (error) { - if (error?.code === 'ENOENT') return null; - throw new Error(`invalid plugin hook launcher ${launcherPath}: ${error.message}`); - } -} - -function readConfiguredLauncher() { - if (process.env.OMX_NATIVE_HOOK_COMMAND) { - return { command: process.env.OMX_NATIVE_HOOK_COMMAND, argsPrefix: [] }; - } - return readPinnedLauncher() ?? { command: 'omx', argsPrefix: [] }; -} - -function buildSpawnOptions(command) { - const options = { - stdio: ['pipe', 'pipe', 'pipe'], - env: process.env, - }; - - if (process.platform !== 'win32') return options; - - const extension = extname(command).toLowerCase(); - if (extension === '.exe' || extension === '.com') { - return { ...options, windowsHide: true }; - } - - return { ...options, shell: true, windowsHide: true }; -} - -function readJsonFile(path) { - try { - return JSON.parse(readFileSync(path, 'utf8')); - } catch { - return null; - } -} - -function isTerminalOutcome(value) { - return ['finish', 'finished', 'complete', 'completed', 'done', 'blocked', 'blocked-on-user', 'blocked_on_user', 'failed', 'fail', 'error', 'cancelled', 'canceled', 'cancel', 'aborted', 'abort', 'userinterlude', 'user-interlude', 'interrupted', 'interrupt', 'askuserquestion', 'ask-user-question', 'askuser', 'question'].includes(String(value ?? '').trim().toLowerCase()); -} - -function isTerminalRunStateForMode(state, mode) { - if (!state) return false; - const runMode = String(state.mode ?? '').trim(); - if (runMode && runMode !== mode) return false; - return isTerminalOutcome(state.outcome) - || isTerminalOutcome(state.run_outcome) - || isTerminalOutcome(state.lifecycle_outcome) - || isTerminalOutcome(state.terminal_outcome); -} - -function canonicalPath(path) { - const absolute = resolve(path); - if (!existsSync(absolute)) return absolute; - try { - return typeof realpathSync.native === 'function' ? realpathSync.native(absolute) : realpathSync(absolute); - } catch { - return absolute; - } -} - -function sameFilePath(leftPath, rightPath) { - return canonicalPath(leftPath) === canonicalPath(rightPath); -} - -function isSessionStateAuthoritativeForCwd(state, cwd) { - if (!isSafeSessionId(state?.session_id)) return false; - const sessionCwd = typeof state.cwd === 'string' ? state.cwd.trim() : ''; - return !sessionCwd || sameFilePath(sessionCwd, cwd); -} - -function listAuthoritativeStateBaseDirs(cwd) { - if (process.env.OMX_TEAM_STATE_ROOT?.trim()) return [process.env.OMX_TEAM_STATE_ROOT.trim()]; - if (process.env.OMX_ROOT?.trim()) return [join(process.env.OMX_ROOT.trim(), '.omx', 'state')]; - if (process.env.OMX_STATE_ROOT?.trim()) return [join(process.env.OMX_STATE_ROOT.trim(), '.omx', 'state')]; - return [join(cwd, '.omx', 'state')]; -} - -function isSafeSessionId(sessionId) { - return typeof sessionId === 'string' && /^[A-Za-z0-9_-]{1,64}$/.test(sessionId.trim()); -} - -function readCurrentSessionId(stateBaseDirs, cwd) { - for (const stateDir of stateBaseDirs) { - const session = readJsonFile(join(stateDir, 'session.json')); - if (isSessionStateAuthoritativeForCwd(session, cwd)) return session.session_id.trim(); - } - const envSessionId = process.env.OMX_SESSION_ID || process.env.CODEX_SESSION_ID; - return isSafeSessionId(envSessionId) ? envSessionId.trim() : null; -} - -function shouldContinueAutopilotState(state) { - if (state?.active !== true) return false; - return !(isTerminalOutcome(state.current_phase) - || isTerminalOutcome(state.run_outcome) - || isTerminalOutcome(state.lifecycle_outcome) - || isTerminalOutcome(state.terminal_outcome) - || isTerminalOutcome(state.outcome) - || (typeof state.completed_at === 'string' && state.completed_at.trim() !== '')); -} - -function hasActiveAutopilotStateForOversizedStop(input) { - const text = input.toString('utf8'); - const cwd = extractTopLevelStringField(text, ['cwd']) || process.cwd(); - const stateBaseDirs = listAuthoritativeStateBaseDirs(cwd); - const sessionId = readCurrentSessionId(stateBaseDirs, cwd); - if (!isSafeSessionId(sessionId)) return false; - - const sessionDir = join(stateBaseDirs[0], 'sessions', sessionId.trim()); - const terminalRunState = readJsonFile(join(sessionDir, 'run-state.json')); - if (isTerminalRunStateForMode(terminalRunState, 'autopilot')) return false; - - const sessionState = readJsonFile(join(sessionDir, 'autopilot-state.json')); - return shouldContinueAutopilotState(sessionState); -} - - -function parseJsonObjectCandidate(text) { - try { - const parsed = JSON.parse(text); - if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return null; - return parsed; - } catch { - return null; - } -} - -function isJsonObjectFragmentLine(text) { - return text.startsWith('{') || text.endsWith('}') || /^"[^"]+"\s*:/.test(text); -} - -function parseSingleJsonObjectOutput(raw) { - const text = String(raw ?? '').trim(); - if (!text) return null; - const direct = parseJsonObjectCandidate(text); - if (direct) return direct; - - const lines = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean); - const lastLine = lines.at(-1); - if (!lastLine || !lastLine.startsWith('{') || !lastLine.endsWith('}')) return null; - for (const line of lines.slice(0, -1)) { - if (parseJsonObjectCandidate(line) || isJsonObjectFragmentLine(line)) return null; - } - return parseJsonObjectCandidate(lastLine); -} - -async function main() { - const { input, oversized, totalBytes } = await readBoundedStdin({ drainOversized: true }); - const payload = parseHookPayload(input); - const routedByLaunchCorrelation = isPluginHookRoutingSession(input, payload); - const isStop = detectStopHookInput(input); - const isCompact = detectCompactHookInput(input); - - if (!routedByLaunchCorrelation) { - writePlainCodexNoop(isStop); - return; - } - - if (oversized) { - const message = `plugin hook stdin exceeded ${MAX_WRAPPER_STDIN_BYTES} bytes before launcher delegation; totalBytes>${totalBytes}`; - if (isStop) { - if (hasActiveAutopilotStateForOversizedStop(input)) { - console.error(`[oh-my-codex] ${message}`); - writeOversizedStopFallback('plugin_stop_hook_stdin_oversized_active_workflow', message); - return; - } - writeOversizedStopNoop(); - return; - } - const eventName = extractTopLevelHookEventName(input.toString('utf8')); - if (eventName === 'PreToolUse' || eventName === 'PostToolUse') { - writeOversizedToolHookOutput(eventName); - return; - } - console.error(`[oh-my-codex] ${message}`); - process.exitCode = 1; - return; - } - - let launcher; - try { - launcher = readConfiguredLauncher(); - } catch (error) { - failLauncher(error, isStop, isCompact); - return; - } - - const { command, argsPrefix } = launcher; - const child = spawn(command, [...argsPrefix, 'codex-native-hook'], buildSpawnOptions(command)); - - const stdoutChunks = []; - let stdoutBytes = 0; - let bufferedStopStdoutBytes = 0; - let stopStdoutOversized = false; - let childSpawnError = null; - let childStdinError = null; - - child.stdout.on('data', (chunk) => { - const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk); - stdoutBytes += buffer.byteLength; - if (isStop) { - if (!stopStdoutOversized) { - const remaining = MAX_STOP_STDOUT_BYTES - bufferedStopStdoutBytes; - if (remaining > 0) { - const slice = buffer.subarray(0, remaining); - stdoutChunks.push(slice); - bufferedStopStdoutBytes += slice.byteLength; - } - if (buffer.byteLength > remaining) { - stopStdoutOversized = true; - child.stdout.destroy(); - child.kill(); - } - } - } else if (!isCompact) { - process.stdout.write(chunk); - } - }); - child.stderr.pipe(process.stderr); - child.stdin.on('error', (error) => { - childStdinError = error; - }); - child.on('error', (error) => { - childSpawnError = error; - }); - child.on('close', (code, signal) => { - if (isStop && stopStdoutOversized) { - writeStopFallback('plugin_stop_hook_launcher_stdout_oversized', `codex-native-hook produced more than ${MAX_STOP_STDOUT_BYTES} bytes of Stop hook stdout`); - return; - } - - if (isStop && stdoutBytes > 0) { - const stdoutText = Buffer.concat(stdoutChunks).toString('utf8'); - const parsed = parseSingleJsonObjectOutput(stdoutText); - if (parsed) { - process.stdout.write(`${JSON.stringify(parsed)}\n`); - process.exitCode = 0; - return; - } - writeStopFallback('plugin_stop_hook_launcher_invalid_stdout', `codex-native-hook produced invalid Stop hook JSON stdout (${stdoutBytes} bytes)`); - return; - } - - if (isCompact) { - process.exitCode = 0; - return; - } - - if (isStop && stdoutBytes === 0) { - if (childSpawnError) { - writeStopFallback('plugin_stop_hook_launcher_spawn_error', `failed to launch ${command} codex-native-hook: ${childSpawnError.message}`); - return; - } - if (signal) { - writeStopFallback('plugin_stop_hook_launcher_signal', `codex-native-hook terminated by ${signal}`); - return; - } - if (code && code !== 0) { - if (childStdinError) { - writeStopFallback('plugin_stop_hook_launcher_stdin_error', `codex-native-hook stdin failed: ${childStdinError.message}`); - return; - } - writeStopFallback('plugin_stop_hook_launcher_exit', `codex-native-hook exited with code ${code}`); - return; - } - writeStopFallback('plugin_stop_hook_launcher_empty_stdout', 'codex-native-hook exited successfully without producing Stop hook JSON'); - return; - } - - if (isCompact) { - if (childSpawnError) { - console.error(`[oh-my-codex] failed to launch ${command} codex-native-hook: ${childSpawnError.message}`); - } else if (signal) { - console.error(`[oh-my-codex] codex-native-hook terminated by ${signal}`); - } else if (code && code !== 0) { - console.error(`[oh-my-codex] codex-native-hook exited with code ${code}`); - } - process.exitCode = 0; - return; - } - - if (childSpawnError) { - console.error(`[oh-my-codex] failed to launch ${command} codex-native-hook: ${childSpawnError.message}`); - process.exitCode = 1; - return; - } - if (signal) { - console.error(`[oh-my-codex] codex-native-hook terminated by ${signal}`); - process.exitCode = 1; - return; - } - process.exitCode = code ?? 0; - }); - - child.stdin.end(input); -} - -main().catch((error) => { - console.error(`[oh-my-codex] plugin hook launcher failed: ${error instanceof Error ? error.message : String(error)}`); - process.exitCode = 1; -}); diff --git a/plugins/codex/plugins/oh-my-codex/hooks/hooks.json b/plugins/codex/plugins/oh-my-codex/hooks/hooks.json deleted file mode 100644 index 4830ec7a..00000000 --- a/plugins/codex/plugins/oh-my-codex/hooks/hooks.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "hooks": { - "SessionStart": [ - { - "hooks": [ - { - "type": "command", - "command": "node \"${PLUGIN_ROOT}/hooks/codex-native-hook.mjs\"" - } - ], - "matcher": "startup|resume|clear" - } - ], - "PreToolUse": [ - { - "hooks": [ - { - "type": "command", - "command": "node \"${PLUGIN_ROOT}/hooks/codex-native-hook.mjs\"" - } - ] - } - ], - "PostToolUse": [ - { - "hooks": [ - { - "type": "command", - "command": "node \"${PLUGIN_ROOT}/hooks/codex-native-hook.mjs\"" - } - ] - } - ], - "UserPromptSubmit": [ - { - "hooks": [ - { - "type": "command", - "command": "node \"${PLUGIN_ROOT}/hooks/codex-native-hook.mjs\"" - } - ] - } - ], - "PreCompact": [ - { - "hooks": [ - { - "type": "command", - "command": "node \"${PLUGIN_ROOT}/hooks/codex-native-hook.mjs\"" - } - ] - } - ], - "PostCompact": [ - { - "hooks": [ - { - "type": "command", - "command": "node \"${PLUGIN_ROOT}/hooks/codex-native-hook.mjs\"" - } - ] - } - ], - "Stop": [ - { - "hooks": [ - { - "type": "command", - "command": "node \"${PLUGIN_ROOT}/hooks/codex-native-hook.mjs\"", - "timeout": 30 - } - ] - } - ] - } -} diff --git a/plugins/codex/plugins/oh-my-codex/skills/ai-slop-cleaner/SKILL.md b/plugins/codex/plugins/oh-my-codex/skills/ai-slop-cleaner/SKILL.md deleted file mode 100644 index fa437d25..00000000 --- a/plugins/codex/plugins/oh-my-codex/skills/ai-slop-cleaner/SKILL.md +++ /dev/null @@ -1,72 +0,0 @@ ---- -name: ai-slop-cleaner -description: Run an anti-slop cleanup/refactor/deslop workflow ---- - -# AI Slop Cleaner Task Card - -Use this bounded helper for cleanup/refactor/deslop work, not as a competing top-level -workflow. Shared operating invariants live in `templates/AGENTS.md`; this card defines -scope, smell taxonomy, passes, and evidence. - -## When to use and inputs - -Use when working code is bloated, noisy, repetitive, over-abstracted, or AI-generated; -the user requests cleanup/refactor/deslop; or a follow-up left duplicate/dead code, -weak boundaries, missing tests, fallback-like paths, or wrappers. Inputs are the requested -feature/files and behavior to preserve. A file list scope is valid; keep the pass bounded -to it. Limit the pass to the calling task's changed files unless broader cleanup was requested. - -## Before editing - -1. **Lock behavior with regression tests first**: identify behavior to preserve, run/add the narrowest targeted tests, and cover both primary and preserved compatibility/fail-safe fallback paths. -2. **Create a cleanup plan before code**: list scope and smells, include fallback findings/classifications/escalation, and order safest/highest-signal fixes first. -3. **Inventory fallback-like code** in scope: quick hacks, temporary workaround, temporary fallback, just bypass, just skip, fallback if it fails, swallowed errors, silent defaults, broad compatibility shims, and duplicate alternate execution paths. -4. Classify each fallback: **Masking fallback slop** hides evidence, bypasses the contract, suppresses validation, swallows failures, silently defaults, or adds untested paths; **Grounded compatibility/fail-safe fallback** is narrow at an external/version/fail-safe boundary, documents rationale, preserves failure evidence, and tests primary plus fallback. -5. Prefer root-cause repair, deletion, boundary repair, or explicit failure behavior. For broad/ambiguous/cross-layer/architectural findings, invoke `$ralplan` for consensus resolution; when already inside ralplan, ultragoal, team, or another OMX workflow, do not spawn a nested `$ralplan`—attach the finding to the active handoff. - -## Smell taxonomy and passes - -Classify before changing: - -- **Fallback-like code**: masking fallbacks, workaround branches, bypasses, swallowed errors, silent defaults, broad shims, alternate paths. -- **Duplication**: repeated logic, copy-paste branches, redundant helpers. -- **Dead code**: unused/unreachable code, stale flags, debug leftovers. -- **Needless abstraction**: pass-through wrappers, speculative indirection, single-use layers. -- **Boundary violations**: hidden coupling, leaky responsibilities, wrong-layer imports/side effects. -- **Missing tests**: behavior not locked or edge cases uncovered. -- **UI/design slop**: context-sensitive signals, not absolute bans; preserve intentional brand, design-system, accessibility, or product-context exceptions. Challenge Korean body text at 11-12px (generally 14px or larger); gratuitous box shadows; repetitive eyebrow + title + description + paragraph stacks and generic emoji badges; default AI blue/purple such as `#3B82F6`; reflexive 3-column or 4-column grids; and extreme gradients unless justified by context. - -Resolve the fallback-like code resolution gate first, then one smell at a time: -**Pass 1: Dead code deletion**; **Pass 2: Duplicate removal**; **Pass 3: Naming/error -handling cleanup**; **Pass 4: Test reinforcement**. Re-run targeted verification after -each pass and avoid unrelated refactors. Prefer deletion/existing utilities; no new -abstractions or dependencies unless explicitly required. - -## Evidence/output contract - -Report: - -```text -AI SLOP CLEANUP REPORT -Scope: [files/feature] -Behavior Lock: [targeted tests added/run] -Cleanup Plan: [bounded smells/order] -Fallback Findings: [finding -> masking fallback slop | grounded compatibility/fail-safe fallback -> escalation] -UI/Design Findings: [none/N/A or signal -> action/defer -> intentional rationale] -Passes Completed: [resolution gate; Passes 1-4] -Quality Gates: Regression tests, Lint, Typecheck, Tests, Static/security scan (PASS/FAIL/N/A) -Changed Files: [path -> simplification] -Remaining Risks: [none or deferred item] -``` - -Include changed files, simplifications, fallback classifications/escalation status, -tests/diagnostics/build checks run, UI findings when relevant, and deferred risks. Keep -writer/reviewer separation for cleanup plans and approvals. - -## Exit condition - -Stop when the requested scope has behavior-lock evidence, each selected smell pass is -complete or explicitly deferred with rationale, verification is reported, and no -unrelated files or temporary artifacts remain. Never present an unverified cleanup as -complete; escalate a real architectural blocker rather than masking it. diff --git a/plugins/codex/plugins/oh-my-codex/skills/analyze/SKILL.md b/plugins/codex/plugins/oh-my-codex/skills/analyze/SKILL.md deleted file mode 100644 index ac71836f..00000000 --- a/plugins/codex/plugins/oh-my-codex/skills/analyze/SKILL.md +++ /dev/null @@ -1,70 +0,0 @@ ---- -name: analyze -description: "Run read-only deep repository analysis and return a ranked synthesis with explicit confidence, concrete file references, and clear evidence-vs-inference boundaries. Use when a user says 'analyze', 'investigate', 'why does', 'what's causing', or needs grounded cross-file explanation before any changes are proposed." ---- - -# Analyze — Read-Only Deep Analysis - -Use `$analyze` to answer a repository question with grounded, read-only evidence. Explain what the code most likely says; do not turn analysis into implementation or generic fix planning. - -Shared operating, delegation, state, hook, team, cancellation, and verification invariants live in [`templates/AGENTS.md`](../../templates/AGENTS.md). Follow that source instead of duplicating its rules here. - -## Use when - -- The user needs a causal, architectural, behavioral, impact, or tradeoff explanation. -- The answer requires tracing multiple files or boundaries, or ranking plausible explanations. -- The user needs confidence and concrete evidence before changing anything. - -Do not use it for edits, implementation, a new product plan, a simple one-file lookup, or OMX team-runtime operation. - -## Inputs and method - -1. Restate the question and define the evidence-backed scope. -2. Identify the smallest files, tests, configs, and docs likely to answer it. -3. Read direct code paths and contracts first; trace boundaries only as far as needed. -4. Compare competing explanations, rank them by support, and mark unresolved points. -5. Stop when the question is answered with sufficient evidence, or name the smallest read-only probe that would resolve the remaining uncertainty. - -## Evidence discipline - -Label every material claim as one of: - -- **Evidence** — directly shown by code, tests, generated artifacts, configuration, or docs. -- **Inference** — a reasoned conclusion drawn from cited evidence. -- **Unknown** — not settled by the repository evidence. - -Prefer direct paths and independent corroboration over contextual clues. Never present guesses as evidence or inference, and never overclaim certainty. - -## Output contract - -Answer the asked question first and use this shape: - -### Question -Restated question, briefly. - -### Ranked synthesis -| Rank | Explanation | Confidence | Basis | -|------|-------------|------------|-------| -| 1 | ... | High / Medium / Low | strongest supporting evidence | -| 2 | ... | High / Medium / Low | why it trails | -| 3 | ... | High / Medium / Low | why it remains possible | - -### Evidence -- `path/to/file:line-line` — direct observation. -- `path/to/file:line-line` — corroborating observation. - -### Inference -- What the evidence most strongly implies. -- Why weaker alternatives were down-ranked. - -### Unknowns / limits -- What the repository does not establish. -- The next discriminating read-only probe, when useful. - -## Stop conditions - -- Do not edit files, run an implementation lane, or make recommendations the evidence cannot support. -- Do not continue searching after the answer and confidence boundary are grounded. -- If evidence is insufficient, report the limit explicitly rather than manufacturing certainty. - -Task: {{ARGUMENTS}} diff --git a/plugins/codex/plugins/oh-my-codex/skills/ask/SKILL.md b/plugins/codex/plugins/oh-my-codex/skills/ask/SKILL.md deleted file mode 100644 index c3c1b860..00000000 --- a/plugins/codex/plugins/oh-my-codex/skills/ask/SKILL.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -name: ask -description: Ask a local external advisor CLI (Claude or Gemini) and capture a reusable artifact ---- - -# Ask (Local Advisor CLI) - -Use a locally installed external advisor CLI for focused questions, reviews, brainstorming, or second opinions. This skill replaces the separate `ask-claude` and `ask-gemini` skills. - -## Usage - -```bash -$ask claude -$ask gemini -omx ask claude "" -omx ask gemini "" -``` - -## Backend selection - -- Use `claude` when the user asks for Claude, Anthropic, or the previous `$ask-claude` behavior. -- Use `gemini` when the user asks for Gemini or the previous `$ask-gemini` behavior. -- If no backend is specified, choose the installed backend that best matches the user request; if neither is clearly available, explain that a local CLI is required. - -## Local CLI commands - -Claude: - -```bash -omx ask claude "{{ARGUMENTS}}" -claude -p "{{ARGUMENTS}}" -``` - -Gemini: - -```bash -omx ask gemini "{{ARGUMENTS}}" -gemini -p "{{ARGUMENTS}}" -``` - -If needed, adapt to the user's installed CLI variant while keeping local execution as the default path. Do not silently switch to an MCP or remote provider when the local binary is missing. - -## Artifact requirement - -After local execution, save a markdown artifact to: - -```text -.omx/artifacts/ask---.md -``` - -Minimum artifact sections: -1. Original user task -2. Backend and final prompt sent to the CLI -3. Raw CLI output -4. Concise summary -5. Action items / next steps - -Task: {{ARGUMENTS}} diff --git a/plugins/codex/plugins/oh-my-codex/skills/autopilot/SKILL.md b/plugins/codex/plugins/oh-my-codex/skills/autopilot/SKILL.md deleted file mode 100644 index 92bd1691..00000000 --- a/plugins/codex/plugins/oh-my-codex/skills/autopilot/SKILL.md +++ /dev/null @@ -1,128 +0,0 @@ ---- -name: autopilot -description: "[OMX] Canonical autonomous orchestrator: $deep-interview -> $ralplan -> $ultragoal" ---- - -# Autopilot - - -Autopilot is the first-class canonical orchestrator for hands-off delivery. Its defining default chain is mandatory: - -```text -$deep-interview -> $ralplan -> $ultragoal -``` - -The chain is not a list of optional hints and Autopilot is not an alias for direct execution. Autopilot supervises each child stage in one recoverable session, carries durable artifacts forward, and continues through implementation verification until the requested outcome is complete or a genuine blocker is recorded. - - - -- The user explicitly invokes `$autopilot`. -- The user asks for end-to-end autonomous delivery from an idea, issue, or requirements seed. -- The work needs requirements clarification, consensus planning, durable execution, and evidence-backed completion as one supervised workflow. - - - -Run or resume these stages in order: - -1. **`deep-interview`** — clarify intent, scope, non-goals, constraints, acceptance criteria, and unresolved decisions. Produce a durable requirements/specification handoff. Deep Interview is a real stage; do not replace it with a one-question check or skip it merely because the task looks actionable. -2. **`ralplan`** — turn the clarified requirements into an execution-ready consensus plan with architecture, sequencing, test, and verification guidance. Preserve review evidence as lifecycle evidence, not host-issued security authority. -3. **`ultragoal`** — execute the approved plan through durable goals and ledger receipts, implementation, focused verification, cleanup, review, and terminal evidence. - -`$team`, `$code-review`, and `$ultraqa` may be used inside the supervised execution when the active Ultragoal plan or verification boundary requires them. They do not replace or weaken the defining three-stage chain. - -When review or QA proves the requirements or plan wrong, keep Autopilot active, return its supervised phase to `ralplan`, attach the findings, and continue through `ultragoal` again. Implementation-only review fixes may remain within Ultragoal's blocker/review loop. - - - -- Autopilot MUST begin at `deep-interview` for a new run and MUST preserve the phase order `deep-interview -> ralplan -> ultragoal`. -- Child stages are supervised phases, not peer workflow activations. Keep `mode:"autopilot"` active and update `current_phase` rather than replacing Autopilot with standalone child state. -- Use the current CLI state SSOT (`omx state ... --json`) and the current session-scoped state root. Do not create a second writer or revive legacy root-state authority. -- Local artifacts, prompts, trackers, transcripts, environment values, and role labels are lifecycle evidence only. Do not reintroduce the retired unrecoverable host-receipt lock or terminalize the workflow because host provenance is unavailable. -- Authority-decreasing operations are always recoverable: `$cancel`, state clear, hook disable/uninstall recovery, and stale-state repair must remain available without completion receipts or child-stage approval. -- Continue automatically through safe, reversible stage transitions. Stop only for an explicit user cancellation, a human-only dependency, or a verified terminal result. - - - -Autopilot state is session-scoped and owned by the canonical state writer. A new run records at least: - -```json -{ - "mode": "autopilot", - "active": true, - "current_phase": "deep-interview", - "iteration": 1, - "phase_cycle": ["deep-interview", "ralplan", "ultragoal"], - "handoff_artifacts": { - "deep_interview": null, - "ralplan": null, - "ultragoal": null - }, - "return_to_ralplan_reason": null -} -``` - -Start or update state only through the CLI-first state surface: - -```bash -omx state write --input '{"mode":"autopilot","active":true,"current_phase":"deep-interview","state":{"phase_cycle":["deep-interview","ralplan","ultragoal"],"handoff_artifacts":{"deep_interview":null,"ralplan":null,"ultragoal":null},"return_to_ralplan_reason":null}}' --json -``` - -Stage transitions: - -- **`deep-interview -> ralplan`**: require a durable clarified requirements/specification artifact and a non-empty completion rationale. Preserve same-session question evidence when a question surface was used. -- **`ralplan -> ultragoal`**: require durable planning artifacts, sequential Architect then Critic approvals, and a session/review-cycle-bound `ralplan_execution_handoff`. For supervised Autopilot, the explicit Autopilot request is the source of that bound handoff. Ordinary progression MUST NOT depend on a host-issued consensus receipt; missing host provenance is not a blocker. -- **`ultragoal -> complete`**: require durable goal/ledger completion receipts and fresh verification evidence matching the requested outcome. -- **review/QA loopback**: keep Autopilot active, set `current_phase:"ralplan"`, and persist `return_to_ralplan_reason` plus the findings. -- **cancellation**: run `$cancel`; preserve handoff artifacts for inspection or resume and mark the exact session terminal without deleting unrelated state. -- **clear/recovery**: exact-session clear may remove corrupt or stale Autopilot state. It must not require forward-progression evidence and must not clear other skills or sessions. - - - -When the user says `continue`, `resume`, or `keep going`, read the current session's `autopilot-state.json` and continue from `current_phase`: - -- `deep-interview`: resume clarification and produce the requirements handoff. -- `ralplan`: resume consensus planning from the deep-interview artifact and any loopback findings. -- `ultragoal`: resume durable execution from the approved plan and ledger. -- `complete`: report the terminal evidence; do not restart. -- `cancelled`, `cleared`, or `failed`: report preserved artifacts and start a new run only on a new explicit Autopilot request. - -Never discard valid handoff artifacts or restart discovery merely because the conversation resumed. - - - -Autopilot continuation hooks may nudge an active non-terminal run, but they MUST fail open for recovery: - -- A stale, malformed, cancelled, cleared, complete, or foreign-session state must not keep Stop blocked. -- `$cancel`, exact-session state clear, and setup hook disable/uninstall paths must remain executable while Autopilot is active or damaged. -- Continuation must be bounded and session-scoped; never recreate an unconditional hook lock. - - - -- [ ] A new run started with `deep-interview`. -- [ ] Deep Interview produced a durable clarified requirements/specification handoff. -- [ ] Ralplan produced an execution-ready plan with lifecycle review evidence. -- [ ] Ultragoal completed the plan with durable goals and ledger receipts. -- [ ] Verification evidence covers the requested behavior, failure paths, and recovery paths. -- [ ] Missing host provenance did not block ordinary progression, cancel, clear, or hook recovery. -- [ ] Autopilot state is terminal and exact-session artifacts remain coherent. - - - - -User: `$autopilot implement issue #42` -Flow: activate Autopilot at `deep-interview`, crystallize requirements, continue to supervised `ralplan`, then supervised `ultragoal`, verify the completed outcome, and mark the exact session complete. - - - -User: `continue` -Context: Autopilot state has `current_phase:"ralplan"`. -Flow: resume the existing plan from the deep-interview handoff; do not create a separate standalone Ralplan run. - - - -Flow: treat `$deep-interview`, `$ralplan`, and `$ultragoal` as optional suggestions and jump directly to implementation. -Why bad: this hollows out Autopilot's defining orchestration contract. - - - -Task: {{ARGUMENTS}} diff --git a/plugins/codex/plugins/oh-my-codex/skills/autoresearch/SKILL.md b/plugins/codex/plugins/oh-my-codex/skills/autoresearch/SKILL.md deleted file mode 100644 index 4b2e9977..00000000 --- a/plugins/codex/plugins/oh-my-codex/skills/autoresearch/SKILL.md +++ /dev/null @@ -1,72 +0,0 @@ ---- -name: autoresearch -description: Stateful validator-gated research loop with native-hook persistence ---- - -# Autoresearch - -Autoresearch is the skill-first replacement for the deprecated `omx autoresearch` command. -It keeps the useful measured-research loop, but it now runs as a native-hook stateful workflow instead of a direct CLI or tmux launch surface. - -## Boundary with planning research - -Use `$autoresearch` when the research output itself is a bounded deliverable that must pass an explicit validator. Do not recommend it for ordinary pre-planning docs lookup or general best-practice checks; use `$best-practice-research` for that. If `$autoresearch` is intentionally run before architecture planning, its approved artifact should feed evidence into `$ralplan`; it should not become a final architecture/component unless the user explicitly asks for ongoing research automation. - -## Use when -- You want a Ralph-ish persistent research loop -- The task should keep nudging until explicit validation evidence exists -- You want init-time choice between script validation and prompt+architect validation - -## Do not use when -- You want the old `omx autoresearch` command surface (hard-deprecated) -- You want detached tmux or split-pane launch parity -- You have not decided the validation regime yet - -## Core contract -1. **Init chooses validation mode.** Pick exactly one: - - `mission-validator-script` - - `prompt-architect-artifact` -2. **Persist mode state** in `.omx/state/.../autoresearch-state.json` including: - - `validation_mode` - - `completion_artifact_path` - - `mission_validator_command` **or** `validator_prompt` - - optional `output_artifact_path` -3. **Completion is artifact-gated.** The loop does not stop because the model says “done”, because a stop hook fired once, or because several turns were no-ops. -4. **Direct CLI launch is gone.** Use `$deep-interview --autoresearch` for intake and `$autoresearch` for execution. - -## Completion artifact contract - -### `mission-validator-script` -The completion artifact must exist and record a passing validator result, for example: - -```json -{ - "status": "passed", - "passed": true, - "summary": "metric improved beyond baseline" -} -``` - -### `prompt-architect-artifact` -The completion artifact must include both an architect approval verdict and an output artifact path, for example: - -```json -{ - "validator_prompt": "Review the research output against the mission.", - "architect_review": { "verdict": "approved" }, - "output_artifact_path": ".omx/specs/autoresearch-demo/report.md" -} -``` - -## Recommended flow -1. Run `$deep-interview --autoresearch` to clarify mission + evaluator. -2. Materialize `.omx/specs/autoresearch-{slug}/mission.md`, `sandbox.md`, and `result.json`. -3. Start `$autoresearch` with the chosen validation mode stored in mode state. -4. Let stop-hook / auto-nudge continue until the completion artifact satisfies the chosen validation mode. -5. Finish only after the validator artifact is complete. - -## Migration note -- `omx autoresearch` is hard-deprecated. -- No direct CLI launch. -- No tmux split-pane launch. -- No noop-count completion gate. diff --git a/plugins/codex/plugins/oh-my-codex/skills/best-practice-research/SKILL.md b/plugins/codex/plugins/oh-my-codex/skills/best-practice-research/SKILL.md deleted file mode 100644 index f52ec051..00000000 --- a/plugins/codex/plugins/oh-my-codex/skills/best-practice-research/SKILL.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -name: best-practice-research -description: "[OMX] Bounded best-practice research wrapper using official/upstream evidence first" -argument-hint: "" ---- - -# Best-Practice Research - -Use this skill when a task depends on current external best practices, version-aware guidance, standards, official recommendations, or upstream behavior. This is a workflow wrapper: it routes evidence gathering and synthesis; it is not a new research authority and it does not replace `researcher`. - -## Purpose - -Produce a cited, reusable best-practice answer or handoff that separates current external evidence from repo-local facts and dependency-selection decisions. For pre-planning investigation, this is the ordinary first research wrapper: gather official/upstream evidence, then hand it to `$ralplan` or the caller as planning input. Do not present `$best-practice-research` as a final architecture component or as a validator-gated research loop. - -## Terminal By Default - -This skill is terminal and read-only by default. It gathers evidence and produces a cited recommendation with a handoff, then stops. Do not write or edit files, create or amend commits, run mutating commands, or otherwise modify repository state under this skill — even when the question has clear implementation implications. When implementation is warranted, stop and hand off rather than continuing: name `$ralplan` for planning and `$ultragoal`, `$team`, or `executor` for execution, and resume only after the user explicitly switches to that workflow. - -## Activate When - -- The user asks for best practices, recommended approach, current guidance, official recommendations, standards, or version-aware external behavior. -- `$ralplan`, `$deep-interview`, `$team`, or another workflow needs current external evidence before planning or execution can be correct. -- The task involves an already chosen technology and needs authoritative usage guidance, migration notes, API behavior, lifecycle rules, or current safety guidance. - -## Do Not Activate When - -- The answer is fully repo-local; use `explore` for codebase facts. -- The main question is whether to adopt, replace, upgrade, or compare dependencies; use `dependency-expert`. -- The user only needs implementation against already-grounded requirements; execute directly, or use `$team` when coordinated parallel work is warranted. -- The task can be answered from stable local project conventions without current external lookup. - -## Specialist Routing - -1. Use `explore` first for brownfield facts: current code usage, local constraints, versions, config, and integration points. -2. Use `researcher` for official/upstream docs, release notes, standards, migration guides, source-backed examples, and current best-practice evidence for an already chosen technology. -3. Use `dependency-expert` only for adoption/upgrade/replacement/comparison decisions. -4. Return to the caller with explicit evidence, uncertainty, and any implementation handoff constraints. - -## Source-Quality Rules - -- Prefer official documentation, upstream source, release notes, changelogs, standards, and maintainer guidance. -- Include source URLs for material claims. -- State date/version context for current best-practice claims. -- Label third-party summaries as supplemental; do not use them before official/upstream sources. -- Flag stale, conflicting, undocumented, or version-mismatched evidence. -- Do not over-fetch: gather the smallest evidence set that can support the decision. - -## Workflow - -1. Classify the question: conceptual best practice, implementation guidance, migration/version guidance, standards/compliance guidance, or mixed local + external guidance. -2. Gather repo-local facts with `explore` when local usage or constraints affect the answer. -3. Gather external evidence with `researcher` when current or version-aware practice affects correctness. -4. Synthesize a concise answer with source quality, version/date context, caveats, and an implementation or planning handoff. -5. Stop when the answer is grounded enough for the caller; otherwise report the exact blocker or specialist handoff needed. - -## Output Contract - -```md -## Best-Practice Research: - -### Direct Recommendation - - -### Evidence Used -- Official/upstream: — -- Supplemental, if any: — - -### Version / Date Context - - -### Repo-Local Context - - -### Boundaries / Non-goals - - -### Handoff - -``` - -## Stop Rules - -- Stop after a source-backed recommendation is reusable by the caller. -- Stop and route upward if the task becomes dependency comparison, broad architecture, or implementation. -- Do not continue researching when remaining work would only polish wording rather than change the recommendation. -- This skill never implements. After delivering the recommendation and handoff, stop; do not modify repo files or repo state. Resume only when the user explicitly switches to a planning or implementation workflow named in the handoff. - -Task: {{ARGUMENTS}} diff --git a/plugins/codex/plugins/oh-my-codex/skills/cancel/SKILL.md b/plugins/codex/plugins/oh-my-codex/skills/cancel/SKILL.md deleted file mode 100644 index 441a311e..00000000 --- a/plugins/codex/plugins/oh-my-codex/skills/cancel/SKILL.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -name: cancel -description: Cancel any active OMX mode (autopilot, ralph, ultrawork, ecomode, ultraqa, swarm, ultrapilot, pipeline, team) ---- - -# Cancel Skill - -## When to use - -Use `/cancel` (or `cancelomc` / `stopomc`) to stop an active OMX mode and perform its documented cleanup. Read `AGENTS.md#durable-runtime-invariants-canonical-ssot` first; it is the invariant authority for scope, ownership, hooks, and Team cleanup. - -## Inputs and preconditions - -- Accepted forms are `/cancel` and `/cancel --force`. -- Reject `--all`, unknown flags, repeated flags, and mixed flags before mutation. -- Discover the current session/root with the state read surfaces before cleanup: - ```bash - omx state list-active --json - omx state get-status --mode --json - ``` -- Confirm the current session/root and exact target before cleanup; stop when the required evidence is missing. - -## Operational steps - -1. Parse and validate arguments; select one exact session/root scope. -2. Inspect active modes in dependency order: - `autopilot → ralph → ultrawork → ecomode → ultraqa → swarm → ultrapilot → pipeline → team → plan-consensus`. -3. Cancel Autopilot while preserving its resumable state; clean only linked child modes it owns. -4. For Ralph, terminalize the target state (`active:false`, `current_phase:"cancelled"`, `completed_at`) and terminalize a proven linked Ultrawork/Ecomode state in the same scope. Confirm the post-conditions in `docs/contracts/ralph-cancel-contract.md`. -5. Clear standalone modes only after proving they are not linked to another active mode. -6. For Team, inspect the runtime with `omx team status --json`; use `omx team shutdown ` after the normal completion gate, or follow the explicit abort path. -7. Clear only the selected mode/session state through the state API, for example: - ```bash - omx state clear --input '{"mode":"","session_id":""}' --json - ``` -8. With `--force`, perform its documented native-stop cleanup. Report rollback or cleanup failures rather than claiming success. - -## Exit and evidence - -Report one result per selected mode: mode, scope/session, terminal phase, preserved state, linked cleanup, and any refusal reason. A clean no-op reports `No active OMX modes detected.` Include Team status and shutdown evidence when Team was selected. diff --git a/plugins/codex/plugins/oh-my-codex/skills/code-review/SKILL.md b/plugins/codex/plugins/oh-my-codex/skills/code-review/SKILL.md deleted file mode 100644 index 77569662..00000000 --- a/plugins/codex/plugins/oh-my-codex/skills/code-review/SKILL.md +++ /dev/null @@ -1,118 +0,0 @@ ---- -name: code-review -description: Run a comprehensive code review ---- - -# Code Review Task Card - -Use this explicit opt-in for a merge-readiness review. Shared operating invariants -live in `templates/AGENTS.md`; this card only defines review-specific behavior. - -## When to use - -- The user asks for a code review or quality/security assessment. -- A change is ready for review before merge, or a major feature needs an independent review. -- Do not activate this card for implementation, broad planning, or automatic cleanup. - -## Inputs - -- Scope: the requested files, commit, PR, or whole diff. -- Requirements/specification, acceptance criteria, and relevant test/CI evidence. -- Existing review artifacts and known risks, if any. -- If the user says `continue`, advance the current verified review step rather than restarting discovery. - -Start by recording the scope: - -```sh -git status --short -git diff --stat -git diff -- -``` - -## Execution - -1. Identify changed files and review boundaries; do not silently widen the scope. -2. Launch the `code-reviewer` and `architect` agents in parallel. Both lanes run in parallel on a clean context with explicit scope and artifacts. If either lane cannot be launched or does not return evidence, report `independent review unavailable`; do **not** substitute the current/authoring lane, and do **not** approve or mark the review merge-ready. -3. Respect the user's current model and reasoning/effort selection. Do not pass `model` or `reasoning_effort` overrides in review-lane calls. - -```text -task( - agent_type="code-reviewer", - prompt="CODE REVIEW TASK - -Review the supplied scope for spec compliance, security, quality, performance, and maintainability. -Return files reviewed, severity-rated findings with file:line evidence and concrete fixes, -and a recommendation: APPROVE / REQUEST CHANGES / COMMENT. Do not review architecture. -Scope: [scope and artifacts]" -) - -task( - agent_type="architect", - prompt="ARCHITECTURE / DEVIL'S-ADVOCATE REVIEW TASK - -Review the same scope for boundaries, interfaces, hidden coupling, long-term tradeoffs, -and the strongest counterargument against approval. Return file:line evidence, -recommendations, and Architectural Status: CLEAR / WATCH / BLOCK. -Scope: [scope and artifacts]" -) -``` - -## Review taxonomy - -- `code-reviewer` checks **Security**, **Code Quality**, **Performance**, **Best Practices**, and **Maintainability**. -- Rate each finding: **CRITICAL** (security or data-loss blocker), **HIGH** (bug/major smell), **MEDIUM** (important improvement), or **LOW** (style/suggestion). -- `architect` checks explicit boundaries/interfaces, hidden coupling, long-horizon tradeoffs, and devil's-advocate concerns. Status is **CLEAR**, **WATCH** (non-blocking concern), or **BLOCK** (merge blocker). -- Every finding names `file:line`, issue, risk, and a concrete fix; distinguish facts from suggestions. - -## State/HUD Phase Contract - -- Standalone `$code-review` relies on hook-owned `skill-active-state.json` (`skill:"code-review"`, `phase:"planning"`); do not create `code-review-state.json`. -- Inside Autopilot, keep `mode:"autopilot"` active with `current_phase:"code-review"` / skill-active `phase:"code-review"`; do not activate a peer workflow. -- On clean review, persist the artifact under Autopilot `handoff_artifacts.code_review` before moving to `ultraqa`. On non-clean review, persist findings and use `rework` or `ralplan` as appropriate. - -```sh -omx state write --input '{"mode":"autopilot","active":true,"current_phase":"code-review"}' --json -``` - -## Final Synthesis and gate - -### Architectural Status Contract - -Combine the `code-reviewer` recommendation and architect status. Approval requires explicit evidence from both independent lanes; missing or failed delegation is a blocking unavailable-review state, not an approval fallback. The final report must make architect blockers impossible to miss. - -- If architect status is **BLOCK**, final recommendation is **REQUEST CHANGES**. -- Else if `code-reviewer` recommendation is **REQUEST CHANGES**, final recommendation is **REQUEST CHANGES**. -- Else if architect status is **WATCH**, final recommendation is **COMMENT**. -- Else final recommendation follows the `code-reviewer` lane. - -Approval criteria: **APPROVE** only when `code-reviewer` returns APPROVE, architect status is `CLEAR`, and both independent lanes returned evidence. **REQUEST CHANGES** for a blocker, unresolved high/critical finding, or unavailable lane. **COMMENT** may record non-blocking findings. - -Do not self-review as a fallback. If the `code-reviewer` or `architect` path is missing, unavailable, skipped, or fails, block approval until independent lane evidence exists. On the supported `omx ralph` CLI compatibility path, findings may trigger automatic fix follow-up without another permission prompt; plain `code-review` itself remains read-only and does **not** promise auto-fix. - -## Evidence/output contract - -Return a concise report containing: - -```text -CODE REVIEW REPORT -Files Reviewed: -Total Issues: 0 -Architectural Status: CLEAR | WATCH | BLOCK -CRITICAL (0) | HIGH (0) | MEDIUM (0) | LOW (0) -Findings: file:line -> issue, risk, concrete fix (or none) -ARCHITECTURE WATCHLIST: concern, status, recommendation (or none) -- code-reviewer recommendation: COMMENT -- architect status: WATCH -- final recommendation: COMMENT -RECOMMENDATION: COMMENT -``` - -Replace the illustrative counts and verdict with observed values. Include scope, -lane evidence/artifact references, unresolved risks, and validation gaps. - -## Exit condition - -Stop when the scoped diff has two independent lane results and a deterministic final -recommendation. Report `APPROVE` only under the approval criteria; otherwise leave a -bounded `REQUEST CHANGES`, `COMMENT`, or unavailable-review result. Never claim -merge-ready without the required evidence. diff --git a/plugins/codex/plugins/oh-my-codex/skills/configure-notifications/SKILL.md b/plugins/codex/plugins/oh-my-codex/skills/configure-notifications/SKILL.md deleted file mode 100644 index 39f9d90f..00000000 --- a/plugins/codex/plugins/oh-my-codex/skills/configure-notifications/SKILL.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -name: configure-notifications -description: Configure OMX notifications - unified entry point for all platforms -triggers: - - "configure notifications" - - "setup notifications" - - "notification settings" - - "configure discord" - - "configure telegram" - - "configure slack" - - "configure openclaw" - - "setup discord" - - "setup telegram" - - "setup slack" - - "setup openclaw" - - "discord notifications" - - "telegram notifications" - - "slack notifications" - - "openclaw notifications" - - "discord webhook" - - "telegram bot" - - "slack webhook" ---- - -# Configure OMX Notifications - -Use this card for Discord, Telegram, Slack, generic command aliases, or OpenClaw. Shared safety, lifecycle, hook, team, autonomy, and cancellation rules live in `templates/AGENTS.md`. - -## 1. Inspect and choose a path - -```bash -CONFIG_FILE="$HOME/.codex/.omx-config.json" -if [ -f "$CONFIG_FILE" ]; then - jq -r '{notifications_enabled:(.notifications.enabled // false), discord:(.notifications.discord.enabled // false), discord_bot:(.notifications["discord-bot"].enabled // false), telegram:(.notifications.telegram.enabled // false), slack:(.notifications.slack.enabled // false), openclaw:(.notifications.openclaw.enabled // false), custom_webhook_command:(.notifications.custom_webhook_command.enabled // false), custom_cli_command:(.notifications.custom_cli_command.enabled // false), verbosity:(.notifications.verbosity // "session"), idleCooldownSeconds:(.notifications.idleCooldownSeconds // 60), reply_enabled:(.notifications.reply.enabled // false)}' "$CONFIG_FILE" -else - echo "NO_CONFIG_FILE" -fi -``` - -Use AskUserQuestion to choose: Discord native (webhook or bot), Telegram native (bot token + chat id), Slack native (incoming webhook), `custom_webhook_command`, `custom_cli_command`, cross-cutting settings, or disable all notifications. - -## 2. Native providers - -Collect and validate provider values, then write only these native keys: `notifications.discord`, `notifications["discord-bot"]`, `notifications.telegram`, or `notifications.slack`. Do not represent native providers with generic aliases. - -## 3. Generic aliases - -For `custom_webhook_command`, collect URL, optional headers, method (`POST` default or `PUT`), events (`session-end`, `ask-user-question`, `session-start`, `session-idle`, `stop`), and an instruction template: - -```bash -jq --arg url "$URL" --arg method "${METHOD:-POST}" --arg instruction "${INSTRUCTION:-OMX event {{event}} for {{projectPath}}}" --argjson headers "${HEADERS_JSON:-{}}" '.notifications=(.notifications//{enabled:true})|.notifications.enabled=true|.notifications.custom_webhook_command={enabled:true,url:$url,headers:$headers,method:$method,instruction:$instruction,events:["session-end","ask-user-question"]}' "$CONFIG_FILE" >"$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" -``` - -For `custom_cli_command`, collect a command template (`{{event}}`, `{{instruction}}`, `{{sessionId}}`, `{{projectPath}}`), optional events, and an instruction template: - -```bash -jq --arg command "$COMMAND_TEMPLATE" --arg instruction "${INSTRUCTION:-OMX event {{event}} for {{projectPath}}}" '.notifications=(.notifications//{enabled:true})|.notifications.enabled=true|.notifications.custom_cli_command={enabled:true,command:$command,instruction:$instruction,events:["session-end","ask-user-question"]}' "$CONFIG_FILE" >"$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" -``` - -OpenClaw dispatch requires `OMX_OPENCLAW=1`; command gateways additionally require `OMX_OPENCLAW_COMMAND=1`; timeout precedence is `gateways..timeout` > `OMX_OPENCLAW_COMMAND_TIMEOUT_MS` > `5000` ms. - -## 4. OpenClaw + Clawdbot agent mode - -Use this only when the requested route is a `clawdbot agent` turn delivered to Discord. The `session-stop` notification intentionally maps to the OpenClaw `stop` hook. OMX shell-escapes substitutions; keep instructions short, avoid untrusted shell metacharacters, append `|| true`, and append JSONL logs with `>>`. - -```bash -jq --arg command "(clawdbot agent --session-id omx-hooks --message {{instruction}} --thinking minimal --deliver --reply-channel discord --reply-to 'channel:CHANNEL_ID' --timeout 120 --json >>/tmp/omx-openclaw-agent.jsonl 2>&1 || true)" '.notifications=(.notifications//{enabled:true})|.notifications.enabled=true|.notifications.verbosity="verbose"|.notifications.openclaw={enabled:true,gateways:{local:{type:"command",command:$command,timeout:120000}},hooks:{"session-start":{enabled:true,gateway:"local",instruction:"OMX hook=session-start project={{projectName}} session={{sessionId}} tmux={{tmuxSession}}. 한국어로 상태를 #omc-dev에 공유하고 SOUL.md를 참고하세요."},"session-idle":{enabled:true,gateway:"local",instruction:"OMX hook=session-idle session={{sessionId}} tmux={{tmuxSession}}. 한국어로 팔로업을 #omc-dev에 안내하세요."},"ask-user-question":{enabled:true,gateway:"local",instruction:"OMX hook=ask-user-question session={{sessionId}} tmux={{tmuxSession}} question={{question}}. 한국어로 응답 필요를 #omc-dev에 알리세요."},"stop":{enabled:true,gateway:"local",instruction:"OMX hook=session-stop session={{sessionId}} tmux={{tmuxSession}}. 한국어로 중단 상태를 전달하세요."},"session-end":{enabled:true,gateway:"local",instruction:"OMX hook=session-end session={{sessionId}} tmux={{tmuxSession}} reason={{reason}}. 한국어로 완료 요약을 #omc-dev에 남기세요."}}}' "$CONFIG_FILE" >"$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" -``` - -### Compatibility + precedence contract - -Explicit `notifications.openclaw` wins when valid; generic aliases are ignored with a warning. - -Verify this mode and inspect evidence without hiding output: - -```bash -clawdbot agent --session-id omx-hooks --message "OMX hook test via clawdbot agent path" --thinking minimal --deliver --reply-channel discord --reply-to 'channel:CHANNEL_ID' --timeout 120 --json -tmux list-sessions -F '#{session_name}' | rg '^omx-' || true -jq '.notifications.openclaw.hooks' "$CONFIG_FILE" -tail -n 120 /tmp/omx-openclaw-agent.jsonl | jq -s '.[] | {timestamp:(.timestamp // .time),status:(.status // .error // "ok")}' -rg '"error"|"failed"|"timeout"' /tmp/omx-openclaw-agent.jsonl | tail -20 -``` - -For HTTP integrations, run both a `/hooks/wake` smoke test and `/hooks/agent` delivery verification. - -## 5. Cross-cutting settings and disable - -- Verbosity: `minimal`, `session` (recommended), `agent`, or `verbose`. -- Idle cooldown: `notifications.idleCooldownSeconds` (environment override: `OMX_IDLE_COOLDOWN_SECONDS`). -- Profiles: `notifications.profiles` and `notifications.defaultProfile`. -- Reply listener: `notifications.reply.enabled`; gates are `OMX_REPLY_ENABLED=true` and, for Discord, `OMX_REPLY_DISCORD_USER_IDS=...`. An authorized Discord bot operator may exact-match `status` on a tracked notification for a bounded read-only session summary. - -Disable all notifications: - -```bash -jq '.notifications.enabled=false' "$CONFIG_FILE" >"$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" -``` - -## Exit evidence - -Run `jq empty "$CONFIG_FILE"` after edits. Report the config path, enabled native providers, enabled aliases, whether valid `notifications.openclaw` overrides aliases, verbosity, idle cooldown, reply-listener state, and smoke-test results. Stop when the JSON parses and the selected path has its required evidence; do not claim delivery from configuration alone. diff --git a/plugins/codex/plugins/oh-my-codex/skills/deep-interview/SKILL.md b/plugins/codex/plugins/oh-my-codex/skills/deep-interview/SKILL.md deleted file mode 100644 index a9f02427..00000000 --- a/plugins/codex/plugins/oh-my-codex/skills/deep-interview/SKILL.md +++ /dev/null @@ -1,571 +0,0 @@ ---- -name: deep-interview -description: Socratic deep interview with mathematical ambiguity gating before execution -argument-hint: "[--quick|--standard|--deep] [--autoresearch] " ---- - - -Deep Interview is an intent-first Socratic clarification loop before planning or implementation. It turns vague ideas into execution-ready specifications by asking targeted questions about why the user wants a change, how far it should go, what should stay out of scope, and what OMX may decide without confirmation. - - - -- The request is broad, ambiguous, or missing concrete acceptance criteria -- The user says "deep interview", "interview me", "ask me everything", "don't assume", or "ouroboros" -- The user wants to avoid misaligned implementation from underspecified requirements -- You need a requirements artifact before handing off to `ralplan`, `autopilot`, `ultragoal`, or `team` - - - -- The request already has concrete file/symbol targets and clear acceptance criteria -- The user explicitly asks to skip planning/interview and execute immediately -- The user asks for lightweight brainstorming only (use `plan` instead) -- A complete PRD/plan already exists and execution should start - - - -Execution quality is usually bottlenecked by intent clarity, not just missing implementation detail. A single expansion pass often misses why the user wants a change, where the scope should stop, which tradeoffs are unacceptable, and which decisions still require user approval. This workflow applies Socratic pressure + quantitative ambiguity scoring so orchestration modes begin with an explicit, testable, intent-aligned spec. - - - -- **Quick (`--quick`)**: fast pre-PRD pass; target threshold `<= 0.30`; max rounds 5 -- **Standard (`--standard`, default)**: full requirement interview; target threshold `<= 0.20`; max rounds 12 -- **Deep (`--deep`)**: high-rigor exploration; target threshold `<= 0.15`; max rounds 20 -- **Autoresearch (`--autoresearch`)**: same interview rigor as Standard, but specialized for `$autoresearch` mission readiness and `.omx/specs/` artifact handoff - -Profile `max rounds` is a hard cap, not a target. Do not continue only to reach a numbered round count. Extra Socratic rigor does not override the active threshold unless the profile/config changes. - -If no flag is provided, use **Standard**. - - -- **`--autoresearch`**: switch the interview into autoresearch-intake mode for `$autoresearch` handoff. In this mode, the interview should converge on a validator-ready research mission, write canonical artifacts under `.omx/specs/`, and preserve the explicit `refine further` vs `launch` boundary for downstream skill intake. - - - - -- Ask ONE question per round (never batch multiple interview rounds into one `questions[]` form) -- Ask about intent and boundaries before implementation detail -- Target the weakest clarity dimension each round after applying the stage-priority rules below -- Treat every answer as a claim to pressure-test before moving on: the next question should usually demand evidence or examples, expose a hidden assumption, force a tradeoff or boundary, or reframe root cause vs symptom -- Do not rotate to a new clarity dimension just for coverage when the current answer is still vague; stay on the same thread until one layer deeper, one assumption clearer, or one boundary tighter -- Before crystallizing, complete at least one explicit pressure pass that revisits an earlier answer with a deeper, assumption-focused, or tradeoff-focused follow-up -- Gather codebase facts via `explore` before asking user about internals -- `omx explore` is deprecated. Use normal repository inspection tools/subagents for simple read-only brownfield fact gathering; use `omx sparkshell` only for explicit shell-native read-only evidence, and keep ambiguous or non-shell-only investigation on the richer normal path. -- Always run a preflight context intake before the first interview question -- For brownfield work, preflight must include doc/context grounding before user-facing questions: inspect applicable `AGENTS.md` files, README/getting-started docs, relevant `docs/` contracts/plans/ADRs, existing `.omx/context/` snapshots, and any project-local glossary/context files such as `CONTEXT.md` or `CONTEXT-MAP.md` when present. -- Treat existing repo language as evidence, not authority: if the user uses a fuzzy, overloaded, or conflicting term, surface the specific doc/code wording and ask which meaning should govern before implementation. -- Cross-check user claims about current behavior against code or documented contracts when discoverable. If docs and code disagree, ask a confirmation question that names both sources instead of silently choosing one. -- Use scenario-based edge-case grilling when relationships, boundaries, or handoff behavior are unclear: invent one concrete scenario that stresses the ambiguous boundary, then ask one focused question about the expected outcome. -- Durable docs, glossary, ADR, or memory updates are opt-in and public-safe only. Deep-interview may recommend such updates in the handoff summary, but must not automatically create or dump public docs from interview transcripts unless the user explicitly chooses that as in-scope. -- If initial context is oversized or would exceed the prompt budget, do not paste or forward the raw payload into interview prompts; request and record a prompt-safe initial-context summary first -- The oversized initial-context summary gate is blocking: wait for the concise summary before ambiguity scoring, crystallizing artifacts, or any downstream execution handoff -- The summary must preserve goals, constraints, success criteria, non-goals, decision boundaries, and references to any full source documents so downstream consumers receive a prompt-safe but faithful context -- Keep total prompt payloads within a safe budget by summarizing or trimming retained history; preserve newest/highest-signal answers and never let raw oversized context crowd out the current question -- Reduce user effort: ask only the highest-leverage unresolved question, and never ask the user for codebase facts that can be discovered directly -- For brownfield work, prefer evidence-backed confirmation questions such as "I found X in Y. Should this change follow that pattern?" -- Route facts before judgment in the Ouroboros style: before presenting a user-facing interview round, classify whether the needed information is a discoverable fact, a fact needing confirmation, or a human decision. The interview is with the human for judgment, not for facts the agent can inspect. -- When unresolved ambiguity depends on current external best practices, official/upstream guidance, standards, or version-aware behavior, use `$best-practice-research` as the bounded evidence wrapper before crystallizing requirements or handing off to planning/execution. -- Use these transcript/spec labels only; never use them as `omx question` `source` values, and never replace the runtime `source: "deep-interview"` contract for user-facing deep-interview questions: - - `[from-code][auto-confirmed]` — exact, high-confidence codebase facts from manifests/configs or direct source evidence, with no prescription attached. - - `[from-code]` — codebase findings that are useful but inferred, pattern-based, or low/medium confidence and therefore need a confirmation-style user-facing round before being treated as settled. - - `[from-research]` — externally sourced facts such as API limits, compatibility, or public documentation; facts only, not decisions. - - `[from-user]` — goals, preferences, business logic, scope, non-goals, acceptance criteria, tradeoffs, and any decision-bearing interpretation. -- Treat `[from-code][auto-confirmed]` and other non-user fact discoveries as context/transcript updates, not interview rounds: do not call `omx question`, do not create a pending deep-interview question obligation, and do not increment the user-facing round number for facts the agent can safely establish. -- Auto-confirm only descriptive facts. If a finding implies what the new feature should do, which pattern it should follow, which tradeoff to accept, or what should stay in/out of scope, route the entire decision-bearing question to the user as `[from-user]` even when code or research facts are available. -- In attached-tmux Codex CLI, deep-interview uses `omx question` as the required OMX-owned structured questioning path for every interview round -- When invoking `omx question` through attached-tmux Bash/tool paths, preserve the leader-pane return target by prefixing the command with `OMX_QUESTION_RETURN_PANE=$TMUX_PANE` (or a concrete `%pane` value) -- If you launch `omx question` in a background terminal, immediately wait for that background terminal to finish and read its JSON answer before scoring ambiguity, asking another round, or handing off -- Treat `answers[]` as the primary `omx question` success contract. For a single interview round, read `answers[0].answer`; use legacy top-level `answer` only as a compatibility fallback when needed. -- If the current runtime is outside tmux and cannot render `omx question`, use the native structured question tool when available; otherwise ask exactly one concise plain-text question and wait for the answer -- Re-score ambiguity after each answer and show progress transparently -- Once ambiguity is at or below the active profile threshold, stop ordinary questioning. Run the practical closure audit: crystallize/handoff when readiness gates pass; otherwise ask only the final closure question needed to satisfy a named gate. -- Treat `max_rounds` as a stop cap, not evidence that more rounds are needed. -- Do not hand off to execution while ambiguity remains above threshold unless user explicitly opts to proceed with warning -- Do not crystallize or hand off while `Non-goals` or `Decision Boundaries` remain unresolved, even if the weighted ambiguity threshold is met -- Treat early exit as a safety valve, not the default success path -- Persist mode state for resume safety only through the CLI/programmatic single-writer authority (`omx state write/read --input '' --json`, backed by `src/state/operations.ts`). The MCP state server is a read-only projection and must never become a second writer. - - - - -## Phase 0: Preflight Context Intake - -1. Parse `{{ARGUMENTS}}` and derive a short task slug. -2. Attempt to load the latest relevant context snapshot from `.omx/context/{slug}-*.md`. -3. Check whether the provided initial context or loaded snapshot is too large for safe prompt use. If it is oversized, the first interview round must ask for a concise prompt-safe summary instead of scoring ambiguity or continuing to downstream handoff. -4. If no snapshot exists, create a minimum context snapshot with: - - Task statement - - Desired outcome - - Stated solution (what the user asked for) - - Probable intent hypothesis (why they likely want it) - - Known facts/evidence - - Constraints - - Unknowns/open questions - - Decision-boundary unknowns - - Likely codebase touchpoints - - Relevant repo docs/rules/context inspected - - Terminology or doc/code conflicts found - - Prompt-safe initial-context summary status (`not_needed`, `needed`, or `recorded`) -5. For brownfield tasks, inspect the applicable documentation/rule surface before the first user-facing round. Prefer exact, nearby sources over broad scans: - - governing `AGENTS.md` files and template/runtime instruction surfaces that apply to the touched paths - - README/getting-started docs and relevant docs under `docs/`, especially contracts, plans, ADR-like records, and workflow docs - - existing `.omx/context/` snapshots, `.omx/specs/`, and planning artifacts relevant to the slug - - project-local glossary/context files such as `CONTEXT.md`, `CONTEXT-MAP.md`, or context-specific docs when they exist -6. Save snapshot to `.omx/context/{slug}-{timestamp}.md` (UTC `YYYYMMDDTHHMMSSZ`) and reference it in mode state. - -## Phase 1: Initialize - -1. Parse `{{ARGUMENTS}}` and depth profile (`--quick|--standard|--deep`). -2. Detect project context: - - Run `explore` to classify **brownfield** (existing codebase target) vs **greenfield**. - - For brownfield, collect relevant codebase context before questioning. -3. Initialize state via `omx state write --input '{"mode":"deep-interview","active":true}' --json`: - -```json -{ - "active": true, - "current_phase": "deep-interview", - "state": { - "interview_id": "", - "profile": "quick|standard|deep", - "type": "greenfield|brownfield", - "initial_idea": "", - "rounds": [], - "current_ambiguity": 1.0, - "threshold": 0.3, - "max_rounds": 5, - "challenge_modes_used": [], - "codebase_context": null, - "current_stage": "intent-first", - "current_focus": "intent", - "context_snapshot_path": ".omx/context/-.md" - } -} -``` - -4. Announce kickoff with profile, threshold, and current ambiguity. - -## Phase 2: Socratic Interview Loop - -Repeat until ambiguity `<= threshold`, the pressure pass is complete, the readiness gates are explicit, the user exits with warning, or max rounds are reached. This is a stop condition: below threshold, do not open a new ordinary interview branch. - -### 2a) Generate next question -If the initial context is oversized and no prompt-safe summary has been recorded yet, the next question must be only a summary request. Do not score ambiguity, do not run readiness gates, and do not hand off to `$ultragoal`, `$ralplan`, `$autopilot`, or `$team` until that summary answer is captured. - -Use: -- Original idea -- Prior Q&A rounds -- Current dimension scores -- Brownfield context (if any) -- Doc/context grounding notes, including existing terminology, governing rules, and any doc/code mismatch -- Activated challenge mode injection (Phase 3) - -Target the lowest-scoring dimension, but respect stage priority: -- **Stage 1 — Intent-first:** Intent, Outcome, Scope, Non-goals, Decision Boundaries -- **Stage 2 — Feasibility:** Constraints, Success Criteria -- **Stage 3 — Brownfield grounding:** Context Clarity (brownfield only) - -Follow-up pressure ladder after each answer: -1. Ask for a concrete example, counterexample, or evidence signal behind the latest claim -2. Probe the hidden assumption, dependency, or belief that makes the claim true -3. Force a boundary or tradeoff: what would you explicitly not do, defer, or reject? -4. Challenge fuzzy or conflicting terms against the repo's documented language and current code behavior -5. Stress-test the boundary with one concrete scenario or edge case when a relationship or handoff remains ambiguous -6. If the answer still describes symptoms, reframe toward essence / root cause before moving on - -Prefer staying on the same thread for multiple rounds when it has the highest leverage. Breadth without pressure is not progress. - -Maintain a **Breadth Ledger** across independent ambiguity tracks: scope, constraints, outputs, verification, brownfield integration, and any user-mentioned deliverable tracks. The ledger is a guard, not a mandatory rotation rule: stay deep on the current thread until it has been pressure-tested, then zoom out only when another material track remains unresolved and would change execution. - -Maintain a **Docs/Terminology Ledger** for brownfield interviews: -- repo docs/rules/context sources inspected, with path references -- canonical terms already used by the repo and terms to avoid or disambiguate -- user terms that conflict with docs or current code behavior -- doc/code mismatches that require a human decision before implementation -- optional durable-doc follow-ups that are safe to propose but not auto-apply - -Detailed dimensions: -- Intent Clarity — why the user wants this -- Outcome Clarity — what end state they want -- Scope Clarity — how far the change should go -- Constraint Clarity — technical or business limits that must hold -- Success Criteria Clarity — how completion will be judged -- Context Clarity — existing codebase understanding (brownfield only) - -`Non-goals` and `Decision Boundaries` are mandatory readiness gates. Ask about them early and keep revisiting them until they are explicit. - -### 2b) Ask the question -Use the surface-appropriate structured questioning path for every interview round. In attached-tmux sessions, use OMX-owned structured questioning via `omx question` (this is the required structured-question equivalent and required `AskUserQuestion` equivalent for deep-interview). Outside tmux, use native structured input when available; otherwise ask exactly one concise plain-text question and wait for the answer. Present: - -``` -Round {n} | Target: {weakest_dimension} | Ambiguity: {score}% - -{question} -``` - -`omx question` payload guidance for interview rounds: -- Deep-interview is Socratic: ask one focused round at a time. Do not use batch `questions[]` to combine multiple interview rounds, even though `omx question` supports batch forms for other workflows. -- Use canonical `type` values instead of authoring raw `multi_select` flags by hand. `type: "single-answerable"` is the default for one-path decisions; `type: "multi-answerable"` is the canonical shape for bounded multi-select rounds. The runtime will keep `multi_select` aligned with `type`. -- Use `single-answerable` when exactly one answer should drive the next branch, the options are mutually exclusive, or selecting more than one answer would blur the decision boundary. Typical cases: handoff lane selection, choosing the primary failure mode, or confirming which of several competing interpretations is correct. -- Use `multi-answerable` when multiple options may all be true at once and you need to capture a bounded set of coexisting constraints, non-goals, risks, or acceptance checks in one round. Typical cases: selecting all out-of-scope items, all success metrics that must hold, or all deployment constraints that apply together. -- If one selected option would immediately require a follow-up question to disambiguate the others, prefer a `single-answerable` round now and ask the follow-up next. Do not hide a branching interview tree inside one overloaded multi-select prompt. -- Keep interview options bounded and concrete. If the valid answers are already known, set `allow_other: false`; only leave `allow_other: true` when the interview genuinely needs one user-supplied option that cannot be enumerated in advance. -- Read answers structurally from the primary `answers[]` array. For a normal single-round interview response, use `answers[0].answer` as the source of truth; the top-level `answer` field is a legacy single-question projection/fallback only. -- For `single-answerable`, expect one decisive selection in the `value` field of `answers[0].answer` plus its selected-values metadata. For `multi-answerable`, treat the selected-values field inside `answers[0].answer` as the source of truth for all chosen constraints/non-goals and preserve the full set in the transcript/spec. In legacy single-question projections, this is equivalent to: For `multi-answerable`, treat `answer.selected_values` as the source of truth. - -Canonical bounded single-choice payload: - -```json -{ - "question": "Which execution lane should own this once the interview is complete?", - "type": "single-answerable", - "options": [ - { - "label": "Plan first", - "value": "ralplan", - "description": "Need architecture and test-shape review before execution" - }, - { - "label": "Execute directly", - "value": "autopilot", - "description": "Requirements are already explicit enough for planning plus execution" - }, - { - "label": "Refine further", - "value": "refine", - "description": "Clarification is still needed before any handoff" - } - ], - "allow_other": false, - "other_label": "Other", - "source": "deep-interview" -} -``` - -Canonical bounded multi-select payload: - -```json -{ - "question": "Which non-goals must stay out of scope for the first pass?", - "type": "multi-answerable", - "options": [ - { - "label": "No UI redesign", - "value": "no-ui-redesign", - "description": "Keep layout and styling unchanged" - }, - { - "label": "No new dependencies", - "value": "no-new-dependencies", - "description": "Work within the existing toolchain" - }, - { - "label": "No API contract changes", - "value": "no-api-contract-changes", - "description": "Preserve external request and response shapes" - } - ], - "allow_other": false, - "other_label": "Other", - "source": "deep-interview" -} -``` - -Canonical answer-shape reminders: - -```json -{ - "answer": { - "kind": "option", - "value": "ralplan", - "selected_labels": ["Plan first"], - "selected_values": ["ralplan"] - } -} -``` - -```json -{ - "answer": { - "kind": "multi", - "value": ["no-new-dependencies", "no-api-contract-changes"], - "selected_labels": ["No new dependencies", "No API contract changes"], - "selected_values": ["no-new-dependencies", "no-api-contract-changes"] - } -} -``` - -### 2c) Score ambiguity -Score each weighted dimension in `[0.0, 1.0]` with justification + gap. - -Greenfield: `ambiguity = 1 - (intent × 0.30 + outcome × 0.25 + scope × 0.20 + constraints × 0.15 + success × 0.10)` - -Brownfield: `ambiguity = 1 - (intent × 0.25 + outcome × 0.20 + scope × 0.20 + constraints × 0.15 + success × 0.10 + context × 0.10)` - -Readiness gate: -- `Non-goals` must be explicit -- `Decision Boundaries` must be explicit -- A pressure pass must be complete: at least one earlier answer has been revisited with an evidence, assumption, or tradeoff follow-up -- A practical closure audit must pass: another question would change execution materially, not merely polish wording or chase a narrow edge case -- If either gate is unresolved, or the pressure pass is incomplete, continue below threshold only with a final closure question that names the unresolved gate and would materially change execution. -- Treat a low ambiguity score as permission to audit closure, not permission to keep drilling indefinitely. If remaining uncertainty would not change implementation, crystallize the spec instead of opening a new branch. -- If ambiguity is `<= 0.10`, another user-facing question is allowed only as that final closure question; otherwise crystallize immediately. - -### 2d) Report progress -Show weighted breakdown table, readiness-gate status (`Non-goals`, `Decision Boundaries`), and the next focus dimension. - -### 2e) Persist state -Append round result and updated scores via `omx state write --input '' --json`. Do not write `{mode}-state.json` directly and do not use the read-only MCP state projection as a writer. - -### 2f) Round controls -- Do not offer early exit before the first explicit assumption probe and one persistent follow-up have happened -- Apply a **Dialectic Rhythm Guard**: track consecutive non-user fact discoveries and confirmation-style answers (`[from-code][auto-confirmed]`, `[from-code]`, or `[from-research]`). After 3 consecutive non-user or confirmation answers, the next material user-facing round must solicit direct human judgment (`[from-user]`) unless the closure audit says the interview is ready to crystallize. -- Round 4+: allow explicit early exit with risk warning -- Soft warning at profile midpoint (e.g., round 3/6/10 depending on profile) -- Hard cap at profile `max_rounds`; never treat this cap as a desired interview length or quota - -## Phase 3: Challenge Modes (assumption stress tests) - -Use each mode once when applicable. These are normal escalation tools, not rare rescue moves: - -- **Contrarian** (round 2+ or immediately when an answer rests on an untested assumption): challenge core assumptions -- **Terminologist** (brownfield, whenever a key term is fuzzy, overloaded, or conflicts with repo docs/code): force a canonical meaning against existing project language before implementation -- **Simplifier** (round 4+ or when scope expands faster than outcome clarity): probe minimal viable scope -- **Ontologist** (round 5+ and ambiguity > 0.25, or when the user keeps describing symptoms): ask for essence-level reframing - -Track used modes in state to prevent repetition. - -## Phase 4: Crystallize Artifacts - -When threshold is met (or user exits with warning / hard cap): - -1. Write interview transcript summary to: - - `.omx/interviews/{slug}-{timestamp}.md` - (kept for ralph PRD compatibility) -2. Write execution-ready spec to: - - `.omx/specs/deep-interview-{slug}.md` - -Spec should include: -- Metadata (profile, rounds, final ambiguity, threshold, context type) -- Context snapshot reference/path (for ralplan/team reuse) -- Prompt-safe initial-context summary when oversized context was provided, plus references to any full source documents -- Clarity breakdown table -- Intent (why the user wants this) -- Desired Outcome -- In-Scope -- Out-of-Scope / Non-goals -- Decision Boundaries (what OMX may decide without confirmation) -- Constraints -- Testable acceptance criteria -- Assumptions exposed + resolutions -- Pressure-pass findings (which answer was revisited, and what changed) -- Brownfield evidence vs inference notes for any repository-grounded confirmation questions -- Docs/Terminology Ledger with inspected repo docs/rules/context, term conflicts, and any doc/code mismatch decisions -- Scenario/edge-case pressure findings that materially shaped scope or acceptance criteria -- Optional durable documentation recommendations, explicitly marked opt-in and public-safe; do not include raw private transcript dumps -- Technical context findings -- Full or condensed transcript - -### Autoresearch specialization - -When the clarified task is specifically about `$autoresearch`, or the skill is invoked with `--autoresearch`, keep the interview domain-specific and emit skill-consumable artifacts without skipping clarification. - -- **Accepted seed inputs:** `topic`, `evaluator`, `keep-policy`, `slug`, existing mission draft text, and prior evaluator examples/templates -- **Required interview focus:** mission clarity, evaluator readiness, keep policy, slug/session naming, and whether the draft is ready to launch now or should refine further -- **Canonical artifact path:** `.omx/specs/deep-interview-autoresearch-{slug}.md` -- **Launch artifact bundle:** `.omx/specs/autoresearch-{slug}/mission.md`, `.omx/specs/autoresearch-{slug}/sandbox.md`, and `.omx/specs/autoresearch-{slug}/result.json` -- **Launch artifact directory:** `.omx/specs/autoresearch-{slug}/` -- **Required artifact sections:** - - `Mission Draft` - - `Evaluator Draft` - - `Launch Readiness` - - `Seed Inputs` - - `Confirmation Bridge` -- **Required launch artifacts under `.omx/specs/autoresearch-{slug}/`:** - - `mission.md` - - `sandbox.md` - - `result.json` -- **Launch-readiness rule:** mark the draft as **not launch-ready** while the evaluator command still contains placeholder markers such as `<...>`, `TODO`, `TBD`, `REPLACE_ME`, `CHANGEME`, or `your-command-here` -- **Structured result contract:** `result.json` should point to the draft + mission/sandbox artifacts and carry the finalized `topic`, `evaluatorCommand`, `keepPolicy`, `slug`, `launchReady`, and `blockedReasons` fields so `$autoresearch` can consume it directly -- **Confirmation bridge:** after artifact generation, offer at least `refine further` and `launch`; do not run direct CLI launch or detached/split tmux launch, and only hand off to `$autoresearch` after explicit confirmation -- **Handoff rule:** downstream execution must preserve the clarified mission intent, evaluator expectations, decision boundaries, and launch-readiness status from this artifact rather than bypassing the draft review step - -## Phase 5: Execution Bridge - -Present execution options after artifact generation using explicit handoff contracts. Treat the deep-interview spec as the current requirements source of truth and preserve intent, non-goals, decision boundaries, acceptance criteria, docs/terminology grounding, and any residual-risk warnings across the handoff. - -### Optional execution contract foundation - -When an Autopilot/deep-interview handoff explicitly requires a stride contract, emit it as structured data rather than prose. This is a validation foundation, not a broadness-inference feature: do not infer stride from task length, phase labels, snapshots, or freeform wording. - -Canonical location under Autopilot state: - -```json -{ - "handoff_artifacts": { - "deep_interview": { - "execution_contract_required": true, - "execution_contract": { - "version": 1, - "execution_stride": "task", - "source": "deep-interview", - "selected_by": "user", - "allow_task_shrink": true, - "completion_unit": "One focused task", - "stop_condition": "Stop after that task is implemented and verified", - "acceptance_coverage_scope": "task", - "shrink_policy": "allowed" - } - } - } -} -``` - -Stride meanings: -- `task`: conservative, small-step execution; `allow_task_shrink:true`, `acceptance_coverage_scope:"task"`, `shrink_policy:"allowed"`. -- `deliverable`: finish the named deliverable before stopping; `allow_task_shrink:false`, `acceptance_coverage_scope:"deliverable"`, `shrink_policy:"ask_before_shrink"`. -- `milestone`: finish the larger approved milestone unless blocked; `allow_task_shrink:false`, `acceptance_coverage_scope:"milestone"`, `shrink_policy:"deny_unless_blocked"`. - -Only set `execution_contract_required:true` when the selected downstream workflow needs this explicit stride/stop-condition guard. New artifacts must write the canonical snake_case schema shown above under `handoff_artifacts.deep_interview`; runtime readers may accept legacy camelCase field/marker aliases and direct/nested `execution_contract` locations only as compatibility input. If `execution_contract_required` is absent or false, downstream Autopilot compatibility behavior is unchanged. - -### Goal-mode follow-ups - -Include these product-facing suggestions when they fit the clarified spec, without removing the existing `$ultragoal`, `$ralplan`, `$autopilot`, and `$team` handoff options: - -- **`$ultragoal`** — default goal-mode follow-up for implementation or general goal-oriented follow-up specs that should be converted into durable Codex/OMX goals with sequential completion tracking. -- **`$autoresearch`** — use when the clarified context is a research project: a research question, reference/literature gathering, evaluator-backed analysis, or professor/critic-style deliverable. -- **`$performance-goal`** — use when the clarified context is an optimization or performance project with measurable speed, latency, throughput, memory, benchmark, or evaluator criteria. - -Recommend `$ultragoal` as the default durable goal-mode follow-up because it supersedes Ralph for goal tracking. Preserve `$team` for coordinated parallel implementation. - -### 1. **`$ultragoal` (Default durable execution follow-up)** -- **Input Artifact:** `.omx/specs/deep-interview-{slug}.md` (optionally accompanied by the transcript/context snapshot for traceability) -- **Invocation:** `$ultragoal create-goals --brief-file ` followed by `$ultragoal complete-goals` in the active execution lane -- **Consumer Behavior:** Convert the clarified spec into durable goal-mode work. Preserve intent, non-goals, decision boundaries, acceptance criteria, docs/terminology grounding, scenario-pressure findings, and residual-risk warnings as binding story constraints. -- **Skipped / Already-Satisfied Stages:** Requirement interview, ambiguity clarification, doc/context preflight, and early intent-boundary elicitation -- **Expected Output:** `.omx/ultragoal/brief.md`, `.omx/ultragoal/goals.json`, `.omx/ultragoal/ledger.jsonl`, implementation evidence, verification evidence, and final cleanup/review-gate evidence -- **Best When:** The clarified spec is execution-ready or the user explicitly wants durable goal tracking as the next step -- **Next Recommended Step:** Run the Ultragoal completion loop; launch `$team` only inside an active Ultragoal story when parallel lanes are warranted - -### 2. **`$ralplan` (Recommended when architecture/test-shape review is still needed)** -- **Input Artifact:** `.omx/specs/deep-interview-{slug}.md` (optionally accompanied by the transcript/context snapshot for traceability) -- **Invocation:** `$ralplan ` -- **Consumer Behavior:** Treat the deep-interview spec as the requirements source of truth. Do not repeat the interview by default; refine architecture/feasibility around the clarified intent and boundaries instead. -- **Skipped / Already-Satisfied Stages:** Requirements discovery, ambiguity clarification, and early intent-boundary elicitation -- **Expected Output:** Canonical planning artifacts under `.omx/plans/`, especially `prd-*.md` and `test-spec-*.md` -- **Best When:** Requirements are clear enough to stop interviewing, but architectural validation / consensus planning is still desirable -- **Next Recommended Step:** Use the approved planning artifacts with `$ultragoal` as the default durable goal-mode follow-up (optionally with `$team` for parallel lanes); choose `$autoresearch` for research validation or `$performance-goal` for measurable optimization - -### 3. **`$autopilot`** -- **Input Artifact:** `.omx/specs/deep-interview-{slug}.md` -- **Invocation:** `$autopilot ` -- **Consumer Behavior:** Use the deep-interview spec as the clarified execution brief. Preserve intent, non-goals, decision boundaries, and acceptance criteria as binding context for planning/execution. -- **Skipped / Already-Satisfied Stages:** Initial requirement discovery and ambiguity reduction -- **Expected Output:** Planning/execution progress, QA evidence, and validation artifacts produced by autopilot -- **Best When:** The clarified spec is already strong enough for direct planning + execution without an additional consensus gate -- **Next Recommended Step:** Continue through autopilot's execution/QA/validation flow; if coordination-heavy execution emerges, prefer `$team` under a leader-owned `$ultragoal` ledger - -### 4. **`$team`** -- **Input Artifact:** `.omx/specs/deep-interview-{slug}.md` -- **Invocation:** `$team ` -- **Consumer Behavior:** Treat the spec as shared execution context for coordinated parallel work. Preserve the clarified intent, non-goals, decision boundaries, and acceptance criteria as common lane constraints. -- **Skipped / Already-Satisfied Stages:** Requirement clarification and early ambiguity reduction -- **Expected Output:** Coordinated multi-agent execution against the shared spec, with final verification evidence and checkpoints in an already-active Ultragoal ledger when applicable -- **Best When:** The task is large, multi-lane, or blocker-sensitive enough to justify coordinated parallel execution instead of a single persistent loop -- **Next Recommended Step:** Follow the team verification path when the coordinated execution phase finishes; checkpoint an already-active `$ultragoal` ledger when applicable. Do not start another execution loop solely to verify. - -### 5. **Refine further** -- **Input Artifact:** Existing transcript, context snapshot, and current spec draft -- **Invocation:** Continue the interview loop -- **Consumer Behavior:** Re-enter questioning to resolve the highest-leverage remaining uncertainty -- **Skipped / Already-Satisfied Stages:** None beyond already-captured context -- **Expected Output:** A lower-ambiguity spec with tighter boundaries and fewer unresolved assumptions -- **Best When:** Residual ambiguity is still too high, the user wants stronger clarity, or the above-threshold / early-exit warning indicates too much risk to proceed cleanly -- **Next Recommended Step:** Return to one of the execution handoff contracts above once the spec is sufficiently clarified - -**Residual-Risk Rule:** If the interview ended via early exit, hard-cap completion, or above-threshold proceed-with-warning, explicitly preserve that residual-risk state in the handoff so the downstream skill knows it inherited a partially clarified brief. - -**IMPORTANT:** Deep-interview is a requirements mode. On handoff, invoke the selected skill using the contract above. **Do NOT implement directly** inside deep-interview. - - - - -- Use `explore` for codebase fact gathering -- Use `omx question` as the OMX-native structured user-input tool for each interview round when an attached tmux renderer is available -- From attached-tmux Bash/tool paths, call it as `OMX_QUESTION_RETURN_PANE=$TMUX_PANE omx question ...` unless an explicit `%pane` return target is already known -- If the current runtime is outside tmux and cannot render `omx question`, use native structured input when available; otherwise ask exactly one concise plain-text question and wait for the answer -- After `omx question` returns JSON, prefer `answers[0].answer` / `answers[]`; use legacy `answer` only as a fallback for older records -- Use `omx state write/read --input '' --json` for resumable mode state through the sole-writer state operations path; the MCP state server is read-only and is not a fallback writer -- If the interview cannot ask a required `omx question` round, persist the blocker as terminal state with `active: false` and `current_phase: "blocked"`; do not write a terminal blocked phase with `active: true` -- Read/write context snapshots under `.omx/context/` -- Read applicable repo docs/rules/context during preflight; write durable docs, glossary, ADR, or memory updates only when the user explicitly opts in and the content is public-safe -- Record whether the oversized-context summary gate is not needed, pending, or satisfied before any scoring or handoff step -- Save transcript/spec artifacts under `.omx/interviews/` and `.omx/specs/` - - - -- User says stop/cancel/abort -> persist state and stop -- Ambiguity stalls for 3 rounds (+/- 0.05) -> force Ontologist mode once -- Max rounds reached -> proceed with explicit residual-risk warning -- All dimensions >= 0.9 -> allow early crystallization even before max rounds - - - -- [ ] Preflight context snapshot exists under `.omx/context/{slug}-{timestamp}.md` -- [ ] Oversized initial context, if present, has a prompt-safe summary recorded before ambiguity scoring or downstream handoff -- [ ] Ambiguity score shown each round -- [ ] Intent-first stage priority used before implementation detail -- [ ] Weakest-dimension targeting used within the active stage -- [ ] At least one explicit assumption probe happened before crystallization -- [ ] At least one persistent follow-up / pressure pass deepened a prior answer -- [ ] Challenge modes triggered at thresholds (when applicable) -- [ ] Transcript written to `.omx/interviews/{slug}-{timestamp}.md` -- [ ] Spec written to `.omx/specs/deep-interview-{slug}.md` -- [ ] Brownfield questions use evidence-backed confirmation when applicable -- [ ] Brownfield preflight inspected applicable repo docs/rules/context before user-facing questions -- [ ] Fuzzy or conflicting terminology was challenged against repo language/current code behavior when applicable -- [ ] Scenario-based edge-case grilling was used when boundary ambiguity would materially affect implementation -- [ ] Durable docs/ADR/memory updates, if any, were explicitly opted into and public-safe -- [ ] Handoff options provided (`$ultragoal`, `$ralplan`, `$autopilot`, `$team`) plus context-sensitive goal-mode suggestions (`$autoresearch`, `$performance-goal`) when applicable -- [ ] No direct implementation performed in this mode - - - -## Suggested Config (optional) - -Deep-interview reads runtime defaults from the first existing config source in this order: - -1. Repository-local `.omx/config.toml` -2. Repository-root `omx.toml` -3. User-global `~/.omx/config.toml` - -This section is currently a deep-interview-specific runtime override surface, not a general replacement for Codex `config.toml` or `.omx-config.json` model/env routing. -Malformed config files are ignored fail-soft so `$deep-interview` activation can continue with built-in defaults. -Explicit `--quick`, `--standard`, or `--deep` invocation flags override `defaultProfile`. - -```toml -[omx.deepInterview] -defaultProfile = "standard" -quickThreshold = 0.30 -standardThreshold = 0.20 -deepThreshold = 0.15 -quickMaxRounds = 5 -standardMaxRounds = 12 -deepMaxRounds = 20 -enableChallengeModes = true -``` - -## Resume - -If interrupted, rerun `$deep-interview`. Resume from persisted mode state via `omx state read --input '{"mode":"deep-interview"}' --json`. - -## Canonical Autopilot Entry Pipeline - -``` -autopilot -> deep-interview -> ralplan -> ultragoal -``` - -- Autopilot begins with this independent deep-interview clarity gate; it must not substitute `$plan --interview`. -- Stage 1 (deep-interview): ambiguity clearance and execution-ready requirements artifact -- Stage 2 (ralplan): feasibility, architecture, and consensus gate -- Stage 3 (ultragoal): durable execution, verification, and completion receipts - diff --git a/plugins/codex/plugins/oh-my-codex/skills/design/SKILL.md b/plugins/codex/plugins/oh-my-codex/skills/design/SKILL.md deleted file mode 100644 index 81b2ee4f..00000000 --- a/plugins/codex/plugins/oh-my-codex/skills/design/SKILL.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -name: design -description: Canonical repo-local DESIGN.md workflow for product, UI/UX, and frontend decision source of truth ---- - -# Design Skill - -Use `$design` to discover product and UI evidence, close only design-critical context gaps, and create or refresh the repository’s durable `DESIGN.md` contract. It is a maintained design brief, not a pixel-matching loop or one-off critique. - -Shared operating, delegation, state, hook, team, cancellation, and verification invariants live in [`templates/AGENTS.md`](../../templates/AGENTS.md). Follow that source instead of duplicating its rules here. - -## Use when - -- Product, UX, frontend, or design-system decisions need a repo-local source of truth. -- A feature needs a design brief before a designer lane or implementation. -- Existing UI, assets, screenshots, or constraints need an actionable design summary. - -Do not use it for visual-reference implementation matching (use `$visual-ralph`), screenshot comparison alone, or backend/infrastructure work without user-facing design impact. - -## Relationship to `$visual-ralph` - -`$design` owns product goals, users, information architecture, visual language, components, accessibility, constraints, and open questions in `DESIGN.md`. `$visual-ralph` owns implementation against an approved visual reference or live-URL baseline, measured verdicts, and pixel-diff evidence. Run `$design` first when both are needed; `DESIGN.md` supports but does not replace the visual verdict target. - -## Workflow - -### 1. Discover local evidence - -Inspect and cite existing `DESIGN.md`, design/UX/frontend docs, README/specs/issues, routes/pages/layouts/components/stories, theme and token files, assets, screenshots/mockups, Storybook or Playwright baselines, and accessibility/responsive/i18n/platform constraints. Separate observations from inferences; note absent evidence. - -### 2. Interview only missing context - -Ask concise questions only for gaps the repository cannot resolve: users/jobs, goals/non-goals, brand personality and forbidden aesthetics, primary flows, accessibility/device/browser targets, or unavailable assets/references. If answers are unavailable, record explicit assumptions and open questions rather than blocking. - -### 3. Create or refresh `DESIGN.md` - -Preserve useful content, remove contradictions, mark unknowns, and keep decisions actionable. The root file must contain these sections: - -# Design -## Source of truth -Status (Draft | Active | Needs refresh), date, product surfaces, evidence reviewed. -## Brand -Personality, trust signals, avoid. -## Product goals -Goals, non-goals, success signals. -## Personas and jobs -Primary personas, user jobs, contexts of use. -## Information architecture -Navigation, routes/screens, content hierarchy. -## Design principles -Principles and tradeoffs. -## Visual language -Color, typography, spacing, shape/elevation, motion, imagery/iconography. -## Components -Existing/new components, variants/states, token ownership. -## Accessibility -Target standard, keyboard/focus, contrast, semantics, reduced motion/sensory concerns. -## Responsive behavior -Breakpoints/devices, layout adaptations, touch/hover differences. -## Interaction states -Loading, empty, error, success, disabled, offline/slow network where applicable. -## Content voice -Tone, terminology, microcopy rules. -## Implementation constraints -Framework/styling, tokens, performance, compatibility, test/screenshot expectations. -## Open questions -`[ ]` question, owner, and impact. - -### 4. Apply the contract - -Before UI decisions, cite relevant `DESIGN.md` sections, reuse documented components/tokens, and update the file or add an open question when implementation exposes a contradiction. Do not invent a parallel design-system layer. - -### 5. Handoff - -For normal frontend work, provide the relevant sections, repo evidence, and acceptance criteria. For visual-reference, image, or live-URL matching, hand off to `$visual-ralph` with the approved baseline and identify `DESIGN.md` as supporting context only. - -## Evidence and completion - -Complete only when design docs/assets/components/screenshots were inspected or noted absent; missing context is answered, assumed, or listed; root `DESIGN.md` contains every required section; recommendations cite it; and any Visual Ralph handoff is clearly separated from design governance. - -Task: {{ARGUMENTS}} diff --git a/plugins/codex/plugins/oh-my-codex/skills/doctor/SKILL.md b/plugins/codex/plugins/oh-my-codex/skills/doctor/SKILL.md deleted file mode 100644 index 84a11a65..00000000 --- a/plugins/codex/plugins/oh-my-codex/skills/doctor/SKILL.md +++ /dev/null @@ -1,118 +0,0 @@ ---- -name: doctor -description: Diagnose and fix oh-my-codex installation issues ---- - -# OMX Doctor - -Use this task card to diagnose installation/discovery problems. Shared safety, lifecycle, hook, team, autonomy, and cancellation rules live in `templates/AGENTS.md`. `~/.codex` means `${CODEX_HOME:-~/.codex}` when `CODEX_HOME` is set. - -## Run diagnostics - -```bash -omx doctor -omx doctor --team # include Team runtime checks -omx doctor --verbose # include detailed check evidence -omx doctor --force # repair eligible repo artifact ownership issues -omx doctor --dry-run --force # preview eligible ownership repairs -``` - -For a local source checkout, build first and invoke `node dist/cli/omx.js doctor`. Keep command output and exact paths for the report. - -### Plugin cache and version - -```bash -PLUGIN_CACHE_ROOT="${CODEX_HOME:-$HOME/.codex}/plugins/cache" -CACHE_ENTRIES=$(find "$PLUGIN_CACHE_ROOT" -path "*/oh-my-codex/*" -mindepth 3 -maxdepth 3 -type d 2>/dev/null) -if [[ -z "$CACHE_ENTRIES" ]]; then - echo "Installed plugin cache: none" -else - while IFS= read -r d; do - m=$(basename "$(dirname "$(dirname "$d")")"); v=$(basename "$d") - printf 'Installed plugin cache: marketplace=%s version=%s path=%s\n' "$m" "$v" "$d" - done <<<"$CACHE_ENTRIES" -fi -LATEST=$(npm view oh-my-codex version 2>/dev/null) -echo "Latest npm: $LATEST" -``` - -No cache is INFO (normal for npm/setup-only installs). A non-`local` cache version differing from `LATEST`, or multiple versions for one marketplace, is WARN. Plugin discovery does not replace npm installation plus `omx setup`; native/runtime wiring remains setup-owned. - -### Hooks, AGENTS, and skill roots - -```bash -CODEX_DIR="${CODEX_HOME:-$HOME/.codex}" -ls -la "$CODEX_DIR/config.toml" "$CODEX_DIR/settings.json" 2>/dev/null -rg 'keyword-detector|persistent-mode|session-start|stop-continuation' "$CODEX_DIR/config.toml" "$CODEX_DIR/settings.json" 2>/dev/null || true -ls -la "$CODEX_DIR/hooks"/*.sh 2>/dev/null -ls -la "$CODEX_DIR/AGENTS.md" 2>/dev/null -grep -q "oh-my-codex Multi-Agent System" "$CODEX_DIR/AGENTS.md" 2>/dev/null && echo "Has OMX config" || echo "Missing OMX config" -ls -la "$CODEX_DIR/agents" "$CODEX_DIR/commands" "$CODEX_DIR/skills" "$HOME/.agents/skills" 2>/dev/null -``` - -Classify legacy hook references as **CRITICAL** (duplicate hooks). The scripts `keyword-detector.sh`, `persistent-mode.sh`, `session-start.sh`, and `stop-continuation.sh` are **WARN**. Missing AGENTS.md is **CRITICAL**; missing the OMX marker is **WARN**. `${CODEX_HOME:-~/.codex}/skills` is the canonical user root. `~/.agents/skills` is historical and overlapping trees can duplicate skill entries. OMX files under legacy `agents/` or `commands/` are **WARN**. - -With `--team`, retain any `resume_blocker`, `slow_shutdown`, `delayed_status_lag`, `stale_leader`, or `orphan_tmux_session` diagnostics; failed checks set a non-zero exit status. - -## Report contract - -```text -## OMX Doctor Report -### Summary -[HEALTHY / ISSUES FOUND] -### Checks -| Check | Status | Details | -| Plugin Version | OK/WARN/INFO | ... | -| Hook Config (config.toml / legacy settings.json) | OK/CRITICAL | ... | -| Legacy Scripts (~/.codex/hooks/) | OK/WARN | ... | -| AGENTS.md | OK/WARN/CRITICAL | ... | -| Plugin Cache | OK/WARN | ... | -| Legacy Agents / Commands | OK/WARN | ... | -| Skills (canonical / historical root) | OK/WARN | ... | -### Issues Found -1. ... -### Recommended Fixes -... -``` - -## Remediation (after user confirmation and confirming each target is OMX-owned) - -Remove only confirmed OMX legacy content; preserve unrelated user files: - -```bash -# If settings.json exists, remove only its legacy "hooks" section. -rm -f "$CODEX_DIR/hooks/keyword-detector.sh" "$CODEX_DIR/hooks/persistent-mode.sh" "$CODEX_DIR/hooks/session-start.sh" "$CODEX_DIR/hooks/stop-continuation.sh" -PLUGIN_CACHE_ROOT="${CODEX_HOME:-$HOME/.codex}/plugins/cache" -find "$PLUGIN_CACHE_ROOT" -path "*/oh-my-codex" -type d -prune -exec rm -rf {} + -``` - -For older curl installs, inspect first and remove only directories containing OMX-managed files: - -```bash -ls -la "$CODEX_DIR/agents" "$CODEX_DIR/commands" "$CODEX_DIR/skills" "$HOME/.agents/skills" 2>/dev/null -# Optional backups: -# mv "$CODEX_DIR/agents" "$CODEX_DIR/agents.bak" -# mv "$CODEX_DIR/commands" "$CODEX_DIR/commands.bak" -# mv "$HOME/.agents/skills" "$HOME/.agents/skills.bak" -rm -rf "$CODEX_DIR/agents" "$CODEX_DIR/commands" "$HOME/.agents/skills" -``` - -If AGENTS.md is missing/outdated, fetch the current raw file and write it to `$CODEX_DIR/AGENTS.md`: - -```text -WebFetch(url: "https://raw.githubusercontent.com/Yeachan-Heo/oh-my-codex/main/docs/AGENTS.md", prompt: "Return the complete raw markdown content exactly as-is") -``` - -To keep only the newest cache version for a selected marketplace: - -```bash -PLUGIN_CACHE_DIR="$PLUGIN_CACHE_ROOT/$MARKETPLACE_NAME/oh-my-codex" -KEEP_VERSION=$(for d in "$PLUGIN_CACHE_DIR"/*; do [[ -d "$d" ]] && basename "$d"; done | sort -V | tail -1) -[[ -n "$KEEP_VERSION" ]] && find "$PLUGIN_CACHE_DIR" -mindepth 1 -maxdepth 1 -type d ! -name "$KEEP_VERSION" -exec rm -rf {} + -``` - -If AGENTS.md is missing or stale, refresh it from the current release source, then rerun `omx doctor`. If legacy `~/.agents/skills` overlaps the canonical root, archive/remove that tree only after checking for custom skills. Restart Codex after fixes and report before/after evidence. - -## Exit conditions - -Stop with **HEALTHY** only when required paths, hook/config checks, AGENTS marker, plugin cache status, and (when requested) Team checks pass. Otherwise report **ISSUES FOUND**, exact failed evidence, and the smallest confirmed remediation; do not claim a fix from a recommendation alone. diff --git a/plugins/codex/plugins/oh-my-codex/skills/hud/SKILL.md b/plugins/codex/plugins/oh-my-codex/skills/hud/SKILL.md deleted file mode 100644 index 88d0b91d..00000000 --- a/plugins/codex/plugins/oh-my-codex/skills/hud/SKILL.md +++ /dev/null @@ -1,98 +0,0 @@ ---- -name: "hud" -description: "Show or configure the OMX HUD (two-layer statusline)" -role: "display" -scope: ".omx/**" ---- - -# HUD Skill - -The OMX HUD uses a two-layer architecture: - -1. **Layer 1 - Codex built-in statusLine**: Real-time TUI footer showing model, git branch, and context usage. Configured via `[tui] status_line` in `~/.codex/config.toml`. Zero code required. - -2. **Layer 2 - `omx hud` CLI command**: Shows OMX-specific orchestration state (ralph, ultrawork, autopilot, team, pipeline, ecomode, turns). Reads `.omx/state/` files. - -## Quick Commands - -| Command | Description | -|---------|-------------| -| `omx hud` | Show current HUD (modes, turns, activity) | -| `omx hud --watch` | Live-updating display (polls every 1s) | -| `omx hud --json` | Raw state output for scripting | -| `omx hud --preset=minimal` | Minimal display | -| `omx hud --preset=focused` | Default display | -| `omx hud --preset=full` | All elements | - -## Presets - -### minimal -``` -[OMX] ralph:3/10 | turns:42 -``` - -### focused (default) -``` -[OMX] ralph:3/10 | ultrawork | team:3 workers | turns:42 | last:5s ago -``` - -### full -``` -[OMX] ralph:3/10 | ultrawork | autopilot:execution | team:3 workers | pipeline:exec | turns:42 | last:5s ago | total-turns:156 -``` - -## Setup - -`omx setup` automatically configures both layers: -- Adds `[tui] status_line` to `~/.codex/config.toml` (Layer 1) -- Writes `.omx/hud-config.json` with default preset (Layer 2) -- Default preset is `focused`; if HUD/statusline changes do not appear, restart Codex CLI once. - -## Layer 1: Codex Built-in StatusLine - -Configured in `~/.codex/config.toml`: -```toml -[tui] -status_line = ["model-with-reasoning", "git-branch", "context-remaining"] -``` - -Available built-in items (Codex CLI v0.101.0+): -`model-name`, `model-with-reasoning`, `current-dir`, `project-root`, `git-branch`, `context-remaining`, `context-used`, `five-hour-limit`, `weekly-limit`, `codex-version`, `context-window-size`, `used-tokens`, `total-input-tokens`, `total-output-tokens`, `session-id` - -## Layer 2: OMX Orchestration HUD - -The `omx hud` command reads these state files: -- `.omx/state/ralph-state.json` - Ralph loop iteration -- `.omx/state/ultrawork-state.json` - Ultrawork mode -- `.omx/state/autopilot-state.json` - Autopilot phase -- `.omx/state/team-state.json` - Team workers -- `.omx/state/pipeline-state.json` - Pipeline stage -- `.omx/state/ecomode-state.json` - Ecomode active -- `.omx/state/hud-state.json` - Last activity (from notify hook) -- `.omx/metrics.json` - Turn counts - -## Configuration - -HUD config stored at `.omx/hud-config.json`: -```json -{ - "preset": "focused" -} -``` - -## Color Coding - -- **Green**: Normal/healthy -- **Yellow**: Warning (ralph >70% of max) -- **Red**: Critical (ralph >90% of max) - -## Troubleshooting - -If the TUI statusline is not showing: -1. Ensure Codex CLI v0.101.0+ is installed -2. Run `omx setup` to configure `[tui]` section -3. Restart Codex CLI - -If `omx hud` shows "No active modes": -- This is expected when no workflows are running -- Start an explicitly requested supported workflow (`$autopilot` or `$ultragoal`) and check again diff --git a/plugins/codex/plugins/oh-my-codex/skills/omx-setup/SKILL.md b/plugins/codex/plugins/oh-my-codex/skills/omx-setup/SKILL.md deleted file mode 100644 index 5784eef1..00000000 --- a/plugins/codex/plugins/oh-my-codex/skills/omx-setup/SKILL.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -name: omx-setup -description: Setup and configure oh-my-codex using current CLI behavior ---- - -# OMX Setup - -Use this task card to install or refresh OMX for the current project and/or user scope. Shared safety, lifecycle, hook, team, autonomy, and cancellation rules live in `templates/AGENTS.md`. Do not infer new CLI behavior from this card; the implementation is authoritative. - -## Command and alternatives - -```bash -omx setup [--force] [--merge-agents|--no-merge-agents|--clear-merge-agents-policy] [--dry-run] [--verbose] [--scope ] [--plugin|--legacy|--install-mode ] [--mcp ] [--no-mcp|--with-mcp] [--disable-team|--enable-team|--team-mode ] -omx agents-init [path] [--dry-run] [--force] [--verbose] -``` - -Use `agents-init` for lightweight AGENTS.md scaffolding only, not a full install. - -Supported setup options: -- `--force`: overwrite/reinstall managed artifacts; transient, not persisted or replayed. -- `--merge-agents`, `--no-merge-agents`, `--clear-merge-agents-policy`: exact bare selectors (no values/equals). Repeated identical selectors are idempotent; conflicting selectors fail before mutations. Set persists an explicit policy per invoking project root; clear removes it and cannot combine with a set. Merge refreshes managed sections; no-merge only suppresses that branch and is not a preserve mode. -- `--dry-run`, `--verbose`: preview actions or show per-file/per-step detail. -- `--scope user|project`: choose user (`~/.codex`, `~/.omx/agents`) or project (`./.codex`, `./.omx/agents`) targets. -- `--plugin` / `--legacy`: select delivery mode; `--install-mode legacy|plugin` is the canonical scripted spelling. Conflicting mode flags fail. -- `--mcp none|compat` (plus `--no-mcp` / `--with-mcp`): omit or enable first-party MCP compatibility and shared registry sync. -- `--disable-team`, `--enable-team`, `--team-mode enabled|disabled`: select Team skill/context generation; conflicting selectors fail. - -## Resolve setup - -1. Scope: explicit flag, valid persisted `./.omx/setup-scope.json`, TTY choice (default `user`), or non-interactive `user`. Legacy `project-local` migrates to `project`. -2. Mode: explicit flag, persisted mode when retained, discovered `${CODEX_HOME:-~/.codex}/plugins/cache/**/.codex-plugin/plugin.json` with `name: oh-my-codex` (plugin default), otherwise legacy. TTY reruns summarize saved choices and offer keep/review/reset; non-interactive runs do not block. -3. Create required directories and atomically persist preferences only after successful setup. - -## What setup changes - -- Legacy mode installs prompts/native agents/skills and merges full config.toml. -- Plugin mode archives/removes legacy OMX-managed prompts/skills, refreshes installable native agent TOMLs and stale generated non-installable agents, and keeps setup-owned runtime hooks. -- Both modes verify Team CLI interop markers in built `dist/cli/team.js`, generate AGENTS defaults only when selected/allowed, and write `./.omx/hud-config.json`; notification hook references are setup-owned outside plugin skill delivery. -- `AGENTS.md` is `./AGENTS.md`. Without `--force` or `--merge-agents`, interactive runs ask before overwrite and non-interactive runs preserve it. Active-session safeguards may skip writes. Plugin mode may separately prompt for AGENTS and `developer_instructions` defaults; non-interactive runs preserve existing files. -- Plugin mode enables setup-owned runtime flags (`plugin_hooks`, `goals`, or legacy `hooks`/`codex_hooks` fallback). Project scope uses `CODEX_HOME=./.codex` unless explicitly overridden. - -## Verify - -```bash -omx setup --force --verbose -omx doctor -``` - -Expect doctor evidence for prompts and skills in the selected scope, project-root AGENTS.md, `.omx/state`, and CLI-first config in the scope target `config.toml`. First-party MCP and shared registry sync are omitted unless setup used `--mcp compat` (if supported by the installed CLI). - -If using local source, run `npm run build` before `node dist/cli/omx.js setup --force --verbose` and `node dist/cli/omx.js doctor`. If AGENTS was not overwritten or merged, stop the active OMX session and rerun with `--force` or `--merge-agents`; do not edit lifecycle state manually. - -## Discovery troubleshooting - -If `$omx-setup` is absent/stale: run `omx setup --verbose`, then `omx doctor`; verify `./.codex/skills/omx-setup/SKILL.md` for project scope or `${CODEX_HOME:-~/.codex}/skills/omx-setup/SKILL.md` for legacy user scope. In plugin mode verify the oh-my-codex plugin is installed/discovered. For duplicates, inspect historical `~/.agents/skills` overlap and follow doctor/setup cleanup guidance. - -## Exit contract - -Report selected scope/mode, dry-run versus mutation, AGENTS policy, affected roots, and doctor indicators. Stop only after setup completes successfully and verification identifies the expected files/config; on failure preserve the exact command output and rerun the smallest corrective command. Do not claim installation from a dry-run alone. diff --git a/plugins/codex/plugins/oh-my-codex/skills/performance-goal/SKILL.md b/plugins/codex/plugins/oh-my-codex/skills/performance-goal/SKILL.md deleted file mode 100644 index dff5c031..00000000 --- a/plugins/codex/plugins/oh-my-codex/skills/performance-goal/SKILL.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -name: performance-goal -description: "Run an evaluator-gated performance optimization workflow over Codex goal mode with durable OMX artifacts and safe goal handoffs." ---- - -# Performance Goal Workflow - -Use this skill when a user asks OMX to optimize performance and wants a goal-oriented loop rather than a one-off review. - -## Contract - -- OMX owns durable workflow state under `.omx/goals/performance//`. -- Codex goal mode owns only the active-thread focus/accounting primitive. -- Shell commands do **not** mutate hidden Codex goal state. They write artifacts and emit model-facing handoff text. -- No optimization work may start until an evaluator command and pass/fail contract exist. -- Do not call `update_goal({status: "complete"})` until the evaluator has a passing checkpoint and a completion audit proves the objective is done; then call `get_goal` again and pass that fresh snapshot to `omx performance-goal complete --codex-goal-json`. - -## CLI - -Create the workflow and evaluator contract: - -```sh -omx performance-goal create \ - --objective "Reduce CLI startup latency by 20%" \ - --evaluator-command "npm run perf:startup" \ - --evaluator-contract "PASS when p95 latency improves by 20% and regression tests pass" \ - --slug startup-latency -``` - -Emit the Codex goal handoff: - -```sh -omx performance-goal start --slug startup-latency -``` - -Record evaluator evidence: - -```sh -omx performance-goal checkpoint --slug startup-latency --status pass --evidence "benchmark + tests passed" -omx performance-goal checkpoint --slug startup-latency --status fail --evidence "benchmark regressed" -omx performance-goal checkpoint --slug startup-latency --status blocked --evidence "missing fixture" -``` - -Complete only after a passing checkpoint: - -```sh -omx performance-goal complete --slug startup-latency --evidence "final evaluator evidence" --codex-goal-json -``` - -## Agent Loop - -1. Run `omx performance-goal create` if no workflow exists. -2. Run `omx performance-goal start` and follow the handoff: - - call `get_goal`; - - call `create_goal` only when no active goal exists and the objective is explicit; - - work only against the evaluator contract; - - after evaluator pass and completion audit, call `update_goal({status: "complete"})`, call `get_goal` again, and pass that snapshot to `omx performance-goal complete --codex-goal-json`; - - after `omx performance-goal complete` succeeds, run `/goal clear` in the Codex UI before starting another goal in this same thread/session; OMX prints this terminal cleanup step but does not invoke hidden clear routes; -3. Optimize in small reversible patches. -4. Run the evaluator and related regression tests. -5. Record each pass/fail/blocker with `checkpoint`. -6. Complete only when the pass artifact exists and no required work remains. - -## Completion Gate - -A performance goal is incomplete unless `.omx/goals/performance//state.json` contains a `lastValidation.status` of `pass` and `omx performance-goal complete` receives a matching complete Codex `get_goal` snapshot via `--codex-goal-json`. Passing ordinary tests alone is not sufficient unless they are the declared evaluator contract. - -Lifecycle: `create_goal` starts the Codex thread goal, `update_goal({status: "complete"})` marks terminal success after the evaluator and audit pass, and `/goal clear` removes the completed thread goal when another same-thread goal is needed. OMX shell commands and hooks reconcile snapshots and print the cleanup instruction; they must not mutate hidden Codex goal state. diff --git a/plugins/codex/plugins/oh-my-codex/skills/plan/SKILL.md b/plugins/codex/plugins/oh-my-codex/skills/plan/SKILL.md deleted file mode 100644 index 8faaac22..00000000 --- a/plugins/codex/plugins/oh-my-codex/skills/plan/SKILL.md +++ /dev/null @@ -1,94 +0,0 @@ ---- -name: plan -description: Lightweight planning with optional interview ---- - - -Plan creates concise, actionable work plans. Auto-detects interview vs direct, supports `--interview` for Socratic clarification and `--review` for evaluation. - - - -- Planning before implementation — "plan this", "let's plan" -- Requirements are vague and need scoping — use `--interview` -- Reviewing an existing plan — `--review` - - - -- Want direct execution — execute the scoped task directly; use `ultragoal` only for durable goals or `team` for coordinated parallel work -- Simple focused fix with obvious scope — skip planning - - - -- Auto-detect interview vs direct; `--interview` forces Socratic mode, `--direct` skips it -- Ask one question at a time; gather codebase facts first -- `omx explore` is deprecated. Use normal repository inspection for codebase facts; `omx sparkshell` only for explicit shell-native evidence -- Plans must be evidence-backed: 80%+ claims cite file/line, 90%+ criteria testable -- Right-size steps to scope; save to `.omx/plans/` - - - - -### Mode Selection -| Mode | Trigger | Behavior | -|------|---------|----------| -| Interview | `--interview` or broad request | Socratic requirements gathering | -| Direct | `--direct` or detailed request | Generate plan immediately | -| Review | `--review` | Critic evaluation of existing plan | - -### Interview (`--interview`) -1. Classify request; ask one focused question via `omx question` -2. Gather codebase facts first, then ask informed follow-ups -3. Build on answers; consult Analyst for hidden requirements -4. Create plan when user signals readiness - -### Direct -1. Quick analysis (optional Analyst) -2. Generate plan immediately -3. Optional Critic review if requested - -### Review (`--review`) -1. Read plan from `.omx/plans/` -2. Evaluate via Critic; verdict APPROVED/REVISE/REJECT -3. If author == reviewer, hand off to `$code-review` - -### Output -Every plan includes: Requirements Summary, Acceptance Criteria (testable), Implementation Steps (with file refs, adaptive count), Risks/Mitigations, Verification. Saved to `.omx/plans/`. - -Outcome-first framing: apply outcome-first framing, concise visible updates for multi-step planning. Local overrides for the active workflow branch apply via newer user task updates. If the user says `continue`, continue the current branch instead of restarting. - - - - -- `omx explore` is deprecated. Use normal repository inspection; `omx sparkshell` only for explicit shell-native evidence -- Use `omx question` for interview questions; plain text only as fallback -- Use `ask_codex` with `planner`/`analyst`/`critic` as needed - - - -- Stop interviewing when requirements are clear — do not over-interview -- Escalate on irreconcilable trade-offs requiring business decision -- "just do it" / "skip planning" → hand off to `$ultragoal` - - - -- [ ] Acceptance criteria testable (90%+ concrete) -- [ ] Claims cite file/line (80%+) -- [ ] Risks have mitigations, no vague terms -- [ ] Plan saved to `.omx/plans/` - - - -## Question Classification -| Type | Examples | Action | -|------|----------|--------| -| Codebase Fact | "Where is X?" | Explore first, do not ask | -| User Preference | "Priority?" | Ask via `omx question` | -| Scope Decision | "Include Y?" | Ask user | - -## Review Quality Criteria -| Criterion | Standard | -|-----------|----------| -| Clarity | 80%+ claims cite file/line | -| Testability | 90%+ criteria concrete | -| Verification | File refs exist | - diff --git a/plugins/codex/plugins/oh-my-codex/skills/ralplan/SKILL.md b/plugins/codex/plugins/oh-my-codex/skills/ralplan/SKILL.md deleted file mode 100644 index e7913519..00000000 --- a/plugins/codex/plugins/oh-my-codex/skills/ralplan/SKILL.md +++ /dev/null @@ -1,149 +0,0 @@ ---- -name: ralplan -description: Consensus planning stage for Planner -> Architect -> Critic handoff to $ultragoal ---- - -# Ralplan (Consensus Planning Alias) - -Ralplan is the canonical consensus-planning stage used by Autopilot between `$deep-interview` and `$ultragoal`. It drives Planner, Architect, and Critic planning and records their review lifecycle with **RALPLAN-DR structured deliberation** (short mode by default, deliberate mode for high-risk work). Local lifecycle evidence is not host-issued security authority, but ordinary progression to Ultragoal must remain reachable after the execution-ready plan and sequential review evidence are durable; missing host provenance must not terminalize Ralplan or block cancel, clear, or recovery. - -## Usage - -``` -$ralplan "task description" -``` - -Standalone advisory planning is explicitly opt-in: - -```text -$ralplan --advisory "task description" -``` - -Advisory runs the same sequential Planner → Architect → Critic review lifecycle, binds the plan and both review artifacts to exact bytes and one tracker-backed iteration, then returns to the caller with `active:false`. It is a cooperative workflow pause, not a security fence or permission system. It never emits a `PreToolUse` allow/block decision, never completes the host consensus gate, never authorizes execution, and never suppresses unrelated host behavior. Terminal state must carry explicit `false` values for the consensus gate, host verification, and execution handoff rather than omitting them. A later concrete affirmative execution request may produce non-authoritative routing context, but it does not persist a permission, rewrite terminal evidence, or create an automatic handoff; quotations, code, examples, documentation, questions, modal requests, negations, and vague approval remain classifier negatives. `approved+proven` requires complete lifecycle digests plus post-write revalidation. Administrative abandonment is append-only, idempotent for prepared or committed journals, and records a separate byte-bound admin event without rewriting the original closeout journal. Real enforcement requires an explicit host-issued, host-verified receipt or capability on a non-user-mintable surface; local Advisory files, prompts, session/thread fields, tracker records, and HERDR observability cannot substitute for it. On Darwin, each Advisory evidence artifact is limited to 128 KiB by the pinned-directory reader; other supported platforms allow up to 8 MiB. - -## Flags - -- `--interactive`: Enables user prompts at key decision points (draft review in step 2 and final approval in step 6). Without this flag the workflow runs fully automated — Planner → Architect → Critic loop — and outputs the final plan without asking for confirmation. -- `--deliberate`: Forces deliberate mode for high-risk work. Adds pre-mortem (3 scenarios) and expanded test planning (unit/integration/e2e/observability). Without this flag, deliberate mode can still auto-enable when the request explicitly signals high risk (auth/security, migrations, destructive changes, production incidents, compliance/PII, public API breakage). -- `--advisory`: Standalone only. It cannot be combined with Autopilot, Pipeline, Team, Ralph, Ultragoal, execution flags, or unknown flags. Activation requires canonical session, root thread, and turn identity. - -## Ontology-heavy review - -For requirements semantics, taxonomy, prompt/spec design, policy distinctions, or category-risk architecture, cite the `architect` role agent's read-only review as advisory evidence. Its findings can inform the plan or follow-up evidence when explicitly used, but `$ralplan` itself records Architect→Critic lifecycle evidence only, and advisory review is never a durable execution authorization. - -## Usage with interactive mode - -``` -$ralplan --interactive "task description" -``` - -## Behavior - -## GPT-5.6 Guidance Alignment - -Use the shared workflow guidance pattern: outcome-first framing, concise visible updates for multi-step planning, local overrides for the active workflow branch, evidence-backed planning and validation expectations, explicit stop rules, right-sized implementation/PRD shape, and automatic continuation for safe reversible steps. Ask only for material, destructive, credentialed, external-production, or preference-dependent branches. - -This skill runs its own consensus runtime; it does not delegate to a nonexistent Plan consensus mode: - -``` -omx ralplan run --task [--session ] -``` - -The consensus workflow: -1. **Planner** creates an adaptive plan (right-sized to task scope; do not default to exactly five steps) and a compact **RALPLAN-DR summary** before review. Current `[main]` vs `[planner]` behavior: standalone `$ralplan` may be authored by the active main planning lane unless the caller/runtime supplies a dedicated planner routing record; inside `$autopilot`, state field `planning_routing.owner:"planner"` means the initial Planner draft/decomposition must use dedicated `[planner]`. Set `.omx-config.json` `agentModels.planner` to opt into a specific planner model and force dedicated planner ownership for complex Autopilot planning even when `[main]` is not cheap/mini. The RALPLAN-DR summary includes: - - Principles (3-5) - - Decision Drivers (top 3) - - Viable Options (>=2) with bounded pros/cons - - If only one viable option remains, explicit invalidation rationale for alternatives - - Deliberate mode only: pre-mortem (3 scenarios) + expanded test plan (unit/integration/e2e/observability) -2. **User feedback** *(--interactive only)*: If `--interactive` is set, use the structured question UI (`omx question` in attached tmux; native structured input outside tmux when available) to present the draft plan **plus the Principles / Drivers / Options summary** before review (Proceed to review / Request changes / Skip review). Otherwise, automatically proceed to review. -**Native role-routing preflight:** Keyword routing may already have selected Ralplan, but it is not authority. Run `omx ralplan preflight --json` only when the native task surface reports `role_routing_unavailable` and this workflow attempts adapted Ralplan Planner, Architect, or Critic authority, adapted role-intent, or adapted consensus authority. On `unsupported_documented_leader_proof`, stop before that adapted authority and use a Codex surface with documented root proof or a reviewed alternative workflow. Do not infer root identity from `session_id`, undocumented `thread_id`, session/pointer/transcript/cwd state, absence of child data, or a prompt label. Ordinary native planning, lifecycle, state, status, health, HUD, runtime, setup, install, sync, and unrelated delegation are outside this preflight boundary and remain governed by existing controls. - -**Native role-routing rule:** When the native surface exposes `agent_type` role routing, set `agent_type` to an installed OMX role and never omit it for OMX work. When it does not (`role_routing_unavailable`), do not fabricate `agent_type`. On the exact reviewed Codex releases 0.144.5, 0.145.0, 0.146.1, and 0.148.0-alpha.5, adapted Ralplan Planner, Architect, Critic, role-intent, and consensus authority are unavailable because they lack documented root proof; every other version remains unknown and fails closed. Do not silently weaken routing with a prompt role label or inferred carrier. Use a Codex surface with documented root proof or a reviewed alternative workflow for that authority. A direct `omx ralplan role-intent write` attempt is denied with machine reason `unsupported_documented_leader_proof`. - -3. **Architect** reviews for architectural soundness and must provide the strongest steelman antithesis, at least one real tradeoff tension, and (when possible) synthesis — **await completion before step 4**. Launch this as a subsequent role-specific `Architect` subagent and pass the full task statement, context snapshot, PRD/test-spec paths, and relevant prior findings; do not substitute an unvalidated reviewer identity or a short improvised reviewer prompt. In deliberate mode, Architect should explicitly flag principle violations. -4. **Critic** evaluates against quality criteria — run only after step 3 completes. Launch this as a subsequent role-specific `Critic` subagent with the full task statement, context snapshot, PRD/test-spec paths, and the completed Architect review; do not ask the Architect subagent to perform the Critic gate and do not substitute an unvalidated reviewer identity or a short improvised reviewer prompt. Critic must enforce principle-option consistency, fair alternatives, risk mitigation clarity, testable acceptance criteria, and concrete verification steps. In deliberate mode, Critic must reject missing/weak pre-mortem or expanded test plan. -5. **Re-review loop** (max 5 iterations): Any non-`APPROVE` Critic verdict (`ITERATE` or `REJECT`) MUST run the same full closed loop: - a. Collect Architect and Critic feedback - b. Revise the plan with Planner - c. Return to Architect review - d. Return to Critic evaluation - e. Repeat this loop until Critic returns `APPROVE` or 5 iterations are reached - f. If 5 iterations are reached without `APPROVE`, present the best version to the user -6. On Critic approval, persist the execution-ready planning artifacts and sequential Architect→Critic evidence. In standalone interactive Ralplan, present the requested future execution lane. Inside Autopilot, the existing explicit `$autopilot` invocation authorizes the supervised transition to its defining next stage, `$ultragoal`; persist an Autopilot-owned `ralplan_execution_handoff` bound to the same session and review cycle. -7. Record `ralplan_execution_handoff` with `{authorized: true, reason, authorized_at, session_id, review_cycle, source: "autopilot"|"user"}`. `source:"autopilot"` is valid only for a supervised active Autopilot run whose current phase is `ralplan`; it does not claim host-consensus authority. -8. Transition to `$ultragoal` after the durable plan, sequential approvals, and bound execution handoff exist. Do not implement directly inside Ralplan. - -> **Important:** Steps 3 and 4 MUST run sequentially as role-specific subagents. Do NOT issue both agent calls in the same parallel batch. Always await the subsequent `Architect` result before invoking the subsequent `Critic`; their completed approvals establish local lifecycle evidence only and cannot satisfy the durable execution gate. - -## Planning/Execution Boundary - -`$ralplan` is a planning mode. While ralplan is active and no explicit execution handoff is active, implementation-focused write tools are out of scope. Ralplan may inspect the repository and may write only planning artifacts such as `.omx/context/`, `.omx/plans/`, `.omx/specs/`, and required `.omx/state/` records. - -The canonical flow is: - -``` -$ralplan -> local Architect→Critic lifecycle evidence -> bound execution handoff -> $ultragoal -``` - -Before any execution lane begins, ralplan must emit terminal planning state (complete, paused, failed, or waiting for input) and the durable handoff record below. Do not continue from consensus planning into direct code edits in the same ralplan session. - -## Durable Consensus Handoff Contract - -Ralplan is not complete, skippable, or ready for execution merely because `.omx/plans/prd-*.md` and `.omx/plans/test-spec-*.md` exist. Those files are planning artifacts, not consensus evidence. - -Before any Autopilot, Ultragoal, Team, or implementation handoff, persist a durable handoff record that distinguishes: - -- `planning_artifacts`: PRD/test-spec paths. -- `ralplan_architect_review`: the completed Architect review with an approving verdict. -- `ralplan_critic_review`: the completed Critic review with an approving verdict, recorded only after the Architect review. -- `ralplan_execution_handoff`: persist `{authorized: true, reason: "", authorized_at: "", session_id: "", review_cycle: , source: "autopilot"|"user"}`. Autopilot may issue this only for its own supervised `ralplan` phase; standalone Ralplan uses `source:"user"`. -- `ralplan_consensus_gate.complete` records lifecycle completion after the sequential Architect and Critic approvals. It is not a host-security claim. Locally authored JSON/env/prompt/tracker/transcript/receipt-shaped evidence must never be described as host-issued authority. - -If Architect is missing/blocked, keep the workflow in Architect review or report that blocker. If Critic is missing/blocked/non-approving, keep the workflow in Critic/re-review or report the max-iteration outcome. After both reviews approve, execution begins only when the matching `ralplan_execution_handoff` is durable. Existing plan/test-spec files alone are never permission to skip Ralplan or execute. - -Follow the Plan skill's full documentation for consensus mode details. - -## Goal-Mode Follow-up Suggestions - -When a bound `ralplan_execution_handoff` permits execution, include product-facing goal-mode suggestions alongside coordinated Team execution. Record the requested lane and persist the handoff without claiming host-issued authority. - -- `$ultragoal` — **default goal-mode follow-up** for implementation or general goal-oriented follow-up plans that should become durable Codex/OMX goals with sequential completion tracking. -- `$autoresearch` — research-project follow-up when the plan centers on a question, literature/reference gathering, evaluator-backed research, or a professor/critic-style research deliverable. (`$autoresearch-goal` was retired to a sunset stub in OMX 0.21.) -- `$performance-goal` — optimization/performance follow-up when the plan centers on speed, latency, throughput, memory, benchmark, or other measurable performance work. - -Use `$ultragoal` for durable goal tracking and `$team` for coordinated parallel implementation. When combined, Ultragoal owns the ledger and Team returns checkpoint-ready execution evidence. -Use the available-agent-types roster to produce explicit role/staffing allocation, reasoning-by-lane guidance, concrete launch hints (including `omx team` when parallel delivery is justified), and team verification responsibilities for any future receipt-authorized execution path. - -## Pre-context Intake - -Before consensus planning or execution handoff, ensure a grounded context snapshot exists: - -1. Derive a task slug from the request. -2. Reuse the latest relevant snapshot in `.omx/context/{slug}-*.md` when available. -3. If none exists, create `.omx/context/{slug}-{timestamp}.md` (UTC `YYYYMMDDTHHMMSSZ`) with: - - task statement - - desired outcome - - known facts/evidence - - constraints - - unknowns/open questions - - likely codebase touchpoints -4. If ambiguity remains high, gather brownfield facts first. `omx explore` is deprecated; use normal repository inspection tools/subagents for simple read-only repository lookups and `omx sparkshell` only for explicit shell-native read-only evidence. Then run `$deep-interview --quick ` before continuing. -5. If the plan depends on official docs, version-aware framework guidance, best practices, or external dependency behavior, use `$best-practice-research` as the bounded evidence wrapper and auto-delegate `researcher` for the official/upstream lookup before finalizing the planning handoff so execution does not start from repo-local recall alone. -6. If a prior `$autoresearch` run exists, treat its approved artifact as evidence for the plan. Read historical artifacts from `$autoresearch-goal` only as compatibility input. Do not include Autoresearch as a final architecture or runtime component unless the user explicitly requested ongoing research automation; otherwise synthesize the evidence into the `$ralplan` ADR, risks, and verification steps. - -Do not hand off to execution modes until this intake is complete; if urgency forces progress, explicitly document the risk tradeoffs. - -## Routing boundary - -Ordinary scoped tasks stay in the direct execution lane described by `templates/AGENTS.md`. -Explicit workflow requests follow the keyword registry (`src/hooks/keyword-registry.ts`) and their owning skill contracts; this card does not define a second activation table. -Once Ralplan is active, preserve the planning/execution and consensus handoff requirements above. A conversational “just do it” is not a substitute for the required review evidence. - -## Scenario Examples - -**Good:** The user says `continue` after the workflow already has a clear next step. Continue the current branch of work instead of restarting or re-asking the same question. - -**Good:** The user changes only the output shape or downstream delivery step (for example `make a PR`). Preserve earlier non-conflicting workflow constraints and apply the update locally. - -**Bad:** The user says `continue`, and the workflow restarts discovery or stops before the missing verification/evidence is gathered. diff --git a/plugins/codex/plugins/oh-my-codex/skills/team/SKILL.md b/plugins/codex/plugins/oh-my-codex/skills/team/SKILL.md deleted file mode 100644 index 8f24f8e3..00000000 --- a/plugins/codex/plugins/oh-my-codex/skills/team/SKILL.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -name: team -description: N coordinated agents on shared task list using tmux-based orchestration ---- - -# Team Skill - -## When to use - -Use only for an explicit `$team` request or `omx team ...` launch that benefits from durable tmux workers, shared task state, mailbox coordination, or worktree isolation. Read `AGENTS.md#durable-runtime-invariants-canonical-ssot`; do not copy its durable rules into this card. - -## Inputs and preconditions - -- Launch shape: `omx team [N:agent-type] ""` (for example `omx team 3:executor "implement X"`). -- Require `tmux -V`, a leader session with `$TMUX`, and the intended `omx` executable. In Codex App/plain non-tmux sessions, explain the runtime boundary instead of pretending Team is available. -- Before launch, ground the task in a recent `.omx/context/{slug}-*.md`; create a concise snapshot when none exists. Include target, evidence, constraints, unknowns, and likely touchpoints. -- Do not launch nested Team runs. Pick worker roles deliberately; use `OMX_TEAM_WORKER_CLI=codex|claude|auto` or `OMX_TEAM_WORKER_CLI_MAP=...` only when needed. -- Per-agent reasoning is set by `agentReasoning`; accepted values are `low`, `medium`, `high`, `xhigh`, and `max`. `max` is passed unchanged and remains capability-dependent. `ultra` is unsupported and is not an alias for `max`. -- Invalid configured values use the built-in role-default fallback. An explicit raw `-c model_reasoning_effort=...` is opaque and wins. When both sources are present, explicit raw reasoning wins over inherited Team reasoning and environment reasoning. Do not downgrade or retry `max` as `xhigh`; built-in role defaults remain unchanged. - -## Operational steps - -1. Start the runtime and capture startup evidence: `Team started: `, tmux target, worker panes, and the leader ACK mailbox. -2. **Current Runtime Behavior:** runtime-owned task files and APIs are the operational interface; the durable ownership and safety rules remain in `templates/AGENTS.md`. -3. Runtime creates `.omx/state/team//config.json`, `manifest.v2.json`, `tasks/task-.json`, worker identities/inboxes, mailbox files, and a dispatch queue. Workers receive `OMX_TEAM_WORKER`, `OMX_TEAM_STATE_ROOT`, and `OMX_TEAM_LEADER_CWD`. -4. Deliver assignments through durable inbox/task state and the CLI API. The worker card defines ACK, claim, transition, mailbox, and idle-status commands. -5. Prefer these machine-readable operations: - ```bash - omx team api send-message --input '{"team_name":"","from_worker":"leader-fixed","to_worker":"worker-1","body":""}' --json - omx team api read-task --input '{"team_name":"","task_id":""}' --json - omx team api transition-task-status --input '{"team_name":"","task_id":"","from":"in_progress","to":"completed","claim_token":""}' --json - ``` -6. Monitor with `omx team status --json` or `omx team await --timeout-ms 30000 --json`; inspect mailbox/state files when a worker is blocked or stale. -7. Keep the team running until `pending=0`, `in_progress=0`, and `failed=0` (or an explicitly acknowledged failure path). Run `omx team shutdown ` only then, unless the user explicitly aborts. -8. Verify shutdown evidence and state cleanup. Do not claim completion while workers are still writing. - -## Team and Ultragoal - -When a leader-owned `.omx/ultragoal/goals.json` exists, workers return task evidence only. The leader checkpoints with a fresh Codex `get_goal` snapshot: -```bash -omx ultragoal checkpoint --goal-id --status complete --evidence "" --codex-goal-json -``` -Team launch remains an explicit separate action; it does not create hidden Codex goals. - -## Dispatch and recovery - -- If dispatch reports `worker_notify_failed:`, inspect `tmux list-panes`, capture the pane, then send one concise trigger and re-check mailbox/state. -- For Claude panes, do not spam Enter while work is active. Confirm runtime status first. -- If a worker reports `omx team api ... ENOENT`, check whether shutdown or state deletion happened too early; preserve state until all transitions finish. -- For a clean retry, kill only known stale worker panes, remove only the exact stale Team root, and relaunch; never kill the leader/HUD pane accidentally. - -## Exit and evidence - -Report team name, launch command, pane/ACK evidence, task counts, worker verification, failures or blockers, shutdown result, and cleaned state paths. Include any worktree or CLI-map choices and keep final integration/verification in the leader lane. diff --git a/plugins/codex/plugins/oh-my-codex/skills/ultragoal/SKILL.md b/plugins/codex/plugins/oh-my-codex/skills/ultragoal/SKILL.md deleted file mode 100644 index c680bb89..00000000 --- a/plugins/codex/plugins/oh-my-codex/skills/ultragoal/SKILL.md +++ /dev/null @@ -1,77 +0,0 @@ ---- -name: ultragoal -description: Create and execute durable repo-native multi-goal plans over Codex goal mode artifacts. ---- - -# Ultragoal Workflow - -## When to use - -Use for `ultragoal`, `create-goals`, `complete-goals`, durable multi-goal planning, or sequential execution over Codex `/goal`. Read `AGENTS.md#durable-runtime-invariants-canonical-ssot` for state ownership and goal-tool boundaries. - -## Inputs and preconditions - -- Durable artifacts are `.omx/ultragoal/brief.md`, `.omx/ultragoal/goals.json`, and `.omx/ultragoal/ledger.jsonl`. -- New plans use aggregate Codex goal mode by default: one stable pointer objective for the plan while OMX tracks individual stories. Use `--codex-goal-mode per-story` only when explicitly requested. -- Do not call `/goal clear` from shell or this skill. After a completed run, the operator may clear the interactive Codex goal in the Codex UI before another same-thread run. - -## Operational steps: create - -Run one command, then inspect the generated plan: -```bash -omx ultragoal create-goals --brief "" -omx ultragoal create-goals --brief-file -cat | omx ultragoal create-goals --from-stdin -omx ultragoal create-goals --codex-goal-mode per-story --brief "" -``` -If refinement is needed, use explicit supported steering rather than editing durable artifacts by hand. - -## Operational steps: execute - -Repeat until `omx ultragoal status` reports all goals complete: - -1. Run `omx ultragoal complete-goals` and read its handoff. -2. Call `get_goal`. Call `create_goal` with the printed payload only when no active Codex goal exists; otherwise continue the matching aggregate objective. -3. Complete one OMX story and audit its objective against real artifacts and verification evidence. -4. For intermediate aggregate stories, keep the Codex goal active and checkpoint: - ```bash - omx ultragoal checkpoint --goal-id --status complete --evidence "" --codex-goal-json - ``` -5. For a blocked/failed story, use `--status blocked|failed` with evidence; resume failed work with `omx ultragoal complete-goals --retry-failed`. -6. On the final story, complete the final gate below before `update_goal({status: "complete"})`; then call `get_goal` again and checkpoint the fresh complete snapshot. - -## Explicit steering - -Use evidence-backed directives only when the decomposition must change: -```bash -omx ultragoal steer --kind add_subgoal --title "" --objective "<objective>" --evidence "<evidence>" --rationale "<reason>" --json -omx ultragoal steer --directive-json ./steering.json --json -``` -Supported kinds: `add_subgoal`, `split_subgoal`, `reorder_pending`, `revise_pending_wording`, `annotate_ledger`, and `mark_blocked_superseded`. Ordinary prose does not mutate the plan; repeated structured directives dedupe. - -## Phase/HUD handoff - -At standalone activation, declare the smallest accurate phase (`planning`, `executing`, `verifying`, `reviewing`, `checkpointing`, or `blocked`): -```bash -omx state write --input '{"mode":"ultragoal","active":true,"current_phase":"planning"}' --json -``` -Inside Autopilot, keep the parent mode active and set `current_phase:"ultragoal"`; persist Ultragoal evidence under `handoff_artifacts.ultragoal` before a code-review handoff. Mark standalone Ultragoal complete only when every durable goal is complete. - -## Optional Team bridge - -For parallel story execution, use the separate Team command. The leader records the Ultragoal checkpoint with a fresh `get_goal` snapshot; see the Team skill for the bridge details. - -## Final gate and exit evidence - -Before final completion: - -1. Run targeted story verification. -2. Run `ai-slop-cleaner` on changed files, then rerun verification. -3. Audit every architecture/domain invariant from the brief/spec/accepted steering against implementation, test, and review evidence. -4. Run `$code-review` through independent `code-reviewer` and `architect` lanes. If review or invariant proof is not clean, do not update the Codex goal; record durable blockers: - ```bash - omx ultragoal record-review-blockers --goal-id <id> --title "Resolve final code-review blockers" --objective "<objective>" --evidence "<findings>" --codex-goal-json <active-get-goal-json-or-path> - ``` -5. If clean, call `update_goal({status: "complete"})`, call `get_goal`, and checkpoint with `--quality-gate-json` containing cleaner, verification, review, and architecture-invariant evidence. - -Report goal ids/statuses, ledger/checkpoint paths, fresh goal snapshot evidence, review verdicts, and any blocker. Never claim completion from OMX state alone. diff --git a/plugins/codex/plugins/oh-my-codex/skills/ultraqa/SKILL.md b/plugins/codex/plugins/oh-my-codex/skills/ultraqa/SKILL.md deleted file mode 100644 index 796eb83f..00000000 --- a/plugins/codex/plugins/oh-my-codex/skills/ultraqa/SKILL.md +++ /dev/null @@ -1,86 +0,0 @@ ---- -name: ultraqa -description: Adversarial dynamic e2e QA workflow - generate hostile scenarios, test, verify, fix, report, and clean up ---- - -# UltraQA Task Card - -Use this explicit opt-in when a runnable behavior needs adversarial dynamic end-to-end -QA. Shared operating invariants live in `templates/AGENTS.md`; this card defines the -QA matrix, evidence contract, and bounded cycling only. - -## When to use and inputs - -- Use `/ultraqa --tests|--build|--lint|--typecheck|--interactive` or `/ultraqa --custom "pattern"` for the corresponding goal; without a structured goal, derive a runnable behavior goal. -- Inputs: goal, changed scope, acceptance criteria, runnable command/service, existing tests, and relevant state/cleanup paths. -- Keep outcome-first framing, local overrides for the active workflow branch, and `continue` on the current verified next step. -- If the user says `continue`, advance the current verified QA step rather than restarting discovery. -- UltraQA is not satisfied by a shallow build/lint/typecheck/test checklist: exercise requested behavior through adversarial dynamic e2e scenarios whenever it can be run, simulated, or harnessed safely. - -## Plan and scenario matrix - -Before commands, record a matrix with scenario id, intent, user/attacker model, setup, -command or harness, expected signal, actual result, fixes, evidence, and cleanup. -Include a normal path and relevant hostile classes: - -1. **Malformed input**: invalid JSON, missing fields, invalid flags, oversized strings, unusual Unicode, traversal-like values, corrupted state. -2. **Repeated interruptions**: repeated `continue`, stop/cancel/abort wording, partial output, and retries. -3. **Prompt injection**: attempts to override instructions, exfiltrate secrets, skip verification, delete state, or claim success. -4. **Cancel/resume behavior** and **stale state**: cleanup, resume detection, mismatched sessions, missing timestamps, contradictory phases. -5. **Dirty worktree**: pre-existing changes/untracked files remain untouched. -6. **Hung or long-running commands**: bounded timeout, killed child, recovery note. -7. **Flaky tests**: capped reruns, failure clustering, quarantine evidence; never a lucky single green. -8. **Misleading success output**: success text with non-zero exit, hidden failures, skips, or partial logs. - -## Cycle (maximum 5) - -1. **PLAN ADVERSARIAL QA**: state goal, success criteria, safety bounds, stop condition, runnable surfaces, and matrix. -2. **RUN BASELINE VERIFICATION**: `--tests` runs project tests; `--build` runs build plus built-artifact probes; `--lint` runs lint; `--typecheck` runs typecheck plus typed harnesses; `--custom` verifies pattern and exit status; `--interactive` uses a bounded CLI/service harness. -3. **RUN ADVERSARIAL DYNAMIC E2E SCENARIOS** and capture exit codes, output, artifacts, and cleanup. -4. **CHECK RESULT**: pass only when baseline, adversarial scenarios, evidence, and cleanup all pass. Otherwise diagnose and fix, then repeat. -5. **ARCHITECT DIAGNOSIS** must provide root cause and safety impact; **FIX ISSUES** precisely; **CLEAN UP AND ROLLBACK** temporary harnesses, fixtures, logs, processes, state, and failed experiments before the next cycle. - -Generate temporary tests, scripts, fixtures, or harnesses only when useful. Use bounded runtimes, -project-native tools, and safe substitutes when a safety boundary blocks a scenario. -Use absolute repo imports and `pathToFileURL(join(repoRoot, "dist", ...)).href`; Never rely on `./dist` from `/tmp`. -Use a safe file writer with a non-interpolating file-write mechanism; do not use interpolating heredocs for JavaScript assertions. -Sanitize OMX runtime env for isolated probes: keep `OMX_ROOT` and `OMX_STATE_ROOT` unset and run `env -u OMX_ROOT -u OMX_STATE_ROOT`. -Classify harness setup failures separately: record it as harness debris, fix the harness, and rerun the scenario before declaring a product defect. - -## Safety, state, and exit - -No destructive commands, secret exfiltration, credential dumping, production writes, or unbounded process spawning. Use no unbounded waits; preserve unrelated dirty work. If a scenario is unsafe, record it blocked and the safe substitute. Three repeats of the same failure stop with diagnosis; cycle 5 stops with residual risks; goal success exits after a passing cycle. - -Use CLI-first lifecycle state and exact commands: - -```sh -omx state write --input '{"mode":"ultraqa","active":true,"current_phase":"planning","iteration":1,"started_at":"<now>","scenario_matrix":[]}' --json -omx state write --input '{"mode":"ultraqa","current_phase":"qa","iteration":<cycle>,"scenario_matrix":"<updated matrix path or summary>"}' --json -omx state write --input '{"mode":"ultraqa","current_phase":"adversarial-e2e"}' --json -omx state write --input '{"mode":"ultraqa","current_phase":"diagnose"}' --json -omx state write --input '{"mode":"ultraqa","current_phase":"fix"}' --json -omx state write --input '{"mode":"ultraqa","current_phase":"cleanup"}' --json -omx state write --input '{"mode":"ultraqa","active":false,"current_phase":"complete","completed_at":"<now>"}' --json -omx state read --input '{"mode":"ultraqa"}' --json -omx state clear --input '{"mode":"ultraqa"}' --json -``` - -On completion, max cycles, same failure, safety boundary, or environment error, clean -state and temporary artifacts. Report cleanup status and clean temporary e2e harnesses. -Never claim complete without current evidence. - -## Evidence/output contract - -Return `# UltraQA Report` with: **Goal and success criteria** (including stop condition -and safety bounds); **Scenario matrix** (all columns above); **Commands run** (exit code, -purpose, timeout, key output); **Failures found** (root/user/safety impact); **Fixes -applied** / **Fixes applied** (files, rationale, scenarios, regression evidence); **Cleanup and rollback** -(artifacts/processes/worktree before/after); **Residual risks**; and **Evidence** -(logs, harness output, screenshots/transcripts where relevant, rerun/flake evidence). - -## Exit condition - -`ULTRAQA COMPLETE: Goal met after N cycles` only follows a passing baseline plus -adversarial matrix, clean artifacts, and complete evidence. Otherwise return the exact -bounded status: `ULTRAQA STOPPED: Max cycles`, `ULTRAQA STOPPED: Same failure detected 3 times`, -`ULTRAQA BLOCKED: ...`, or `ULTRAQA ERROR: ...` with owner and next safe step. diff --git a/plugins/codex/plugins/oh-my-codex/skills/visual-ralph/SKILL.md b/plugins/codex/plugins/oh-my-codex/skills/visual-ralph/SKILL.md deleted file mode 100644 index c0da9fc5..00000000 --- a/plugins/codex/plugins/oh-my-codex/skills/visual-ralph/SKILL.md +++ /dev/null @@ -1,83 +0,0 @@ ---- -name: visual-ralph -description: "Visual Ralph orchestration for frontend UI from generated references, static references, or live URL targets, using $ultragoal with built-in visual verdict and pixel-diff evidence until the implementation matches and leaves a reproducible design system." ---- - -# Visual Ralph Skill - -Use `$visual-ralph` for measured frontend implementation from an approved generated reference, static image, or live-URL baseline. The loop is: - -`description / URL -> approved reference -> $ultragoal implementation -> Visual Ralph verdict + pixel diff -> reusable design system`. - -For URL cloning, this skill owns the migrated `$web-clone` use case; preserve URL, viewport, fidelity, and interaction notes here. Do not invoke standalone `$web-clone`. - -Shared operating, delegation, state, hook, team, cancellation, and verification invariants live in [`templates/AGENTS.md`](../../templates/AGENTS.md). Follow that source instead of duplicating its rules here. - -## Use when - -- The user wants a web/app UI built or restyled against a visual target. -- A live URL or generated raster mockup needs measured implementation and pixel-level iteration. -- The result must leave reusable repo-native tokens/components, not only a matching screenshot. - -Do not use it for a durable `DESIGN.md` brief (`$design`), non-visual backend work, comparison-only fixes that can be executed directly, or deterministic SVG/code-native assets. - -## Workflow - -### 1. Ground the repository - -Inspect package manager/scripts, frontend framework and routes, styling/token conventions, screenshot tooling, and reusable components. Choose stack-specific commands only when repository evidence supports them. - -### 2. Establish a reference - -For a live URL, capture or document an artifact containing source URL and permission/scope, viewport(s), route/state, seed/login assumptions, baseline screenshot path or capture command, visible-control parity notes, and exclusions (backend/API/auth, personalized data, crawling, third-party widgets). - -For a generated concept, use `$imagegen` with classification `ui-mockup`, viewport/aspect ratio, surface, layout hierarchy, typography, color mood, exact text, no logos/watermarks/unrequested marks, and readable/feasible UI details. In OMX runtime, queue the continuation checkpoint before the built-in image tool: - -```bash -omx imagegen continuation <session-id> --artifact <slug-or-filename> --generated-dir "$CODEX_HOME/generated_images/<session>" --work-dir ".omx/artifacts/visual-ralph/<slug>" -``` - -Copy the approved reference into `.omx/artifacts/visual-ralph/<slug>/reference.png`; do not leave it only under `$CODEX_HOME/generated_images`. - -### 3. Approval gate - -Stop after generation or URL capture and obtain approval of one reference image/state (or a targeted regeneration/capture adjustment). Before approval, do not implement or invoke `$ultragoal`. After approval, the image/baseline is the visual source of truth; major pivots require an explicit user request. - -### 4. Hand off to `$ultragoal` - -Pass the approved reference/baseline, URL and permission note when applicable, viewport/content state, interaction parity and exclusions, user description, detected frontend context, screenshot command/viewport, and the completion checklist. The active Ultragoal execution lane edits, runs, captures, and iterates after approval until matched or blocked. - -### 5. Verdict before every edit - -For each iteration, capture the current screenshot with viewport/state, run Visual Ralph verdict (using `vision` when needed), and treat its JSON as authoritative. If `score < 90`, turn `differences[]` and `suggestions[]` into the next edit plan and rerun before editing. Required verdict keys: `score`, `verdict`, `category_match`, `differences[]`, `suggestions[]`, `reasoning`. - -### 6. Secondary diff evidence - -Use pixel diff/pixelmatch overlays only to locate hotspots and translate them into edits; they never replace the verdict. Record final reference, screenshot, and diff artifacts for auditability. - -### 7. Reusable design system - -Encode the match in existing repo-native CSS variables, theme tokens, config, component variants, stories, or `DESIGN.md` updates. Capture applicable colors, spacing, typography/weights, radii, shadows/elevation, and important variants/states. Extend existing patterns rather than adding a parallel layer. - -## Completion evidence and stop conditions - -Do not declare done until the approved reference/baseline and reproduction command (viewport, route, state, output path) are saved; final verdict is `>= 90`; secondary diff evidence is recorded; reusable tokens/components exist; equivalent build/lint/test verification passes; no unapproved pivot occurred; and remaining differences are documented. Stop at the approval gate or report a concrete blocker when evidence cannot satisfy these conditions. - -## Handoff template - -```text -$ultragoal "Implement the approved frontend reference. -Reference: <workspace reference or URL-derived artifact> -Source URL and permission/scope: <when applicable> -Viewport/content state: <viewport, route/state, seed/login assumptions> -Interaction parity and exclusions: <visible controls and known limits> -Route/surface: <route or component> -Screenshot command: <command and viewport> -Run Visual Ralph verdict before every next edit; pass threshold >= 90. -Use pixel diff only as secondary evidence. -Extract reusable tokens/components for colors, spacing, typography, radii, shadows, and variants. -Run the repository's equivalent verification before completion. -Do not make major design pivots unless explicitly requested." -``` - -Task: {{ARGUMENTS}} diff --git a/plugins/codex/plugins/oh-my-codex/skills/wiki/SKILL.md b/plugins/codex/plugins/oh-my-codex/skills/wiki/SKILL.md deleted file mode 100644 index 4009687a..00000000 --- a/plugins/codex/plugins/oh-my-codex/skills/wiki/SKILL.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -name: wiki -description: Persistent markdown project wiki stored under repository omx_wiki with keyword search and lifecycle capture -triggers: ["wiki add", "wiki lint", "wiki query", "wiki read", "wiki delete"] ---- - -# Wiki - -Persistent, self-maintained markdown knowledge base for project and session knowledge. - -## Operations - -### Ingest -```bash -omx wiki wiki_ingest --input '{"title":"Auth Architecture","content":"...","tags":["auth","architecture"],"category":"architecture"}' --json -``` - -### Query -```bash -omx wiki wiki_query --input '{"query":"authentication","tags":["auth"],"category":"architecture"}' --json -``` - -### Lint -```bash -omx wiki wiki_lint --json -``` - -### Quick Add -```bash -omx wiki wiki_add --input '{"title":"Page Title","content":"...","tags":["tag1"],"category":"decision"}' --json -``` - -### List / Read / Delete -```bash -omx wiki wiki_list --json -omx wiki wiki_read --input '{"page":"auth-architecture"}' --json -omx wiki wiki_delete --input '{"page":"outdated-page"}' --json -omx wiki wiki_refresh --json -``` - -## Categories -`architecture`, `decision`, `pattern`, `debugging`, `environment`, `session-log`, `reference`, `convention` - -## Storage -- Pages: `omx_wiki/*.md` -- Index: `omx_wiki/index.md` -- Log: `omx_wiki/log.md` - -## Cross-References -Use `[[page-name]]` wiki-link syntax to create cross-references between pages. - -## Auto-Capture -At session end, discoveries can be captured as `session-log-*` pages. Configure via `wiki.autoCapture` in `.omx-config.json`. - -## Hard Constraints -- No vector embeddings — query uses keyword + tag matching only -- Wiki files are repository project knowledge under `omx_wiki/`; legacy `.omx/wiki/` is read-only compatibility input when no canonical wiki exists diff --git a/plugins/codex/plugins/oh-my-codex/skills/worker/SKILL.md b/plugins/codex/plugins/oh-my-codex/skills/worker/SKILL.md deleted file mode 100644 index 07f46c57..00000000 --- a/plugins/codex/plugins/oh-my-codex/skills/worker/SKILL.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -name: worker -description: Team worker protocol (ACK, mailbox, task lifecycle) for tmux-based OMX teams ---- - -# Worker Skill - -## When to use - -Use only when the session was started as an OMX Team worker. Read `AGENTS.md#durable-runtime-invariants-canonical-ssot` before acting; it defines the durable ownership and coordination boundaries. - -## Inputs and preconditions - -- Require `OMX_TEAM_WORKER=<team-name>/worker-<n>`. -- Resolve this skill from the first existing path: - `${CODEX_HOME:-~/.codex}/skills/worker/SKILL.md`, `~/.codex/skills/worker/SKILL.md`, `<leader_cwd>/.codex/skills/worker/SKILL.md`, then `<leader_cwd>/skills/worker/SKILL.md`. -- Resolve the Team state root in this order: `OMX_TEAM_STATE_ROOT`, worker identity `team_state_root`, config/manifest `team_state_root`, local `.omx/state`. - -## Operational steps - -1. Split the environment into `teamName` and `workerName`; Send a startup ACK before task work: - ```bash - omx team api send-message --input '{"team_name":"<teamName>","from_worker":"<workerName>","to_worker":"leader-fixed","body":"ACK: <workerName> initialized"}' --json - ``` -2. Read `<team_state_root>/team/<teamName>/workers/<workerName>/inbox.md` and take the first unblocked assignment. -3. Read `<team_state_root>/team/<teamName>/tasks/task-<id>.json`; APIs use the bare numeric `task_id` (for example `"1"`). -4. Claim before editing: - ```bash - omx team api claim-task --input '{"team_name":"<teamName>","task_id":"<id>","worker":"<workerName>"}' --json - ``` -5. Do the assigned work. Do not write task lifecycle fields directly. -6. Complete or fail through the lifecycle API, from `in_progress` to `completed` or `failed`: - ```bash - omx team api transition-task-status --input '{"team_name":"<teamName>","task_id":"<id>","from":"in_progress","to":"completed","claim_token":"<token>","result":"<evidence>"}' --json - ``` - Use `release-task-claim` only to requeue a blocked task to `pending`. -7. Check and acknowledge mailbox messages: - ```bash - omx team api mailbox-list --input '{"team_name":"<teamName>","worker":"<workerName>"}' --json - omx team api mailbox-mark-delivered --input '{"team_name":"<teamName>","worker":"<workerName>","message_id":"<MESSAGE_ID>"}' --json - ``` -8. Write idle status after the transition: - `<team_state_root>/team/<teamName>/workers/<workerName>/status.json` with `{"state":"idle","updated_at":"<ISO timestamp>"}`. - -## Exit and evidence - -Completion evidence names the task id, changed artifacts, verification performed, and any blocker. ACKs, task transitions, mailbox acknowledgements, and status writes must be observable through the Team API/state files. On shutdown, follow the lead's inbox instructions and write the required shutdown acknowledgement before exiting. diff --git a/plugins/codex/plugins/superpowers-zh/.codex-plugin/plugin.json b/plugins/codex/plugins/superpowers-zh/.codex-plugin/plugin.json deleted file mode 100644 index 0c7243b0..00000000 --- a/plugins/codex/plugins/superpowers-zh/.codex-plugin/plugin.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "superpowers-zh", - "version": "6.3.0-zh.1", - "description": "Superpowers 的简体中文本地化版本,提供规划、TDD、系统化调试、代码评审和交付工作流。", - "author": { - "name": "Jesse Vincent", - "email": "jesse@fsck.com", - "url": "https://github.com/obra" - }, - "homepage": "https://github.com/AreChen/superpowers-zh", - "repository": "https://github.com/AreChen/superpowers-zh", - "license": "MIT", - "keywords": [ - "brainstorming", - "subagent-driven-development", - "skills", - "planning", - "tdd", - "debugging", - "code-review", - "workflow" - ], - "skills": "./skills/", - "hooks": {}, - "interface": { - "displayName": "Superpowers-zh", - "shortDescription": "Superpowers 简体中文本地化版本。", - "longDescription": "Superpowers-zh 是官方 Superpowers 的简体中文本地化版本,保留规划、测试驱动开发、系统化调试、并行执行、代码评审和分支收尾工作流。", - "developerName": "AreChen / Jesse Vincent", - "category": "Developer Tools", - "capabilities": [ - "Interactive", - "Read", - "Write" - ], - "defaultPrompt": [ - "我有一个想实现的点子。", - "让我们给这个项目添加一个功能。" - ], - "websiteURL": "https://github.com/AreChen/superpowers-zh", - "privacyPolicyURL": "https://docs.github.com/en/site-policy/privacy-policies/github-general-privacy-statement", - "termsOfServiceURL": "https://docs.github.com/en/site-policy/github-terms/github-terms-of-service", - "brandColor": "#F59E0B", - "composerIcon": "./assets/superpowers-small.svg", - "logo": "./assets/app-icon.png", - "screenshots": [] - } -} diff --git a/plugins/codex/plugins/superpowers-zh/CODE_OF_CONDUCT.md b/plugins/codex/plugins/superpowers-zh/CODE_OF_CONDUCT.md deleted file mode 100644 index f6422545..00000000 --- a/plugins/codex/plugins/superpowers-zh/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,128 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, religion, or sexual identity -and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the - overall community - -Examples of unacceptable behavior include: - -* The use of sexualized language or imagery, and sexual attention or - advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email - address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -jesse@primeradiant.com. -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series -of actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or -permanent ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within -the community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.0, available at -https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. - -Community Impact Guidelines were inspired by [Mozilla's code of conduct -enforcement ladder](https://github.com/mozilla/diversity). - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see the FAQ at -https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations. diff --git a/plugins/codex/plugins/superpowers-zh/LICENSE b/plugins/codex/plugins/superpowers-zh/LICENSE deleted file mode 100644 index abf03903..00000000 --- a/plugins/codex/plugins/superpowers-zh/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2025 Jesse Vincent - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/plugins/codex/plugins/superpowers-zh/README.md b/plugins/codex/plugins/superpowers-zh/README.md deleted file mode 100644 index bf0e2b0f..00000000 --- a/plugins/codex/plugins/superpowers-zh/README.md +++ /dev/null @@ -1,378 +0,0 @@ -# Superpowers 中文版 - -Superpowers 是一套面向编程 Agent 的完整软件开发方法论。它由一组可组合的技能和启动指令构成,确保 Agent 能在合适的时机主动调用这些技能。 - -本项目是 [obra/superpowers](https://github.com/obra/superpowers) 的中文分叉版本,主要面向使用 Codex、OpenCode 的中文用户,同时兼容 Windows、Linux 和 macOS。 - -## 版本对齐 - -- **当前中文发行版:** `v6.3.0-zh.1` -- **对齐的上游正式版:** [`obra/superpowers v6.3.0`](https://github.com/obra/superpowers/releases/tag/v6.3.0) -- **上游基线提交:** [`b36e082`](https://github.com/obra/superpowers/commit/b36e082) -- **对齐日期:** 2026-08-21 - -版本号中的 `zh.1` 表示:功能基线与上游 `v6.3.0` 对齐,这是该基线上的第 1 个中文发行版。后续同步新的上游版本时,会先更新前三段版本号,再从 `zh.1` 重新开始计数。 - -## 目录 - -- [工作原理](#工作原理) -- [商业服务](#商业服务) -- [安装](#安装) - - [Claude Code](#claude-code) - - [Antigravity](#antigravity) - - [Codex App](#codex-app) - - [Codex CLI](#codex-cli) - - [Cursor](#cursor) - - [Devin CLI](#devin-cli) - - [Factory Droid](#factory-droid) - - [Gemini CLI](#gemini-cli) - - [GitHub Copilot CLI](#github-copilot-cli) - - [Grok Build CLI](#grok-build-cli) - - [Kimi Code](#kimi-code) - - [OpenCode](#opencode) - - [Pi](#pi) - - [Hermes Agent](#hermes-agent) -- [基本工作流](#基本工作流) -- [社区](#社区) -- [包含内容](#包含内容) -- [核心理念](#核心理念) -- [参与贡献](#参与贡献) -- [更新](#更新) -- [许可证](#许可证) -- [Visual Companion 遥测](#visual-companion-遥测) - -## 工作原理 - -Superpowers 从你启动编程 Agent 的那一刻开始发挥作用。当 Agent 发现你准备构建某项功能时,它不会立刻埋头写代码,而是先退一步,询问你真正想解决的问题。 - -Agent 会通过对话逐步梳理出规格,并以便于阅读和确认的小段内容向你展示。 - -设计获批后,Agent 会编写一份足够清晰的实施计划:即使交给一名缺少项目背景、工程判断欠佳且不喜欢测试的初级工程师,也能据此完成任务。计划强调真正的红灯—绿灯 TDD、YAGNI(你不会需要它)和 DRY。 - -当你确认“开始”后,Agent 会启动由子 Agent 驱动的开发流程。不同 Agent 会依次完成各项工程任务,检查并审查彼此的工作,然后继续推进。只要计划足够清晰,Agent 连续自主工作数小时而不偏离目标并不罕见。 - -系统还包含更多细节,但以上就是核心流程。由于技能会自动触发,你不需要记忆特殊命令——安装后,你的编程 Agent 就拥有了 Superpowers。 - -## 商业服务 - -如果你在企业环境中使用 Superpowers,并需要商业支持、额外工具或托管式成本管理,可联系上游团队:sales@primeradiant.com。 - -## 安装 - -不同编程 Agent 的安装方式不同。如果你同时使用多个 Agent,需要分别安装。 - -### Claude Code - -推荐直接注册本中文仓库提供的插件市场: - -```bash -/plugin marketplace add AreChen/superpowers-zh -``` - -然后安装中文插件: - -```bash -/plugin install superpowers@superpowers-dev -``` - -如果你希望安装上游英文版,也可以使用 [Claude 官方插件市场](https://claude.com/plugins/superpowers): - -```bash -/plugin install superpowers@claude-plugins-official -``` - -### Antigravity - -直接从本仓库安装插件: - -```bash -agy plugin install https://github.com/AreChen/superpowers-zh -``` - -Antigravity 会运行插件的会话启动钩子,因此 Superpowers 会从第一条消息起生效。更新时重新执行同一条命令即可。 - -### Codex App - -先通过 Codex CLI 添加本项目的 Git 插件市场: - -```bash -codex plugin marketplace add AreChen/superpowers-zh --ref main -``` - -然后: - -1. 重启 Codex App。 -2. 在侧边栏打开“插件”。 -3. 选择 `Superpowers 中文版开发版` 市场。 -4. 找到 `Superpowers 中文版`,点击旁边的 `+` 并按提示安装。 - -### Codex CLI - -先添加本项目的插件市场: - -```bash -codex plugin marketplace add AreChen/superpowers-zh --ref main -``` - -打开插件界面: - -```text -/plugins -``` - -选择 `Superpowers 中文版开发版` 市场,搜索 `superpowers` 并安装插件。 - -查看已配置的市场: - -```bash -codex plugin marketplace list -``` - -更新市场: - -```bash -codex plugin marketplace upgrade superpowers-dev -``` - -### Cursor - -在 Cursor Agent 对话中从插件市场安装: - -```text -/add-plugin superpowers -``` - -也可以在插件市场中搜索 `superpowers`。请确认安装详情显示的是 `Superpowers 中文版`;如果只看到上游英文版,可改用本仓库的本地插件方式。 - -### Devin CLI - -从本中文仓库安装插件: - -```bash -devin plugins install AreChen/superpowers-zh -``` - -更新到最新版本: - -```bash -devin plugins update superpowers -``` - -### Factory Droid - -注册本中文仓库的插件市场: - -```bash -droid plugin marketplace add https://github.com/AreChen/superpowers-zh -``` - -安装插件: - -```bash -droid plugin install superpowers@superpowers-dev -``` - -### Gemini CLI - -安装本中文仓库提供的扩展: - -```bash -gemini extensions install https://github.com/AreChen/superpowers-zh -``` - -后续更新: - -```bash -gemini extensions update superpowers -``` - -### GitHub Copilot CLI - -注册本中文仓库的插件市场: - -```bash -copilot plugin marketplace add AreChen/superpowers-zh -``` - -安装插件: - -```bash -copilot plugin install superpowers@superpowers-dev -``` - -### Grok Build CLI - -Superpowers 可通过 [Grok 官方插件市场](https://github.com/xai-org/plugin-marketplace)安装。当前官方市场对应上游插件: - -```bash -grok plugin install superpowers@xai-official --trust -``` - -也可以在 TUI 中打开市场,搜索 Superpowers 并安装: - -```text -/marketplace -``` - -### Kimi Code - -可以直接从本中文仓库安装: - -```text -/plugins install https://github.com/AreChen/superpowers-zh -``` - -也可以打开 Kimi Code 的插件管理器: - -```text -/plugins -``` - -进入 `Marketplace`,搜索并安装 `Superpowers`。请在安装前确认插件描述为中文版本。 - -详细说明:[docs/README.kimi.md](docs/README.kimi.md) - -### OpenCode - -OpenCode 使用自己的插件安装机制。即使你已经在其他编程 Agent 中安装了 Superpowers,仍需为 OpenCode 单独安装。 - -在全局或项目级 `opencode.json` 的 `plugin` 数组中加入: - -```json -{ - "plugin": [ - "superpowers@git+https://github.com/AreChen/superpowers-zh.git" - ] -} -``` - -重启 OpenCode。插件会自动注册本项目中的全部技能,无需手动为技能目录创建符号链接。 - -也可以让 OpenCode 获取并遵循仓库内的安装说明: - -```text -获取并遵循 https://raw.githubusercontent.com/AreChen/superpowers-zh/refs/heads/main/.opencode/INSTALL.md 中的说明 -``` - -详细说明:[docs/README.opencode.md](docs/README.opencode.md) - -### Pi - -从本仓库安装为 Pi 软件包: - -```bash -pi install git:github.com/AreChen/superpowers-zh -``` - -本地开发时,可以把当前检出目录作为临时软件包加载: - -```bash -pi -e /path/to/superpowers-zh -``` - -Pi 软件包会加载 Superpowers 技能,并通过一个小型扩展在会话启动和上下文压缩后注入 `using-superpowers` 引导。Pi 原生支持技能,因此不需要兼容性的 `Skill` 工具。子 Agent 和任务列表工具仍可作为可选的 Pi 配套软件包使用。 - -### Hermes Agent - -从本中文仓库安装 Hermes 插件: - -```bash -hermes plugins install AreChen/superpowers-zh --enable -``` - -安装后重启正在运行的 Hermes 会话。注意:Hermes 没有上下文压缩后的钩子;如果超长会话在第一轮之后发生压缩并导致技能停止触发,请启动新会话。 - -## 基本工作流 - -1. **brainstorming** —— 在编写代码前触发。通过提问澄清初步想法、探索替代方案,分段展示设计供用户确认,并保存设计文档。 - -2. **using-git-worktrees** —— 设计获批后触发。在新分支上创建隔离工作区,完成项目初始化,并验证测试基线干净。 - -3. **writing-plans** —— 获得批准的设计后触发。把工作拆分为小型任务,每项任务通常需要 2–5 分钟,并包含准确的文件路径、完整代码和验证步骤。 - -4. **subagent-driven-development** 或 **executing-plans** —— 计划完成后触发。前者为每项任务派遣新的子 Agent,并进行规格符合性和代码质量两阶段审查;后者按批次执行,并在关键节点等待人工确认。 - -5. **test-driven-development** —— 实施期间触发。强制执行“红灯—绿灯—重构”:先写失败测试并确认失败,再编写最小实现并确认通过,然后提交。测试之前编写的生产代码需要删除并重新按 TDD 实现。 - -6. **requesting-code-review** —— 在任务之间触发。根据计划审查实现,按严重程度报告问题;严重问题会阻止继续推进。 - -7. **finishing-a-development-branch** —— 全部任务完成后触发。验证测试并提供合并、创建 PR、保留或丢弃分支等选项,最后清理 worktree。 - -**Agent 会在执行任何任务前检查是否存在相关技能。** 这些是必须遵循的工作流,而不是可选建议。 - -## 社区 - -Superpowers 由 [Jesse Vincent](https://blog.fsck.com) 和 [Prime Radiant](https://primeradiant.com) 团队创建。本中文分叉由 [AreChen](https://github.com/AreChen) 维护。 - -- **Discord**:[加入上游社区](https://discord.gg/35wsABTejz),获取支持、提出问题并分享你使用 Superpowers 构建的项目。 -- **问题反馈**:https://github.com/AreChen/superpowers-zh/issues -- **上游版本公告**:[订阅通知](https://primeradiant.com/superpowers/) - -## 包含内容 - -### 技能库 - -**测试** - -- **test-driven-development** —— 红灯—绿灯—重构循环,包含测试反模式参考。 - -**调试** - -- **systematic-debugging** —— 四阶段根因分析流程,包含根因追踪、纵深防御和基于条件的等待技术。 -- **verification-before-completion** —— 在声称完成前用证据确认问题确实已经解决。 - -**协作** - -- **brainstorming** —— 通过苏格拉底式提问完善设计。 -- **writing-plans** —— 编写详细实施计划。 -- **executing-plans** —— 带检查点的分批执行。 -- **dispatching-parallel-agents** —— 并行子 Agent 工作流。 -- **requesting-code-review** —— 请求审查前的完整流程。 -- **receiving-code-review** —— 理解并处理审查反馈。 -- **using-git-worktrees** —— 使用隔离分支并行开发。 -- **finishing-a-development-branch** —— 合并或创建 PR 的决策流程。 -- **subagent-driven-development** —— 通过规格符合性和代码质量两阶段审查快速迭代。 - -**元技能** - -- **writing-skills** —— 按最佳实践创建和测试新技能。 -- **using-superpowers** —— 技能系统的入口和使用规则。 - -## 核心理念 - -- **测试驱动开发** —— 始终先写测试。 -- **系统化优于临时应对** —— 遵循流程,不靠猜测。 -- **降低复杂度** —— 把简单作为首要目标。 -- **证据优于声明** —— 声称成功之前先验证。 - -参阅 [Superpowers 最初的发布公告](https://blog.fsck.com/2025/10/09/superpowers/)。 - -## 参与贡献 - -本项目跟随上游 Superpowers 的总体贡献原则。通常不接受随意新增技能;任何技能修改都必须能在项目支持的编程 Agent 和主要操作系统上正常工作。 - -1. Fork [AreChen/superpowers-zh](https://github.com/AreChen/superpowers-zh)。 -2. 同步最新的 `main` 分支。 -3. 为你的改动创建独立分支。 -4. 创建或修改技能时,遵循 `writing-skills` 技能完成编写和测试。 -5. 向 `main` 提交 PR,并完整填写 PR 模板。 - -技能行为测试使用 [superpowers-evals](https://github.com/prime-radiant-inc/superpowers-evals/) 中的 drill 评测框架,将其克隆到 `evals/` 后可按 `evals/README.md` 配置。插件基础设施测试位于 `tests/`,通过对应的 `run-*.sh` 或 `npm test` 运行。 - -完整指南见 [`skills/writing-skills/SKILL.md`](skills/writing-skills/SKILL.md)。 - -## 更新 - -Superpowers 的更新方式取决于你使用的编程 Agent。通过 Git 市场安装时,通常可以使用对应 Agent 的市场更新命令;通过仓库 URL 安装时,可重新执行安装命令或拉取最新的 `main` 分支。 - -## 许可证 - -本项目采用 MIT 许可证,详情见 [LICENSE](LICENSE)。 - -## Visual Companion 遥测 - -技能和插件通常不会向作者提供任何使用反馈,因此我们无法直接了解有多少人在使用 Superpowers。默认情况下,`brainstorming` 的可选 Visual Companion 会从 Prime Radiant 网站加载品牌图标,并附带当前 Superpowers 版本号。 - -该请求不包含你的项目、提示词或编程 Agent 的详细信息,也不会记录点击行为或你正在构建的内容。它只用于粗略了解使用人数和版本分布,并且完全可选。 - -如需关闭,请把环境变量 `SUPERPOWERS_DISABLE_TELEMETRY` 设置为任意真值。Superpowers 也会遵循 Claude Code 的 `DISABLE_TELEMETRY` 和 `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` 退出设置。 diff --git a/plugins/codex/plugins/superpowers-zh/THIRD_PARTY_SOURCE.json b/plugins/codex/plugins/superpowers-zh/THIRD_PARTY_SOURCE.json deleted file mode 100644 index 32a32b2a..00000000 --- a/plugins/codex/plugins/superpowers-zh/THIRD_PARTY_SOURCE.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "sourceId": "superpowers-zh", - "repo": "https://github.com/AreChen/superpowers-zh.git", - "ref": "main", - "commit": "e6ae7ddf5da1b5f7ec0388ce053c894d23a64857", - "adapter": "codex-plugin", - "sourcePath": ".", - "syncedAt": "2026-08-21T06:41:32Z" -} diff --git a/plugins/codex/plugins/superpowers-zh/assets/app-icon.png b/plugins/codex/plugins/superpowers-zh/assets/app-icon.png deleted file mode 100644 index 25518da4625e850d97b15a9e0a27e7115969dc0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48231 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 diff --git a/plugins/codex/plugins/superpowers-zh/assets/superpowers-small.svg b/plugins/codex/plugins/superpowers-zh/assets/superpowers-small.svg deleted file mode 100644 index c514fe33..00000000 --- a/plugins/codex/plugins/superpowers-zh/assets/superpowers-small.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><svg id="Calque_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M394.28,207.8c.81,2.41,1.39,4.78,1.8,7.07,1.61,9.03-.93,17.78-5.99,21.74-22.6,17.7-49.85,29.35-75.34,38.6-.59.22-1.09.28-1.4.34-2.22.47-4.95,1.04-7.25,0-1.46-.66-2.25-1.74-2.66-2.3-1.56-2.1-1.59-4.31-1.56-5.13.1-2.67-.01-4.69,0-4.82.45-3.52.91-10.66,1.41-21.28.6-3.87,2.16-9.63,6.94-13.96,4.01-3.62,8.33-4.6,14.59-5.87,10.76-2.19,37.21-8.22,47.42-16.56,1.63-1.33,2.97-2.65,4.19-3.96,3.72-3.99,6.39-7.92,7.93-10.36,3.22,3.22,7.25,8.48,9.92,16.47Z"/><path d="M428.67,185.28c-2.33,11.99-8.91,22.32-15.88,30.38.27-5.5-.05-12.11-1.86-19.08-5.04-19.36-19.74-34.7-37.78-37.78-32.21-9.74-70.59,3.79-99.08,18.29-3.87,1.95-9.52-2.77-11.84-8.16-3.32-7.71-1.63-6.28,2.61-8.49,38.31-20.03,82.01-39.61,123.91-29.7,8.26,1.95,15.96,5.26,23.48,10.54,11.32,7.96,20.21,24.74,16.44,44Z"/><path d="M117.72,304.2c-.81-2.41-1.39-4.78-1.8-7.07-1.61-9.03.93-17.78,5.99-21.74,22.6-17.7,49.85-29.35,75.34-38.6.59-.22,1.09-.28,1.4-.34,2.22-.47,4.95-1.04,7.25,0,1.46.66,2.25,1.74,2.66,2.3,1.56,2.1,1.59,4.31,1.56,5.13-.1,2.67.01,4.69,0,4.82-.45,3.52-.91,10.66-1.41,21.28-.6,3.87-2.16,9.63-6.94,13.96-4.01,3.62-8.33,4.6-14.59,5.87-10.76,2.19-37.21,8.22-47.42,16.56-1.63,1.33-2.97,2.65-4.19,3.96-3.72,3.99-6.39,7.92-7.93,10.36-3.22-3.22-7.25-8.48-9.92-16.47Z"/><path d="M83.33,326.72c2.33-11.99,8.91-22.32,15.88-30.38-.27,5.5.05,12.11,1.86,19.08,5.04,19.36,19.74,34.7,37.78,37.78,32.21,9.74,70.59-3.79,99.08-18.29,3.87-1.95,9.52,2.77,11.84,8.16,3.32,7.71,1.63,6.28-2.61,8.49-38.31,20.03-82.01,39.61-123.91,29.7-8.26-1.95-15.96-5.26-23.48-10.54-11.32-7.96-20.21-24.74-16.44-44Z"/><ellipse cx="255.16" cy="258.86" rx="28.95" ry="28.76"/></svg> \ No newline at end of file diff --git a/plugins/codex/plugins/superpowers-zh/skills/brainstorming/SKILL.md b/plugins/codex/plugins/superpowers-zh/skills/brainstorming/SKILL.md deleted file mode 100644 index 8f1c7fa1..00000000 --- a/plugins/codex/plugins/superpowers-zh/skills/brainstorming/SKILL.md +++ /dev/null @@ -1,203 +0,0 @@ ---- -name: brainstorming -description: "在进行任何创造性工作之前,你都必须使用此技能——包括创建功能、构建组件、添加功能或修改行为。在实现之前探索用户意图、需求和设计。" ---- - -# 通过头脑风暴将想法转化为设计 - -通过自然的协作式对话,帮助将想法转化为完整成形的设计和规格说明。 - -先判断请求需要多少流程,再沿着对应路径推进:了解上下文、完善想法、展示设计,并获得人类伙伴的批准。 - -<HARD-GATE> -在你告知人类伙伴打算做什么并获得批准之前,不得调用任何实现技能、编写任何代码、搭建任何项目脚手架或采取任何实现行动。以下每条路径上的每项任务都适用——流程仪式会随任务调整,但批准关卡永远不会取消。 -</HARD-GATE> - -## 三条路径 - -在提出第一个问题之前先分类,并大声说出分类——例如“这看起来是有边界的任务,所以我会在这里展示一个简短设计,而不是写规格说明”——让人类伙伴可以推翻你的判断: - -- **探索(Spike)**——可行性问题(“能不能……”“是否可能……”“快速粗糙地做也可以”),产出是答案而不是要保留的代码。用 2-3 句话说明问题和尝试方式,得到同意后,以正确性允许的最低成本进行探查。不写设计文档或规格文件。把发现报告为建议;构建的任何内容都必须标记为一次性产物。 -- **有边界(Bounded)**——对本仓库已有代码的范围明确的改动:新标志、小端点、单文件修复。仅仅理解应用类型还不够——有边界意味着要修改的流程已经存在且可供阅读。如果没有可修改的现有流程,任务就不是有边界的。提出关键澄清问题,在对话中展示简短设计(几句话到几段短文),然后停止。只有人类伙伴明确同意设计后才能实现;不写规格文件或实现计划文档。 -- **架构性(Architectural)**——新项目、新子系统、改变组件关系或其他人依赖的接口。走完整流程:提问、方案、分节设计、书面规格说明,然后调用 writing-plans 技能。 - -如果在两条路径之间犹豫,选择较重的路径。这个棘轮只能向上:任务中途发现隐藏复杂性时,停止、说明情况并升级路径;不会中途降级。 - -## 反模式:“太简单了,不需要批准” - -每条路径都必须在人类伙伴批准意图后才能实现。待办事项列表、单函数工具、配置变更——设计可以只是聊天中的两句话,但仍必须展示并获得批准。“简单”任务最容易因未经审视的假设而浪费工作。随简单程度调整的是产物,不是批准。 - -## 危险信号 - -| 想法 | 事实 | -|---------|---------| -| “这太简单了,不需要设计” | 简单意味着设计简短,而不是没有设计。聊天中写两句话,然后获得批准。 | -| “我把它叫作有边界,就可以跳过规格” | 为了省事而贴标签本身就是疑点——选择更重的路径。 | -| “它有边界而且设计显而易见——他们阅读时我先开始” | 关卡是批准,不是设计长短。展示后停止,直到听到同意。 | -| “我了解这种应用,所以它有边界” | 有边界衡量的是仓库,而不是你的熟悉程度。新项目没有现有流程,属于架构性任务。 | -| “探索成功了,我就保留代码” | 探索的产出是答案。保留代码是新请求,需要重新分类。 | -| “它变复杂了,但我快做完了——不用重新分类” | 隐藏复杂性会在任务中途升级路径。停止并说明情况。 | -| “他们批准了探索,所以后续改动也获批了” | 每个任务都有自己的分类和自己的批准。 | - -## 清单 - -先分类并宣布路径,然后为对应路径上的每一项创建任务并按顺序完成。 - -**探索路径:** -1. **探索项目上下文**——获取足以界定探查范围的信息 -2. **展示问题和探查计划**——2-3 句话 -3. **获得批准**——得到同意即可 -4. **进行调查**——以正确性允许的最低成本推进 -5. **报告发现**——给出建议,并标记所有构建内容为一次性产物 - -**有边界路径:** -1. **探索项目上下文**——检查文件、文档和最近提交 -2. **提出澄清问题**——一次一个,只问关键问题 -3. **在聊天中展示简短设计**——方案、涉及文件和测试 -4. **获得批准**——停止并等待明确的“是”;展示设计后立即开始就是跳过关卡 -5. **实现**——使用正常开发流程推进(TDD 仍适用);不写计划文档 - -**架构性路径:** -1. **探索项目上下文**——检查文件、文档和最近的提交 -2. **在恰当时机提供视觉伴侣**——不要预先提供。当某个问题第一次确实用展示比用描述更清楚时,就在那时提出使用它(用一条单独的消息);获得批准后,它的浏览器标签页会为你打开。如果始终没有出现视觉问题,就绝不要提出使用它。参见下方的“视觉伴侣”一节。 -3. **提出澄清问题**——一次一个,了解目的、约束和成功标准 -4. **提出 2-3 种方案**——说明权衡取舍和你的建议 -5. **展示设计**——按复杂度划分并调整各节内容,每展示一节后都要获得用户批准 -6. **编写设计文档**——保存到 `docs/superpowers/specs/YYYY-MM-DD-<topic>-design.md` 并提交 -7. **规格说明自审**——快速进行内联检查,查找占位符、矛盾、歧义和范围问题(见下文) -8. **用户审查已写好的规格说明**——请用户在继续之前审查规格说明文件 -9. **过渡到实现**——调用 writing-plans 技能来创建实现计划 - -## 流程 - -```dot -digraph brainstorming { - "Classify: spike / bounded / architectural" [shape=diamond, label="分类:探索 / 有边界 / 架构性"]; - "Present question + probe (2-3 sentences)" [shape=box, label="展示问题和探查(2-3 句话)"]; - "Ask clarifying questions (bounded)" [shape=box, label="提出澄清问题(有边界)"]; - "Present short design in chat" [shape=box, label="在聊天中展示简短设计"]; - "Human approves?" [shape=diamond, label="人类批准了吗?"]; - "Investigate; report recommendation" [shape=doublecircle, label="调查并报告建议"]; - "Implement via normal workflow (no plan doc)" [shape=doublecircle, label="通过正常流程实现(无计划文档)"]; - "Explore project context" [shape=box, label="探索项目上下文"]; - "Ask clarifying questions" [shape=box, label="提出澄清问题"]; - "Propose 2-3 approaches" [shape=box, label="提出 2-3 种方案"]; - "Present design sections" [shape=box, label="分节呈现设计"]; - "User approves design?" [shape=diamond, label="用户批准设计了吗?"]; - "Write design doc" [shape=box, label="编写设计文档"]; - "Spec self-review\n(fix inline)" [shape=box, label="规格说明自审\n(内联修正)"]; - "User reviews spec?" [shape=diamond, label="用户审查规格说明了吗?"]; - "Invoke writing-plans skill" [shape=doublecircle, label="调用 writing-plans 技能"]; - "Hidden complexity? Upgrade path" [shape=box, label="发现隐藏复杂性?升级路径"]; - - "Classify: spike / bounded / architectural" -> "Present question + probe (2-3 sentences)" [label="探索"]; - "Classify: spike / bounded / architectural" -> "Ask clarifying questions (bounded)" [label="有边界"]; - "Classify: spike / bounded / architectural" -> "Explore project context" [label="架构性"]; - "Present question + probe (2-3 sentences)" -> "Human approves?"; - "Ask clarifying questions (bounded)" -> "Present short design in chat"; - "Present short design in chat" -> "Human approves?"; - "Human approves?" -> "Investigate; report recommendation" [label="探索:是"]; - "Human approves?" -> "Implement via normal workflow (no plan doc)" [label="有边界:是"]; - "Hidden complexity? Upgrade path" -> "Classify: spike / bounded / architectural"; - "Explore project context" -> "Ask clarifying questions"; - "Ask clarifying questions" -> "Propose 2-3 approaches"; - "Propose 2-3 approaches" -> "Present design sections"; - "Present design sections" -> "User approves design?"; - "User approves design?" -> "Present design sections" [label="否,修改"]; - "User approves design?" -> "Write design doc" [label="是"]; - "Write design doc" -> "Spec self-review\n(fix inline)"; - "Spec self-review\n(fix inline)" -> "User reviews spec?"; - "User reviews spec?" -> "Write design doc" [label="要求更改"]; - "User reviews spec?" -> "Invoke writing-plans skill" [label="已批准"]; -} -``` -**终止状态取决于路径。** 架构性路径:brainstorming 之后唯一调用的技能是 writing-plans,绝不要调用 frontend-design、mcp-builder 或其他实现技能。有边界路径:获批后直接使用正常开发流程实现,不写计划文档。探索路径:终止状态是报告建议。 - -## 流程 - -下面的子节服务于有边界和架构性路径(探索路径在“展示探查、获得同意”处停止)。从**探索方案**开始的内容属于架构性深度;对有边界任务来说,上下文、几个问题和一份聊天中的简短设计就是完整流程。 - -**理解想法:** - -- 首先查看当前项目状态(文件、文档、最近的提交) -- 在询问详细问题之前,先评估范围:如果请求描述了多个相互独立的子系统(例如,“构建一个包含聊天、文件存储、计费和分析的平台”),应立即指出这一点。不要把问题浪费在细化一个首先需要拆分的项目的细节上。 -- 如果项目规模过大,无法用一份规格说明涵盖,请帮助用户将其拆分为多个子项目:哪些是独立的部分、它们如何关联、应按什么顺序构建?然后按照正常的设计流程,对第一个子项目进行头脑风暴。每个子项目都有其自己的规格说明 → 计划 → 实现周期。 -- 对于范围适当的项目,每次询问一个问题以细化想法 -- 尽可能优先使用多项选择题,但开放式问题也可以 -- 每条消息只问一个问题——如果某个主题需要进一步探索,请将其拆成多个问题 -- 专注于理解:目的、约束、成功标准 - -**探索方案:** - -- 提出 2-3 种不同的方案,并说明各自的权衡 -- 以对话方式呈现选项,并给出你的建议和理由 -- 先介绍你推荐的选项,并解释原因 -- 坚决贯彻 YAGNI——从每一种方案和设计中移除不必要的功能 - -**呈现设计:** - -- 一旦你确信自己理解了要构建的内容,就呈现设计 -- 根据每个部分的复杂程度调整篇幅:如果简单直接,就用几句话;如果细节微妙复杂,则最多使用 200-300 词 -- 在每个部分之后询问目前看来是否正确 -- 涵盖:架构、组件、数据流、错误处理、测试 -- 如果有些内容不合理,随时准备返回并澄清 - -**为隔离性和清晰性而设计:** -- 将系统拆分为更小的单元,每个单元都有一个明确的用途,通过定义清晰的接口进行通信,并且可以被独立理解和测试 -- 对于每个单元,你都应该能够回答:它做什么、如何使用它,以及它依赖什么? -- 其他人能否在不阅读某个单元内部实现的情况下理解它做什么?你能否在不破坏使用方的情况下更改其内部实现?如果不能,就需要改进边界。 -- 更小且边界清晰的单元也更便于你处理——对于能够一次性纳入上下文的代码,你可以进行更好的推理;当文件职责集中时,你的编辑也会更可靠。当一个文件变得很大时,这通常表明它承担了过多职责。 - -**在现有代码库中工作:** - -- 在提出更改之前,先探索当前结构。遵循现有模式。 -- 如果现有代码存在影响这项工作的问题(例如,文件变得过大、边界不清晰、职责纠缠),应将有针对性的改进纳入设计——就像优秀的开发者会改进自己正在处理的代码一样。 -- 不要提出无关的重构。始终专注于服务当前目标的内容。 - -## 设计之后(架构性路径) - -**文档:** - -- 将经过验证的设计(规格说明)写入 `docs/superpowers/specs/YYYY-MM-DD-<topic>-design.md` - - (用户对规格说明位置的偏好会覆盖此默认设置) -- 如果可用,使用 elements-of-style:writing-clearly-and-concisely 技能 -- 将设计文档提交到 git - -**规格说明自审:** -写完规格说明文档后,以全新的视角审视它: - -1. **占位符扫描:** 是否有任何 "TBD"、"TODO"、未完成的章节或模糊的要求?修复它们。 -2. **内部一致性:** 是否有任何章节相互矛盾?架构是否与功能描述一致? -3. **范围检查:** 范围是否足够聚焦,可以用单个实现计划完成,还是需要进行拆分? -4. **歧义检查:** 是否有任何要求可能被理解为两种不同的含义?如果有,选择一种并明确写出。 - -直接修复所有问题。无需再次审查——修复后继续推进。 -**用户审查关卡:** -规格审查循环通过后,在继续之前,请用户审查已写好的规格说明: - -> “规格说明已写入 `<path>` 并提交。请审查它,并告诉我在我们开始编写实施计划之前,你是否希望进行任何更改。” - -等待用户回复。如果他们要求更改,就进行更改并重新运行规格审查循环。只有在用户批准后才能继续。 - -**实施:** - -- 调用 writing-plans 技能来创建详细的实施计划 -- 不要调用任何其他技能。writing-plans 是下一步。 - -## 可视化伴侣 - -一个基于浏览器的伴侣工具,用于在头脑风暴期间展示原型图、图表和可视化选项。它作为工具提供——而不是一种模式。同意使用该伴侣工具,意味着它可用于那些采用可视化方式会更有帮助的问题;这并不意味着每个问题都要通过浏览器进行。 - -**提供伴侣工具(即时):** 不要一开始就提出使用它。等到某个问题确实用展示的方式会比口头描述更清楚时——真正涉及原型图 / 布局 / 图表的问题,而不仅仅是一个 UI *话题*。第一次出现这种情况时,就在那时提出使用它,并且该提议要单独作为一条消息: -> “接下来的这部分如果展示给你看,可能会更容易理解——在我们推进的过程中,我可以在浏览器标签页中制作原型图、图表和对比内容。它仍是新功能,并且可能会消耗大量 token。你想让我这样做吗?我会为你打开它。” - -**此提议必须单独作为一条消息发送。** 只发送该提议——不要包含澄清问题、摘要或任何其他内容。等待用户回复。如果他们接受,使用 `--open` 启动服务器,以便他们的浏览器自动打开第一个界面。如果他们拒绝,则继续仅使用文本,并且不要再次提出使用它,除非他们主动提起。 -**逐问题决策:** 即使用户接受了,也要针对每个问题分别决定使用浏览器还是终端。判断标准是:**相比阅读,用户通过观看是否能更好地理解这一点?** - -- **使用浏览器**呈现确实属于视觉内容的内容——模型图、线框图、布局对比、架构图、并排的视觉设计 -- **使用终端**呈现文本内容——需求问题、概念性选择、权衡清单、A/B/C/D 文本选项、范围决策 - -关于 UI 主题的问题并不自动等同于视觉问题。“在此上下文中,个性意味着什么?”是一个概念性问题——使用终端。“哪种向导布局效果更好?”是一个视觉问题——使用浏览器。 - -如果他们同意使用视觉伴侣,请在继续之前阅读详细指南: -`skills/brainstorming/visual-companion.md` diff --git a/plugins/codex/plugins/superpowers-zh/skills/brainstorming/scripts/frame-template.html b/plugins/codex/plugins/superpowers-zh/skills/brainstorming/scripts/frame-template.html deleted file mode 100644 index b1cae3f1..00000000 --- a/plugins/codex/plugins/superpowers-zh/skills/brainstorming/scripts/frame-template.html +++ /dev/null @@ -1,213 +0,0 @@ -<!DOCTYPE html> -<html lang="zh-CN"> -<head> - <meta charset="utf-8"> - <title>Superpowers 头脑风暴 - - - -
- -
正在连接…
-
- -
-
- -
-
- - - diff --git a/plugins/codex/plugins/superpowers-zh/skills/brainstorming/scripts/helper.js b/plugins/codex/plugins/superpowers-zh/skills/brainstorming/scripts/helper.js deleted file mode 100644 index 7987b3af..00000000 --- a/plugins/codex/plugins/superpowers-zh/skills/brainstorming/scripts/helper.js +++ /dev/null @@ -1,167 +0,0 @@ -(function() { - const MIN_RECONNECT_MS = 500; - const MAX_RECONNECT_MS = 30000; - const TOMBSTONE_AFTER_MS = 15000; // show the "paused" overlay after this long disconnected - - // Pure: next backoff delay (doubles, capped). Exported for unit tests. - function nextReconnectDelay(current, max) { - return Math.min(current * 2, max); - } - if (typeof module !== 'undefined' && module.exports) { - module.exports = { nextReconnectDelay, MIN_RECONNECT_MS, MAX_RECONNECT_MS, TOMBSTONE_AFTER_MS }; - } - - // Everything below is browser-only; bail out when loaded in Node (tests). - if (typeof window === 'undefined') return; - - let ws = null; - let eventQueue = []; - let reconnectDelay = MIN_RECONNECT_MS; - let reconnectTimer = null; - let disconnectedSince = null; - let everConnected = false; - let tombstoneShown = false; - - function sessionKey() { - try { - return window.sessionStorage && window.sessionStorage.getItem('brainstorm-session-key'); - } catch (e) {} - return null; - } - - function websocketUrl() { - const key = sessionKey(); - return 'ws://' + window.location.host + (key ? '/?key=' + encodeURIComponent(key) : ''); - } - - function reloadAfterRecovery() { - const key = sessionKey(); - if (key) { - window.location.replace('/?key=' + encodeURIComponent(key)); - } else { - window.location.reload(); - } - } - - // Reflect connection state in the frame's status pill (absent on full-doc screens). - function setStatus(state) { - const el = document.querySelector('.status'); - if (!el) return; - const map = { - connecting: ['正在连接…', 'var(--text-tertiary)'], - connected: ['已连接', 'var(--success)'], - reconnecting: ['正在重新连接…', 'var(--warning)'], - disconnected: ['已断开连接', 'var(--error)'] - }; - const [text, color] = map[state] || map.disconnected; - el.textContent = text; - el.style.setProperty('--status-color', color); - } - - // Self-styled so it works on framed and full-document screens alike. - function showTombstone() { - if (tombstoneShown) return; - tombstoneShown = true; - const el = document.createElement('div'); - el.id = 'bs-tombstone'; - el.style.cssText = 'position:fixed;inset:0;z-index:99999;display:flex;' + - 'align-items:center;justify-content:center;padding:2rem;text-align:center;' + - 'background:rgba(20,20,22,0.92);color:#f5f5f7;font-family:system-ui,sans-serif'; - el.innerHTML = '
' + - '

伴侣已暂停

' + - '

头脑风暴伴侣已停止。' + - '请让你的编程 Agent 重新启动它——本页面会自动重新连接。

'; - if (document.body) document.body.appendChild(el); - } - - function connect() { - if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; } - setStatus(everConnected ? 'reconnecting' : 'connecting'); - ws = new WebSocket(websocketUrl()); - - ws.onopen = () => { - const recovered = tombstoneShown; - everConnected = true; - disconnectedSince = null; - reconnectDelay = MIN_RECONNECT_MS; - tombstoneShown = false; - setStatus('connected'); - eventQueue.forEach(e => ws.send(JSON.stringify(e))); - eventQueue = []; - // Recovered from a tombstoned outage (e.g. the server restarted on the same - // port) — reload through the keyed bootstrap when possible so the cookie is - // refreshed before the visible URL returns to bare /. - if (recovered) reloadAfterRecovery(); - }; - - ws.onmessage = (msg) => { - let data; - try { data = JSON.parse(msg.data); } catch (e) { return; } - if (data.type === 'reload') window.location.reload(); - }; - - ws.onclose = () => { - ws = null; - if (disconnectedSince === null) disconnectedSince = Date.now(); - if (Date.now() - disconnectedSince >= TOMBSTONE_AFTER_MS) { - setStatus('disconnected'); - showTombstone(); - } else { - setStatus('reconnecting'); - } - reconnectTimer = setTimeout(connect, reconnectDelay); - reconnectDelay = nextReconnectDelay(reconnectDelay, MAX_RECONNECT_MS); - }; - - // Let onclose own reconnection so we don't schedule it twice. - ws.onerror = () => { try { ws.close(); } catch (e) {} }; - } - - function sendEvent(event) { - event.timestamp = Date.now(); - if (ws && ws.readyState === WebSocket.OPEN) { - ws.send(JSON.stringify(event)); - } else { - eventQueue.push(event); - } - } - - // Capture clicks on choice elements - document.addEventListener('click', (e) => { - const target = e.target.closest('[data-choice]'); - if (!target) return; - - sendEvent({ - type: 'click', - text: target.textContent.trim(), - choice: target.dataset.choice, - id: target.id || null - }); - - }); - - // Frame UI: selection tracking - window.selectedChoice = null; - - window.toggleSelect = function(el) { - const container = el.closest('.options') || el.closest('.cards'); - const multi = container && container.dataset.multiselect !== undefined; - if (container && !multi) { - container.querySelectorAll('.option, .card').forEach(o => o.classList.remove('selected')); - } - if (multi) { - el.classList.toggle('selected'); - } else { - el.classList.add('selected'); - } - window.selectedChoice = el.dataset.choice; - }; - - // Expose API for explicit use - window.brainstorm = { - send: sendEvent, - choice: (value, metadata = {}) => sendEvent({ type: 'choice', value, ...metadata }) - }; - - connect(); -})(); diff --git a/plugins/codex/plugins/superpowers-zh/skills/brainstorming/scripts/server.cjs b/plugins/codex/plugins/superpowers-zh/skills/brainstorming/scripts/server.cjs deleted file mode 100644 index 91fb8796..00000000 --- a/plugins/codex/plugins/superpowers-zh/skills/brainstorming/scripts/server.cjs +++ /dev/null @@ -1,723 +0,0 @@ -const crypto = require('crypto'); -const http = require('http'); -const fs = require('fs'); -const path = require('path'); - -// ========== WebSocket Protocol (RFC 6455) ========== - -const OPCODES = { TEXT: 0x01, CLOSE: 0x08, PING: 0x09, PONG: 0x0A }; -const WS_MAGIC = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; -const MAX_FRAME_PAYLOAD_BYTES = 10 * 1024 * 1024; - -function computeAcceptKey(clientKey) { - return crypto.createHash('sha1').update(clientKey + WS_MAGIC).digest('base64'); -} - -function encodeFrame(opcode, payload) { - const fin = 0x80; - const len = payload.length; - let header; - - if (len < 126) { - header = Buffer.alloc(2); - header[0] = fin | opcode; - header[1] = len; - } else if (len < 65536) { - header = Buffer.alloc(4); - header[0] = fin | opcode; - header[1] = 126; - header.writeUInt16BE(len, 2); - } else { - header = Buffer.alloc(10); - header[0] = fin | opcode; - header[1] = 127; - header.writeBigUInt64BE(BigInt(len), 2); - } - - return Buffer.concat([header, payload]); -} - -function decodeFrame(buffer) { - if (buffer.length < 2) return null; - - const secondByte = buffer[1]; - const opcode = buffer[0] & 0x0F; - const masked = (secondByte & 0x80) !== 0; - let payloadLen = secondByte & 0x7F; - let offset = 2; - - if (!masked) throw new Error('Client frames must be masked'); - - if (payloadLen === 126) { - if (buffer.length < 4) return null; - payloadLen = buffer.readUInt16BE(2); - offset = 4; - } else if (payloadLen === 127) { - if (buffer.length < 10) return null; - const extendedLen = buffer.readBigUInt64BE(2); - if (extendedLen > BigInt(MAX_FRAME_PAYLOAD_BYTES)) { - throw new Error('WebSocket frame payload exceeds maximum allowed size'); - } - payloadLen = Number(extendedLen); - offset = 10; - } - - if (payloadLen > MAX_FRAME_PAYLOAD_BYTES) { - throw new Error('WebSocket frame payload exceeds maximum allowed size'); - } - - const maskOffset = offset; - const dataOffset = offset + 4; - const totalLen = dataOffset + payloadLen; - if (buffer.length < totalLen) return null; - - const mask = buffer.slice(maskOffset, dataOffset); - const data = Buffer.alloc(payloadLen); - for (let i = 0; i < payloadLen; i++) { - data[i] = buffer[dataOffset + i] ^ mask[i % 4]; - } - - return { opcode, payload: data, bytesConsumed: totalLen }; -} - -// ========== Configuration ========== - -const PORT_FILE = process.env.BRAINSTORM_PORT_FILE || null; -const randomPort = () => 49152 + Math.floor(Math.random() * 16383); -// Prefer an explicit port, else the port this session last bound (so a restart -// reuses it and an already-open browser tab reconnects), else a random high port. -function preferredPort() { - if (process.env.BRAINSTORM_PORT) return Number(process.env.BRAINSTORM_PORT); - if (PORT_FILE) { - try { - const p = Number(fs.readFileSync(PORT_FILE, 'utf-8').trim()); - if (Number.isInteger(p) && p > 1023 && p < 65536) return p; - } catch (e) { /* no prior port recorded */ } - } - return randomPort(); -} -let PORT = preferredPort(); -const HOST = process.env.BRAINSTORM_HOST || '127.0.0.1'; -const URL_HOST = process.env.BRAINSTORM_URL_HOST || (HOST === '127.0.0.1' ? 'localhost' : HOST); -const SESSION_DIR = process.env.BRAINSTORM_DIR || '/tmp/brainstorm'; -const CONTENT_DIR = path.join(SESSION_DIR, 'content'); -const STATE_DIR = path.join(SESSION_DIR, 'state'); -const SUPERPOWERS_VERSION = readSuperpowersVersion(); -const SUPERPOWERS_BRAND_IMAGE_URL = 'https://primeradiant.com/brand/superpowers-visual-brainstorming-logo.png'; -const TELEMETRY_DISABLE_ENV_VARS = [ - 'SUPERPOWERS_DISABLE_TELEMETRY', - 'DISABLE_TELEMETRY', - 'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC' -]; -const SUPERPOWERS_TELEMETRY_DISABLED = TELEMETRY_DISABLE_ENV_VARS.some(name => isTruthyEnv(process.env[name])); -let ownerPid = process.env.BRAINSTORM_OWNER_PID ? Number(process.env.BRAINSTORM_OWNER_PID) : null; - -// Per-session secret key. The companion is reachable by any local browser tab -// and, when bound to a non-loopback host, by any host that can route to it. -// The key authenticates the real client uniformly across loopback, tunnel, and -// remote binds — and defeats DNS rebinding — where a Host/Origin allowlist -// cannot. It rides the served URL as ?key= and is mirrored into a cookie on -// first load so same-origin subresources and the WebSocket carry it for free. -// Persisted alongside the port (BRAINSTORM_TOKEN_FILE) so a restart keeps the -// same key and an already-open tab's cookie still validates. -const TOKEN_FILE = process.env.BRAINSTORM_TOKEN_FILE || null; -function generateToken() { - return crypto.randomBytes(32).toString('hex'); -} - -function chmodOwnerOnly(file) { - try { fs.chmodSync(file, 0o600); } catch (e) { /* best effort */ } -} - -function initialToken() { - if (process.env.BRAINSTORM_TOKEN) { - return { value: process.env.BRAINSTORM_TOKEN, source: 'env' }; - } - if (TOKEN_FILE) { - try { - const t = fs.readFileSync(TOKEN_FILE, 'utf-8').trim(); - if (/^[0-9a-f]{32,}$/i.test(t)) { - chmodOwnerOnly(TOKEN_FILE); - return { value: t, source: 'file' }; - } - } catch (e) { /* no prior token recorded */ } - } - return { value: generateToken(), source: 'generated' }; -} - -const tokenInfo = initialToken(); -let TOKEN = tokenInfo.value; -let tokenSource = tokenInfo.source; -let COOKIE_NAME = 'brainstorm-key-' + PORT; // refined to the actual bound port in onListen - -const MIME_TYPES = { - '.html': 'text/html', '.css': 'text/css', '.js': 'application/javascript', - '.json': 'application/json', '.png': 'image/png', '.jpg': 'image/jpeg', - '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.svg': 'image/svg+xml' -}; - -// ========== Templates and Constants ========== - -function waitingPage() { - return renderBranding(` - -头脑风暴伴侣 - - -

头脑风暴伴侣

-

正在等待 Agent 推送界面…

`); -} - -const FORBIDDEN_PAGE = ` - -需要会话密钥 - - -

需要会话密钥

-

本页面需要编程 Agent 提供的完整 URL,其中必须包含 -?key=… 部分。请复制完整 URL 后重新打开。

`; - -function bootstrapPage(key) { - const jsonKey = JSON.stringify(String(key)); - return ` - -正在打开头脑风暴伴侣 - - - -`; -} - -const frameTemplate = fs.readFileSync(path.join(__dirname, 'frame-template.html'), 'utf-8'); -const helperScript = fs.readFileSync(path.join(__dirname, 'helper.js'), 'utf-8'); -const helperInjection = ''; - -// ========== Helper Functions ========== - -function readSuperpowersVersion() { - const root = path.join(__dirname, '../../..'); - const manifests = [ - path.join(root, 'package.json'), - path.join(root, '.codex-plugin/plugin.json') - ]; - - for (const manifest of manifests) { - try { - const data = JSON.parse(fs.readFileSync(manifest, 'utf-8')); - if (data.version) return String(data.version); - } catch (e) { - // Packaged Codex plugins omit package.json; try the next manifest. - } - } - - return 'unknown'; -} - -function isTruthyEnv(value) { - if (!value) return false; - const normalized = String(value).trim().toLowerCase(); - if (!normalized) return false; - return !['0', 'false', 'no', 'off'].includes(normalized); -} - -function escapeHtmlText(value) { - return String(value) - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"'); -} - -function brandMarkup() { - const version = escapeHtmlText(SUPERPOWERS_VERSION); - const text = SUPERPOWERS_TELEMETRY_DISABLED - ? 'Prime Radiant Superpowers v' + version - : 'Superpowers v' + version; - const logo = SUPERPOWERS_TELEMETRY_DISABLED - ? '' - : ''; - - return ''; -} - -function renderBranding(html) { - return html.split('').join(brandMarkup()); -} - -function isFullDocument(html) { - const trimmed = html.trimStart().toLowerCase(); - return trimmed.startsWith('', content); -} - -function getNewestScreen() { - const files = fs.readdirSync(CONTENT_DIR) - .filter(f => !f.startsWith('.') && f.endsWith('.html')) - .map(f => { - const fp = path.join(CONTENT_DIR, f); - if (!isRegularFileInsideContentDir(fp)) return null; - return { path: fp, mtime: fs.statSync(fp).mtime.getTime() }; - }) - .filter(Boolean) - .sort((a, b) => b.mtime - a.mtime); - return files.length > 0 ? files[0].path : null; -} - -function urlHostForHttp(host) { - const h = String(host); - if (h.startsWith('[') && h.endsWith(']')) return h; - return h.includes(':') ? '[' + h + ']' : h; -} - -function companionUrl() { - return 'http://' + urlHostForHttp(URL_HOST) + ':' + PORT + '/?key=' + TOKEN; -} - -function browserLauncherForPlatform(url, { - platform = process.platform, - osRelease = require('os').release(), - env = process.env -} = {}) { - const isWSL = platform === 'linux' && /microsoft/i.test(osRelease); - if (platform === 'darwin') return { bin: 'open', args: [url] }; - if (platform === 'win32' || isWSL) { - return { bin: 'rundll32.exe', args: ['url.dll,FileProtocolHandler', url] }; - } - if (env.DISPLAY || env.WAYLAND_DISPLAY) return { bin: 'xdg-open', args: [url] }; - return null; -} - -function isRegularFileInsideContentDir(filePath) { - let stat, realContentDir, realFilePath; - try { - stat = fs.lstatSync(filePath); - if (stat.isSymbolicLink()) return false; - if (!stat.isFile()) return false; - if (stat.nlink !== 1) return false; - realContentDir = fs.realpathSync(CONTENT_DIR); - realFilePath = fs.realpathSync(filePath); - } catch (e) { - return false; - } - return realFilePath.startsWith(realContentDir + path.sep); -} - -// ========== Authentication ========== - -function timingSafeEqualStr(a, b) { - const ab = Buffer.from(String(a)); - const bb = Buffer.from(String(b)); - if (ab.length !== bb.length) return false; - return crypto.timingSafeEqual(ab, bb); -} - -function parseCookies(header) { - const out = {}; - if (!header) return out; - for (const part of header.split(';')) { - const eq = part.indexOf('='); - if (eq < 0) continue; - out[part.slice(0, eq).trim()] = part.slice(eq + 1).trim(); - } - return out; -} - -// A request is authorized if it carries the session key as ?key= or as the -// session cookie. Both are compared in constant time. -function isAuthorized(req) { - const q = req.url.indexOf('?'); - if (q >= 0) { - const params = new URLSearchParams(req.url.slice(q + 1)); - if (params.has('key')) { - const key = params.get('key'); - return Boolean(key && timingSafeEqualStr(key, TOKEN)); - } - } - const cookie = parseCookies(req.headers['cookie'])[COOKIE_NAME]; - if (cookie && timingSafeEqualStr(cookie, TOKEN)) return true; - return false; -} - -function pathnameOf(url) { - const q = url.indexOf('?'); - return q >= 0 ? url.slice(0, q) : url; -} - -function queryKey(url) { - const q = url.indexOf('?'); - if (q < 0) return null; - return new URLSearchParams(url.slice(q + 1)).get('key'); -} - -function securityHeaders(headers = {}) { - return { - 'Referrer-Policy': 'no-referrer', - 'Cache-Control': 'no-store', - 'X-Frame-Options': 'DENY', - 'Content-Security-Policy': "frame-ancestors 'none'", - 'Cross-Origin-Resource-Policy': 'same-origin', - ...headers - }; -} - -function isAllowedWebSocketOrigin(req) { - const origin = req.headers.origin; - if (!origin) return true; - const host = req.headers.host; - if (!host) return false; - return origin === 'http://' + host; -} - -// ========== HTTP Request Handler ========== - -function handleRequest(req, res) { - if (!isAuthorized(req)) { - res.writeHead(403, securityHeaders({ 'Content-Type': 'text/html; charset=utf-8' })); - res.end(FORBIDDEN_PAGE); - return; - } - touchActivity(); // only authorized requests count as activity - - // Mirror the key into a cookie so same-origin subresources (/files/*) can - // authenticate after bootstrap. HttpOnly keeps it away from page scripts; the - // WebSocket Origin check below is what blocks cross-origin localhost injection. - res.setHeader('Set-Cookie', - COOKIE_NAME + '=' + TOKEN + '; HttpOnly; SameSite=Strict; Path=/'); - - const pathname = pathnameOf(req.url); - const keyFromQuery = queryKey(req.url); - if (req.method === 'GET' && pathname === '/' && keyFromQuery && timingSafeEqualStr(keyFromQuery, TOKEN)) { - res.writeHead(200, securityHeaders({ 'Content-Type': 'text/html; charset=utf-8' })); - res.end(bootstrapPage(keyFromQuery)); - } else if (req.method === 'GET' && pathname === '/') { - const screenFile = getNewestScreen(); - let html = screenFile - ? (raw => isFullDocument(raw) ? raw : wrapInFrame(raw))(fs.readFileSync(screenFile, 'utf-8')) - : waitingPage(); - - if (html.includes('')) { - html = html.replace('', helperInjection + '\n'); - } else { - html += helperInjection; - } - - res.writeHead(200, securityHeaders({ 'Content-Type': 'text/html; charset=utf-8' })); - res.end(html); - } else if (req.method === 'GET' && pathname.startsWith('/files/')) { - const fileName = path.basename(pathname.slice(7)); - const filePath = path.join(CONTENT_DIR, fileName); - // Reject empty/dotfile names and anything that isn't a regular file — - // `/files/` would otherwise resolve to CONTENT_DIR and crash readFileSync (EISDIR). - if (!fileName || fileName.startsWith('.') || !isRegularFileInsideContentDir(filePath)) { - res.writeHead(404, securityHeaders()); - res.end('Not found'); - return; - } - const ext = path.extname(filePath).toLowerCase(); - const contentType = MIME_TYPES[ext] || 'application/octet-stream'; - res.writeHead(200, securityHeaders({ 'Content-Type': contentType })); - res.end(fs.readFileSync(filePath)); - } else { - res.writeHead(404, securityHeaders()); - res.end('Not found'); - } -} - -// ========== WebSocket Connection Handling ========== - -const clients = new Set(); - -function handleUpgrade(req, socket) { - if (!isAuthorized(req) || !isAllowedWebSocketOrigin(req)) { socket.destroy(); return; } - - const key = req.headers['sec-websocket-key']; - if (!key) { socket.destroy(); return; } - - const accept = computeAcceptKey(key); - socket.write( - 'HTTP/1.1 101 Switching Protocols\r\n' + - 'Upgrade: websocket\r\n' + - 'Connection: Upgrade\r\n' + - 'Sec-WebSocket-Accept: ' + accept + '\r\n\r\n' - ); - - let buffer = Buffer.alloc(0); - clients.add(socket); - - socket.on('data', (chunk) => { - buffer = Buffer.concat([buffer, chunk]); - while (buffer.length > 0) { - let result; - try { - result = decodeFrame(buffer); - } catch (e) { - socket.end(encodeFrame(OPCODES.CLOSE, Buffer.alloc(0))); - clients.delete(socket); - return; - } - if (!result) break; - buffer = buffer.slice(result.bytesConsumed); - - switch (result.opcode) { - case OPCODES.TEXT: - handleMessage(result.payload.toString()); - break; - case OPCODES.CLOSE: - socket.end(encodeFrame(OPCODES.CLOSE, Buffer.alloc(0))); - clients.delete(socket); - return; - case OPCODES.PING: - socket.write(encodeFrame(OPCODES.PONG, result.payload)); - break; - case OPCODES.PONG: - break; - default: { - const closeBuf = Buffer.alloc(2); - closeBuf.writeUInt16BE(1003); - socket.end(encodeFrame(OPCODES.CLOSE, closeBuf)); - clients.delete(socket); - return; - } - } - } - }); - - socket.on('close', () => clients.delete(socket)); - socket.on('error', () => clients.delete(socket)); -} - -function handleMessage(text) { - let event; - try { - event = JSON.parse(text); - } catch (e) { - console.error('Failed to parse WebSocket message:', e.message); - return; - } - touchActivity(); - console.log(JSON.stringify({ source: 'user-event', ...event })); - if (event && event.choice) { - const eventsFile = path.join(STATE_DIR, 'events'); - fs.appendFileSync(eventsFile, JSON.stringify(event) + '\n'); - } -} - -function broadcast(msg) { - const frame = encodeFrame(OPCODES.TEXT, Buffer.from(JSON.stringify(msg))); - for (const socket of clients) { - try { socket.write(frame); } catch (e) { clients.delete(socket); } - } -} - -// Best-effort: open the user's browser the first time a screen is actually ready -// to show. Skips when disabled, on a non-loopback (remote) bind, or when a -// browser is already connected. Override the launcher with BRAINSTORM_OPEN_CMD. -let browserOpened = false; -function maybeOpenBrowser() { - if (browserOpened) return; - browserOpened = true; - if (!process.env.BRAINSTORM_OPEN) return; // opt-in: only after the user approves the companion - if (HOST !== '127.0.0.1' && HOST !== 'localhost') return; - if (clients.size > 0) return; // the user already opened it - const url = companionUrl(); // must carry the key or the gate 403s it - const cp = require('child_process'); - // Operator-provided launcher: run as given (this env var is trusted operator input). - if (process.env.BRAINSTORM_OPEN_CMD) { - try { cp.exec(process.env.BRAINSTORM_OPEN_CMD + ' ' + JSON.stringify(url), () => {}); } catch (e) { /* best effort */ } - return; - } - // Platform launchers: pass the URL as an argv element via execFile (no shell), - // so a url-host containing shell metacharacters can't inject a command. - const launcher = browserLauncherForPlatform(url); - if (!launcher) return; // headless: nothing to open - try { cp.execFile(launcher.bin, launcher.args, () => {}); } catch (e) { /* best effort */ } -} - -// ========== Activity Tracking ========== - -// Idle timeout: shut down after this long with no activity. Default 4 hours; -// override with BRAINSTORM_IDLE_TIMEOUT_MS (start-server.sh: --idle-timeout-minutes). -const IDLE_TIMEOUT_MS = (() => { - const ms = Number(process.env.BRAINSTORM_IDLE_TIMEOUT_MS); - return Number.isFinite(ms) && ms > 0 ? ms : 4 * 60 * 60 * 1000; -})(); -// How often the watchdog checks for owner-death / idleness. Configurable mainly -// so tests can run fast; production default is 60s. -const LIFECYCLE_CHECK_MS = (() => { - const ms = Number(process.env.BRAINSTORM_LIFECYCLE_CHECK_MS); - return Number.isFinite(ms) && ms > 0 ? ms : 60 * 1000; -})(); -let lastActivity = Date.now(); - -function touchActivity() { - lastActivity = Date.now(); -} - -// ========== File Watching ========== - -const debounceTimers = new Map(); - -// ========== Server Startup ========== - -function startServer() { - if (!fs.existsSync(CONTENT_DIR)) fs.mkdirSync(CONTENT_DIR, { recursive: true }); - if (!fs.existsSync(STATE_DIR)) fs.mkdirSync(STATE_DIR, { recursive: true }); - - // Track known files to distinguish new screens from updates. - // macOS fs.watch reports 'rename' for both new files and overwrites, - // so we can't rely on eventType alone. - const knownFiles = new Set( - fs.readdirSync(CONTENT_DIR).filter(f => !f.startsWith('.') && f.endsWith('.html')) - ); - - const server = http.createServer(handleRequest); - server.on('upgrade', handleUpgrade); - - const watcher = fs.watch(CONTENT_DIR, (eventType, filename) => { - if (!filename || filename.startsWith('.') || !filename.endsWith('.html')) return; - - if (debounceTimers.has(filename)) clearTimeout(debounceTimers.get(filename)); - debounceTimers.set(filename, setTimeout(() => { - debounceTimers.delete(filename); - const filePath = path.join(CONTENT_DIR, filename); - - if (!fs.existsSync(filePath)) return; // file was deleted - touchActivity(); - - if (!knownFiles.has(filename)) { - knownFiles.add(filename); - const eventsFile = path.join(STATE_DIR, 'events'); - if (fs.existsSync(eventsFile)) fs.unlinkSync(eventsFile); - console.log(JSON.stringify({ type: 'screen-added', file: filePath })); - maybeOpenBrowser(); - } else { - console.log(JSON.stringify({ type: 'screen-updated', file: filePath })); - } - - broadcast({ type: 'reload' }); - }, 100)); - }); - watcher.on('error', (err) => console.error('fs.watch error:', err.message)); - - function shutdown(reason) { - console.log(JSON.stringify({ type: 'server-stopped', reason })); - const infoFile = path.join(STATE_DIR, 'server-info'); - if (fs.existsSync(infoFile)) fs.unlinkSync(infoFile); - fs.writeFileSync( - path.join(STATE_DIR, 'server-stopped'), - JSON.stringify({ reason, timestamp: Date.now() }) + '\n' - ); - watcher.close(); - clearInterval(lifecycleCheck); - // Close any upgraded WebSocket sockets so server.close() can complete and - // the process actually exits instead of lingering on an open connection. - for (const socket of clients) { - try { socket.destroy(); } catch (e) { /* already gone */ } - } - server.close(() => process.exit(0)); - } - - function ownerAlive() { - if (!ownerPid) return true; - try { process.kill(ownerPid, 0); return true; } catch (e) { return e.code === 'EPERM'; } - } - - // Periodically exit if the owner process died or we've been idle too long. - const lifecycleCheck = setInterval(() => { - if (!ownerAlive()) shutdown('owner process exited'); - else if (Date.now() - lastActivity > IDLE_TIMEOUT_MS) shutdown('idle timeout'); - }, LIFECYCLE_CHECK_MS); - lifecycleCheck.unref(); - - // Validate owner PID at startup. If it's already dead, the PID resolution - // was wrong (common on WSL, Tailscale SSH, and cross-user scenarios). - // Disable monitoring and rely on the idle timeout instead. - if (ownerPid) { - try { process.kill(ownerPid, 0); } - catch (e) { - if (e.code !== 'EPERM') { - console.log(JSON.stringify({ type: 'owner-pid-invalid', pid: ownerPid, reason: 'dead at startup' })); - ownerPid = null; - } - } - } - - // If the preferred port is already taken (e.g. a previous server is still - // alive), fall back to a random port once instead of failing. - let triedFallback = false; - - function onListen() { - // Cookie name keys on the ACTUAL bound port (may differ from the preferred - // one after an EADDRINUSE fallback) so it can't collide with another server's - // cookie in the shared localhost jar. - COOKIE_NAME = 'brainstorm-key-' + PORT; - // Record the bound port AND token so the next restart of this session reuses - // them — but ONLY when we got our preferred port. On a fallback we bound a - // *different* port because someone else holds the preferred one; persisting - // would overwrite the shared files and strand that other session's open tab. - if (PORT_FILE && !triedFallback) { - try { fs.writeFileSync(PORT_FILE, String(PORT)); } catch (e) { /* best effort */ } - if (TOKEN_FILE) { - try { - fs.writeFileSync(TOKEN_FILE, TOKEN, { mode: 0o600 }); - chmodOwnerOnly(TOKEN_FILE); - } catch (e) { /* best effort */ } - } - } - const info = JSON.stringify({ - type: 'server-started', port: Number(PORT), host: HOST, - url_host: URL_HOST, url: companionUrl(), - screen_dir: CONTENT_DIR, state_dir: STATE_DIR, idle_timeout_ms: IDLE_TIMEOUT_MS - }); - console.log(info); - // server-info embeds the key — keep it owner-only. - fs.writeFileSync(path.join(STATE_DIR, 'server-info'), info + '\n', { mode: 0o600 }); - } - - server.on('error', (err) => { - if (err.code === 'EADDRINUSE' && !triedFallback) { - if (tokenSource === 'env') { - console.error('Server failed to bind: preferred port is in use and BRAINSTORM_TOKEN is set; refusing fallback with explicit token'); - process.exit(1); - } - triedFallback = true; - PORT = randomPort(); - if (tokenSource === 'file') { - TOKEN = generateToken(); - tokenSource = 'generated-fallback'; - } - server.listen(PORT, HOST, onListen); - } else { - console.error('Server failed to bind:', err.message); - process.exit(1); - } - }); - server.listen(PORT, HOST, onListen); -} - -if (require.main === module) { - startServer(); -} - -module.exports = { - computeAcceptKey, - encodeFrame, - decodeFrame, - browserLauncherForPlatform, - OPCODES, - MAX_FRAME_PAYLOAD_BYTES -}; diff --git a/plugins/codex/plugins/superpowers-zh/skills/brainstorming/scripts/start-server.sh b/plugins/codex/plugins/superpowers-zh/skills/brainstorming/scripts/start-server.sh deleted file mode 100755 index 016a8e48..00000000 --- a/plugins/codex/plugins/superpowers-zh/skills/brainstorming/scripts/start-server.sh +++ /dev/null @@ -1,209 +0,0 @@ -#!/usr/bin/env bash -# Start the brainstorm server and output connection info -# Usage: start-server.sh [--project-dir ] [--host ] [--url-host ] [--foreground] [--background] -# -# Starts server on a random high port, outputs JSON with URL. -# Each session gets its own directory to avoid conflicts. -# -# Options: -# --project-dir Store session files under /.superpowers/brainstorm/ -# instead of /tmp. Files persist after server stops. -# --host Host/interface to bind (default: 127.0.0.1). -# Use 0.0.0.0 in remote/containerized environments. -# --url-host Hostname shown in returned URL JSON. -# --idle-timeout-minutes Shut down after n minutes idle (default 240 = 4h). -# --open Auto-open the browser on the first screen (use only -# after the user approves the visual companion). -# --foreground Run server in the current terminal (no backgrounding). -# --background Force background mode (overrides Codex auto-foreground). - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" - -# Parse arguments -PROJECT_DIR="" -FOREGROUND="false" -FORCE_BACKGROUND="false" -BIND_HOST="127.0.0.1" -URL_HOST="" -IDLE_TIMEOUT_MINUTES="" -while [[ $# -gt 0 ]]; do - case "$1" in - --project-dir) - PROJECT_DIR="$2" - shift 2 - ;; - --host) - BIND_HOST="$2" - shift 2 - ;; - --url-host) - URL_HOST="$2" - shift 2 - ;; - --idle-timeout-minutes) - IDLE_TIMEOUT_MINUTES="$2" - shift 2 - ;; - --open) - export BRAINSTORM_OPEN=1 - shift - ;; - --foreground|--no-daemon) - FOREGROUND="true" - shift - ;; - --background|--daemon) - FORCE_BACKGROUND="true" - shift - ;; - *) - echo "{\"error\": \"Unknown argument: $1\"}" - exit 1 - ;; - esac -done - -if [[ -z "$URL_HOST" ]]; then - if [[ "$BIND_HOST" == "127.0.0.1" || "$BIND_HOST" == "localhost" ]]; then - URL_HOST="localhost" - else - URL_HOST="$BIND_HOST" - fi -fi - -if [[ -n "$IDLE_TIMEOUT_MINUTES" ]]; then - if ! [[ "$IDLE_TIMEOUT_MINUTES" =~ ^[0-9]+$ ]] || [[ "$IDLE_TIMEOUT_MINUTES" -lt 1 ]]; then - echo "{\"error\": \"--idle-timeout-minutes must be a positive integer\"}" - exit 1 - fi - export BRAINSTORM_IDLE_TIMEOUT_MS=$(( IDLE_TIMEOUT_MINUTES * 60 * 1000 )) -fi - -is_windows_like_shell() { - case "${OSTYPE:-}" in - msys*|cygwin*|mingw*) return 0 ;; - esac - if [[ -n "${MSYSTEM:-}" ]]; then - return 0 - fi - local uname_s - uname_s="$(uname -s 2>/dev/null || true)" - case "$uname_s" in - MSYS*|MINGW*|CYGWIN*) return 0 ;; - esac - return 1 -} - -# Some environments reap detached/background processes. Auto-foreground when detected. -if [[ -n "${CODEX_CI:-}" && "$FOREGROUND" != "true" && "$FORCE_BACKGROUND" != "true" ]]; then - FOREGROUND="true" -fi - -# Windows/Git Bash reaps nohup background processes. Auto-foreground when detected. -if [[ "$FOREGROUND" != "true" && "$FORCE_BACKGROUND" != "true" ]]; then - if is_windows_like_shell; then - FOREGROUND="true" - fi -fi - -# Session files (server.log, server-info, .last-token) embed the session key — -# keep everything this script and the server create owner-only. -umask 077 - -# Generate unique session directory -SESSION_ID="$$-$(date +%s)" - -if [[ -n "$PROJECT_DIR" ]]; then - SESSION_DIR="${PROJECT_DIR}/.superpowers/brainstorm/${SESSION_ID}" - # Persist the bound port and key per project so a restart reuses them and an - # already-open browser tab reconnects to the same URL with a valid cookie. - export BRAINSTORM_PORT_FILE="${PROJECT_DIR}/.superpowers/brainstorm/.last-port" - export BRAINSTORM_TOKEN_FILE="${PROJECT_DIR}/.superpowers/brainstorm/.last-token" -else - SESSION_DIR="/tmp/brainstorm-${SESSION_ID}" -fi - -STATE_DIR="${SESSION_DIR}/state" -PID_FILE="${STATE_DIR}/server.pid" -LOG_FILE="${STATE_DIR}/server.log" -SERVER_ID_FILE="${STATE_DIR}/server-instance-id" - -# Create fresh session directory with content and state peers -mkdir -p "${SESSION_DIR}/content" "$STATE_DIR" - -SERVER_ID="" -if [[ -r /dev/urandom ]]; then - SERVER_ID="$(od -An -N24 -tx1 /dev/urandom 2>/dev/null | tr -d ' \n' || true)" -fi -if ! [[ "$SERVER_ID" =~ ^[A-Za-z0-9_-]{32,64}$ ]]; then - SERVER_ID="$(printf '%08x%08x%08x%08x' "$$" "$(date +%s)" "${RANDOM:-0}" "${RANDOM:-0}")" -fi -printf '%s\n' "$SERVER_ID" > "$SERVER_ID_FILE" -chmod 600 "$SERVER_ID_FILE" 2>/dev/null || true - -# Kill any existing server -if [[ -f "$PID_FILE" ]]; then - old_pid=$(cat "$PID_FILE") - kill "$old_pid" 2>/dev/null - rm -f "$PID_FILE" -fi - -cd "$SCRIPT_DIR" || exit 1 - -# Resolve the harness PID (grandparent of this script). -# $PPID is the ephemeral shell the harness spawned to run us — it dies -# when this script exits. The harness itself is $PPID's parent. -OWNER_PID="$(ps -o ppid= -p "$PPID" 2>/dev/null | tr -d ' ')" -if [[ -z "$OWNER_PID" || "$OWNER_PID" == "1" ]]; then - OWNER_PID="$PPID" -fi - -# Windows/MSYS2: Node.js cannot see POSIX PIDs from the MSYS2 namespace. -# Passing a PID node cannot verify causes server to log owner-pid-invalid -# and self-terminate at the 60-second lifecycle check. Clear it so the -# watchdog is disabled and the idle timeout becomes the only shutdown trigger. -if is_windows_like_shell; then - OWNER_PID="" -fi - -# Foreground mode for environments that reap detached/background processes. -if [[ "$FOREGROUND" == "true" ]]; then - env BRAINSTORM_DIR="$SESSION_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs "--brainstorm-server-id=$SERVER_ID" & - SERVER_PID=$! - echo "$SERVER_PID" > "$PID_FILE" - wait "$SERVER_PID" - exit $? -fi - -# Start server, capturing output to log file -# Use nohup to survive shell exit; disown to remove from job table -nohup env BRAINSTORM_DIR="$SESSION_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs "--brainstorm-server-id=$SERVER_ID" > "$LOG_FILE" 2>&1 & -SERVER_PID=$! -disown "$SERVER_PID" 2>/dev/null -echo "$SERVER_PID" > "$PID_FILE" - -# Wait for server-started message (check log file) -for _ in {1..50}; do - if grep -q "server-started" "$LOG_FILE" 2>/dev/null; then - # Verify server is still alive after a short window (catches process reapers) - alive="true" - for _ in {1..20}; do - if ! kill -0 "$SERVER_PID" 2>/dev/null; then - alive="false" - break - fi - sleep 0.1 - done - if [[ "$alive" != "true" ]]; then - echo "{\"error\": \"Server started but was killed. Retry in a persistent terminal with: $SCRIPT_DIR/start-server.sh${PROJECT_DIR:+ --project-dir $PROJECT_DIR} --host $BIND_HOST --url-host $URL_HOST --foreground\"}" - exit 1 - fi - grep "server-started" "$LOG_FILE" | head -1 - exit 0 - fi - sleep 0.1 -done - -# Timeout - server didn't start -echo '{"error": "Server failed to start within 5 seconds"}' -exit 1 diff --git a/plugins/codex/plugins/superpowers-zh/skills/brainstorming/scripts/stop-server.sh b/plugins/codex/plugins/superpowers-zh/skills/brainstorming/scripts/stop-server.sh deleted file mode 100755 index 7cacfe94..00000000 --- a/plugins/codex/plugins/superpowers-zh/skills/brainstorming/scripts/stop-server.sh +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env bash -# Stop the brainstorm server and clean up -# Usage: stop-server.sh -# -# Kills the server process. Only deletes session directory if it's -# under /tmp (ephemeral). Persistent directories (.superpowers/) are -# kept so mockups can be reviewed later. - -SESSION_DIR="$1" - -if [[ -z "$SESSION_DIR" ]]; then - echo '{"error": "Usage: stop-server.sh "}' - exit 1 -fi - -STATE_DIR="${SESSION_DIR}/state" -PID_FILE="${STATE_DIR}/server.pid" -SERVER_ID_FILE="${STATE_DIR}/server-instance-id" - -mark_stopped() { - local reason="$1" - rm -f "${STATE_DIR}/server-info" - printf '{"reason":"%s","timestamp":%s}\n' "$reason" "$(date +%s)" > "${STATE_DIR}/server-stopped" -} - -read_expected_server_id() { - [[ -f "$SERVER_ID_FILE" ]] || return 1 - local id - id="$(tr -d '\r\n' < "$SERVER_ID_FILE" 2>/dev/null || true)" - [[ "$id" =~ ^[A-Za-z0-9_-]{32,64}$ ]] || return 1 - printf '%s\n' "$id" -} - -command_line_for_pid() { - local pid="$1" - if [[ -r "/proc/$pid/cmdline" ]]; then - tr '\0' '\n' < "/proc/$pid/cmdline" 2>/dev/null || true - return 0 - fi - ps -ww -p "$pid" -o command= 2>/dev/null || ps -f -p "$pid" 2>/dev/null | sed '1d' || true -} - -command_has_server_id() { - local pid="$1" - local expected="$2" - local expected_arg="--brainstorm-server-id=$expected" - if [[ -r "/proc/$pid/cmdline" ]]; then - local arg - while IFS= read -r -d '' arg || [[ -n "$arg" ]]; do - [[ "$arg" == "$expected_arg" ]] && return 0 - done < "/proc/$pid/cmdline" - return 1 - fi - local command_line - command_line="$(command_line_for_pid "$pid")" - [[ -n "$command_line" ]] || return 1 - case " $command_line " in - *" $expected_arg "*) return 0 ;; - *) return 1 ;; - esac -} - -# Confirm a PID has this session's per-start instance id, not just a familiar -# process name. Ambiguous or legacy metadata fails closed as stale_pid. -is_brainstorm_server() { - kill -0 "$1" 2>/dev/null || return 1 - local expected_id - expected_id="$(read_expected_server_id)" || return 1 - command_has_server_id "$1" "$expected_id" || return 1 - return 0 -} - -if [[ -f "$PID_FILE" ]]; then - pid=$(cat "$PID_FILE") - - # Refuse to signal a PID we can't prove is our server. A stale pid file may - # point at an unrelated process after a reboot/PID wraparound. - if ! is_brainstorm_server "$pid"; then - rm -f "$PID_FILE" "$SERVER_ID_FILE" - mark_stopped "stale_pid" - echo '{"status": "stale_pid"}' - exit 0 - fi - - # Try to stop gracefully, fallback to force if still alive - kill "$pid" 2>/dev/null || true - - # Wait for graceful shutdown (up to ~2s) - for _ in {1..20}; do - if ! kill -0 "$pid" 2>/dev/null; then - break - fi - sleep 0.1 - done - - # If still running, escalate to SIGKILL - if kill -0 "$pid" 2>/dev/null; then - kill -9 "$pid" 2>/dev/null || true - - # Give SIGKILL a moment to take effect - sleep 0.1 - fi - - if kill -0 "$pid" 2>/dev/null; then - echo '{"status": "failed", "error": "process still running"}' - exit 1 - fi - - rm -f "$PID_FILE" "$SERVER_ID_FILE" "${STATE_DIR}/server.log" - mark_stopped "stop-server.sh" - - # Only delete ephemeral /tmp directories - if [[ "$SESSION_DIR" == /tmp/* ]]; then - rm -rf "$SESSION_DIR" - fi - - echo '{"status": "stopped"}' -else - echo '{"status": "not_running"}' -fi diff --git a/plugins/codex/plugins/superpowers-zh/skills/brainstorming/spec-document-reviewer-prompt.md b/plugins/codex/plugins/superpowers-zh/skills/brainstorming/spec-document-reviewer-prompt.md deleted file mode 100644 index 35938dc7..00000000 --- a/plugins/codex/plugins/superpowers-zh/skills/brainstorming/spec-document-reviewer-prompt.md +++ /dev/null @@ -1,48 +0,0 @@ -# 规范文档审查员提示词模板 - -派遣规范文档审查员子 Agent 时,请使用此模板。 - -**目的:** 验证规范是否完整、一致,并已准备好进入实施规划阶段。 - -**派遣时机:** 规范文档已写入 docs/superpowers/specs/ 后 - -``` -子 Agent (general-purpose): - description: "审查规范文档" - prompt: | - 你是一名规范文档审查员。请验证此规范是否完整并已准备好进入规划阶段。 - - **待审查的规范:** [SPEC_FILE_PATH] - - ## 检查内容 - - | 类别 | 检查要点 | - |----------|------------------| - | 完整性 | TODOs、占位符、"TBD"、不完整的章节 | - | 一致性 | 内部矛盾、相互冲突的要求 | - | 清晰度 | 要求是否含糊到足以导致他人构建出错误的内容 | - | 范围 | 是否足够聚焦,可由单个计划涵盖——而不是覆盖多个相互独立的子系统 | - | YAGNI | 未请求的功能、过度工程化 | - - ## 校准 - - **只标记会在实施规划期间造成实际问题的问题。** - 缺少章节、存在矛盾,或某项要求含糊到可能被解读为两种不同的含义—— - 这些才是问题。轻微的措辞改进、风格偏好以及“某些章节不如其他章节详细”均不属于问题。 - - 除非存在会导致计划有缺陷的严重缺漏,否则应予以批准。 - - ## 输出格式 - - ## 规范审查 - - **状态:** 通过 | 发现问题 - - **问题(如有):** - - [第 X 节]:[具体问题] — [它为何会影响规划] - - **建议(仅供参考,不阻碍批准):** - - [改进建议] -``` - -**审查员返回:** 状态、问题(如有)、建议 diff --git a/plugins/codex/plugins/superpowers-zh/skills/brainstorming/visual-companion.md b/plugins/codex/plugins/superpowers-zh/skills/brainstorming/visual-companion.md deleted file mode 100644 index d415b983..00000000 --- a/plugins/codex/plugins/superpowers-zh/skills/brainstorming/visual-companion.md +++ /dev/null @@ -1,288 +0,0 @@ -# 可视化伴侣指南 - -基于浏览器的可视化头脑风暴伴侣,用于展示模型稿、图示和选项。 - -## 何时使用 - -针对每个问题而非每个会话进行决定。判断标准是:**用户通过看它而不是阅读它,是否能理解得更好?** - -当内容本身是可视化内容时,**使用浏览器**: - -- **UI 模型稿**——线框图、布局、导航结构、组件设计 -- **架构图**——系统组件、数据流、关系图 -- **并排视觉比较**——比较两种布局、两套配色方案、两个设计方向 -- **设计润色**——当问题涉及观感、间距、视觉层级时 -- **空间关系**——以图示形式呈现的状态机、流程图、实体关系 - -当内容是文本或表格时,**使用终端**: - -- **需求和范围问题**——“X 是什么意思?”、“哪些功能在范围内?” -- **概念性 A/B/C 选择**——在用文字描述的方案之间进行选择 -- **权衡清单**——优缺点、比较表 -- **技术决策**——API 设计、数据建模、架构方案选择 -- **澄清问题**——任何答案是文字而非视觉偏好的问题 - -一个*关于* UI 主题的问题并不自动等同于视觉问题。“你想要哪种向导?”是概念性问题——使用终端。“这些向导布局中,哪一种感觉合适?”是视觉问题——使用浏览器。 - -## 工作原理 - -服务器会监视一个目录中的 HTML 文件,并将最新的文件提供给浏览器。你将 HTML 内容写入 `screen_dir`,用户会在其浏览器中看到内容,并可通过点击选择选项。选择结果会记录到 `state_dir/events`,供你在下一轮读取。 - -**内容片段与完整文档:**如果你的 HTML 文件以 `/.superpowers/brainstorm/` 中查找会话目录。 - -**注意:**将项目根目录作为 `--project-dir` 传入,以便模型稿持久保存在 `.superpowers/brainstorm/` 中,并在服务器重启后继续保留。如果不这样做,文件会写入 `/tmp` 并被清理。如果 `.superpowers/` 尚未加入 `.gitignore`,请提醒用户添加它。 - -**按平台启动服务器:** - -**Claude Code:** -```bash -# 默认模式即可——脚本会自行将服务器置于后台运行。 -scripts/start-server.sh --project-dir /path/to/project --open -``` - -在 Windows 上,脚本会自动检测并切换到前台模式(这会阻塞工具调用)。在 Bash 工具调用中使用 `run_in_background: true`,使服务器能够跨对话轮次持续运行,然后在下一轮读取 `$STATE_DIR/server-info` 以获取 URL 和端口。 - -**Codex:** -```bash -# Codex 会清理后台进程。脚本会自动检测 CODEX_CI 并 -# 切换到前台模式。正常运行即可——无需额外标志。 -scripts/start-server.sh --project-dir /path/to/project --open -``` - -**Gemini CLI:** -```bash -# 使用 --foreground,并在 shell 工具调用中设置 is_background: true, -# 使进程能够跨轮次持续运行 -scripts/start-server.sh --project-dir /path/to/project --open --foreground -``` - -**Copilot CLI:** -```bash -# 使用 Copilot CLI 的非阻塞/后台 shell 机制启动,使服务器跨轮次持续运行。 -# 保留 --foreground,让运行平台而不是脚本负责后台化。启动脚本是 .sh, -# 因此通过 bash 调用(Windows 上从 PowerShell 工具调用 Git Bash 的 bash.exe)。 -bash scripts/start-server.sh --project-dir /path/to/project --open --foreground -``` - -**其他环境:**服务器必须在后台跨对话轮次持续运行。如果你的环境会清理已分离的进程,请使用 `--foreground`,并通过你所在平台的后台执行机制启动该命令。 - -如果你的浏览器无法访问该 URL(这在远程/容器化环境中很常见),请绑定非环回主机: - -```bash -scripts/start-server.sh \ - --project-dir /path/to/project \ - --host 0.0.0.0 \ - --url-host localhost -``` - -使用 `--url-host` 控制返回的 URL JSON 中输出的主机名。 - -## 循环 -1. **检查服务器是否仍在运行**,然后**将 HTML 写入** `screen_dir` 中的新文件: - - **必需:在提及 URL 或推送界面之前,确认服务器仍在运行。** 检查 `$STATE_DIR/server-info` 是否存在,并且 `$STATE_DIR/server-stopped` 不存在。如果服务器已关闭,请使用**相同的 `--project-dir`** 通过 `start-server.sh` 重新启动它——它会复用相同的端口,因此用户已打开的标签页会自行重新连接(服务器停机期间会显示“已暂停”遮罩层),你无需发送新的 URL。服务器空闲 4 小时后会自动退出(可通过 `--idle-timeout-minutes` 配置)。 - - 使用语义化文件名:`platform.html`、`visual-style.html`、`layout.html` - - **绝不要重复使用文件名**——每个界面都使用一个全新的文件 - - 使用你的文件创建工具——**绝不要使用 cat/heredoc**(会向终端倾倒大量杂乱信息) - - 服务器会自动提供最新的文件 - -2. **告诉用户会看到什么,然后结束你的回合:** - - 每一步都提醒他们 URL(而不只是第一步) - - 简要概括界面上显示的内容(例如,“正在展示主页的 3 种布局选项”) - - 请他们在终端中回复:“看一下,然后告诉我你的想法。如果愿意,请点击选择一个选项。” - -3. **在你的下一个回合中**——用户在终端中回复后: - - 如果 `$STATE_DIR/events` 存在,则读取它——其中以 JSON 行的形式包含用户的浏览器交互 - - 将其与用户的终端文本合并,以了解完整情况 - - 终端消息是主要反馈;`state_dir/events` 提供结构化交互数据 - -4. **迭代或推进**——如果反馈会改变当前界面,则写入一个新文件(例如 `layout-v2.html`)。只有当前步骤得到验证后,才能进入下一个问题。 - -5. **返回终端时卸载内容**——当下一步不需要浏览器时(例如,提出澄清问题、讨论权衡取舍),推送一个等待界面以清除过时内容: - - ```html - -
-

继续在终端中...

-
- ``` - - 这样可避免在对话已经继续推进后,用户仍盯着一个已经解决的选择。当下一个可视化问题出现时,像往常一样推送一个新的内容文件。 - -6. 重复上述步骤,直到完成。 - -## 编写内容片段 - -只编写放入页面内部的内容。服务器会自动使用框架模板将其包装起来(页眉、主题 CSS、连接状态以及所有交互基础设施)。 - -**最小示例:** - -```html -

哪种布局更合适?

-

请考虑可读性和视觉层级

- -
-
-
A
-
-

单栏

-

简洁、专注的阅读体验

-
-
-
-
B
-
-

双栏

-

侧边栏导航搭配主内容区

-
-
-
-``` - -就这些。不需要 ``、CSS,也不需要 ` - -`; -} - -const frameTemplate = fs.readFileSync(path.join(__dirname, 'frame-template.html'), 'utf-8'); -const helperScript = fs.readFileSync(path.join(__dirname, 'helper.js'), 'utf-8'); -const helperInjection = ''; - -// ========== Helper Functions ========== - -function readSuperpowersVersion() { - const root = path.join(__dirname, '../../..'); - const manifests = [ - path.join(root, 'package.json'), - path.join(root, '.codex-plugin/plugin.json') - ]; - - for (const manifest of manifests) { - try { - const data = JSON.parse(fs.readFileSync(manifest, 'utf-8')); - if (data.version) return String(data.version); - } catch (e) { - // Packaged Codex plugins omit package.json; try the next manifest. - } - } - - return 'unknown'; -} - -function isTruthyEnv(value) { - if (!value) return false; - const normalized = String(value).trim().toLowerCase(); - if (!normalized) return false; - return !['0', 'false', 'no', 'off'].includes(normalized); -} - -function escapeHtmlText(value) { - return String(value) - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"'); -} - -function brandMarkup() { - const version = escapeHtmlText(SUPERPOWERS_VERSION); - const text = SUPERPOWERS_TELEMETRY_DISABLED - ? 'Prime Radiant Superpowers v' + version - : 'Superpowers v' + version; - const logo = SUPERPOWERS_TELEMETRY_DISABLED - ? '' - : ''; - - return ''; -} - -function renderBranding(html) { - return html.split('').join(brandMarkup()); -} - -function isFullDocument(html) { - const trimmed = html.trimStart().toLowerCase(); - return trimmed.startsWith('', content); -} - -function getNewestScreen() { - const files = fs.readdirSync(CONTENT_DIR) - .filter(f => !f.startsWith('.') && f.endsWith('.html')) - .map(f => { - const fp = path.join(CONTENT_DIR, f); - if (!isRegularFileInsideContentDir(fp)) return null; - return { path: fp, mtime: fs.statSync(fp).mtime.getTime() }; - }) - .filter(Boolean) - .sort((a, b) => b.mtime - a.mtime); - return files.length > 0 ? files[0].path : null; -} - -function urlHostForHttp(host) { - const h = String(host); - if (h.startsWith('[') && h.endsWith(']')) return h; - return h.includes(':') ? '[' + h + ']' : h; -} - -function companionUrl() { - return 'http://' + urlHostForHttp(URL_HOST) + ':' + PORT + '/?key=' + TOKEN; -} - -function browserLauncherForPlatform(url, { - platform = process.platform, - osRelease = require('os').release(), - env = process.env -} = {}) { - const isWSL = platform === 'linux' && /microsoft/i.test(osRelease); - if (platform === 'darwin') return { bin: 'open', args: [url] }; - if (platform === 'win32' || isWSL) { - return { bin: 'rundll32.exe', args: ['url.dll,FileProtocolHandler', url] }; - } - if (env.DISPLAY || env.WAYLAND_DISPLAY) return { bin: 'xdg-open', args: [url] }; - return null; -} - -function isRegularFileInsideContentDir(filePath) { - let stat, realContentDir, realFilePath; - try { - stat = fs.lstatSync(filePath); - if (stat.isSymbolicLink()) return false; - if (!stat.isFile()) return false; - if (stat.nlink !== 1) return false; - realContentDir = fs.realpathSync(CONTENT_DIR); - realFilePath = fs.realpathSync(filePath); - } catch (e) { - return false; - } - return realFilePath.startsWith(realContentDir + path.sep); -} - -// ========== Authentication ========== - -function timingSafeEqualStr(a, b) { - const ab = Buffer.from(String(a)); - const bb = Buffer.from(String(b)); - if (ab.length !== bb.length) return false; - return crypto.timingSafeEqual(ab, bb); -} - -function parseCookies(header) { - const out = {}; - if (!header) return out; - for (const part of header.split(';')) { - const eq = part.indexOf('='); - if (eq < 0) continue; - out[part.slice(0, eq).trim()] = part.slice(eq + 1).trim(); - } - return out; -} - -// A request is authorized if it carries the session key as ?key= or as the -// session cookie. Both are compared in constant time. -function isAuthorized(req) { - const q = req.url.indexOf('?'); - if (q >= 0) { - const params = new URLSearchParams(req.url.slice(q + 1)); - if (params.has('key')) { - const key = params.get('key'); - return Boolean(key && timingSafeEqualStr(key, TOKEN)); - } - } - const cookie = parseCookies(req.headers['cookie'])[COOKIE_NAME]; - if (cookie && timingSafeEqualStr(cookie, TOKEN)) return true; - return false; -} - -function pathnameOf(url) { - const q = url.indexOf('?'); - return q >= 0 ? url.slice(0, q) : url; -} - -function queryKey(url) { - const q = url.indexOf('?'); - if (q < 0) return null; - return new URLSearchParams(url.slice(q + 1)).get('key'); -} - -function securityHeaders(headers = {}) { - return { - 'Referrer-Policy': 'no-referrer', - 'Cache-Control': 'no-store', - 'X-Frame-Options': 'DENY', - 'Content-Security-Policy': "frame-ancestors 'none'", - 'Cross-Origin-Resource-Policy': 'same-origin', - ...headers - }; -} - -function isAllowedWebSocketOrigin(req) { - const origin = req.headers.origin; - if (!origin) return true; - const host = req.headers.host; - if (!host) return false; - return origin === 'http://' + host; -} - -// ========== HTTP Request Handler ========== - -function handleRequest(req, res) { - if (!isAuthorized(req)) { - res.writeHead(403, securityHeaders({ 'Content-Type': 'text/html; charset=utf-8' })); - res.end(FORBIDDEN_PAGE); - return; - } - touchActivity(); // only authorized requests count as activity - - // Mirror the key into a cookie so same-origin subresources (/files/*) can - // authenticate after bootstrap. HttpOnly keeps it away from page scripts; the - // WebSocket Origin check below is what blocks cross-origin localhost injection. - res.setHeader('Set-Cookie', - COOKIE_NAME + '=' + TOKEN + '; HttpOnly; SameSite=Strict; Path=/'); - - const pathname = pathnameOf(req.url); - const keyFromQuery = queryKey(req.url); - if (req.method === 'GET' && pathname === '/' && keyFromQuery && timingSafeEqualStr(keyFromQuery, TOKEN)) { - res.writeHead(200, securityHeaders({ 'Content-Type': 'text/html; charset=utf-8' })); - res.end(bootstrapPage(keyFromQuery)); - } else if (req.method === 'GET' && pathname === '/') { - const screenFile = getNewestScreen(); - let html = screenFile - ? (raw => isFullDocument(raw) ? raw : wrapInFrame(raw))(fs.readFileSync(screenFile, 'utf-8')) - : waitingPage(); - - if (html.includes('')) { - html = html.replace('', helperInjection + '\n'); - } else { - html += helperInjection; - } - - res.writeHead(200, securityHeaders({ 'Content-Type': 'text/html; charset=utf-8' })); - res.end(html); - } else if (req.method === 'GET' && pathname.startsWith('/files/')) { - const fileName = path.basename(pathname.slice(7)); - const filePath = path.join(CONTENT_DIR, fileName); - // Reject empty/dotfile names and anything that isn't a regular file — - // `/files/` would otherwise resolve to CONTENT_DIR and crash readFileSync (EISDIR). - if (!fileName || fileName.startsWith('.') || !isRegularFileInsideContentDir(filePath)) { - res.writeHead(404, securityHeaders()); - res.end('Not found'); - return; - } - const ext = path.extname(filePath).toLowerCase(); - const contentType = MIME_TYPES[ext] || 'application/octet-stream'; - res.writeHead(200, securityHeaders({ 'Content-Type': contentType })); - res.end(fs.readFileSync(filePath)); - } else { - res.writeHead(404, securityHeaders()); - res.end('Not found'); - } -} - -// ========== WebSocket Connection Handling ========== - -const clients = new Set(); - -function handleUpgrade(req, socket) { - if (!isAuthorized(req) || !isAllowedWebSocketOrigin(req)) { socket.destroy(); return; } - - const key = req.headers['sec-websocket-key']; - if (!key) { socket.destroy(); return; } - - const accept = computeAcceptKey(key); - socket.write( - 'HTTP/1.1 101 Switching Protocols\r\n' + - 'Upgrade: websocket\r\n' + - 'Connection: Upgrade\r\n' + - 'Sec-WebSocket-Accept: ' + accept + '\r\n\r\n' - ); - - let buffer = Buffer.alloc(0); - clients.add(socket); - - socket.on('data', (chunk) => { - buffer = Buffer.concat([buffer, chunk]); - while (buffer.length > 0) { - let result; - try { - result = decodeFrame(buffer); - } catch (e) { - socket.end(encodeFrame(OPCODES.CLOSE, Buffer.alloc(0))); - clients.delete(socket); - return; - } - if (!result) break; - buffer = buffer.slice(result.bytesConsumed); - - switch (result.opcode) { - case OPCODES.TEXT: - handleMessage(result.payload.toString()); - break; - case OPCODES.CLOSE: - socket.end(encodeFrame(OPCODES.CLOSE, Buffer.alloc(0))); - clients.delete(socket); - return; - case OPCODES.PING: - socket.write(encodeFrame(OPCODES.PONG, result.payload)); - break; - case OPCODES.PONG: - break; - default: { - const closeBuf = Buffer.alloc(2); - closeBuf.writeUInt16BE(1003); - socket.end(encodeFrame(OPCODES.CLOSE, closeBuf)); - clients.delete(socket); - return; - } - } - } - }); - - socket.on('close', () => clients.delete(socket)); - socket.on('error', () => clients.delete(socket)); -} - -function handleMessage(text) { - let event; - try { - event = JSON.parse(text); - } catch (e) { - console.error('Failed to parse WebSocket message:', e.message); - return; - } - touchActivity(); - console.log(JSON.stringify({ source: 'user-event', ...event })); - if (event && event.choice) { - const eventsFile = path.join(STATE_DIR, 'events'); - fs.appendFileSync(eventsFile, JSON.stringify(event) + '\n'); - } -} - -function broadcast(msg) { - const frame = encodeFrame(OPCODES.TEXT, Buffer.from(JSON.stringify(msg))); - for (const socket of clients) { - try { socket.write(frame); } catch (e) { clients.delete(socket); } - } -} - -// Best-effort: open the user's browser the first time a screen is actually ready -// to show. Skips when disabled, on a non-loopback (remote) bind, or when a -// browser is already connected. Override the launcher with BRAINSTORM_OPEN_CMD. -let browserOpened = false; -function maybeOpenBrowser() { - if (browserOpened) return; - browserOpened = true; - if (!process.env.BRAINSTORM_OPEN) return; // opt-in: only after the user approves the companion - if (HOST !== '127.0.0.1' && HOST !== 'localhost') return; - if (clients.size > 0) return; // the user already opened it - const url = companionUrl(); // must carry the key or the gate 403s it - const cp = require('child_process'); - // Operator-provided launcher: run as given (this env var is trusted operator input). - if (process.env.BRAINSTORM_OPEN_CMD) { - try { cp.exec(process.env.BRAINSTORM_OPEN_CMD + ' ' + JSON.stringify(url), () => {}); } catch (e) { /* best effort */ } - return; - } - // Platform launchers: pass the URL as an argv element via execFile (no shell), - // so a url-host containing shell metacharacters can't inject a command. - const launcher = browserLauncherForPlatform(url); - if (!launcher) return; // headless: nothing to open - try { cp.execFile(launcher.bin, launcher.args, () => {}); } catch (e) { /* best effort */ } -} - -// ========== Activity Tracking ========== - -// Idle timeout: shut down after this long with no activity. Default 4 hours; -// override with BRAINSTORM_IDLE_TIMEOUT_MS (start-server.sh: --idle-timeout-minutes). -const IDLE_TIMEOUT_MS = (() => { - const ms = Number(process.env.BRAINSTORM_IDLE_TIMEOUT_MS); - return Number.isFinite(ms) && ms > 0 ? ms : 4 * 60 * 60 * 1000; -})(); -// How often the watchdog checks for owner-death / idleness. Configurable mainly -// so tests can run fast; production default is 60s. -const LIFECYCLE_CHECK_MS = (() => { - const ms = Number(process.env.BRAINSTORM_LIFECYCLE_CHECK_MS); - return Number.isFinite(ms) && ms > 0 ? ms : 60 * 1000; -})(); -let lastActivity = Date.now(); - -function touchActivity() { - lastActivity = Date.now(); -} - -// ========== File Watching ========== - -const debounceTimers = new Map(); - -// ========== Server Startup ========== - -function startServer() { - if (!fs.existsSync(CONTENT_DIR)) fs.mkdirSync(CONTENT_DIR, { recursive: true }); - if (!fs.existsSync(STATE_DIR)) fs.mkdirSync(STATE_DIR, { recursive: true }); - - // Track known files to distinguish new screens from updates. - // macOS fs.watch reports 'rename' for both new files and overwrites, - // so we can't rely on eventType alone. - const knownFiles = new Set( - fs.readdirSync(CONTENT_DIR).filter(f => !f.startsWith('.') && f.endsWith('.html')) - ); - - const server = http.createServer(handleRequest); - server.on('upgrade', handleUpgrade); - - const watcher = fs.watch(CONTENT_DIR, (eventType, filename) => { - if (!filename || filename.startsWith('.') || !filename.endsWith('.html')) return; - - if (debounceTimers.has(filename)) clearTimeout(debounceTimers.get(filename)); - debounceTimers.set(filename, setTimeout(() => { - debounceTimers.delete(filename); - const filePath = path.join(CONTENT_DIR, filename); - - if (!fs.existsSync(filePath)) return; // file was deleted - touchActivity(); - - if (!knownFiles.has(filename)) { - knownFiles.add(filename); - const eventsFile = path.join(STATE_DIR, 'events'); - if (fs.existsSync(eventsFile)) fs.unlinkSync(eventsFile); - console.log(JSON.stringify({ type: 'screen-added', file: filePath })); - maybeOpenBrowser(); - } else { - console.log(JSON.stringify({ type: 'screen-updated', file: filePath })); - } - - broadcast({ type: 'reload' }); - }, 100)); - }); - watcher.on('error', (err) => console.error('fs.watch error:', err.message)); - - function shutdown(reason) { - console.log(JSON.stringify({ type: 'server-stopped', reason })); - const infoFile = path.join(STATE_DIR, 'server-info'); - if (fs.existsSync(infoFile)) fs.unlinkSync(infoFile); - fs.writeFileSync( - path.join(STATE_DIR, 'server-stopped'), - JSON.stringify({ reason, timestamp: Date.now() }) + '\n' - ); - watcher.close(); - clearInterval(lifecycleCheck); - // Close any upgraded WebSocket sockets so server.close() can complete and - // the process actually exits instead of lingering on an open connection. - for (const socket of clients) { - try { socket.destroy(); } catch (e) { /* already gone */ } - } - server.close(() => process.exit(0)); - } - - function ownerAlive() { - if (!ownerPid) return true; - try { process.kill(ownerPid, 0); return true; } catch (e) { return e.code === 'EPERM'; } - } - - // Periodically exit if the owner process died or we've been idle too long. - const lifecycleCheck = setInterval(() => { - if (!ownerAlive()) shutdown('owner process exited'); - else if (Date.now() - lastActivity > IDLE_TIMEOUT_MS) shutdown('idle timeout'); - }, LIFECYCLE_CHECK_MS); - lifecycleCheck.unref(); - - // Validate owner PID at startup. If it's already dead, the PID resolution - // was wrong (common on WSL, Tailscale SSH, and cross-user scenarios). - // Disable monitoring and rely on the idle timeout instead. - if (ownerPid) { - try { process.kill(ownerPid, 0); } - catch (e) { - if (e.code !== 'EPERM') { - console.log(JSON.stringify({ type: 'owner-pid-invalid', pid: ownerPid, reason: 'dead at startup' })); - ownerPid = null; - } - } - } - - // If the preferred port is already taken (e.g. a previous server is still - // alive), fall back to a random port once instead of failing. - let triedFallback = false; - - function onListen() { - // Cookie name keys on the ACTUAL bound port (may differ from the preferred - // one after an EADDRINUSE fallback) so it can't collide with another server's - // cookie in the shared localhost jar. - COOKIE_NAME = 'brainstorm-key-' + PORT; - // Record the bound port AND token so the next restart of this session reuses - // them — but ONLY when we got our preferred port. On a fallback we bound a - // *different* port because someone else holds the preferred one; persisting - // would overwrite the shared files and strand that other session's open tab. - if (PORT_FILE && !triedFallback) { - try { fs.writeFileSync(PORT_FILE, String(PORT)); } catch (e) { /* best effort */ } - if (TOKEN_FILE) { - try { - fs.writeFileSync(TOKEN_FILE, TOKEN, { mode: 0o600 }); - chmodOwnerOnly(TOKEN_FILE); - } catch (e) { /* best effort */ } - } - } - const info = JSON.stringify({ - type: 'server-started', port: Number(PORT), host: HOST, - url_host: URL_HOST, url: companionUrl(), - screen_dir: CONTENT_DIR, state_dir: STATE_DIR, idle_timeout_ms: IDLE_TIMEOUT_MS - }); - console.log(info); - // server-info embeds the key — keep it owner-only. - fs.writeFileSync(path.join(STATE_DIR, 'server-info'), info + '\n', { mode: 0o600 }); - } - - server.on('error', (err) => { - if (err.code === 'EADDRINUSE' && !triedFallback) { - if (tokenSource === 'env') { - console.error('Server failed to bind: preferred port is in use and BRAINSTORM_TOKEN is set; refusing fallback with explicit token'); - process.exit(1); - } - triedFallback = true; - PORT = randomPort(); - if (tokenSource === 'file') { - TOKEN = generateToken(); - tokenSource = 'generated-fallback'; - } - server.listen(PORT, HOST, onListen); - } else { - console.error('Server failed to bind:', err.message); - process.exit(1); - } - }); - server.listen(PORT, HOST, onListen); -} - -if (require.main === module) { - startServer(); -} - -module.exports = { - computeAcceptKey, - encodeFrame, - decodeFrame, - browserLauncherForPlatform, - OPCODES, - MAX_FRAME_PAYLOAD_BYTES -}; diff --git a/plugins/codex/plugins/superpowers/skills/brainstorming/scripts/start-server.sh b/plugins/codex/plugins/superpowers/skills/brainstorming/scripts/start-server.sh deleted file mode 100755 index 016a8e48..00000000 --- a/plugins/codex/plugins/superpowers/skills/brainstorming/scripts/start-server.sh +++ /dev/null @@ -1,209 +0,0 @@ -#!/usr/bin/env bash -# Start the brainstorm server and output connection info -# Usage: start-server.sh [--project-dir ] [--host ] [--url-host ] [--foreground] [--background] -# -# Starts server on a random high port, outputs JSON with URL. -# Each session gets its own directory to avoid conflicts. -# -# Options: -# --project-dir Store session files under /.superpowers/brainstorm/ -# instead of /tmp. Files persist after server stops. -# --host Host/interface to bind (default: 127.0.0.1). -# Use 0.0.0.0 in remote/containerized environments. -# --url-host Hostname shown in returned URL JSON. -# --idle-timeout-minutes Shut down after n minutes idle (default 240 = 4h). -# --open Auto-open the browser on the first screen (use only -# after the user approves the visual companion). -# --foreground Run server in the current terminal (no backgrounding). -# --background Force background mode (overrides Codex auto-foreground). - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" - -# Parse arguments -PROJECT_DIR="" -FOREGROUND="false" -FORCE_BACKGROUND="false" -BIND_HOST="127.0.0.1" -URL_HOST="" -IDLE_TIMEOUT_MINUTES="" -while [[ $# -gt 0 ]]; do - case "$1" in - --project-dir) - PROJECT_DIR="$2" - shift 2 - ;; - --host) - BIND_HOST="$2" - shift 2 - ;; - --url-host) - URL_HOST="$2" - shift 2 - ;; - --idle-timeout-minutes) - IDLE_TIMEOUT_MINUTES="$2" - shift 2 - ;; - --open) - export BRAINSTORM_OPEN=1 - shift - ;; - --foreground|--no-daemon) - FOREGROUND="true" - shift - ;; - --background|--daemon) - FORCE_BACKGROUND="true" - shift - ;; - *) - echo "{\"error\": \"Unknown argument: $1\"}" - exit 1 - ;; - esac -done - -if [[ -z "$URL_HOST" ]]; then - if [[ "$BIND_HOST" == "127.0.0.1" || "$BIND_HOST" == "localhost" ]]; then - URL_HOST="localhost" - else - URL_HOST="$BIND_HOST" - fi -fi - -if [[ -n "$IDLE_TIMEOUT_MINUTES" ]]; then - if ! [[ "$IDLE_TIMEOUT_MINUTES" =~ ^[0-9]+$ ]] || [[ "$IDLE_TIMEOUT_MINUTES" -lt 1 ]]; then - echo "{\"error\": \"--idle-timeout-minutes must be a positive integer\"}" - exit 1 - fi - export BRAINSTORM_IDLE_TIMEOUT_MS=$(( IDLE_TIMEOUT_MINUTES * 60 * 1000 )) -fi - -is_windows_like_shell() { - case "${OSTYPE:-}" in - msys*|cygwin*|mingw*) return 0 ;; - esac - if [[ -n "${MSYSTEM:-}" ]]; then - return 0 - fi - local uname_s - uname_s="$(uname -s 2>/dev/null || true)" - case "$uname_s" in - MSYS*|MINGW*|CYGWIN*) return 0 ;; - esac - return 1 -} - -# Some environments reap detached/background processes. Auto-foreground when detected. -if [[ -n "${CODEX_CI:-}" && "$FOREGROUND" != "true" && "$FORCE_BACKGROUND" != "true" ]]; then - FOREGROUND="true" -fi - -# Windows/Git Bash reaps nohup background processes. Auto-foreground when detected. -if [[ "$FOREGROUND" != "true" && "$FORCE_BACKGROUND" != "true" ]]; then - if is_windows_like_shell; then - FOREGROUND="true" - fi -fi - -# Session files (server.log, server-info, .last-token) embed the session key — -# keep everything this script and the server create owner-only. -umask 077 - -# Generate unique session directory -SESSION_ID="$$-$(date +%s)" - -if [[ -n "$PROJECT_DIR" ]]; then - SESSION_DIR="${PROJECT_DIR}/.superpowers/brainstorm/${SESSION_ID}" - # Persist the bound port and key per project so a restart reuses them and an - # already-open browser tab reconnects to the same URL with a valid cookie. - export BRAINSTORM_PORT_FILE="${PROJECT_DIR}/.superpowers/brainstorm/.last-port" - export BRAINSTORM_TOKEN_FILE="${PROJECT_DIR}/.superpowers/brainstorm/.last-token" -else - SESSION_DIR="/tmp/brainstorm-${SESSION_ID}" -fi - -STATE_DIR="${SESSION_DIR}/state" -PID_FILE="${STATE_DIR}/server.pid" -LOG_FILE="${STATE_DIR}/server.log" -SERVER_ID_FILE="${STATE_DIR}/server-instance-id" - -# Create fresh session directory with content and state peers -mkdir -p "${SESSION_DIR}/content" "$STATE_DIR" - -SERVER_ID="" -if [[ -r /dev/urandom ]]; then - SERVER_ID="$(od -An -N24 -tx1 /dev/urandom 2>/dev/null | tr -d ' \n' || true)" -fi -if ! [[ "$SERVER_ID" =~ ^[A-Za-z0-9_-]{32,64}$ ]]; then - SERVER_ID="$(printf '%08x%08x%08x%08x' "$$" "$(date +%s)" "${RANDOM:-0}" "${RANDOM:-0}")" -fi -printf '%s\n' "$SERVER_ID" > "$SERVER_ID_FILE" -chmod 600 "$SERVER_ID_FILE" 2>/dev/null || true - -# Kill any existing server -if [[ -f "$PID_FILE" ]]; then - old_pid=$(cat "$PID_FILE") - kill "$old_pid" 2>/dev/null - rm -f "$PID_FILE" -fi - -cd "$SCRIPT_DIR" || exit 1 - -# Resolve the harness PID (grandparent of this script). -# $PPID is the ephemeral shell the harness spawned to run us — it dies -# when this script exits. The harness itself is $PPID's parent. -OWNER_PID="$(ps -o ppid= -p "$PPID" 2>/dev/null | tr -d ' ')" -if [[ -z "$OWNER_PID" || "$OWNER_PID" == "1" ]]; then - OWNER_PID="$PPID" -fi - -# Windows/MSYS2: Node.js cannot see POSIX PIDs from the MSYS2 namespace. -# Passing a PID node cannot verify causes server to log owner-pid-invalid -# and self-terminate at the 60-second lifecycle check. Clear it so the -# watchdog is disabled and the idle timeout becomes the only shutdown trigger. -if is_windows_like_shell; then - OWNER_PID="" -fi - -# Foreground mode for environments that reap detached/background processes. -if [[ "$FOREGROUND" == "true" ]]; then - env BRAINSTORM_DIR="$SESSION_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs "--brainstorm-server-id=$SERVER_ID" & - SERVER_PID=$! - echo "$SERVER_PID" > "$PID_FILE" - wait "$SERVER_PID" - exit $? -fi - -# Start server, capturing output to log file -# Use nohup to survive shell exit; disown to remove from job table -nohup env BRAINSTORM_DIR="$SESSION_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs "--brainstorm-server-id=$SERVER_ID" > "$LOG_FILE" 2>&1 & -SERVER_PID=$! -disown "$SERVER_PID" 2>/dev/null -echo "$SERVER_PID" > "$PID_FILE" - -# Wait for server-started message (check log file) -for _ in {1..50}; do - if grep -q "server-started" "$LOG_FILE" 2>/dev/null; then - # Verify server is still alive after a short window (catches process reapers) - alive="true" - for _ in {1..20}; do - if ! kill -0 "$SERVER_PID" 2>/dev/null; then - alive="false" - break - fi - sleep 0.1 - done - if [[ "$alive" != "true" ]]; then - echo "{\"error\": \"Server started but was killed. Retry in a persistent terminal with: $SCRIPT_DIR/start-server.sh${PROJECT_DIR:+ --project-dir $PROJECT_DIR} --host $BIND_HOST --url-host $URL_HOST --foreground\"}" - exit 1 - fi - grep "server-started" "$LOG_FILE" | head -1 - exit 0 - fi - sleep 0.1 -done - -# Timeout - server didn't start -echo '{"error": "Server failed to start within 5 seconds"}' -exit 1 diff --git a/plugins/codex/plugins/superpowers/skills/brainstorming/scripts/stop-server.sh b/plugins/codex/plugins/superpowers/skills/brainstorming/scripts/stop-server.sh deleted file mode 100755 index 7cacfe94..00000000 --- a/plugins/codex/plugins/superpowers/skills/brainstorming/scripts/stop-server.sh +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env bash -# Stop the brainstorm server and clean up -# Usage: stop-server.sh -# -# Kills the server process. Only deletes session directory if it's -# under /tmp (ephemeral). Persistent directories (.superpowers/) are -# kept so mockups can be reviewed later. - -SESSION_DIR="$1" - -if [[ -z "$SESSION_DIR" ]]; then - echo '{"error": "Usage: stop-server.sh "}' - exit 1 -fi - -STATE_DIR="${SESSION_DIR}/state" -PID_FILE="${STATE_DIR}/server.pid" -SERVER_ID_FILE="${STATE_DIR}/server-instance-id" - -mark_stopped() { - local reason="$1" - rm -f "${STATE_DIR}/server-info" - printf '{"reason":"%s","timestamp":%s}\n' "$reason" "$(date +%s)" > "${STATE_DIR}/server-stopped" -} - -read_expected_server_id() { - [[ -f "$SERVER_ID_FILE" ]] || return 1 - local id - id="$(tr -d '\r\n' < "$SERVER_ID_FILE" 2>/dev/null || true)" - [[ "$id" =~ ^[A-Za-z0-9_-]{32,64}$ ]] || return 1 - printf '%s\n' "$id" -} - -command_line_for_pid() { - local pid="$1" - if [[ -r "/proc/$pid/cmdline" ]]; then - tr '\0' '\n' < "/proc/$pid/cmdline" 2>/dev/null || true - return 0 - fi - ps -ww -p "$pid" -o command= 2>/dev/null || ps -f -p "$pid" 2>/dev/null | sed '1d' || true -} - -command_has_server_id() { - local pid="$1" - local expected="$2" - local expected_arg="--brainstorm-server-id=$expected" - if [[ -r "/proc/$pid/cmdline" ]]; then - local arg - while IFS= read -r -d '' arg || [[ -n "$arg" ]]; do - [[ "$arg" == "$expected_arg" ]] && return 0 - done < "/proc/$pid/cmdline" - return 1 - fi - local command_line - command_line="$(command_line_for_pid "$pid")" - [[ -n "$command_line" ]] || return 1 - case " $command_line " in - *" $expected_arg "*) return 0 ;; - *) return 1 ;; - esac -} - -# Confirm a PID has this session's per-start instance id, not just a familiar -# process name. Ambiguous or legacy metadata fails closed as stale_pid. -is_brainstorm_server() { - kill -0 "$1" 2>/dev/null || return 1 - local expected_id - expected_id="$(read_expected_server_id)" || return 1 - command_has_server_id "$1" "$expected_id" || return 1 - return 0 -} - -if [[ -f "$PID_FILE" ]]; then - pid=$(cat "$PID_FILE") - - # Refuse to signal a PID we can't prove is our server. A stale pid file may - # point at an unrelated process after a reboot/PID wraparound. - if ! is_brainstorm_server "$pid"; then - rm -f "$PID_FILE" "$SERVER_ID_FILE" - mark_stopped "stale_pid" - echo '{"status": "stale_pid"}' - exit 0 - fi - - # Try to stop gracefully, fallback to force if still alive - kill "$pid" 2>/dev/null || true - - # Wait for graceful shutdown (up to ~2s) - for _ in {1..20}; do - if ! kill -0 "$pid" 2>/dev/null; then - break - fi - sleep 0.1 - done - - # If still running, escalate to SIGKILL - if kill -0 "$pid" 2>/dev/null; then - kill -9 "$pid" 2>/dev/null || true - - # Give SIGKILL a moment to take effect - sleep 0.1 - fi - - if kill -0 "$pid" 2>/dev/null; then - echo '{"status": "failed", "error": "process still running"}' - exit 1 - fi - - rm -f "$PID_FILE" "$SERVER_ID_FILE" "${STATE_DIR}/server.log" - mark_stopped "stop-server.sh" - - # Only delete ephemeral /tmp directories - if [[ "$SESSION_DIR" == /tmp/* ]]; then - rm -rf "$SESSION_DIR" - fi - - echo '{"status": "stopped"}' -else - echo '{"status": "not_running"}' -fi diff --git a/plugins/codex/plugins/superpowers/skills/brainstorming/spec-document-reviewer-prompt.md b/plugins/codex/plugins/superpowers/skills/brainstorming/spec-document-reviewer-prompt.md deleted file mode 100644 index 60993129..00000000 --- a/plugins/codex/plugins/superpowers/skills/brainstorming/spec-document-reviewer-prompt.md +++ /dev/null @@ -1,49 +0,0 @@ -# Spec Document Reviewer Prompt Template - -Use this template when dispatching a spec document reviewer subagent. - -**Purpose:** Verify the spec is complete, consistent, and ready for implementation planning. - -**Dispatch after:** Spec document is written to docs/superpowers/specs/ - -``` -Subagent (general-purpose): - description: "Review spec document" - prompt: | - You are a spec document reviewer. Verify this spec is complete and ready for planning. - - **Spec to review:** [SPEC_FILE_PATH] - - ## What to Check - - | Category | What to Look For | - |----------|------------------| - | Completeness | TODOs, placeholders, "TBD", incomplete sections | - | Consistency | Internal contradictions, conflicting requirements | - | Clarity | Requirements ambiguous enough to cause someone to build the wrong thing | - | Scope | Focused enough for a single plan — not covering multiple independent subsystems | - | YAGNI | Unrequested features, over-engineering | - - ## Calibration - - **Only flag issues that would cause real problems during implementation planning.** - A missing section, a contradiction, or a requirement so ambiguous it could be - interpreted two different ways — those are issues. Minor wording improvements, - stylistic preferences, and "sections less detailed than others" are not. - - Approve unless there are serious gaps that would lead to a flawed plan. - - ## Output Format - - ## Spec Review - - **Status:** Approved | Issues Found - - **Issues (if any):** - - [Section X]: [specific issue] - [why it matters for planning] - - **Recommendations (advisory, do not block approval):** - - [suggestions for improvement] -``` - -**Reviewer returns:** Status, Issues (if any), Recommendations diff --git a/plugins/codex/plugins/superpowers/skills/brainstorming/visual-companion.md b/plugins/codex/plugins/superpowers/skills/brainstorming/visual-companion.md deleted file mode 100644 index 8dca065b..00000000 --- a/plugins/codex/plugins/superpowers/skills/brainstorming/visual-companion.md +++ /dev/null @@ -1,299 +0,0 @@ -# Visual Companion Guide - -Browser-based visual brainstorming companion for showing mockups, diagrams, and options. - -## When to Use - -Decide per-question, not per-session. The test: **would the user understand this better by seeing it than reading it?** - -**Use the browser** when the content itself is visual: - -- **UI mockups** — wireframes, layouts, navigation structures, component designs -- **Architecture diagrams** — system components, data flow, relationship maps -- **Side-by-side visual comparisons** — comparing two layouts, two color schemes, two design directions -- **Design polish** — when the question is about look and feel, spacing, visual hierarchy -- **Spatial relationships** — state machines, flowcharts, entity relationships rendered as diagrams - -**Use the terminal** when the content is text or tabular: - -- **Requirements and scope questions** — "what does X mean?", "which features are in scope?" -- **Conceptual A/B/C choices** — picking between approaches described in words -- **Tradeoff lists** — pros/cons, comparison tables -- **Technical decisions** — API design, data modeling, architectural approach selection -- **Clarifying questions** — anything where the answer is words, not a visual preference - -A question *about* a UI topic is not automatically a visual question. "What kind of wizard do you want?" is conceptual — use the terminal. "Which of these wizard layouts feels right?" is visual — use the browser. - -## How It Works - -The server watches a directory for HTML files and serves the newest one to the browser. You write HTML content to `screen_dir`, the user sees it in their browser and can click to select options. Selections are recorded to `state_dir/events` that you read on your next turn. - -**Content fragments vs full documents:** If your HTML file starts with `/.superpowers/brainstorm/` for the session directory. - -**Note:** Pass the project root as `--project-dir` so mockups persist in `.superpowers/brainstorm/` and survive server restarts. Without it, files go to `/tmp` and get cleaned up. Remind the user to add `.superpowers/` to `.gitignore` if it's not already there. - -**Launching the server by platform:** - -**Claude Code:** -```bash -# Default mode works — the script backgrounds the server itself. -bash scripts/start-server.sh --project-dir /path/to/project --open -``` - -On Windows, the script auto-detects and switches to foreground mode (which blocks the tool call). Use `run_in_background: true` on the Bash tool call so the server survives across conversation turns, then read `$STATE_DIR/server-info` on the next turn to get the URL and port. - -**Codex:** -```bash -# Codex reaps background processes. The script auto-detects CODEX_CI and -# switches to foreground mode. Run it normally — no extra flags needed. -bash scripts/start-server.sh --project-dir /path/to/project --open -``` - -**Gemini CLI:** -```bash -# Use --foreground and set is_background: true on your shell tool call -# so the process survives across turns -bash scripts/start-server.sh --project-dir /path/to/project --open --foreground -``` - -**Copilot CLI:** -```bash -# Start it with Copilot CLI's non-blocking/background shell mechanism so the -# server survives across turns. Keep --foreground so the harness, not the -# script, owns backgrounding. The launcher is a .sh, so invoke it via bash -# (on Windows, call Git Bash's bash.exe from the PowerShell tool). -bash scripts/start-server.sh --project-dir /path/to/project --open --foreground -``` - -**Other environments:** The server must keep running in the background across conversation turns. If your environment reaps detached processes, use `--foreground` and launch the command with your platform's background execution mechanism. - -If the URL is unreachable from your browser (common in remote/containerized setups), bind a non-loopback host: - -```bash -bash scripts/start-server.sh \ - --project-dir /path/to/project \ - --host 0.0.0.0 \ - --url-host localhost -``` - -Use `--url-host` to control what hostname is printed in the returned URL JSON. - -## The Loop - -1. **Check server is alive**, then **write HTML** to a new file in `screen_dir`: - - **Required: confirm the server is alive before referring to the URL or pushing a screen.** Check that `$STATE_DIR/server-info` exists and `$STATE_DIR/server-stopped` does not. If it has shut down, restart it with `start-server.sh` using the **same `--project-dir`** — it reuses the same port, so the user's open tab reconnects on its own (it shows a "paused" overlay while the server is down) and you don't need to send a new URL. The server auto-exits after 4 hours idle (configurable with `--idle-timeout-minutes`). - - Use semantic filenames: `platform.html`, `visual-style.html`, `layout.html` - - **Never reuse filenames** — each screen gets a fresh file - - Use your file-creation tool — **never use cat/heredoc** (dumps noise into terminal) - - Server automatically serves the newest file - -2. **Tell user what to expect and end your turn:** - - Remind them of the URL (every step, not just first) - - Give a brief text summary of what's on screen (e.g., "Showing 3 layout options for the homepage") - - Ask them to respond in the terminal: "Take a look and let me know what you think. Click to select an option if you'd like." - -3. **On your next turn** — after the user responds in the terminal: - - Read `$STATE_DIR/events` if it exists — this contains the user's browser interactions (clicks, selections) as JSON lines - - Merge with the user's terminal text to get the full picture - - The terminal message is the primary feedback; `state_dir/events` provides structured interaction data - -4. **Iterate or advance** — if feedback changes current screen, write a new file (e.g., `layout-v2.html`). Only move to the next question when the current step is validated. - -5. **Unload when returning to terminal** — when the next step doesn't need the browser (e.g., a clarifying question, a tradeoff discussion), push a waiting screen to clear the stale content: - - ```html - -
-

Continuing in terminal...

-
- ``` - - This prevents the user from staring at a resolved choice while the conversation has moved on. When the next visual question comes up, push a new content file as usual. - -6. Repeat until done. - -## Writing Content Fragments - -Write just the content that goes inside the page. The server wraps it in the frame template automatically (header, theme CSS, connection status, and all interactive infrastructure). - -**Minimal example:** - -```html -

Which layout works better?

-

Consider readability and visual hierarchy

- -
-
-
A
-
-

Single Column

-

Clean, focused reading experience

-
-
-
-
B
-
-

Two Column

-

Sidebar navigation with main content

-
-
-
-``` - -That's it. No ``, no CSS, no `