@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.
- package/LICENSE +21 -0
- package/README.md +202 -0
- package/bin/lib/codex-installer.js +228 -0
- package/bin/lib/interactive-select.js +96 -0
- package/bin/superharness.js +67 -0
- package/package.json +52 -0
- package/plugins/superharness/.claude-plugin/plugin.json +19 -0
- package/plugins/superharness/.codex-plugin/plugin.json +31 -0
- package/plugins/superharness/.mcp.json +9 -0
- package/plugins/superharness/CODE_OF_CONDUCT.md +79 -0
- package/plugins/superharness/LICENSE +21 -0
- package/plugins/superharness/README.md +57 -0
- package/plugins/superharness/agents/code-reviewer.md +48 -0
- package/plugins/superharness/archived-skills/using-superpowers/SKILL.md +140 -0
- package/plugins/superharness/archived-skills/using-superpowers/references/codex-tools.md +25 -0
- package/plugins/superharness/archived-skills/using-superpowers/references/copilot-tools.md +52 -0
- package/plugins/superharness/archived-skills/using-superpowers/references/gemini-tools.md +33 -0
- package/plugins/superharness/archived-skills/using-superpowers/references/hermes-tools.md +44 -0
- package/plugins/superharness/commands/free.md +6 -0
- package/plugins/superharness/commands/rollback.md +30 -0
- package/plugins/superharness/commands-codex/free.md +29 -0
- package/plugins/superharness/commands-codex/rollback.md +33 -0
- package/plugins/superharness/hooks/hooks-codex.json +50 -0
- package/plugins/superharness/hooks/hooks.json +50 -0
- package/plugins/superharness/hooks/lib/free-mode-check.mjs +27 -0
- package/plugins/superharness/hooks/run-hook.cmd +58 -0
- package/plugins/superharness/hooks/workflow-context +4 -0
- package/plugins/superharness/hooks/workflow-context.mjs +184 -0
- package/plugins/superharness/hooks/workflow-post-transition +4 -0
- package/plugins/superharness/hooks/workflow-post-transition.mjs +89 -0
- package/plugins/superharness/hooks/workflow-pre-tool-use +4 -0
- package/plugins/superharness/hooks/workflow-pre-tool-use.mjs +97 -0
- package/plugins/superharness/hooks/workflow-stop +4 -0
- package/plugins/superharness/hooks/workflow-stop.mjs +136 -0
- package/plugins/superharness/scripts/rollback.mjs +86 -0
- package/plugins/superharness/scripts/set-free-mode.mjs +77 -0
- package/plugins/superharness/skills/brainstorming/SKILL.md +182 -0
- package/plugins/superharness/skills/brainstorming/scripts/frame-template.html +214 -0
- package/plugins/superharness/skills/brainstorming/scripts/helper.js +88 -0
- package/plugins/superharness/skills/brainstorming/scripts/server.cjs +338 -0
- package/plugins/superharness/skills/brainstorming/scripts/start-server.sh +153 -0
- package/plugins/superharness/skills/brainstorming/scripts/stop-server.sh +55 -0
- package/plugins/superharness/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
- package/plugins/superharness/skills/brainstorming/visual-companion.md +286 -0
- package/plugins/superharness/skills/chinese-code-review/SKILL.md +277 -0
- package/plugins/superharness/skills/chinese-commit-conventions/SKILL.md +364 -0
- package/plugins/superharness/skills/chinese-documentation/SKILL.md +448 -0
- package/plugins/superharness/skills/chinese-git-workflow/SKILL.md +547 -0
- package/plugins/superharness/skills/dispatching-parallel-agents/SKILL.md +186 -0
- package/plugins/superharness/skills/exploration/SKILL.md +197 -0
- package/plugins/superharness/skills/finishing/SKILL.md +200 -0
- package/plugins/superharness/skills/intake/SKILL.md +134 -0
- package/plugins/superharness/skills/mcp-builder/SKILL.md +255 -0
- package/plugins/superharness/skills/parallel-execution/SKILL.md +368 -0
- package/plugins/superharness/skills/parallel-execution/implementer-prompt.md +144 -0
- package/plugins/superharness/skills/parallel-execution/spec-reviewer-prompt.md +84 -0
- package/plugins/superharness/skills/parallel-execution/wave-final-manual-qa-prompt.md +61 -0
- package/plugins/superharness/skills/parallel-execution/wave-final-quality-prompt.md +59 -0
- package/plugins/superharness/skills/parallel-execution/wave-final-scope-fidelity-prompt.md +69 -0
- package/plugins/superharness/skills/parallel-execution/wave-final-spec-prompt.md +56 -0
- package/plugins/superharness/skills/planning/SKILL.md +265 -0
- package/plugins/superharness/skills/planning/plan-document-reviewer-prompt.md +80 -0
- package/plugins/superharness/skills/receiving-code-review/SKILL.md +213 -0
- package/plugins/superharness/skills/requesting-code-review/SKILL.md +107 -0
- package/plugins/superharness/skills/requesting-code-review/code-reviewer.md +146 -0
- package/plugins/superharness/skills/serial-execution/SKILL.md +183 -0
- package/plugins/superharness/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/plugins/superharness/skills/systematic-debugging/SKILL.md +320 -0
- package/plugins/superharness/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/plugins/superharness/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/plugins/superharness/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/plugins/superharness/skills/systematic-debugging/find-polluter.sh +63 -0
- package/plugins/superharness/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/plugins/superharness/skills/systematic-debugging/test-academic.md +14 -0
- package/plugins/superharness/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/plugins/superharness/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/plugins/superharness/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/plugins/superharness/skills/test-driven-development/SKILL.md +371 -0
- package/plugins/superharness/skills/test-driven-development/testing-anti-patterns.md +299 -0
- package/plugins/superharness/skills/trivial/SKILL.md +118 -0
- package/plugins/superharness/skills/using-git-worktrees/SKILL.md +218 -0
- package/plugins/superharness/skills/verification/SKILL.md +139 -0
- package/plugins/superharness/skills/workflow-runner/SKILL.md +172 -0
- package/plugins/superharness/skills/writing-skills/SKILL.md +655 -0
- package/plugins/superharness/skills/writing-skills/anthropic-best-practices.md +1149 -0
- package/plugins/superharness/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/plugins/superharness/skills/writing-skills/graphviz-conventions.dot +172 -0
- package/plugins/superharness/skills/writing-skills/persuasion-principles.md +187 -0
- package/plugins/superharness/skills/writing-skills/render-graphs.js +168 -0
- package/plugins/superharness/skills/writing-skills/testing-skills-with-subagents.md +385 -0
- package/plugins/superharness/workflow/default-workflow.yaml +84 -0
- package/plugins/superharness/workflow-state-server/bootstrap.js +44 -0
- package/plugins/superharness/workflow-state-server/package-lock.json +2853 -0
- package/plugins/superharness/workflow-state-server/package.json +22 -0
- package/plugins/superharness/workflow-state-server/render-context.js +124 -0
- package/plugins/superharness/workflow-state-server/schema.sql +39 -0
- package/plugins/superharness/workflow-state-server/server.js +290 -0
- package/plugins/superharness/workflow-state-server/state.js +424 -0
- package/plugins/superharness/workflow-state-server/validate-workflow.js +165 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
|
|
8
|
+
async function readStdinJson() {
|
|
9
|
+
const chunks = [];
|
|
10
|
+
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
11
|
+
const raw = Buffer.concat(chunks).toString('utf8').trim();
|
|
12
|
+
return raw ? JSON.parse(raw) : {};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function resolvePluginRoot() {
|
|
16
|
+
return path.resolve(
|
|
17
|
+
process.env.CLAUDE_PLUGIN_ROOT
|
|
18
|
+
|| process.env.CODEX_PLUGIN_ROOT
|
|
19
|
+
|| process.env.PLUGIN_ROOT
|
|
20
|
+
|| path.join(__dirname, '..'),
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function loadInstalledSkills(pluginRoot) {
|
|
25
|
+
const skillsDir = path.join(pluginRoot, 'skills');
|
|
26
|
+
if (!fs.existsSync(skillsDir)) return new Set();
|
|
27
|
+
return new Set(
|
|
28
|
+
fs.readdirSync(skillsDir, { withFileTypes: true })
|
|
29
|
+
.filter((e) => e.isDirectory())
|
|
30
|
+
.filter((e) => fs.existsSync(path.join(skillsDir, e.name, 'SKILL.md')))
|
|
31
|
+
.map((e) => e.name),
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const MAX_BLOCKS = 3;
|
|
36
|
+
|
|
37
|
+
function blockReason({ state, allowedTransitions }) {
|
|
38
|
+
return [
|
|
39
|
+
`[Superharness Workflow] 本轮检测到未完成的状态切换。`,
|
|
40
|
+
``,
|
|
41
|
+
`- 当前 state: ${state}`,
|
|
42
|
+
`- 该 state 要求 agent 在每轮结束前显式调用 transition_state`,
|
|
43
|
+
``,
|
|
44
|
+
`请用 AskUserQuestion 让用户在以下类别的选项中决定本轮去向:`,
|
|
45
|
+
``,
|
|
46
|
+
`1. **继续/调整工作方向** — 1~2 个选项(agent 根据本轮上下文拟)。用户选了 → 继续干。`,
|
|
47
|
+
`2. **切到允许的下一态** — 列出 [${allowedTransitions.join(', ')}] 里合适的目标。用户选了 → 调 transition_state。`,
|
|
48
|
+
`3. **终止本轮,让用户先看结果** — 用户选了 → 调 release_stop_block(reason="<原话>") 然后结束输出。`,
|
|
49
|
+
``,
|
|
50
|
+
`(AskUserQuestion 工具会自动提供 "Other" 开放输入。)`,
|
|
51
|
+
].join('\n');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function main() {
|
|
55
|
+
let store;
|
|
56
|
+
try {
|
|
57
|
+
const input = await readStdinJson();
|
|
58
|
+
const workspaceRoot = path.resolve(input.cwd || process.cwd());
|
|
59
|
+
const pluginRoot = resolvePluginRoot();
|
|
60
|
+
const workflowStateDir = path.join(pluginRoot, 'workflow-state-server');
|
|
61
|
+
|
|
62
|
+
// Free-mode check: skip stop-block entirely
|
|
63
|
+
const { isFreeMode } = await import(pathToFileURL(path.join(pluginRoot, 'hooks', 'lib', 'free-mode-check.mjs')).href);
|
|
64
|
+
if (await isFreeMode({ pluginRoot, workspaceRoot })) {
|
|
65
|
+
process.stdout.write(JSON.stringify({}) + '\n');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const { loadWorkflowConfig, buildWorkflowGraph } = await import(pathToFileURL(path.join(workflowStateDir, 'validate-workflow.js')).href);
|
|
70
|
+
const { openWorkflowStateStore, getWorkflowState, getTurn, incrementBlockCount, resolveWorkflowDbPath } =
|
|
71
|
+
await import(pathToFileURL(path.join(workflowStateDir, 'state.js')).href);
|
|
72
|
+
|
|
73
|
+
const config = loadWorkflowConfig({ pluginRoot, workspaceRoot });
|
|
74
|
+
const installedSkills = loadInstalledSkills(pluginRoot);
|
|
75
|
+
const graph = buildWorkflowGraph(config, { installedSkills });
|
|
76
|
+
|
|
77
|
+
store = openWorkflowStateStore({
|
|
78
|
+
mode: process.env.SUPERHARNESS_WORKFLOW_STATE_MODE,
|
|
79
|
+
dbPath: process.env.SUPERHARNESS_WORKFLOW_STATE_DB || resolveWorkflowDbPath({ workspaceRoot }),
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const stateInfo = getWorkflowState(store, { workspaceRoot, workflowGraph: graph });
|
|
83
|
+
const turn = getTurn(store, { workspaceRoot });
|
|
84
|
+
|
|
85
|
+
if (!turn) {
|
|
86
|
+
process.stdout.write(JSON.stringify({}) + '\n');
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// 1. 本轮 turn_id 内有 source='agent-tool' 的 transition → 放行
|
|
91
|
+
const hasTransition = store.db.prepare(
|
|
92
|
+
"SELECT 1 FROM workflow_transition_log WHERE turn_id = ? AND source = 'agent-tool' LIMIT 1"
|
|
93
|
+
).get(turn.turn_id);
|
|
94
|
+
if (hasTransition) {
|
|
95
|
+
process.stdout.write(JSON.stringify({}) + '\n');
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 2. 用户已授权 escape → 放行
|
|
100
|
+
if (turn.stop_block_released === 1) {
|
|
101
|
+
process.stdout.write(JSON.stringify({}) + '\n');
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 3. silent_stop_allowed=true → 放行
|
|
106
|
+
const stateNode = graph.states.get(stateInfo.state);
|
|
107
|
+
if (stateNode?.silent_stop_allowed) {
|
|
108
|
+
process.stdout.write(JSON.stringify({}) + '\n');
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 4. block_count < 3 → 拦截
|
|
113
|
+
if (turn.block_count < MAX_BLOCKS) {
|
|
114
|
+
incrementBlockCount(store, { workspaceRoot });
|
|
115
|
+
const reason = blockReason({
|
|
116
|
+
state: stateInfo.state,
|
|
117
|
+
allowedTransitions: stateNode?.next ?? [],
|
|
118
|
+
});
|
|
119
|
+
process.stdout.write(JSON.stringify({ decision: 'block', reason }) + '\n');
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 5. block_count >= 3 → 兜底逃生
|
|
124
|
+
console.error(`[superharness-stop] escape after ${MAX_BLOCKS} blocks: ws=${workspaceRoot} state=${stateInfo.state}`);
|
|
125
|
+
process.stdout.write(JSON.stringify({}) + '\n');
|
|
126
|
+
} catch {
|
|
127
|
+
// fail-open
|
|
128
|
+
process.stdout.write(JSON.stringify({}) + '\n');
|
|
129
|
+
} finally {
|
|
130
|
+
store?.close?.();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
135
|
+
await main();
|
|
136
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
5
|
+
|
|
6
|
+
const [, , workspaceRootArg, toStateArg, reasonArg] = process.argv;
|
|
7
|
+
|
|
8
|
+
if (!workspaceRootArg || !toStateArg || !reasonArg) {
|
|
9
|
+
console.error('usage: rollback.mjs <workspaceRoot> <to_state> <reason>');
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const workspaceRoot = path.resolve(workspaceRootArg);
|
|
14
|
+
const dbPath = path.join(workspaceRoot, '.superharness', 'workflow-state.db');
|
|
15
|
+
|
|
16
|
+
if (!fs.existsSync(dbPath)) {
|
|
17
|
+
console.error('workspace 未初始化,无 state 可回退');
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (toStateArg === 'systematic_debugging') {
|
|
22
|
+
console.error('rollback 不支持目标 systematic_debugging(systematic_debugging 是 preemptive 分支,需 previous_state 字段;rollback 只回主流程态)');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
27
|
+
const pluginRoot = path.resolve(scriptDir, '..');
|
|
28
|
+
const stateModule = await import(pathToFileURL(path.join(pluginRoot, 'workflow-state-server', 'state.js')).href);
|
|
29
|
+
const validateModule = await import(pathToFileURL(path.join(pluginRoot, 'workflow-state-server', 'validate-workflow.js')).href);
|
|
30
|
+
|
|
31
|
+
const store = stateModule.openWorkflowStateStore({ dbPath });
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
// 1. 校验 to_state 是否在历史中曾以 to_state 出现过
|
|
35
|
+
const seen = store.prepare(
|
|
36
|
+
'SELECT 1 FROM workflow_transition_log WHERE workspace_id = ? AND to_state = ? LIMIT 1'
|
|
37
|
+
).get(workspaceRoot, toStateArg);
|
|
38
|
+
if (!seen) {
|
|
39
|
+
const choices = store.prepare(
|
|
40
|
+
'SELECT DISTINCT to_state FROM workflow_transition_log WHERE workspace_id = ? ORDER BY id DESC LIMIT 10'
|
|
41
|
+
).all(workspaceRoot).map((r) => r.to_state);
|
|
42
|
+
console.error(`state '${toStateArg}' 未在历史中出现过;可用:${choices.join(', ')}`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 2. 取当前 state 用于 stdout 和 log
|
|
47
|
+
const cur = store.prepare('SELECT state FROM workflow_state WHERE workspace_id = ?').get(workspaceRoot);
|
|
48
|
+
const fromState = cur?.state ?? null;
|
|
49
|
+
|
|
50
|
+
// 3. 加载 workflow graph 解析目标 state 的 active_skill
|
|
51
|
+
const skillsDir = path.join(pluginRoot, 'skills');
|
|
52
|
+
const installedSkills = new Set(
|
|
53
|
+
fs.readdirSync(skillsDir, { withFileTypes: true })
|
|
54
|
+
.filter((e) => e.isDirectory())
|
|
55
|
+
.map((e) => e.name),
|
|
56
|
+
);
|
|
57
|
+
const config = validateModule.loadWorkflowConfig({ pluginRoot, workspaceRoot });
|
|
58
|
+
const graph = validateModule.buildWorkflowGraph(config, { installedSkills });
|
|
59
|
+
const activeSkill = graph.states.get(toStateArg)?.skill ?? null;
|
|
60
|
+
|
|
61
|
+
const now = Date.now();
|
|
62
|
+
|
|
63
|
+
// 4. 写 workflow_state
|
|
64
|
+
store.prepare(`
|
|
65
|
+
UPDATE workflow_state
|
|
66
|
+
SET state = ?, status = 'active', previous_state = NULL, active_skill = ?, updated_at = ?
|
|
67
|
+
WHERE workspace_id = ?
|
|
68
|
+
`).run(toStateArg, activeSkill, now, workspaceRoot);
|
|
69
|
+
|
|
70
|
+
// 5. 写 transition_log(source = user-reset)
|
|
71
|
+
store.prepare(`
|
|
72
|
+
INSERT INTO workflow_transition_log
|
|
73
|
+
(workspace_id, from_state, to_state, previous_state, reason, source, turn_id, created_at)
|
|
74
|
+
VALUES (?, ?, ?, NULL, ?, 'user-reset', NULL, ?)
|
|
75
|
+
`).run(workspaceRoot, fromState, toStateArg, reasonArg, now);
|
|
76
|
+
|
|
77
|
+
// 6. 清零 turn 状态
|
|
78
|
+
// 注意:workflow_turn.turn_id 是 NOT NULL,不能 SET turn_id = NULL,所以 DELETE。
|
|
79
|
+
// 下一次 UserPromptSubmit 时 createTurn 会重新 INSERT OR REPLACE。
|
|
80
|
+
store.prepare('DELETE FROM workflow_turn WHERE workspace_id = ?').run(workspaceRoot);
|
|
81
|
+
|
|
82
|
+
console.log(`已从 ${fromState} 回到 ${toStateArg}`);
|
|
83
|
+
process.exit(0);
|
|
84
|
+
} finally {
|
|
85
|
+
store.close();
|
|
86
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
5
|
+
|
|
6
|
+
const [, , workspaceRootArg, actionArg] = process.argv;
|
|
7
|
+
const action = actionArg || 'status';
|
|
8
|
+
|
|
9
|
+
if (!workspaceRootArg || !['on', 'off', 'status'].includes(action)) {
|
|
10
|
+
console.error('usage: set-free-mode.mjs <workspaceRoot> [on|off|status]');
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const workspaceRoot = path.resolve(workspaceRootArg);
|
|
15
|
+
const dbPath = path.join(workspaceRoot, '.superharness', 'workflow-state.db');
|
|
16
|
+
|
|
17
|
+
if (!fs.existsSync(dbPath)) {
|
|
18
|
+
console.error('workspace 未初始化');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
const pluginRoot = path.resolve(__dirname, '..');
|
|
24
|
+
const stateModule = await import(pathToFileURL(path.join(pluginRoot, 'workflow-state-server', 'state.js')).href);
|
|
25
|
+
|
|
26
|
+
const store = stateModule.openWorkflowStateStore({ dbPath });
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const cur = store.prepare('SELECT state, free_mode, free_started_at FROM workflow_state WHERE workspace_id = ?').get(workspaceRoot);
|
|
30
|
+
if (!cur) {
|
|
31
|
+
console.error('workspace state 不存在');
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (action === 'status') {
|
|
36
|
+
if (cur.free_mode === 1) {
|
|
37
|
+
console.log(`当前在 free mode(自 ${new Date(cur.free_started_at).toISOString()})`);
|
|
38
|
+
} else {
|
|
39
|
+
console.log('当前不在 free mode');
|
|
40
|
+
}
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (action === 'on') {
|
|
45
|
+
if (cur.free_mode === 1) {
|
|
46
|
+
console.log(`已在 free mode(自 ${new Date(cur.free_started_at).toISOString()})`);
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
const now = Date.now();
|
|
50
|
+
store.prepare(`UPDATE workflow_state SET free_mode = 1, free_started_at = ?, updated_at = ? WHERE workspace_id = ?`).run(now, now, workspaceRoot);
|
|
51
|
+
store.prepare(`
|
|
52
|
+
INSERT INTO workflow_transition_log
|
|
53
|
+
(workspace_id, from_state, to_state, previous_state, reason, source, turn_id, created_at)
|
|
54
|
+
VALUES (?, ?, ?, NULL, '[free-on] 用户输入 /free on', 'user-reset', NULL, ?)
|
|
55
|
+
`).run(workspaceRoot, cur.state, cur.state, now);
|
|
56
|
+
console.log('已进入 free mode;workflow context 注入已暂停');
|
|
57
|
+
process.exit(0);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (action === 'off') {
|
|
61
|
+
if (cur.free_mode === 0) {
|
|
62
|
+
console.log('未在 free mode,无需操作');
|
|
63
|
+
process.exit(0);
|
|
64
|
+
}
|
|
65
|
+
const now = Date.now();
|
|
66
|
+
store.prepare(`UPDATE workflow_state SET free_mode = 0, free_started_at = NULL, updated_at = ? WHERE workspace_id = ?`).run(now, workspaceRoot);
|
|
67
|
+
store.prepare(`
|
|
68
|
+
INSERT INTO workflow_transition_log
|
|
69
|
+
(workspace_id, from_state, to_state, previous_state, reason, source, turn_id, created_at)
|
|
70
|
+
VALUES (?, ?, ?, NULL, '[free-off] 用户输入 /free off', 'user-reset', NULL, ?)
|
|
71
|
+
`).run(workspaceRoot, cur.state, cur.state, now);
|
|
72
|
+
console.log('已退出 free mode;workflow 恢复');
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
} finally {
|
|
76
|
+
store.close();
|
|
77
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: brainstorming
|
|
3
|
+
description: 在任何创意工作(构建功能、设计组件、新增能力或改动行为)之前必须使用——通过持续盘问用户的意图、需求和约束,按依赖顺序逐枝拆解决策树,达成共识后再动手实现。也可在用户希望被"质询/grill"打磨方案、想做"头脑风暴"或显式说出 "brainstorm" / "grill me" 时触发。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 头脑风暴:将想法转化为设计
|
|
7
|
+
|
|
8
|
+
通过有节奏的**盘问**把想法转化为完整的设计和规格说明。盘问不是闲聊——它是对设计树的分层遍历:按依赖顺序先确认根节点,再确认中层,最后确认叶子,每个层级都要逼出明确答案。
|
|
9
|
+
|
|
10
|
+
首先了解当前项目的上下文(**能查代码就别问**),然后用 ask user question tool / 用户提问工具按同一决策深度批量收集意见。一旦你理解了要构建的内容,就展示设计方案并获得用户批准。
|
|
11
|
+
|
|
12
|
+
<HARD-GATE>
|
|
13
|
+
在你展示设计方案并获得用户批准之前,不要调用任何实现技能、编写任何代码、搭建任何项目或采取任何实现行动。这适用于所有项目,无论看起来多简单。
|
|
14
|
+
</HARD-GATE>
|
|
15
|
+
|
|
16
|
+
## 反模式:"这个太简单了,不需要设计"
|
|
17
|
+
|
|
18
|
+
每个项目都要经过这个流程。一个待办事项列表、一个单函数工具、一个配置变更——全都需要。"简单"的项目恰恰是未经检验的假设造成最多浪费的地方。设计可以很简短(对于真正简单的项目几句话就够了),但你必须展示出来并获得批准。
|
|
19
|
+
|
|
20
|
+
## 盘问风格:不留情面,但不粗暴
|
|
21
|
+
|
|
22
|
+
把头脑风暴当作"不留情面的盘问"来跑——直到对设计的每一处细节达成共识。这不是态度问题,是对模糊性零容忍。
|
|
23
|
+
|
|
24
|
+
- **分层盘问决策树:** 设计是一棵树。按依赖顺序遍历——先根节点(目的、约束、范围),再中层(架构选择),最后叶子(实现细节)。**父节点未定,不下钻子节点。**
|
|
25
|
+
- **同深度批量提问:** 使用 ask user question tool / 用户提问工具,在每一轮工具调用中尽量合并当前同一深度的多个决策节点。不要退回普通多轮对话逐个问。
|
|
26
|
+
- **每问必给选项和推荐:** 每个问题节点都提供多个互斥选项 / 方案,以及 1 个开放填写选项(让用户自己填)。从选项中标出 1 个推荐选项,并给出推荐理由。
|
|
27
|
+
- **代码库优先:** 如果某个问题能从代码、文档、commit history 里读出答案,**先去查,别问**。问用户只用来获取代码里没有的信息——意图、偏好、约束。
|
|
28
|
+
- **反馈后重算决策树:** 每轮得到用户意见后,先判断是否需要确认、修剪、重排或细化决策树,再决定下一轮是追问当前深度还是进入下一深度。
|
|
29
|
+
- **追问到底:** 遇到以下情况必须进入下一轮用户提问工具调用继续确认:用户给出选项外且影响决策的回答;回答含糊、矛盾或无法映射到某个选项;用户补充的新目标、新约束或新信息会改变父节点或架构决策。追问要聚焦受影响的节点,不要退回普通多轮消息逐问。
|
|
30
|
+
|
|
31
|
+
## 检查清单
|
|
32
|
+
|
|
33
|
+
你必须为以下每个条目创建任务,并按顺序完成:
|
|
34
|
+
|
|
35
|
+
1. **探索项目上下文** — 检查文件、文档、最近的 commit。能从代码读出的事实就不要问用户。
|
|
36
|
+
2. **提供视觉伴侣**(如果主题涉及视觉问题)— 这是一条独立的消息,不要与澄清问题合并。参见下方的"视觉伴侣"部分。
|
|
37
|
+
3. **分层批量盘问决策树** — 用用户提问工具批量询问同一深度的多个节点,每问提供多个选项、1 个开放填写选项和 1 个推荐选项,按依赖顺序从根到叶
|
|
38
|
+
4. **提出 2-3 种方案** — 附带权衡分析和你的推荐
|
|
39
|
+
5. **展示设计** — 按复杂度分节展示,每节展示后获得用户批准
|
|
40
|
+
6. **编写设计文档** — 保存到 `docs/superharness/specs/YYYY-MM-DD-<topic>-design.md` 并 commit
|
|
41
|
+
7. **规格自检** — 快速内联检查占位符、矛盾、模糊性、范围(详见下方)
|
|
42
|
+
8. **用户审查书面规格** — 在继续之前请用户审查规格文件
|
|
43
|
+
9. **过渡到实现** — transition 到 planning 状态创建实现计划
|
|
44
|
+
|
|
45
|
+
## 流程图
|
|
46
|
+
|
|
47
|
+
```dot
|
|
48
|
+
digraph brainstorming {
|
|
49
|
+
"探索项目上下文\n(能查代码就别问)" [shape=box];
|
|
50
|
+
"有视觉相关问题?" [shape=diamond];
|
|
51
|
+
"提供视觉伴侣\n(独立消息,不含其他内容)" [shape=box];
|
|
52
|
+
"分层批量盘问决策树\n(同深度合并提问)" [shape=box];
|
|
53
|
+
"提出 2-3 种方案" [shape=box];
|
|
54
|
+
"分节展示设计" [shape=box];
|
|
55
|
+
"用户批准设计?" [shape=diamond];
|
|
56
|
+
"编写设计文档" [shape=box];
|
|
57
|
+
"规格自检\n(内联修复)" [shape=box];
|
|
58
|
+
"用户审查规格?" [shape=diamond];
|
|
59
|
+
"transition 到 planning" [shape=doublecircle];
|
|
60
|
+
|
|
61
|
+
"探索项目上下文\n(能查代码就别问)" -> "有视觉相关问题?";
|
|
62
|
+
"有视觉相关问题?" -> "提供视觉伴侣\n(独立消息,不含其他内容)" [label="是"];
|
|
63
|
+
"有视觉相关问题?" -> "分层批量盘问决策树\n(同深度合并提问)" [label="否"];
|
|
64
|
+
"提供视觉伴侣\n(独立消息,不含其他内容)" -> "分层批量盘问决策树\n(同深度合并提问)";
|
|
65
|
+
"分层批量盘问决策树\n(同深度合并提问)" -> "提出 2-3 种方案";
|
|
66
|
+
"提出 2-3 种方案" -> "分节展示设计";
|
|
67
|
+
"分节展示设计" -> "用户批准设计?";
|
|
68
|
+
"用户批准设计?" -> "分节展示设计" [label="否,修改"];
|
|
69
|
+
"用户批准设计?" -> "编写设计文档" [label="是"];
|
|
70
|
+
"编写设计文档" -> "规格自检\n(内联修复)";
|
|
71
|
+
"规格自检\n(内联修复)" -> "用户审查规格?";
|
|
72
|
+
"用户审查规格?" -> "编写设计文档" [label="要求修改"];
|
|
73
|
+
"用户审查规格?" -> "transition 到 planning" [label="批准"];
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**终止状态是 transition 到 planning。** 不要调用 frontend-design、mcp-builder 或任何其他实现技能。头脑风暴之后你唯一要进入的状态是 planning。
|
|
78
|
+
|
|
79
|
+
## 流程详述
|
|
80
|
+
|
|
81
|
+
**理解想法(决策树式盘问):**
|
|
82
|
+
|
|
83
|
+
- **先查代码,再问人:** 在提任何问题之前,先看当前项目状态(文件、文档、最近的 commit)。能从代码读出的事实,绝不浪费用户的时间去问。
|
|
84
|
+
- 在提出详细问题之前,先评估范围:如果需求描述了多个独立子系统(例如"构建一个包含聊天、文件存储、计费和分析的平台"),立即指出这一点。不要花时间用问题去细化一个需要先拆分的项目。
|
|
85
|
+
- 如果项目规模过大,单个规格说明无法覆盖,帮助用户分解为子项目:有哪些独立的部分,它们之间有什么关系,应该按什么顺序构建?然后通过正常的设计流程进行第一个子项目的头脑风暴。每个子项目都有自己的规格 → 计划 → 实现周期。
|
|
86
|
+
- 对于范围适当的项目,**按依赖顺序分层批量盘问决策树**:根节点(目的 / 约束 / 成功标准)→ 中层(架构选择)→ 叶子(实现细节)。父节点未定,不下钻子节点。
|
|
87
|
+
- 使用 ask user question tool / 用户提问工具,每轮尽量合并当前同一深度的多个问题;批量大小由可用工具能力和用户负担决定,不写死数量。
|
|
88
|
+
- 每个问题节点都**提供多个互斥选项 / 方案、1 个开放填写选项(让用户自己填)、1 个推荐选项和推荐理由**。不要只问"是 / 否 / 换一个"。
|
|
89
|
+
- 每轮回答后,先评估用户意见是否改变决策树:确认、修剪、重排或细化节点;下一轮再决定追问当前深度还是进入下一深度。
|
|
90
|
+
- 尽量使用选择题。每轮回答后先把用户意见映射到已给选项或开放选项;若出现选项外且影响决策的答案、无法决策的含糊 / 矛盾表达,或会改变父节点 / 架构决策的新信息,必须用下一轮工具调用继续确认。
|
|
91
|
+
- 重点理解:目的、约束、成功标准。
|
|
92
|
+
|
|
93
|
+
**探索方案:**
|
|
94
|
+
|
|
95
|
+
- 提出 2-3 种不同的方案及其权衡
|
|
96
|
+
- 以对话的方式展示选项,保留开放填写入口,并附上你的推荐和理由
|
|
97
|
+
- 先展示你推荐的方案并解释原因
|
|
98
|
+
|
|
99
|
+
**展示设计:**
|
|
100
|
+
|
|
101
|
+
- 一旦你认为理解了要构建的内容,就展示设计
|
|
102
|
+
- 每个部分的篇幅与其复杂度匹配:简单的几句话,复杂的最多 200-300 字
|
|
103
|
+
- 每个部分展示后询问是否正确
|
|
104
|
+
- 涵盖:架构、组件、数据流、错误处理、测试
|
|
105
|
+
- 随时准备回头澄清不明确的地方
|
|
106
|
+
|
|
107
|
+
**面向隔离和清晰的设计:**
|
|
108
|
+
|
|
109
|
+
- 将系统拆分为更小的单元,每个单元有一个明确的职责,通过定义良好的接口通信,可以独立理解和测试
|
|
110
|
+
- 对于每个单元,你应该能回答:它做什么,如何使用,它依赖什么?
|
|
111
|
+
- 别人能否不看内部实现就理解一个单元的功能?你能否在不影响调用者的情况下修改内部实现?如果不能,边界需要调整。
|
|
112
|
+
- 更小、边界清晰的单元也更便于你工作——你对能一次放入上下文的代码推理得更好,文件越专注你的编辑越可靠。当文件变大时,这通常意味着它承担了过多职责。
|
|
113
|
+
|
|
114
|
+
**在现有代码库中工作:**
|
|
115
|
+
|
|
116
|
+
- 在提出更改之前先探索现有结构。遵循现有模式。
|
|
117
|
+
- 如果现有代码存在影响当前工作的问题(例如文件过大、边界不清、职责纠缠),在设计中包含有针对性的改进——就像一个优秀的开发者在工作中改进经手的代码一样。
|
|
118
|
+
- 不要提议无关的重构。专注于服务当前目标的事情。
|
|
119
|
+
|
|
120
|
+
## 设计之后
|
|
121
|
+
|
|
122
|
+
**文档:**
|
|
123
|
+
|
|
124
|
+
- 将验证通过的设计(规格说明)写入 `docs/superharness/specs/YYYY-MM-DD-<topic>-design.md`
|
|
125
|
+
- (用户对规格位置的偏好优先于此默认值)
|
|
126
|
+
- 如果可用,使用 elements-of-style:writing-clearly-and-concisely 技能
|
|
127
|
+
- 将设计文档 commit 到 git
|
|
128
|
+
|
|
129
|
+
**规格自检:**
|
|
130
|
+
编写规格文档后,以全新的视角审视它:
|
|
131
|
+
|
|
132
|
+
1. **占位符扫描:** 有没有"待定"、"TODO"、未完成的章节或模糊的需求?修复它们。
|
|
133
|
+
2. **内部一致性:** 各章节之间有矛盾吗?架构和功能描述匹配吗?
|
|
134
|
+
3. **范围检查:** 这是否聚焦到可以用一个实现计划覆盖,还是需要进一步拆分?
|
|
135
|
+
4. **模糊性检查:** 有没有需求可以被两种方式理解?如果有,选择一种并明确写出来。
|
|
136
|
+
|
|
137
|
+
发现问题就直接内联修复。无需重新审查——修好继续推进。
|
|
138
|
+
|
|
139
|
+
**用户审查关卡:**
|
|
140
|
+
规格自检完成后,请用户在继续之前审查书面规格:
|
|
141
|
+
|
|
142
|
+
> "规格已编写并 commit 到 `<path>`。请审查一下,如果在我们开始编写实现计划之前你想做任何修改,请告诉我。"
|
|
143
|
+
|
|
144
|
+
等待用户回复。如果他们要求修改,做出修改并重新运行规格自检。只有在用户批准后才继续。
|
|
145
|
+
|
|
146
|
+
**实现:**
|
|
147
|
+
|
|
148
|
+
- transition 到 planning 状态创建详细的实现计划
|
|
149
|
+
- 不要调用任何其他技能。planning 是下一步。
|
|
150
|
+
|
|
151
|
+
## 核心原则
|
|
152
|
+
|
|
153
|
+
- **每轮同深度批量提问** — 使用用户提问工具,在一轮中尽量合并当前同一深度的多个决策问题
|
|
154
|
+
- **每问必给选项和推荐** — 每个问题节点都提供多个互斥选项 / 方案、1 个开放填写选项、1 个推荐选项和推荐理由
|
|
155
|
+
- **代码库优先** — 能从代码 / 文档 / commit 读出的答案就不要问用户
|
|
156
|
+
- **追问到底** — 对选项外且影响决策的回答、无法映射到选项的含糊 / 矛盾回答、会改变父节点或架构的新信息,必须用下一轮工具调用继续确认
|
|
157
|
+
- **分层推进** — 按依赖顺序遍历决策树,父节点未定不下钻子节点
|
|
158
|
+
- **优先结构化选择** — 用多个清晰选项降低回答成本,同时保留开放填写选项承接意外答案
|
|
159
|
+
- **严格遵循 YAGNI** — 从所有设计中移除不必要的功能
|
|
160
|
+
- **探索替代方案** — 在做决定之前始终提出 2-3 种方案
|
|
161
|
+
- **增量验证** — 展示设计,获得批准后再继续
|
|
162
|
+
- **保持灵活** — 有不明确的地方就回头澄清
|
|
163
|
+
|
|
164
|
+
## 视觉伴侣
|
|
165
|
+
|
|
166
|
+
一个基于浏览器的伴侣工具,用于在头脑风暴过程中展示原型、图表和视觉选项。它是一个工具——不是一种模式。接受伴侣意味着它可用于适合视觉呈现的问题;并不意味着每个问题都要通过浏览器。
|
|
167
|
+
|
|
168
|
+
**提供伴侣:** 当你预计后续问题会涉及视觉内容(原型、布局、图表)时,提供一次以获得同意:
|
|
169
|
+
> "我们接下来讨论的一些内容,如果能在浏览器中展示给你看可能会更直观。我可以在讨论过程中为你制作原型、图表、对比图和其他视觉材料。这个功能还比较新,可能会消耗较多 token。要试试吗?(需要打开一个本地 URL)"
|
|
170
|
+
|
|
171
|
+
**此提议必须是一条独立的消息。** 不要将它与澄清问题、上下文摘要或任何其他内容合并。消息中应该只包含上述提议,没有其他内容。等待用户回复后再继续。如果他们拒绝,继续纯文本的头脑风暴。
|
|
172
|
+
|
|
173
|
+
**逐问题决策:** 即使用户接受了,也要对每个问题单独决定是使用浏览器还是终端。判断标准:**用户看到它是否比读到它更容易理解?**
|
|
174
|
+
|
|
175
|
+
- **使用浏览器** 展示本身就是视觉的内容——原型、线框图、布局对比、架构图、并排视觉设计
|
|
176
|
+
- **使用终端** 展示文本内容——需求问题、概念选择、权衡列表、A/B/C/D 文字选项、范围决策
|
|
177
|
+
|
|
178
|
+
关于 UI 主题的问题不一定是视觉问题。"在这个上下文中个性化是什么意思?"是一个概念问题——使用终端。"哪种向导布局更好?"是一个视觉问题——使用浏览器。
|
|
179
|
+
|
|
180
|
+
如果他们同意使用伴侣,在继续之前阅读详细指南:
|
|
181
|
+
`skills/brainstorming/visual-companion.md`
|
|
182
|
+
|