@relipa/ai-flow-kit 0.0.8 → 0.0.9-beta.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/README.md +2 -2
- package/bin/aiflow.js +408 -399
- package/bin/ak.js +3 -3
- package/custom/mcp-presets/README.md +66 -0
- package/custom/mcp-presets/figma-desktop.json +9 -0
- package/custom/rules/project-conventions.md +51 -0
- package/custom/templates/python.md +79 -79
- package/custom/templates/shared/gate-workflow.md +2 -0
- package/docs/common/CHANGELOG.md +18 -0
- package/docs/common/workflows/figma.md +105 -0
- package/package.json +1 -1
- package/scripts/hooks/block-git-write.js +52 -52
- package/scripts/hooks/session-start.js +295 -274
- package/scripts/hooks/session-stop.js +122 -55
- package/scripts/init.js +13 -5
- package/scripts/task.js +41 -1
- package/scripts/telemetry/record.js +5 -1
|
@@ -1,274 +1,295 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* SessionStart hook for ai-flow-kit
|
|
4
|
-
* Injects:
|
|
5
|
-
* 1. using-superpowers skill content
|
|
6
|
-
* 2. Active ticket context + gate-aware workflow instruction
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const fs = require('fs');
|
|
10
|
-
const path = require('path');
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
lines.push(
|
|
162
|
-
|
|
163
|
-
lines.push(
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
lines.push(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
lines.push(
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
lines.push(
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
lines.push('
|
|
208
|
-
lines.push(
|
|
209
|
-
|
|
210
|
-
lines.push('
|
|
211
|
-
lines.push('
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
lines.push('1
|
|
215
|
-
lines.push('
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
lines.push('
|
|
219
|
-
lines.push(
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
lines.push('');
|
|
223
|
-
lines.push('
|
|
224
|
-
lines.push(
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
'
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* SessionStart hook for ai-flow-kit
|
|
4
|
+
* Injects:
|
|
5
|
+
* 1. using-superpowers skill content
|
|
6
|
+
* 2. Active ticket context + gate-aware workflow instruction
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
|
|
13
|
+
function loadTelemetry() {
|
|
14
|
+
try { return require('../telemetry/record'); } catch (_) { }
|
|
15
|
+
return { record: () => { } };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function loadTelemetryConfig() {
|
|
19
|
+
try { return require('../telemetry/config'); } catch (_) { }
|
|
20
|
+
return { loadConfig: () => ({}) };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const { record } = loadTelemetry();
|
|
24
|
+
const { loadConfig } = loadTelemetryConfig();
|
|
25
|
+
|
|
26
|
+
// ── Paths (set once stdin is read) ────────────────────────────
|
|
27
|
+
let tasksDir = '';
|
|
28
|
+
|
|
29
|
+
let raw = '';
|
|
30
|
+
process.stdin.setEncoding('utf-8');
|
|
31
|
+
process.stdin.on('data', chunk => { raw += chunk; });
|
|
32
|
+
process.stdin.on('end', () => {
|
|
33
|
+
let hookData = {};
|
|
34
|
+
try { hookData = JSON.parse(raw || '{}'); } catch (_) { }
|
|
35
|
+
|
|
36
|
+
const sessionModel = hookData.model || '';
|
|
37
|
+
const cfg = loadConfig();
|
|
38
|
+
|
|
39
|
+
record('session.start', {
|
|
40
|
+
ai_tool: 'claude',
|
|
41
|
+
model: sessionModel || undefined,
|
|
42
|
+
ai_email: cfg.email || '',
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// This script lives at .claude/hooks/session-start.js
|
|
46
|
+
// Project root is 2 levels up: .claude/hooks → .claude → project root
|
|
47
|
+
const projectRoot = path.resolve(__dirname, '..', '..');
|
|
48
|
+
const skillPath = path.join(projectRoot, '.claude', 'skills', 'using-superpowers', 'SKILL.md');
|
|
49
|
+
const contextPath = path.join(projectRoot, '.aiflow', 'context', 'current.json');
|
|
50
|
+
tasksDir = path.join(projectRoot, '.aiflow', 'tasks');
|
|
51
|
+
|
|
52
|
+
// ── 1. Load superpowers skill ──────────────────────────────────
|
|
53
|
+
let skillContent = '';
|
|
54
|
+
try {
|
|
55
|
+
skillContent = fs.readFileSync(skillPath, 'utf-8');
|
|
56
|
+
} catch (_) {
|
|
57
|
+
process.stderr.write('[aiflow] WARNING: using-superpowers skill missing — run `aiflow update`\n');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ── 2. Load active ticket context ──────────────────────────────
|
|
61
|
+
let contextBlock = '';
|
|
62
|
+
try {
|
|
63
|
+
if (fs.existsSync(contextPath)) {
|
|
64
|
+
const ctx = JSON.parse(fs.readFileSync(contextPath, 'utf-8'));
|
|
65
|
+
if (ctx.taskId && ctx.title) {
|
|
66
|
+
const taskState = loadTaskState(ctx.taskId);
|
|
67
|
+
contextBlock = buildContextPrompt(ctx, taskState);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} catch (_) { }
|
|
71
|
+
|
|
72
|
+
// ── 3. Check GitNexus background indexing status ───────────────
|
|
73
|
+
const GITNEXUS_STALE_MS = 3 * 60 * 60 * 1000;
|
|
74
|
+
const gitNexusStatusPath = path.join(projectRoot, '.aiflow', 'gitnexus-status.json');
|
|
75
|
+
try {
|
|
76
|
+
if (fs.existsSync(gitNexusStatusPath)) {
|
|
77
|
+
const gn = JSON.parse(fs.readFileSync(gitNexusStatusPath, 'utf-8'));
|
|
78
|
+
if (!gn.notified) {
|
|
79
|
+
if (gn.status === 'running' && gn.startedAt) {
|
|
80
|
+
const age = Date.now() - new Date(gn.startedAt).getTime();
|
|
81
|
+
if (age > GITNEXUS_STALE_MS) {
|
|
82
|
+
gn.status = 'error';
|
|
83
|
+
gn.error = 'timed out — process may have been killed or ran out of memory';
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (gn.status === 'done') {
|
|
87
|
+
process.stderr.write('[aiflow] ✓ GitNexus indexing complete — code intelligence tools are ready.\n');
|
|
88
|
+
} else if (gn.status === 'error') {
|
|
89
|
+
const detail = gn.error ? ` (${gn.error})` : gn.exitCode != null ? ` (exit ${gn.exitCode})` : '';
|
|
90
|
+
process.stderr.write(`[aiflow] ✗ GitNexus indexing failed${detail}.\n`);
|
|
91
|
+
}
|
|
92
|
+
if (gn.status === 'done' || gn.status === 'error') {
|
|
93
|
+
gn.notified = true;
|
|
94
|
+
fs.writeFileSync(gitNexusStatusPath, JSON.stringify(gn, null, 2));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
} catch (_) { }
|
|
99
|
+
|
|
100
|
+
// ── 4. Combine and output ──────────────────────────────────────
|
|
101
|
+
const parts = [];
|
|
102
|
+
if (skillContent) {
|
|
103
|
+
parts.push(`<EXTREMELY_IMPORTANT>\nYou have superpowers.\n\n**Below is the full content of your 'superpowers:using-superpowers' skill - your introduction to using skills. For all other skills, use the 'Skill' tool:**\n\n${skillContent}\n\n</EXTREMELY_IMPORTANT>`);
|
|
104
|
+
}
|
|
105
|
+
if (contextBlock) {
|
|
106
|
+
parts.push(contextBlock);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const combined = parts.join('\n\n');
|
|
110
|
+
const escaped = combined
|
|
111
|
+
.replace(/\\/g, '\\\\')
|
|
112
|
+
.replace(/"/g, '\\"')
|
|
113
|
+
.replace(/\n/g, '\\n')
|
|
114
|
+
.replace(/\r/g, '\\r')
|
|
115
|
+
.replace(/\t/g, '\\t');
|
|
116
|
+
|
|
117
|
+
process.stdout.write(JSON.stringify({
|
|
118
|
+
hookSpecificOutput: {
|
|
119
|
+
hookEventName: 'SessionStart',
|
|
120
|
+
additionalContext: escaped
|
|
121
|
+
}
|
|
122
|
+
}));
|
|
123
|
+
|
|
124
|
+
process.exit(0);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// ── Helpers ────────────────────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
function loadTaskState(taskId) {
|
|
130
|
+
try {
|
|
131
|
+
const statePath = path.join(tasksDir, taskId, 'task-state.json');
|
|
132
|
+
if (fs.existsSync(statePath)) {
|
|
133
|
+
return JSON.parse(fs.readFileSync(statePath, 'utf-8'));
|
|
134
|
+
}
|
|
135
|
+
} catch (_) { }
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function buildContextPrompt(ctx, taskState) {
|
|
140
|
+
const lines = [];
|
|
141
|
+
lines.push('<ACTIVE_TASK>');
|
|
142
|
+
lines.push(`**Active Ticket:** ${ctx.taskId} — ${ctx.title}`);
|
|
143
|
+
lines.push(`**Type:** ${ctx.taskType || 'feature'}`);
|
|
144
|
+
lines.push(`**Status:** ${ctx.status || 'Unknown'}`);
|
|
145
|
+
lines.push(`**Assignee:** ${ctx.assignee || 'Unknown'}`);
|
|
146
|
+
lines.push('');
|
|
147
|
+
|
|
148
|
+
if (ctx.description) {
|
|
149
|
+
lines.push('**Description:**');
|
|
150
|
+
lines.push(ctx.description.substring(0, 2000));
|
|
151
|
+
lines.push('');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (ctx.acceptanceCriteria && ctx.acceptanceCriteria.length > 0) {
|
|
155
|
+
lines.push('**Acceptance Criteria:**');
|
|
156
|
+
for (const c of ctx.acceptanceCriteria) lines.push(`- ${c}`);
|
|
157
|
+
lines.push('');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (ctx.comments && ctx.comments.length > 0) {
|
|
161
|
+
lines.push(`**Comments (${ctx.comments.length}):**`);
|
|
162
|
+
const recent = ctx.comments.slice(-5);
|
|
163
|
+
for (const c of recent) { lines.push(c); lines.push(''); }
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (ctx.context && ctx.context.files && ctx.context.files.length > 0) {
|
|
167
|
+
lines.push('**Related Files:**');
|
|
168
|
+
for (const f of ctx.context.files) lines.push(`- \`${f}\``);
|
|
169
|
+
lines.push('');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
lines.push('---');
|
|
173
|
+
lines.push('');
|
|
174
|
+
|
|
175
|
+
const mode = ctx.mode || 'fast';
|
|
176
|
+
const currentGate = taskState && taskState.currentGate ? taskState.currentGate : 1;
|
|
177
|
+
const isResume = taskState && (taskState.pausedAt || taskState.currentGate > 1);
|
|
178
|
+
|
|
179
|
+
if (isResume && currentGate > 1) {
|
|
180
|
+
const gateApprovals = taskState.gateApprovals || {};
|
|
181
|
+
lines.push(`**RESUMING TASK: ${ctx.taskId} — Gate ${currentGate}**`);
|
|
182
|
+
lines.push('');
|
|
183
|
+
lines.push('This task was previously paused and is now being resumed.');
|
|
184
|
+
lines.push('');
|
|
185
|
+
lines.push('**Gate Progress:**');
|
|
186
|
+
for (let g = 1; g < currentGate; g++) {
|
|
187
|
+
const approvedAt = gateApprovals[String(g)];
|
|
188
|
+
const stamp = approvedAt ? ` — approved ${approvedAt}` : ' — approved';
|
|
189
|
+
lines.push(`- Gate ${g}: ✅ APPROVED${stamp}`);
|
|
190
|
+
}
|
|
191
|
+
lines.push(`- Gate ${currentGate}: 🔄 IN PROGRESS`);
|
|
192
|
+
lines.push('');
|
|
193
|
+
lines.push(`**DO NOT restart from Gate 1.** Resume directly at Gate ${currentGate} (${gateLabel(currentGate)}).`);
|
|
194
|
+
lines.push('');
|
|
195
|
+
|
|
196
|
+
if (currentGate === 2) {
|
|
197
|
+
lines.push('Gate 1 is already APPROVED. The requirement document is at:');
|
|
198
|
+
lines.push(` plan/${ctx.taskId}/requirement.md`);
|
|
199
|
+
lines.push('');
|
|
200
|
+
lines.push('Proceed to Gate 2: create the implementation plan.');
|
|
201
|
+
lines.push(buildModeInstruction(mode, 2));
|
|
202
|
+
} else if (currentGate === 3) {
|
|
203
|
+
lines.push('Gates 1 and 2 are already APPROVED. Plans are at:');
|
|
204
|
+
lines.push(` plan/${ctx.taskId}/requirement.md`);
|
|
205
|
+
lines.push(` plan/${ctx.taskId}/plan.md`);
|
|
206
|
+
lines.push('');
|
|
207
|
+
lines.push('Proceed to Gate 3: implement code following the plan (TDD).');
|
|
208
|
+
lines.push(buildModeInstruction(mode, 3));
|
|
209
|
+
} else if (currentGate === 4) {
|
|
210
|
+
lines.push('Gates 1–3 are APPROVED. Code has been implemented.');
|
|
211
|
+
lines.push('Proceed to Gate 4: run verification + impact-analysis + review checklist.');
|
|
212
|
+
lines.push(buildModeInstruction(mode, 4));
|
|
213
|
+
} else if (currentGate === 5) {
|
|
214
|
+
lines.push('Gates 1–4 are APPROVED. Self-review is done.');
|
|
215
|
+
lines.push('Proceed to Gate 5: guide the developer to create a Pull Request.');
|
|
216
|
+
}
|
|
217
|
+
if (taskState.notes) {
|
|
218
|
+
lines.push('');
|
|
219
|
+
lines.push(`**Note from developer:** ${taskState.notes}`);
|
|
220
|
+
}
|
|
221
|
+
} else {
|
|
222
|
+
lines.push('**AUTO-START GATE 1: You MUST begin analyzing this ticket immediately.**');
|
|
223
|
+
lines.push('');
|
|
224
|
+
lines.push(`INVOKE the \`read-study-requirement\` skill NOW for ticket ${ctx.taskId}.`);
|
|
225
|
+
lines.push('');
|
|
226
|
+
|
|
227
|
+
if (mode === 'fast') {
|
|
228
|
+
lines.push('**MODE: fast** — Use the FAST TRACK path in the skill:');
|
|
229
|
+
lines.push('- Read ticket + identify changed files only (no deep source scan)');
|
|
230
|
+
lines.push('- Skip Q&A unless there is a critical blocking ambiguity');
|
|
231
|
+
lines.push('- Output a lite requirement doc (Sections 1, 3, 5 only)');
|
|
232
|
+
lines.push('- Target: Gate 1 complete in < 5 minutes');
|
|
233
|
+
} else {
|
|
234
|
+
lines.push('Gate 1 process:');
|
|
235
|
+
lines.push('1. Read the ticket context above');
|
|
236
|
+
lines.push('2. Read CLAUDE.md — understand project architecture and conventions');
|
|
237
|
+
lines.push('3. Read source code — identify related files, data flow, patterns');
|
|
238
|
+
lines.push('4. If anything is unclear — ask ONE question at a time');
|
|
239
|
+
lines.push('5. Output plan/' + ctx.taskId + '/requirement.md');
|
|
240
|
+
lines.push('6. Display GATE 1 prompt and wait for APPROVED');
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
lines.push('');
|
|
244
|
+
lines.push('DO NOT wait for the developer to ask. START NOW.');
|
|
245
|
+
lines.push('If you have not started automatically, begin as soon as the developer types **"start"**.');
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
lines.push('</ACTIVE_TASK>');
|
|
249
|
+
return lines.join('\n');
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function gateLabel(n) {
|
|
253
|
+
const labels = {
|
|
254
|
+
1: 'AI Analyze Requirement',
|
|
255
|
+
2: 'Implementation Plan',
|
|
256
|
+
3: 'Code Generation',
|
|
257
|
+
4: 'AI Self-Review',
|
|
258
|
+
5: 'Peer Review & PR',
|
|
259
|
+
};
|
|
260
|
+
return labels[n] || `Gate ${n}`;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function buildModeInstruction(mode, gate) {
|
|
264
|
+
if (mode !== 'fast') return '';
|
|
265
|
+
|
|
266
|
+
const instructions = {
|
|
267
|
+
2: [
|
|
268
|
+
'**MODE: fast** — Gate 2 fast track:',
|
|
269
|
+
'- Create a simple task list (task-level, NOT step-level)',
|
|
270
|
+
'- DO NOT invoke `superpowers:writing-plans`',
|
|
271
|
+
'- Format: numbered list of tasks, each with file + action',
|
|
272
|
+
'- No code snippets, no step-by-step TDD breakdown required',
|
|
273
|
+
],
|
|
274
|
+
3: [
|
|
275
|
+
'**MODE: fast** — Gate 3 fast track:',
|
|
276
|
+
'- INVOKE `tdd-lean` skill (NOT superpowers:test-driven-development)',
|
|
277
|
+
'- tdd-lean uses Batch Red-Green-Refactor: 2–3 test runs total, never per-test',
|
|
278
|
+
'- Test Output Discipline: keep only summary line after each run, discard stack traces',
|
|
279
|
+
'- DO NOT use `superpowers:subagent-driven-development`',
|
|
280
|
+
'- DO NOT spawn Spec Reviewer or Code Quality Reviewer subagents',
|
|
281
|
+
'- Call `aiflow checkpoint` at each milestone (tests-written, code-done, tests-pass)',
|
|
282
|
+
],
|
|
283
|
+
4: [
|
|
284
|
+
'**MODE: fast** — Gate 4 fast track:',
|
|
285
|
+
'- Run the full test suite — this is MANDATORY',
|
|
286
|
+
'- Impact Analysis: quick scan only — reason from git diff and changed files.',
|
|
287
|
+
' DO NOT grep the entire codebase. List affected files and assess impact level.',
|
|
288
|
+
'- Review checklist (3 items only): tests pass ✅, no hardcoded secrets ✅, no SQL injection ✅',
|
|
289
|
+
'- summary.md: short format (Implementation table + Test result only)',
|
|
290
|
+
],
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
const lines = instructions[gate];
|
|
294
|
+
return lines ? '\n' + lines.join('\n') : '';
|
|
295
|
+
}
|