claude-prism 0.3.2 → 0.4.1
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/README.ko.md +44 -19
- package/README.md +43 -17
- package/bin/cli.mjs +29 -0
- package/hooks/alignment.mjs +94 -0
- package/hooks/commit-guard.mjs +5 -3
- package/hooks/debug-loop.mjs +20 -5
- package/hooks/scope-guard.mjs +32 -8
- package/hooks/test-tracker.mjs +65 -10
- package/hooks/turn-reporter.mjs +70 -0
- package/lib/adapter.mjs +1 -0
- package/lib/config.mjs +21 -2
- package/lib/installer.mjs +119 -9
- package/lib/messages.mjs +60 -0
- package/lib/pipeline.mjs +188 -0
- package/lib/session.mjs +108 -0
- package/package.json +9 -1
- package/templates/commands/claude-prism/checkpoint.md +17 -1
- package/templates/commands/claude-prism/prism.md +19 -6
- package/templates/rules.en.md +33 -3
- package/templates/rules.ja.md +33 -3
- package/templates/rules.ko.md +33 -3
- package/templates/rules.zh.md +33 -3
- package/templates/runners/post-tool.mjs +13 -0
- package/templates/runners/pre-tool.mjs +9 -0
- package/templates/runners/user-prompt.mjs +7 -0
- package/templates/settings.json +10 -19
- package/templates/skills/prism/SKILL.md +19 -6
package/lib/pipeline.mjs
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* claude-prism — Hook Pipeline
|
|
3
|
+
* Runs multiple rules in a single hook invocation for reduced I/O
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFileSync } from 'fs';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { sanitizeId } from './utils.mjs';
|
|
9
|
+
import { loadConfig } from './config.mjs';
|
|
10
|
+
import { getStateDir } from './state.mjs';
|
|
11
|
+
|
|
12
|
+
const TOOL_ACTION_MAP = {
|
|
13
|
+
'Edit': 'edit',
|
|
14
|
+
'Write': 'write',
|
|
15
|
+
'Bash': 'command',
|
|
16
|
+
'Read': 'read',
|
|
17
|
+
'Task': 'subagent',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const EVENT_PHASE_MAP = {
|
|
21
|
+
'PreToolUse': 'pre',
|
|
22
|
+
'PostToolUse': 'post',
|
|
23
|
+
'UserPromptSubmit': 'prompt',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function parseInput() {
|
|
27
|
+
try {
|
|
28
|
+
return JSON.parse(readFileSync(0, 'utf8'));
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function toContext(input, hookEventName) {
|
|
35
|
+
const toolName = input.tool_name || '';
|
|
36
|
+
return {
|
|
37
|
+
action: TOOL_ACTION_MAP[toolName] || toolName.toLowerCase(),
|
|
38
|
+
phase: EVENT_PHASE_MAP[hookEventName] || 'pre',
|
|
39
|
+
filePath: input.tool_input?.file_path || undefined,
|
|
40
|
+
command: input.tool_input?.command || undefined,
|
|
41
|
+
oldString: input.tool_input?.old_string || undefined,
|
|
42
|
+
sessionId: sanitizeId(input.session_id),
|
|
43
|
+
agentId: sanitizeId(input.agent_id || ''),
|
|
44
|
+
stdout: input.tool_response?.stdout ?? undefined,
|
|
45
|
+
stderr: input.tool_response?.stderr ?? undefined,
|
|
46
|
+
interrupted: input.tool_response?.interrupted ?? false,
|
|
47
|
+
userPrompt: input.tool_input?.user_prompt ?? undefined,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function formatOutput(hookEventName, messages) {
|
|
52
|
+
if (messages.length === 0) return null;
|
|
53
|
+
return JSON.stringify({
|
|
54
|
+
hookSpecificOutput: {
|
|
55
|
+
hookEventName,
|
|
56
|
+
additionalContext: messages.join('\n')
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Run a pipeline of rules for a single hook event
|
|
63
|
+
* @param {Array<{name: string, rule: Object}>} rules - Rules with evaluate() methods
|
|
64
|
+
* @param {string} hookEventName - 'PreToolUse' or 'PostToolUse'
|
|
65
|
+
*/
|
|
66
|
+
export function runPipeline(rules, hookEventName) {
|
|
67
|
+
const input = parseInput();
|
|
68
|
+
if (!input) process.exit(0);
|
|
69
|
+
|
|
70
|
+
// Read config ONCE
|
|
71
|
+
const fullConfig = loadConfig(process.cwd());
|
|
72
|
+
|
|
73
|
+
const ctx = toContext(input, hookEventName);
|
|
74
|
+
const stateDir = getStateDir(ctx.sessionId, ctx.agentId);
|
|
75
|
+
|
|
76
|
+
const messages = [];
|
|
77
|
+
let blocked = false;
|
|
78
|
+
let blockMessage = '';
|
|
79
|
+
|
|
80
|
+
for (const { name, rule } of rules) {
|
|
81
|
+
// Build hook-specific config (same as getHookConfig but without re-reading file)
|
|
82
|
+
const hookConfig = fullConfig.hooks?.[name] || { enabled: true };
|
|
83
|
+
hookConfig.language = fullConfig.language || 'en';
|
|
84
|
+
hookConfig.sourceExtensions = fullConfig.sourceExtensions;
|
|
85
|
+
hookConfig.testPatterns = fullConfig.testPatterns;
|
|
86
|
+
|
|
87
|
+
if (!hookConfig.enabled) continue;
|
|
88
|
+
|
|
89
|
+
const result = rule.evaluate(ctx, hookConfig, stateDir);
|
|
90
|
+
|
|
91
|
+
if (result.type === 'block') {
|
|
92
|
+
blocked = true;
|
|
93
|
+
blockMessage = result.message || '🌈 Prism ✋ Action blocked.';
|
|
94
|
+
break; // First block wins, stop pipeline
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (result.message) {
|
|
98
|
+
messages.push(result.message);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (blocked) {
|
|
103
|
+
process.stderr.write(blockMessage);
|
|
104
|
+
process.exit(2);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const output = formatOutput(hookEventName, messages);
|
|
108
|
+
if (output) {
|
|
109
|
+
process.stdout.write(output);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Load custom rules from config and merge with built-in rules
|
|
115
|
+
* @param {Array<{name: string, rule: Object}>} builtInRules
|
|
116
|
+
* @param {string[]} customRulePaths - Paths relative to project root
|
|
117
|
+
* @returns {Promise<Array<{name: string, rule: Object}>>}
|
|
118
|
+
*/
|
|
119
|
+
export async function loadCustomRules(builtInRules, customRulePaths) {
|
|
120
|
+
if (!customRulePaths || customRulePaths.length === 0) return builtInRules;
|
|
121
|
+
|
|
122
|
+
const rules = [...builtInRules];
|
|
123
|
+
for (const rulePath of customRulePaths) {
|
|
124
|
+
try {
|
|
125
|
+
const absPath = join(process.cwd(), rulePath);
|
|
126
|
+
const mod = await import(absPath);
|
|
127
|
+
const rule = mod.default || mod[Object.keys(mod)[0]];
|
|
128
|
+
if (rule && typeof rule.evaluate === 'function') {
|
|
129
|
+
rules.push({ name: rule.name || rulePath, rule });
|
|
130
|
+
}
|
|
131
|
+
} catch {
|
|
132
|
+
// Skip invalid rules silently — don't break the pipeline
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return rules;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Run pipeline with custom rules support (async)
|
|
140
|
+
* @param {Array<{name: string, rule: Object}>} builtInRules
|
|
141
|
+
* @param {string} hookEventName - 'PreToolUse' or 'PostToolUse'
|
|
142
|
+
*/
|
|
143
|
+
export async function runPipelineAsync(builtInRules, hookEventName) {
|
|
144
|
+
const input = parseInput();
|
|
145
|
+
if (!input) process.exit(0);
|
|
146
|
+
|
|
147
|
+
const fullConfig = loadConfig(process.cwd());
|
|
148
|
+
const customRulePaths = fullConfig.customRules || [];
|
|
149
|
+
const rules = await loadCustomRules(builtInRules, customRulePaths);
|
|
150
|
+
|
|
151
|
+
const ctx = toContext(input, hookEventName);
|
|
152
|
+
const stateDir = getStateDir(ctx.sessionId, ctx.agentId);
|
|
153
|
+
|
|
154
|
+
const messages = [];
|
|
155
|
+
let blocked = false;
|
|
156
|
+
let blockMessage = '';
|
|
157
|
+
|
|
158
|
+
for (const { name, rule } of rules) {
|
|
159
|
+
const hookConfig = fullConfig.hooks?.[name] || { enabled: true };
|
|
160
|
+
hookConfig.language = fullConfig.language || 'en';
|
|
161
|
+
hookConfig.sourceExtensions = fullConfig.sourceExtensions;
|
|
162
|
+
hookConfig.testPatterns = fullConfig.testPatterns;
|
|
163
|
+
|
|
164
|
+
if (hookConfig.enabled === false) continue;
|
|
165
|
+
|
|
166
|
+
const result = rule.evaluate(ctx, hookConfig, stateDir);
|
|
167
|
+
|
|
168
|
+
if (result.type === 'block') {
|
|
169
|
+
blocked = true;
|
|
170
|
+
blockMessage = result.message || '🌈 Prism ✋ Action blocked.';
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (result.message) {
|
|
175
|
+
messages.push(result.message);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (blocked) {
|
|
180
|
+
process.stderr.write(blockMessage);
|
|
181
|
+
process.exit(2);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const output = formatOutput(hookEventName, messages);
|
|
185
|
+
if (output) {
|
|
186
|
+
process.stdout.write(output);
|
|
187
|
+
}
|
|
188
|
+
}
|
package/lib/session.mjs
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* claude-prism — Session Event Logger
|
|
3
|
+
* JSONL-based event recording for session analysis
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFileSync, appendFileSync, existsSync, mkdirSync, readdirSync } from 'fs';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { tmpdir } from 'os';
|
|
9
|
+
|
|
10
|
+
const SESSION_ROOT = join(tmpdir(), '.prism', 'sessions');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get session log file path
|
|
14
|
+
*/
|
|
15
|
+
export function getSessionLogPath(sessionId) {
|
|
16
|
+
mkdirSync(SESSION_ROOT, { recursive: true, mode: 0o700 });
|
|
17
|
+
return join(SESSION_ROOT, `${sessionId}.jsonl`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Append an event to the session log
|
|
22
|
+
*/
|
|
23
|
+
export function logEvent(sessionId, event) {
|
|
24
|
+
const logPath = getSessionLogPath(sessionId);
|
|
25
|
+
const entry = {
|
|
26
|
+
ts: Date.now(),
|
|
27
|
+
...event
|
|
28
|
+
};
|
|
29
|
+
appendFileSync(logPath, JSON.stringify(entry) + '\n', { mode: 0o600 });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Read all events from a session log
|
|
34
|
+
*/
|
|
35
|
+
export function readSessionLog(sessionId) {
|
|
36
|
+
const logPath = getSessionLogPath(sessionId);
|
|
37
|
+
if (!existsSync(logPath)) return [];
|
|
38
|
+
try {
|
|
39
|
+
const content = readFileSync(logPath, 'utf8').trim();
|
|
40
|
+
if (!content) return [];
|
|
41
|
+
return content.split('\n').map(line => {
|
|
42
|
+
try { return JSON.parse(line); } catch { return null; }
|
|
43
|
+
}).filter(Boolean);
|
|
44
|
+
} catch {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get session summary from event log
|
|
51
|
+
*/
|
|
52
|
+
export function getSessionSummary(sessionId) {
|
|
53
|
+
const events = readSessionLog(sessionId);
|
|
54
|
+
if (events.length === 0) return null;
|
|
55
|
+
|
|
56
|
+
const summary = {
|
|
57
|
+
sessionId,
|
|
58
|
+
totalEvents: events.length,
|
|
59
|
+
turns: 0,
|
|
60
|
+
filesCreated: 0,
|
|
61
|
+
filesModified: 0,
|
|
62
|
+
testsRun: 0,
|
|
63
|
+
testsPassed: 0,
|
|
64
|
+
testsFailed: 0,
|
|
65
|
+
blocks: 0,
|
|
66
|
+
warnings: 0,
|
|
67
|
+
startedAt: events[0]?.ts || null,
|
|
68
|
+
lastEventAt: events[events.length - 1]?.ts || null,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
for (const event of events) {
|
|
72
|
+
switch (event.type) {
|
|
73
|
+
case 'turn':
|
|
74
|
+
summary.turns++;
|
|
75
|
+
break;
|
|
76
|
+
case 'file-edit':
|
|
77
|
+
summary.filesModified++;
|
|
78
|
+
break;
|
|
79
|
+
case 'file-create':
|
|
80
|
+
summary.filesCreated++;
|
|
81
|
+
break;
|
|
82
|
+
case 'test-run':
|
|
83
|
+
summary.testsRun++;
|
|
84
|
+
if (event.passed) summary.testsPassed++;
|
|
85
|
+
else summary.testsFailed++;
|
|
86
|
+
break;
|
|
87
|
+
case 'block':
|
|
88
|
+
summary.blocks++;
|
|
89
|
+
break;
|
|
90
|
+
case 'warn':
|
|
91
|
+
summary.warnings++;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return summary;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* List all session log files
|
|
101
|
+
*/
|
|
102
|
+
export function listSessions() {
|
|
103
|
+
if (!existsSync(SESSION_ROOT)) return [];
|
|
104
|
+
return readdirSync(SESSION_ROOT)
|
|
105
|
+
.filter(f => f.endsWith('.jsonl'))
|
|
106
|
+
.map(f => f.replace('.jsonl', ''))
|
|
107
|
+
.sort();
|
|
108
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-prism",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "AI coding problem decomposition tool — Understand, Decompose, Execute, Checkpoint.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -36,6 +36,14 @@
|
|
|
36
36
|
"README.md",
|
|
37
37
|
"README.ko.md"
|
|
38
38
|
],
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/lazysaturday91/claude-prism.git"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/lazysaturday91/claude-prism#readme",
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/lazysaturday91/claude-prism/issues"
|
|
46
|
+
},
|
|
39
47
|
"author": "lazysaturday91",
|
|
40
48
|
"license": "MIT"
|
|
41
49
|
}
|
|
@@ -22,13 +22,29 @@ When this command is invoked:
|
|
|
22
22
|
|
|
23
23
|
### Progress
|
|
24
24
|
|
|
25
|
+
```
|
|
26
|
+
Phase: [current phase] | Batch: [N/M] | Tasks: [done/total] ([%])
|
|
27
|
+
[████████░░] 80% — Next: [next batch name]
|
|
28
|
+
```
|
|
29
|
+
|
|
25
30
|
- Batches complete: N/M
|
|
26
31
|
- Current batch: [name] — [X/Y tasks done]
|
|
27
32
|
- Remaining: [batch names]
|
|
28
33
|
- Blockers: [any issues encountered]
|
|
29
34
|
|
|
35
|
+
### Task Size Distribution
|
|
36
|
+
|
|
37
|
+
| Size | Count | Done |
|
|
38
|
+
|------|-------|------|
|
|
39
|
+
| [S] Small | N | N |
|
|
40
|
+
| [M] Medium | N | N |
|
|
41
|
+
| [L] Large | N | N |
|
|
42
|
+
|
|
30
43
|
4. **Show summary**:
|
|
31
44
|
- Files created/modified so far
|
|
32
45
|
- Tests added and their status
|
|
33
46
|
- Commits made
|
|
34
|
-
5. **
|
|
47
|
+
5. **Checkpoint policy check**:
|
|
48
|
+
- If 3+ consecutive approvals → suggest expanding batch size to 5-8
|
|
49
|
+
- If phase boundary → always stop
|
|
50
|
+
6. **Ask**: "Continue with the current plan, adjust, or stop?"
|
|
@@ -42,8 +42,15 @@ When this command is invoked, follow the UDEC framework strictly:
|
|
|
42
42
|
- Independent verification: each unit has a pass criterion
|
|
43
43
|
- Files specified: list files to create/modify per unit
|
|
44
44
|
- Dependencies noted: mark if unit depends on a previous one
|
|
45
|
-
9. **
|
|
46
|
-
|
|
45
|
+
9. **Assign size tags** to every task: [S] <30 LOC, [M] 30-100 LOC, [L] >100 LOC
|
|
46
|
+
- Batch composition: S+S+M = 1 batch, L = 1 batch alone
|
|
47
|
+
10. **Assign verification strategy** per task: `| Verify: TDD` or `| Verify: Build` or `| Verify: Visual`
|
|
48
|
+
11. **Pre-decomposition checklist**:
|
|
49
|
+
- Required types/interfaces have the necessary fields?
|
|
50
|
+
- External package APIs behave as expected?
|
|
51
|
+
- Cross-package dependencies identified and noted as prerequisites?
|
|
52
|
+
12. **Save plan** to `docs/plans/YYYY-MM-DD-<topic>.md`
|
|
53
|
+
13. **Get approval**: "Proceed with this plan?"
|
|
47
54
|
|
|
48
55
|
## E — EXECUTE
|
|
49
56
|
|
|
@@ -70,15 +77,21 @@ When this command is invoked, follow the UDEC framework strictly:
|
|
|
70
77
|
|
|
71
78
|
## C — CHECKPOINT
|
|
72
79
|
|
|
73
|
-
|
|
80
|
+
20. After each batch, report using this format:
|
|
74
81
|
|
|
75
82
|
| Item | Before | After |
|
|
76
83
|
|------|--------|-------|
|
|
77
84
|
| [what changed] | [old behavior] | [new behavior] |
|
|
78
85
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
86
|
+
```
|
|
87
|
+
Phase: [current] | Batch: [N/M] | Tasks: [done/total] ([%])
|
|
88
|
+
[████████░░] 80% — Next: [next batch name]
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
21. Include: verification results, files modified, tests status
|
|
92
|
+
22. **Checkpoint policy**: after 3 consecutive approvals, increase batch size to 5-8 for the rest of the phase
|
|
93
|
+
23. Ask: "Continue to next batch?"
|
|
94
|
+
24. User can redirect, adjust scope, or stop at any checkpoint
|
|
82
95
|
|
|
83
96
|
## OMC Integration
|
|
84
97
|
|
package/templates/rules.en.md
CHANGED
|
@@ -105,11 +105,16 @@ One sentence: what we're building and why.
|
|
|
105
105
|
Tech stack, key decisions, 2-3 sentences max.
|
|
106
106
|
|
|
107
107
|
## Batch 1: [Name]
|
|
108
|
-
- [ ] Task 1.1: [description] → `path/to/file`
|
|
108
|
+
- [ ] Task 1.1: [S] [description] | Verify: Build → `path/to/file`
|
|
109
|
+
- Pass criterion: [specific assertion]
|
|
110
|
+
- [ ] Task 1.2: [M] [description] | Verify: TDD → `path/to/file`
|
|
111
|
+
- Prerequisite: Task 1.1
|
|
112
|
+
- Test: `path/to/test` — [what it verifies]
|
|
113
|
+
- Pass criterion: [specific assertion]
|
|
114
|
+
- [ ] Task 1.3: [L] [description] | Verify: TDD → `path/to/file`
|
|
115
|
+
- Prerequisite: Task 1.1, Task 1.2
|
|
109
116
|
- Test: `path/to/test` — [what it verifies]
|
|
110
117
|
- Pass criterion: [specific assertion]
|
|
111
|
-
- [ ] Task 1.2: ...
|
|
112
|
-
- [ ] Task 1.3: ...
|
|
113
118
|
|
|
114
119
|
## Batch 2: [Name]
|
|
115
120
|
- [ ] Task 2.1: ...
|
|
@@ -118,6 +123,20 @@ Tech stack, key decisions, 2-3 sentences max.
|
|
|
118
123
|
- [Known unknowns or potential blockers]
|
|
119
124
|
```
|
|
120
125
|
|
|
126
|
+
### 2-6. Task Sizing and Pre-Decomposition Check
|
|
127
|
+
|
|
128
|
+
**Size Tags (assign to every task):**
|
|
129
|
+
- **[S]** Small: <30 LOC, config/style/single-function changes
|
|
130
|
+
- **[M]** Medium: 30-100 LOC, feature implementation, component creation
|
|
131
|
+
- **[L]** Large: >100 LOC, multi-file rewrite, new module/architecture
|
|
132
|
+
|
|
133
|
+
**Batch composition by size**: S+S+M = 1 batch, L = 1 batch alone, S+S+S+S = 1 batch
|
|
134
|
+
|
|
135
|
+
**Pre-decomposition checklist (before creating plan):**
|
|
136
|
+
- [ ] Required types/interfaces have the necessary fields?
|
|
137
|
+
- [ ] External package APIs behave as expected?
|
|
138
|
+
- [ ] Cross-package dependencies identified and noted as prerequisites?
|
|
139
|
+
|
|
121
140
|
---
|
|
122
141
|
|
|
123
142
|
## 3. EXECUTE — Execution Protocol
|
|
@@ -212,6 +231,17 @@ After each batch:
|
|
|
212
231
|
- Preview next batch
|
|
213
232
|
- "Continue?"
|
|
214
233
|
|
|
234
|
+
**Checkpoint frequency policy:**
|
|
235
|
+
- **Phase boundary**: always stop (mandatory)
|
|
236
|
+
- **Batch boundary**: stop by default → after 3 consecutive approvals, increase batch size to 5-8 for the remainder of the phase
|
|
237
|
+
- **Blocker encountered**: always stop (mandatory)
|
|
238
|
+
|
|
239
|
+
**Progress dashboard (include at each checkpoint):**
|
|
240
|
+
```
|
|
241
|
+
Phase: [current phase] | Batch: [N/M] | Tasks: [done/total] ([%])
|
|
242
|
+
[████████░░] 80% — Next: [next batch name]
|
|
243
|
+
```
|
|
244
|
+
|
|
215
245
|
### 4-2. Direction Change
|
|
216
246
|
|
|
217
247
|
User says "change direction" → return to UNDERSTAND
|
package/templates/rules.ja.md
CHANGED
|
@@ -105,11 +105,16 @@ DECOMPOSEに進む前に確認:
|
|
|
105
105
|
技術スタック、主要決定事項、2-3文。
|
|
106
106
|
|
|
107
107
|
## Batch 1: [名前]
|
|
108
|
-
- [ ] Task 1.1: [説明] → `path/to/file`
|
|
108
|
+
- [ ] Task 1.1: [S] [説明] | 検証: Build → `path/to/file`
|
|
109
|
+
- 合格基準: [具体的なアサーション]
|
|
110
|
+
- [ ] Task 1.2: [M] [説明] | 検証: TDD → `path/to/file`
|
|
111
|
+
- 前提: Task 1.1
|
|
112
|
+
- テスト: `path/to/test` — [検証内容]
|
|
113
|
+
- 合格基準: [具体的なアサーション]
|
|
114
|
+
- [ ] Task 1.3: [L] [説明] | 検証: TDD → `path/to/file`
|
|
115
|
+
- 前提: Task 1.1, Task 1.2
|
|
109
116
|
- テスト: `path/to/test` — [検証内容]
|
|
110
117
|
- 合格基準: [具体的なアサーション]
|
|
111
|
-
- [ ] Task 1.2: ...
|
|
112
|
-
- [ ] Task 1.3: ...
|
|
113
118
|
|
|
114
119
|
## Batch 2: [名前]
|
|
115
120
|
- [ ] Task 2.1: ...
|
|
@@ -118,6 +123,20 @@ DECOMPOSEに進む前に確認:
|
|
|
118
123
|
- [既知の不確実性や潜在的ブロッカー]
|
|
119
124
|
```
|
|
120
125
|
|
|
126
|
+
### 2-6. タスクサイズ見積もりと事前分解チェック
|
|
127
|
+
|
|
128
|
+
**サイズタグ(すべてのタスクに付与):**
|
|
129
|
+
- **[S]** Small: <30 LOC、設定/スタイル/単一関数の変更
|
|
130
|
+
- **[M]** Medium: 30-100 LOC、機能実装、コンポーネント作成
|
|
131
|
+
- **[L]** Large: >100 LOC、複数ファイルの書き換え、新規モジュール/アーキテクチャ
|
|
132
|
+
|
|
133
|
+
**サイズに基づくバッチ構成**: S+S+M = 1バッチ、L = 単独1バッチ、S+S+S+S = 1バッチ
|
|
134
|
+
|
|
135
|
+
**事前分解チェックリスト(計画作成前に必須):**
|
|
136
|
+
- [ ] 必要な型/インターフェースに必要なフィールドが存在するか?
|
|
137
|
+
- [ ] 外部パッケージのAPIが期待通りに動作するか?
|
|
138
|
+
- [ ] パッケージ間の依存関係が特定され、前提条件として記録されているか?
|
|
139
|
+
|
|
121
140
|
---
|
|
122
141
|
|
|
123
142
|
## 3. EXECUTE (実行) — 実行プロトコル
|
|
@@ -212,6 +231,17 @@ DECOMPOSEに進む前に確認:
|
|
|
212
231
|
- 次のバッチをプレビュー
|
|
213
232
|
- 「続行しますか?」
|
|
214
233
|
|
|
234
|
+
**チェックポイント頻度ポリシー:**
|
|
235
|
+
- **フェーズ境界**: 常に停止(必須)
|
|
236
|
+
- **バッチ境界**: デフォルトで停止 → 3回連続承認後、残りのフェーズはバッチサイズを5-8に拡大
|
|
237
|
+
- **ブロッカー発生**: 常に停止(必須)
|
|
238
|
+
|
|
239
|
+
**進捗ダッシュボード(各チェックポイントに含める):**
|
|
240
|
+
```
|
|
241
|
+
Phase: [現在のフェーズ] | Batch: [N/M] | Tasks: [完了/合計] ([%])
|
|
242
|
+
[████████░░] 80% — 次: [次のバッチ名]
|
|
243
|
+
```
|
|
244
|
+
|
|
215
245
|
### 4-2. 方向転換
|
|
216
246
|
|
|
217
247
|
ユーザーが「方向を変える」と言う → UNDERSTANDに戻る
|
package/templates/rules.ko.md
CHANGED
|
@@ -105,11 +105,16 @@ DECOMPOSE로 넘어가기 전 확인:
|
|
|
105
105
|
기술 스택, 주요 결정, 2-3문장.
|
|
106
106
|
|
|
107
107
|
## Batch 1: [이름]
|
|
108
|
-
- [ ] Task 1.1: [설명] → `path/to/file`
|
|
108
|
+
- [ ] Task 1.1: [S] [설명] | 검증: Build → `path/to/file`
|
|
109
|
+
- 통과 기준: [구체적 단언]
|
|
110
|
+
- [ ] Task 1.2: [M] [설명] | 검증: TDD → `path/to/file`
|
|
111
|
+
- 선행: Task 1.1
|
|
112
|
+
- 테스트: `path/to/test` — [검증 대상]
|
|
113
|
+
- 통과 기준: [구체적 단언]
|
|
114
|
+
- [ ] Task 1.3: [L] [설명] | 검증: TDD → `path/to/file`
|
|
115
|
+
- 선행: Task 1.1, Task 1.2
|
|
109
116
|
- 테스트: `path/to/test` — [검증 대상]
|
|
110
117
|
- 통과 기준: [구체적 단언]
|
|
111
|
-
- [ ] Task 1.2: ...
|
|
112
|
-
- [ ] Task 1.3: ...
|
|
113
118
|
|
|
114
119
|
## Batch 2: [이름]
|
|
115
120
|
- [ ] Task 2.1: ...
|
|
@@ -118,6 +123,20 @@ DECOMPOSE로 넘어가기 전 확인:
|
|
|
118
123
|
- [알려진 불확실성 또는 잠재적 블로커]
|
|
119
124
|
```
|
|
120
125
|
|
|
126
|
+
### 2-6. 태스크 크기 산정 및 사전 분해 점검
|
|
127
|
+
|
|
128
|
+
**크기 태그 (모든 태스크에 부여):**
|
|
129
|
+
- **[S]** Small: <30 LOC, 설정/스타일/단일 함수 변경
|
|
130
|
+
- **[M]** Medium: 30-100 LOC, 기능 구현, 컴포넌트 생성
|
|
131
|
+
- **[L]** Large: >100 LOC, 멀티파일 리라이트, 새 모듈/아키텍처
|
|
132
|
+
|
|
133
|
+
**크기별 배치 구성**: S+S+M = 1배치, L = 단독 1배치, S+S+S+S = 1배치
|
|
134
|
+
|
|
135
|
+
**사전 분해 체크리스트 (플랜 작성 전 필수):**
|
|
136
|
+
- [ ] 필요한 타입/인터페이스에 요구되는 필드가 존재하는가?
|
|
137
|
+
- [ ] 외부 패키지 API가 예상대로 동작하는가?
|
|
138
|
+
- [ ] 교차 패키지 의존성이 식별되고 선행 조건으로 기록되었는가?
|
|
139
|
+
|
|
121
140
|
---
|
|
122
141
|
|
|
123
142
|
## 3. EXECUTE — 실행 프로토콜
|
|
@@ -212,6 +231,17 @@ DECOMPOSE로 넘어가기 전 확인:
|
|
|
212
231
|
- 다음 배치 미리보기
|
|
213
232
|
- "계속 진행할까요?"
|
|
214
233
|
|
|
234
|
+
**체크포인트 빈도 정책:**
|
|
235
|
+
- **Phase 경계**: 항상 멈춤 (필수)
|
|
236
|
+
- **Batch 경계**: 기본 멈춤 → 3회 연속 승인 시 남은 Phase 동안 배치 크기 5-8로 확대
|
|
237
|
+
- **블로커 발생**: 항상 멈춤 (필수)
|
|
238
|
+
|
|
239
|
+
**진행률 대시보드 (각 체크포인트에 포함):**
|
|
240
|
+
```
|
|
241
|
+
Phase: [현재 페이즈] | Batch: [N/M] | Tasks: [완료/전체] ([%])
|
|
242
|
+
[████████░░] 80% — 다음: [다음 배치 이름]
|
|
243
|
+
```
|
|
244
|
+
|
|
215
245
|
### 4-2. 방향 전환
|
|
216
246
|
|
|
217
247
|
사용자가 "방향 바꾸자" → UNDERSTAND로 돌아감
|
package/templates/rules.zh.md
CHANGED
|
@@ -105,11 +105,16 @@
|
|
|
105
105
|
技术栈、关键决策,2-3 句。
|
|
106
106
|
|
|
107
107
|
## Batch 1: [名称]
|
|
108
|
-
- [ ] Task 1.1: [描述] → `path/to/file`
|
|
108
|
+
- [ ] Task 1.1: [S] [描述] | 验证: Build → `path/to/file`
|
|
109
|
+
- 通过标准: [具体断言]
|
|
110
|
+
- [ ] Task 1.2: [M] [描述] | 验证: TDD → `path/to/file`
|
|
111
|
+
- 前置: Task 1.1
|
|
112
|
+
- 测试: `path/to/test` — [验证什么]
|
|
113
|
+
- 通过标准: [具体断言]
|
|
114
|
+
- [ ] Task 1.3: [L] [描述] | 验证: TDD → `path/to/file`
|
|
115
|
+
- 前置: Task 1.1, Task 1.2
|
|
109
116
|
- 测试: `path/to/test` — [验证什么]
|
|
110
117
|
- 通过标准: [具体断言]
|
|
111
|
-
- [ ] Task 1.2: ...
|
|
112
|
-
- [ ] Task 1.3: ...
|
|
113
118
|
|
|
114
119
|
## Batch 2: [名称]
|
|
115
120
|
- [ ] Task 2.1: ...
|
|
@@ -118,6 +123,20 @@
|
|
|
118
123
|
- [已知的不确定性或潜在阻塞]
|
|
119
124
|
```
|
|
120
125
|
|
|
126
|
+
### 2-6. 任务规模评估与分解前检查
|
|
127
|
+
|
|
128
|
+
**规模标签(为每个任务分配):**
|
|
129
|
+
- **[S]** Small:<30 LOC,配置/样式/单函数变更
|
|
130
|
+
- **[M]** Medium:30-100 LOC,功能实现,组件创建
|
|
131
|
+
- **[L]** Large:>100 LOC,多文件重写,新模块/架构
|
|
132
|
+
|
|
133
|
+
**按规模组成批次**:S+S+M = 1批次,L = 单独1批次,S+S+S+S = 1批次
|
|
134
|
+
|
|
135
|
+
**分解前检查清单(创建计划前必须):**
|
|
136
|
+
- [ ] 所需的类型/接口是否具有必要的字段?
|
|
137
|
+
- [ ] 外部包的 API 是否按预期工作?
|
|
138
|
+
- [ ] 是否已识别跨包依赖并记录为前置条件?
|
|
139
|
+
|
|
121
140
|
---
|
|
122
141
|
|
|
123
142
|
## 3. EXECUTE (执行) — 执行协议
|
|
@@ -212,6 +231,17 @@
|
|
|
212
231
|
- 预览下一批次
|
|
213
232
|
- "继续吗?"
|
|
214
233
|
|
|
234
|
+
**检查点频率策略:**
|
|
235
|
+
- **阶段边界**:始终停止(必须)
|
|
236
|
+
- **批次边界**:默认停止 → 连续3次批准后,剩余阶段的批次大小扩大至5-8
|
|
237
|
+
- **遇到阻塞**:始终停止(必须)
|
|
238
|
+
|
|
239
|
+
**进度仪表板(在每个检查点包含):**
|
|
240
|
+
```
|
|
241
|
+
Phase: [当前阶段] | Batch: [N/M] | Tasks: [已完成/总数] ([%])
|
|
242
|
+
[████████░░] 80% — 下一步: [下一批次名称]
|
|
243
|
+
```
|
|
244
|
+
|
|
215
245
|
### 4-2. 方向变更
|
|
216
246
|
|
|
217
247
|
用户说"改变方向" → 返回 UNDERSTAND
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { runPipelineAsync } from '../lib/pipeline.mjs';
|
|
3
|
+
import { debugLoop } from '../rules/debug-loop.mjs';
|
|
4
|
+
import { scopeGuard } from '../rules/scope-guard.mjs';
|
|
5
|
+
import { testTracker } from '../rules/test-tracker.mjs';
|
|
6
|
+
import { alignment } from '../rules/alignment.mjs';
|
|
7
|
+
|
|
8
|
+
await runPipelineAsync([
|
|
9
|
+
{ name: 'debug-loop', rule: debugLoop },
|
|
10
|
+
{ name: 'scope-guard', rule: scopeGuard },
|
|
11
|
+
{ name: 'test-tracker', rule: testTracker },
|
|
12
|
+
{ name: 'alignment', rule: alignment },
|
|
13
|
+
], 'PostToolUse');
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { runPipelineAsync } from '../lib/pipeline.mjs';
|
|
3
|
+
import { commitGuard } from '../rules/commit-guard.mjs';
|
|
4
|
+
import { alignment } from '../rules/alignment.mjs';
|
|
5
|
+
|
|
6
|
+
await runPipelineAsync([
|
|
7
|
+
{ name: 'commit-guard', rule: commitGuard },
|
|
8
|
+
{ name: 'alignment', rule: alignment },
|
|
9
|
+
], 'PreToolUse');
|