@shirlytaylor73/superharness 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +202 -0
  3. package/bin/lib/codex-installer.js +228 -0
  4. package/bin/lib/interactive-select.js +96 -0
  5. package/bin/superharness.js +67 -0
  6. package/package.json +52 -0
  7. package/plugins/superharness/.claude-plugin/plugin.json +19 -0
  8. package/plugins/superharness/.codex-plugin/plugin.json +31 -0
  9. package/plugins/superharness/.mcp.json +9 -0
  10. package/plugins/superharness/CODE_OF_CONDUCT.md +79 -0
  11. package/plugins/superharness/LICENSE +21 -0
  12. package/plugins/superharness/README.md +57 -0
  13. package/plugins/superharness/agents/code-reviewer.md +48 -0
  14. package/plugins/superharness/archived-skills/using-superpowers/SKILL.md +140 -0
  15. package/plugins/superharness/archived-skills/using-superpowers/references/codex-tools.md +25 -0
  16. package/plugins/superharness/archived-skills/using-superpowers/references/copilot-tools.md +52 -0
  17. package/plugins/superharness/archived-skills/using-superpowers/references/gemini-tools.md +33 -0
  18. package/plugins/superharness/archived-skills/using-superpowers/references/hermes-tools.md +44 -0
  19. package/plugins/superharness/commands/free.md +6 -0
  20. package/plugins/superharness/commands/rollback.md +30 -0
  21. package/plugins/superharness/commands-codex/free.md +29 -0
  22. package/plugins/superharness/commands-codex/rollback.md +33 -0
  23. package/plugins/superharness/hooks/hooks-codex.json +50 -0
  24. package/plugins/superharness/hooks/hooks.json +50 -0
  25. package/plugins/superharness/hooks/lib/free-mode-check.mjs +27 -0
  26. package/plugins/superharness/hooks/run-hook.cmd +58 -0
  27. package/plugins/superharness/hooks/workflow-context +4 -0
  28. package/plugins/superharness/hooks/workflow-context.mjs +184 -0
  29. package/plugins/superharness/hooks/workflow-post-transition +4 -0
  30. package/plugins/superharness/hooks/workflow-post-transition.mjs +89 -0
  31. package/plugins/superharness/hooks/workflow-pre-tool-use +4 -0
  32. package/plugins/superharness/hooks/workflow-pre-tool-use.mjs +97 -0
  33. package/plugins/superharness/hooks/workflow-stop +4 -0
  34. package/plugins/superharness/hooks/workflow-stop.mjs +136 -0
  35. package/plugins/superharness/scripts/rollback.mjs +86 -0
  36. package/plugins/superharness/scripts/set-free-mode.mjs +77 -0
  37. package/plugins/superharness/skills/brainstorming/SKILL.md +182 -0
  38. package/plugins/superharness/skills/brainstorming/scripts/frame-template.html +214 -0
  39. package/plugins/superharness/skills/brainstorming/scripts/helper.js +88 -0
  40. package/plugins/superharness/skills/brainstorming/scripts/server.cjs +338 -0
  41. package/plugins/superharness/skills/brainstorming/scripts/start-server.sh +153 -0
  42. package/plugins/superharness/skills/brainstorming/scripts/stop-server.sh +55 -0
  43. package/plugins/superharness/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
  44. package/plugins/superharness/skills/brainstorming/visual-companion.md +286 -0
  45. package/plugins/superharness/skills/chinese-code-review/SKILL.md +277 -0
  46. package/plugins/superharness/skills/chinese-commit-conventions/SKILL.md +364 -0
  47. package/plugins/superharness/skills/chinese-documentation/SKILL.md +448 -0
  48. package/plugins/superharness/skills/chinese-git-workflow/SKILL.md +547 -0
  49. package/plugins/superharness/skills/dispatching-parallel-agents/SKILL.md +186 -0
  50. package/plugins/superharness/skills/exploration/SKILL.md +197 -0
  51. package/plugins/superharness/skills/finishing/SKILL.md +200 -0
  52. package/plugins/superharness/skills/intake/SKILL.md +134 -0
  53. package/plugins/superharness/skills/mcp-builder/SKILL.md +255 -0
  54. package/plugins/superharness/skills/parallel-execution/SKILL.md +368 -0
  55. package/plugins/superharness/skills/parallel-execution/implementer-prompt.md +144 -0
  56. package/plugins/superharness/skills/parallel-execution/spec-reviewer-prompt.md +84 -0
  57. package/plugins/superharness/skills/parallel-execution/wave-final-manual-qa-prompt.md +61 -0
  58. package/plugins/superharness/skills/parallel-execution/wave-final-quality-prompt.md +59 -0
  59. package/plugins/superharness/skills/parallel-execution/wave-final-scope-fidelity-prompt.md +69 -0
  60. package/plugins/superharness/skills/parallel-execution/wave-final-spec-prompt.md +56 -0
  61. package/plugins/superharness/skills/planning/SKILL.md +265 -0
  62. package/plugins/superharness/skills/planning/plan-document-reviewer-prompt.md +80 -0
  63. package/plugins/superharness/skills/receiving-code-review/SKILL.md +213 -0
  64. package/plugins/superharness/skills/requesting-code-review/SKILL.md +107 -0
  65. package/plugins/superharness/skills/requesting-code-review/code-reviewer.md +146 -0
  66. package/plugins/superharness/skills/serial-execution/SKILL.md +183 -0
  67. package/plugins/superharness/skills/systematic-debugging/CREATION-LOG.md +119 -0
  68. package/plugins/superharness/skills/systematic-debugging/SKILL.md +320 -0
  69. package/plugins/superharness/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
  70. package/plugins/superharness/skills/systematic-debugging/condition-based-waiting.md +115 -0
  71. package/plugins/superharness/skills/systematic-debugging/defense-in-depth.md +122 -0
  72. package/plugins/superharness/skills/systematic-debugging/find-polluter.sh +63 -0
  73. package/plugins/superharness/skills/systematic-debugging/root-cause-tracing.md +169 -0
  74. package/plugins/superharness/skills/systematic-debugging/test-academic.md +14 -0
  75. package/plugins/superharness/skills/systematic-debugging/test-pressure-1.md +58 -0
  76. package/plugins/superharness/skills/systematic-debugging/test-pressure-2.md +68 -0
  77. package/plugins/superharness/skills/systematic-debugging/test-pressure-3.md +69 -0
  78. package/plugins/superharness/skills/test-driven-development/SKILL.md +371 -0
  79. package/plugins/superharness/skills/test-driven-development/testing-anti-patterns.md +299 -0
  80. package/plugins/superharness/skills/trivial/SKILL.md +118 -0
  81. package/plugins/superharness/skills/using-git-worktrees/SKILL.md +218 -0
  82. package/plugins/superharness/skills/verification/SKILL.md +139 -0
  83. package/plugins/superharness/skills/workflow-runner/SKILL.md +172 -0
  84. package/plugins/superharness/skills/writing-skills/SKILL.md +655 -0
  85. package/plugins/superharness/skills/writing-skills/anthropic-best-practices.md +1149 -0
  86. package/plugins/superharness/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
  87. package/plugins/superharness/skills/writing-skills/graphviz-conventions.dot +172 -0
  88. package/plugins/superharness/skills/writing-skills/persuasion-principles.md +187 -0
  89. package/plugins/superharness/skills/writing-skills/render-graphs.js +168 -0
  90. package/plugins/superharness/skills/writing-skills/testing-skills-with-subagents.md +385 -0
  91. package/plugins/superharness/workflow/default-workflow.yaml +84 -0
  92. package/plugins/superharness/workflow-state-server/bootstrap.js +44 -0
  93. package/plugins/superharness/workflow-state-server/package-lock.json +2853 -0
  94. package/plugins/superharness/workflow-state-server/package.json +22 -0
  95. package/plugins/superharness/workflow-state-server/render-context.js +124 -0
  96. package/plugins/superharness/workflow-state-server/schema.sql +39 -0
  97. package/plugins/superharness/workflow-state-server/server.js +290 -0
  98. package/plugins/superharness/workflow-state-server/state.js +424 -0
  99. package/plugins/superharness/workflow-state-server/validate-workflow.js +165 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 jnMetaCode
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,202 @@
1
+ # Superharness
2
+
3
+ Superharness 是面向 AI 编程代理的程序化工作流运行时。它把“什么时候该做需求分析、什么时候该写计划、什么时候该执行、什么时候必须验证”从纯 prompt 纪律升级为可校验、可审计、可恢复的状态机。
4
+
5
+ 核心能力:
6
+
7
+ - **状态机驱动的 active skills**:覆盖会话入口分流(intake)、只读探索、轻量改动、需求澄清、计划编写、串行/并行执行、TDD、系统化调试、代码审查、完成前验证、分支收尾、中文工程规范等开发环节。
8
+ - **程序化状态机**:默认工作流由 `workflow/default-workflow.yaml` 描述,状态切换通过 MCP 工具管理,而不是让 agent 直接改状态文件。
9
+ - **Hook 动态注入**:Claude Code / Codex 通过 `UserPromptSubmit` 注入当前工作流上下文。
10
+ - **状态保护**:`PreToolUse` 阻止直接写入 `.superharness/`,要求通过工作流状态工具完成状态变更。
11
+ - **双端支持**:Claude Code、Codex 都接入同一套 skills、状态机与上下文渲染机制。
12
+ - **用户控制命令**:`/rollback` 和 `/free` 把“何时回退、何时暂停 workflow”的决策权交还给用户。
13
+ - **`/rollback [state]`**:把当前 workflow state 回退到 `transition_log` 中走过的某个历史 state。
14
+ - 无参 → 列出最近 5 个独特 state 供选择。
15
+ - 有参(如 `/rollback brainstorming`)→ 直接回到指定 state。
16
+ - 仅允许回退到日志里真实出现过的 state,避免凭空跳转。
17
+ - **`/free on|off|status`**:会话级暂停 / 恢复 workflow context 注入。
18
+ - free-mode 期间 hook 不再注入 SKILL.md,MCP 的 mutating 工具(`transition_state` / `classify_request` / `release_stop_block`)全部锁定。
19
+ - `.superharness/` 写保护始终生效,free mode 不影响审计与状态文件的安全性。
20
+ - 适合临时跳出状态机做一些不想被规范约束的探索或对话。
21
+
22
+ ## 架构
23
+
24
+ ```text
25
+ 用户请求
26
+ -> Hook 注入当前工作流状态
27
+ -> Agent 按 active skill 执行
28
+ -> superharness-workflow-state MCP 管理状态跳转
29
+ -> .superharness/ 持久化状态与审计事实
30
+ ```
31
+
32
+ ### 工作流状态机(v3)
33
+
34
+ 每次会话都从 `intake` 开始,由它把请求分流到三条支线之一;执行支线结束后再回到 `intake` 等待下一任务。`systematic_debugging` 是抢占式分支,任何执行/验证/收尾节点失败都可以转入,调试完成后通过 `previous_state` 回边返回原状态:
35
+
36
+ `intake → exploration / trivial / (brainstorming → planning → serial_execution | parallel_execution → verification → finishing) → intake`
37
+
38
+ ```mermaid
39
+ flowchart TD
40
+ START([Session start]) --> Intake
41
+ Intake[intake]:::interactive
42
+ Explor[exploration]:::interactive
43
+ Triv[trivial]:::execution
44
+ BS[brainstorming]:::interactive
45
+ PL[planning]:::interactive
46
+ SE[serial_execution]:::execution
47
+ PE[parallel_execution]:::execution
48
+ VF[verification]:::gate
49
+ FN[finishing]:::gate
50
+ DBG[/systematic_debugging/]:::preempt
51
+
52
+ Intake -->|只读探索| Explor
53
+ Intake -->|轻量改动| Triv
54
+ Intake -->|功能/bugfix| BS
55
+
56
+ Explor --> Intake
57
+ Triv --> Intake
58
+
59
+ BS --> PL
60
+ PL -->|串行| SE
61
+ PL -->|并行| PE
62
+ SE --> VF
63
+ PE --> VF
64
+ VF --> FN
65
+ FN -->|下一任务| Intake
66
+
67
+ Triv -.->|测试失败/报错| DBG
68
+ SE -.->|遇到 bug| DBG
69
+ PE -.->|遇到 bug| DBG
70
+ VF -.->|验证失败| DBG
71
+ FN -.->|git/CI 失败| DBG
72
+
73
+ DBG -.->|previous_state| Intake
74
+ DBG -->|重写计划| PL
75
+ DBG -->|重新执行| SE
76
+
77
+ classDef interactive fill:#065f46,stroke:#10b981,color:#fff
78
+ classDef router fill:#78350f,stroke:#f59e0b,color:#fff
79
+ classDef execution fill:#1e40af,stroke:#3b82f6,color:#fff
80
+ classDef gate fill:#7f1d1d,stroke:#ef4444,color:#fff
81
+ classDef preempt fill:#581c87,stroke:#a855f7,color:#fff
82
+ ```
83
+
84
+ 关键目录:
85
+
86
+ | 路径 | 说明 |
87
+ |---|---|
88
+ | `plugins/superharness/skills/` | 当前 active skills 目录 |
89
+ | `plugins/superharness/workflow/` | 默认工作流配置 |
90
+ | `plugins/superharness/workflow-state-server/` | 状态机 MCP 与渲染逻辑 |
91
+ | `plugins/superharness/hooks/` | Claude Code / Codex hook 配置与入口 |
92
+ | `plugins/superharness/archived-skills/` | 已归档、不可发现的历史 skill |
93
+
94
+ ## 安装
95
+
96
+ ### Claude Code
97
+
98
+ ```text
99
+ /plugin marketplace add ShirlyTaylor73/superharness
100
+ /plugin install superharness@superharness
101
+ ```
102
+
103
+ 更新 / 卸载:
104
+
105
+ ```text
106
+ /plugin update superharness
107
+ /plugin uninstall superharness
108
+ ```
109
+
110
+ ### Codex CLI
111
+
112
+ #### Codex 快速安装
113
+
114
+ ```bash
115
+ npx @shirlytaylor73/superharness@latest
116
+ ```
117
+
118
+ 安装器会用方向键询问安装到当前项目目录还是用户级目录。项目级会写入 `.agents/skills/` 和 `.codex/`;用户级会写入 `~/.agents/skills/` 和 `~/.codex/`。非交互环境使用:
119
+
120
+ ```bash
121
+ npx @shirlytaylor73/superharness@latest --project
122
+ npx @shirlytaylor73/superharness@latest --user
123
+ ```
124
+
125
+ 安装器会把 active skills 平铺安装到 `.agents/skills/`,并把 Codex 版 `free` / `rollback` command 写入 `.codex/commands/`。由于 Codex 不支持 Claude Code 的 `!node` 原生 command 执行语法,Codex command 会指示 agent 用 shell tool 执行等价 Node 脚本。
126
+
127
+ #### Codex 插件市场
128
+
129
+ ```bash
130
+ codex plugin marketplace add ShirlyTaylor73/superharness
131
+ ```
132
+
133
+ 然后启动 Codex,输入 `/plugins`,选择 `superharness` 安装。
134
+
135
+ 更新 / 移除:
136
+
137
+ ```bash
138
+ codex plugin marketplace upgrade superharness
139
+ codex plugin marketplace remove superharness
140
+ ```
141
+
142
+ ### 本地开发安装
143
+
144
+ ```bash
145
+ git clone https://github.com/ShirlyTaylor73/superharness.git
146
+ cd superharness
147
+ ```
148
+
149
+ Claude / Codex 推荐使用插件市场安装。若只想手动使用 skills,可把 `plugins/superharness/skills/` 复制或链接到目标工具的 skills 目录。
150
+
151
+ 常见目标目录:
152
+
153
+ | 工具 | skills 目录 |
154
+ |---|---|
155
+ | Claude Code | `.claude/skills/` |
156
+ | Codex CLI | `.agents/skills/` |
157
+ | Gemini CLI | `.gemini/skills/` |
158
+ | Aider | `.aider/skills/` |
159
+ | Windsurf | `.windsurf/skills/` |
160
+ | OpenClaw | `skills/` |
161
+
162
+ ## Active Skills
163
+
164
+ | Skill | 用途 |
165
+ |---|---|
166
+ | `intake` | 会话入口与请求 triage(分流到 exploration / trivial / brainstorming) |
167
+ | `exploration` | 只读深度探索,不写文件 |
168
+ | `trivial` | 单点轻量改动 + 自带验证 |
169
+ | `brainstorming` | 需求澄清与设计规格 |
170
+ | `planning` | 编写可执行实现计划 |
171
+ | `serial-execution` | 小计划串行执行 |
172
+ | `parallel-execution` | 大计划 wave 化并行执行 |
173
+ | `test-driven-development` | TDD 红绿重构 |
174
+ | `systematic-debugging` | 系统化定位和修复问题 |
175
+ | `requesting-code-review` | 请求代码审查 |
176
+ | `receiving-code-review` | 处理代码审查反馈 |
177
+ | `verification` | 完成前验证 |
178
+ | `finishing` | 开发分支收尾 |
179
+ | `dispatching-parallel-agents` | 非 plan 场景并行派发 |
180
+ | `using-git-worktrees` | 隔离式开发工作树 |
181
+ | `writing-skills` | 创建和改进 skills |
182
+ | `chinese-code-review` | 中文团队代码审查规范 |
183
+ | `chinese-git-workflow` | 国内 Git 平台工作流 |
184
+ | `chinese-documentation` | 中文技术文档规范 |
185
+ | `chinese-commit-conventions` | 中文提交规范 |
186
+ | `mcp-builder` | MCP 服务器构建方法论 |
187
+ | `workflow-runner` | 多角色 YAML 工作流执行 |
188
+
189
+ ## 废弃机制
190
+
191
+ 历史入口 skill `using-superpowers` 已归档到 `archived-skills/`,不再注册、不再注入、不再作为运行时入口。当前入口由 hook 注入的 workflow context 和 `superharness-workflow-state` MCP 接管。
192
+
193
+ ## 验证
194
+
195
+ ```bash
196
+ cd plugins/superharness/workflow-state-server
197
+ npm.cmd test
198
+ ```
199
+
200
+ ## License
201
+
202
+ MIT
@@ -0,0 +1,228 @@
1
+ import { spawn } from 'node:child_process';
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+
5
+ export const INSTALLER_TOKEN = `{{SUPERHARNESS_${'PLUGIN_ROOT'}}}`;
6
+ export const COMMAND_NAMES = ['free.md', 'rollback.md'];
7
+ const LEGACY_PLUGIN_ROOT_PLACEHOLDER = `installed-${'plugin-root'}`;
8
+
9
+ export function parseArgs(argv) {
10
+ const parsed = { mode: null, force: false, help: false };
11
+
12
+ for (const arg of argv) {
13
+ if (arg === '--project') {
14
+ setMode(parsed, 'project');
15
+ } else if (arg === '--user' || arg === '--global') {
16
+ setMode(parsed, 'user');
17
+ } else if (arg === '--force') {
18
+ parsed.force = true;
19
+ } else if (arg === '--help' || arg === '-h') {
20
+ parsed.help = true;
21
+ } else {
22
+ throw new Error(`unknown argument: ${arg}`);
23
+ }
24
+ }
25
+
26
+ return parsed;
27
+ }
28
+
29
+ function setMode(parsed, mode) {
30
+ if (parsed.mode && parsed.mode !== mode) {
31
+ throw new Error('choose only one install target');
32
+ }
33
+ parsed.mode = mode;
34
+ }
35
+
36
+ export function resolveInstallTarget({
37
+ mode,
38
+ cwd = process.cwd(),
39
+ homeDir = process.env.USERPROFILE || process.env.HOME,
40
+ }) {
41
+ if (mode !== 'project' && mode !== 'user') {
42
+ throw new Error('install target must be project or user');
43
+ }
44
+ if (mode === 'user' && !homeDir) {
45
+ throw new Error('cannot resolve user home directory');
46
+ }
47
+
48
+ const codexRoot = mode === 'project'
49
+ ? path.resolve(cwd, '.codex')
50
+ : path.resolve(homeDir, '.codex');
51
+ const agentsRoot = mode === 'project'
52
+ ? path.resolve(cwd, '.agents')
53
+ : path.resolve(homeDir, '.agents');
54
+
55
+ return {
56
+ mode,
57
+ codexRoot,
58
+ agentsRoot,
59
+ pluginRoot: path.join(codexRoot, 'plugins', 'superharness'),
60
+ commandsRoot: path.join(codexRoot, 'commands'),
61
+ skillsRoot: path.join(agentsRoot, 'skills'),
62
+ };
63
+ }
64
+
65
+ export async function backupExistingPath(target, timestamp) {
66
+ if (!(await exists(target))) {
67
+ return null;
68
+ }
69
+
70
+ let backupPath = `${target}.bak-${timestamp}`;
71
+ let counter = 1;
72
+ while (await exists(backupPath)) {
73
+ backupPath = `${target}.bak-${timestamp}-${counter}`;
74
+ counter += 1;
75
+ }
76
+
77
+ await fs.rename(target, backupPath);
78
+ return backupPath;
79
+ }
80
+
81
+ export async function copyPluginRuntime({ packageRoot, pluginRoot, timestamp }) {
82
+ const source = path.join(packageRoot, 'plugins', 'superharness');
83
+ const backup = await backupExistingPath(pluginRoot, timestamp);
84
+ await fs.mkdir(path.dirname(pluginRoot), { recursive: true });
85
+ await fs.cp(source, pluginRoot, {
86
+ recursive: true,
87
+ filter: (sourcePath) => path.basename(sourcePath) !== 'node_modules',
88
+ });
89
+ return backup;
90
+ }
91
+
92
+ export async function copyAgentSkills({ packageRoot, skillsRoot, timestamp }) {
93
+ const source = path.join(packageRoot, 'plugins', 'superharness', 'skills');
94
+ const backups = [];
95
+ await fs.mkdir(skillsRoot, { recursive: true });
96
+
97
+ const entries = await fs.readdir(source, { withFileTypes: true });
98
+ for (const entry of entries) {
99
+ const sourcePath = path.join(source, entry.name);
100
+ const destination = path.join(skillsRoot, entry.name);
101
+ const backup = await backupExistingPath(destination, timestamp);
102
+ if (backup) {
103
+ backups.push(backup);
104
+ }
105
+ await fs.cp(sourcePath, destination, { recursive: entry.isDirectory() });
106
+ }
107
+
108
+ return backups;
109
+ }
110
+
111
+ export function renderCommandTemplate(template, pluginRoot) {
112
+ const rendered = template.replaceAll(INSTALLER_TOKEN, pluginRoot);
113
+ if (rendered.includes(INSTALLER_TOKEN) || rendered.includes(LEGACY_PLUGIN_ROOT_PLACEHOLDER)) {
114
+ throw new Error('command template still contains unresolved plugin root placeholder');
115
+ }
116
+ return rendered;
117
+ }
118
+
119
+ export async function installWorkflowDependencies({ pluginRoot, runCommand = spawnCommand }) {
120
+ const cwd = path.join(pluginRoot, 'workflow-state-server');
121
+ try {
122
+ await runCommand('npm', ['install', '--omit=dev'], { cwd });
123
+ } catch (error) {
124
+ throw new Error(`npm install --omit=dev failed in ${cwd}: ${error.message}`);
125
+ }
126
+ }
127
+
128
+ export async function installCodexSupport({
129
+ mode,
130
+ cwd = process.cwd(),
131
+ homeDir = process.env.USERPROFILE || process.env.HOME,
132
+ packageRoot,
133
+ now = () => new Date(),
134
+ runCommand,
135
+ } = {}) {
136
+ assertSupportedNode();
137
+ if (!packageRoot) {
138
+ throw new Error('packageRoot is required');
139
+ }
140
+
141
+ const target = resolveInstallTarget({ mode, cwd, homeDir });
142
+ const timestamp = formatTimestamp(now());
143
+ const backups = [];
144
+
145
+ await fs.mkdir(target.commandsRoot, { recursive: true });
146
+ const pluginBackup = await copyPluginRuntime({
147
+ packageRoot,
148
+ pluginRoot: target.pluginRoot,
149
+ timestamp,
150
+ });
151
+ if (pluginBackup) {
152
+ backups.push(pluginBackup);
153
+ }
154
+ backups.push(...await copyAgentSkills({
155
+ packageRoot,
156
+ skillsRoot: target.skillsRoot,
157
+ timestamp,
158
+ }));
159
+
160
+ for (const commandName of COMMAND_NAMES) {
161
+ const source = path.join(packageRoot, 'plugins', 'superharness', 'commands-codex', commandName);
162
+ const destination = path.join(target.commandsRoot, commandName);
163
+ const template = await fs.readFile(source, 'utf8');
164
+ const rendered = renderCommandTemplate(template, target.pluginRoot);
165
+ const backup = await backupExistingPath(destination, timestamp);
166
+ if (backup) {
167
+ backups.push(backup);
168
+ }
169
+ await fs.writeFile(destination, rendered, 'utf8');
170
+ }
171
+
172
+ await installWorkflowDependencies({ pluginRoot: target.pluginRoot, runCommand });
173
+
174
+ return {
175
+ mode: target.mode,
176
+ pluginRoot: target.pluginRoot,
177
+ commandsRoot: target.commandsRoot,
178
+ skillsRoot: target.skillsRoot,
179
+ backups,
180
+ };
181
+ }
182
+
183
+ function assertSupportedNode() {
184
+ const major = Number.parseInt(process.versions.node.split('.')[0], 10);
185
+ if (major < 20) {
186
+ throw new Error(`Superharness Codex installer requires Node.js >=20; current version is ${process.version}`);
187
+ }
188
+ }
189
+
190
+ function formatTimestamp(date) {
191
+ const parts = [
192
+ date.getUTCFullYear(),
193
+ date.getUTCMonth() + 1,
194
+ date.getUTCDate(),
195
+ date.getUTCHours(),
196
+ date.getUTCMinutes(),
197
+ date.getUTCSeconds(),
198
+ ].map((part, index) => (index === 0 ? String(part) : String(part).padStart(2, '0')));
199
+
200
+ return `${parts[0]}${parts[1]}${parts[2]}-${parts[3]}${parts[4]}${parts[5]}`;
201
+ }
202
+
203
+ async function exists(filePath) {
204
+ try {
205
+ await fs.access(filePath);
206
+ return true;
207
+ } catch {
208
+ return false;
209
+ }
210
+ }
211
+
212
+ function spawnCommand(command, args, options) {
213
+ return new Promise((resolve, reject) => {
214
+ const child = spawn(command, args, {
215
+ ...options,
216
+ stdio: 'inherit',
217
+ shell: process.platform === 'win32',
218
+ });
219
+ child.on('error', reject);
220
+ child.on('exit', (code, signal) => {
221
+ if (code === 0) {
222
+ resolve();
223
+ } else {
224
+ reject(new Error(signal ? `terminated by ${signal}` : `exited with code ${code}`));
225
+ }
226
+ });
227
+ });
228
+ }
@@ -0,0 +1,96 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import readline from 'node:readline';
4
+
5
+ const OPTIONS = [
6
+ {
7
+ value: 'project',
8
+ label: 'Project install',
9
+ description: "Install into this repository's .agents and .codex directories",
10
+ },
11
+ {
12
+ value: 'user',
13
+ label: 'User install',
14
+ description: 'Install into ~/.agents and ~/.codex for all Codex projects',
15
+ },
16
+ {
17
+ value: 'cancel',
18
+ label: 'Cancel',
19
+ description: '',
20
+ },
21
+ ];
22
+
23
+ export async function selectInstallTarget({
24
+ cwd = process.cwd(),
25
+ input = process.stdin,
26
+ output = process.stdout,
27
+ } = {}) {
28
+ let selectedIndex = await hasCodexDir(cwd) ? 0 : 1;
29
+
30
+ readline.emitKeypressEvents(input);
31
+ const canSetRawMode = typeof input.setRawMode === 'function';
32
+ if (canSetRawMode) {
33
+ input.setRawMode(true);
34
+ }
35
+ input.resume();
36
+ render(output, selectedIndex, false);
37
+
38
+ return new Promise((resolve) => {
39
+ const finish = (value) => {
40
+ input.off('keypress', onKeypress);
41
+ if (canSetRawMode) {
42
+ input.setRawMode(false);
43
+ }
44
+ output.write('\n');
45
+ resolve(value);
46
+ };
47
+
48
+ const onKeypress = (_text, key = {}) => {
49
+ if (key.ctrl && key.name === 'c') {
50
+ finish('cancel');
51
+ return;
52
+ }
53
+ if (key.name === 'up') {
54
+ selectedIndex = (selectedIndex + OPTIONS.length - 1) % OPTIONS.length;
55
+ render(output, selectedIndex, true);
56
+ return;
57
+ }
58
+ if (key.name === 'down') {
59
+ selectedIndex = (selectedIndex + 1) % OPTIONS.length;
60
+ render(output, selectedIndex, true);
61
+ return;
62
+ }
63
+ if (key.name === 'return' || key.name === 'enter') {
64
+ finish(OPTIONS[selectedIndex].value);
65
+ }
66
+ };
67
+
68
+ input.on('keypress', onKeypress);
69
+ });
70
+ }
71
+
72
+ async function hasCodexDir(cwd) {
73
+ try {
74
+ const stat = await fs.stat(path.join(cwd, '.codex'));
75
+ return stat.isDirectory();
76
+ } catch {
77
+ return false;
78
+ }
79
+ }
80
+
81
+ function render(output, selectedIndex, rerender) {
82
+ if (rerender) {
83
+ output.write('\x1b[5A\x1b[J');
84
+ }
85
+
86
+ const lines = [
87
+ 'Where should Superharness Codex support be installed?',
88
+ '',
89
+ ...OPTIONS.map((option, index) => {
90
+ const prefix = index === selectedIndex ? '>' : ' ';
91
+ return `${prefix} ${option.label.padEnd(16)}${option.description}`;
92
+ }),
93
+ ];
94
+
95
+ output.write(`${lines.join('\n')}\n`);
96
+ }
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env node
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { installCodexSupport, parseArgs } from './lib/codex-installer.js';
5
+ import { selectInstallTarget } from './lib/interactive-select.js';
6
+
7
+ export async function main(argv = process.argv.slice(2), env = process.env) {
8
+ const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
9
+ const options = parseArgs(argv);
10
+
11
+ if (options.help) {
12
+ process.stdout.write(usage());
13
+ return 0;
14
+ }
15
+
16
+ let mode = options.mode;
17
+ const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY && !env.CI);
18
+ if (!mode) {
19
+ if (!interactive) {
20
+ throw new Error('non-interactive install requires --project or --user');
21
+ }
22
+ mode = await selectInstallTarget();
23
+ if (mode === 'cancel') {
24
+ return 0;
25
+ }
26
+ }
27
+
28
+ const result = await installCodexSupport({ mode, packageRoot });
29
+ process.stdout.write(successMessage(result));
30
+ return 0;
31
+ }
32
+
33
+ export function usage() {
34
+ return `Usage: superharness [--project|--user] [--force]
35
+
36
+ Install Superharness Codex support into project or user .agents and .codex directories.
37
+
38
+ Options:
39
+ --project Install into the current repository's .agents and .codex directories
40
+ --user Install into ~/.agents and ~/.codex for all Codex projects
41
+ --global Alias for --user
42
+ --force Backup and overwrite existing installed assets
43
+ -h, --help Show this help
44
+ `;
45
+ }
46
+
47
+ function successMessage({ mode, pluginRoot, commandsRoot, skillsRoot, backups }) {
48
+ const scope = mode === 'project' ? 'project' : 'user';
49
+ const backupLine = backups.length > 0
50
+ ? `Backups created:\n${backups.map((backup) => ` - ${backup}`).join('\n')}\n`
51
+ : '';
52
+
53
+ return `Superharness Codex support installed for ${scope} scope.
54
+ Skills: ${skillsRoot}
55
+ Plugin: ${pluginRoot}
56
+ Commands: ${commandsRoot}
57
+ ${backupLine}`;
58
+ }
59
+
60
+ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
61
+ main().then((code) => {
62
+ process.exitCode = code;
63
+ }).catch((error) => {
64
+ process.stderr.write(`${error.message}\n`);
65
+ process.exitCode = 1;
66
+ });
67
+ }
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@shirlytaylor73/superharness",
3
+ "version": "1.5.0",
4
+ "engines": {
5
+ "node": ">=20.0.0"
6
+ },
7
+ "description": "Programmatic workflow harness for AI coding agents with skills, hooks, MCP state management, and cross-client support.",
8
+ "type": "module",
9
+ "bin": {
10
+ "superharness": "bin/superharness.js"
11
+ },
12
+ "files": [
13
+ "bin/",
14
+ "plugins/superharness/"
15
+ ],
16
+ "scripts": {
17
+ "test:installer": "node --test tests/codex-installer/*.test.mjs"
18
+ },
19
+ "keywords": [
20
+ "superharness",
21
+ "claude-code",
22
+ "codex",
23
+ "gemini",
24
+ "kiro",
25
+ "deerflow",
26
+ "trae",
27
+ "antigravity",
28
+ "openclaw",
29
+ "windsurf",
30
+ "aider",
31
+ "qwen",
32
+ "vscode",
33
+ "copilot",
34
+ "hermes",
35
+ "hermes-agent",
36
+ "claw-code",
37
+ "ai-coding",
38
+ "skills",
39
+ "chinese",
40
+ "中文",
41
+ "tdd",
42
+ "debugging",
43
+ "code-review"
44
+ ],
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "git+https://github.com/ShirlyTaylor73/superharness.git"
48
+ },
49
+ "homepage": "https://github.com/ShirlyTaylor73/superharness",
50
+ "author": "ShirlyTaylor73",
51
+ "license": "MIT"
52
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "superharness",
3
+ "description": "Programmatic workflow harness for AI coding agents: state-machine driven skills, hooks, and MCP state management.",
4
+ "version": "1.5.0",
5
+ "author": {
6
+ "name": "ShirlyTaylor73",
7
+ "url": "https://github.com/ShirlyTaylor73"
8
+ },
9
+ "homepage": "https://github.com/ShirlyTaylor73/superharness",
10
+ "repository": "https://github.com/ShirlyTaylor73/superharness",
11
+ "license": "MIT",
12
+ "keywords": ["skills", "tdd", "debugging", "chinese", "中文", "best-practices", "workflows"],
13
+ "mcpServers": {
14
+ "superharness-workflow-state": {
15
+ "command": "node",
16
+ "args": ["${CLAUDE_PLUGIN_ROOT}/workflow-state-server/bootstrap.js"]
17
+ }
18
+ }
19
+ }