@relipa/ai-flow-kit 0.0.5-beta.1 → 0.0.6-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 +81 -69
- package/bin/aiflow.js +46 -2
- package/custom/rules/java/spring-boot-examples.md +329 -0
- package/custom/skills/read-study-requirement/SKILL.md +54 -0
- package/custom/skills/review-plan/SKILL.md +21 -0
- package/custom/templates/shared/gate-workflow.md +88 -88
- package/custom/templates/spring-boot.md +224 -523
- package/docs/common/AIFLOW.md +501 -462
- package/docs/common/CHANGELOG.md +162 -132
- package/docs/common/QUICK_START.md +44 -12
- package/docs/common/cli-reference.md +115 -6
- package/docs/common/getting-started.md +3 -0
- package/docs/project/ARCHITECTURE.md +28 -28
- package/package.json +6 -1
- package/scripts/doctor.js +89 -48
- package/scripts/guide.js +28 -5
- package/scripts/hooks/session-start.js +144 -45
- package/scripts/init.js +109 -50
- package/scripts/prompt.js +431 -431
- package/scripts/task.js +384 -0
- package/scripts/telemetry/cli.js +243 -243
- package/scripts/telemetry/config.js +91 -91
- package/scripts/telemetry/crypto.js +20 -20
- package/scripts/telemetry/flush.js +162 -162
- package/scripts/telemetry/record.js +138 -138
- package/scripts/use.js +94 -2
package/scripts/doctor.js
CHANGED
|
@@ -1,48 +1,89 @@
|
|
|
1
|
-
const fs = require('fs-extra');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const chalk = require('chalk');
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (!(await fs.pathExists(path.join(projectDir, 'CLAUDE.md')))) {
|
|
27
|
-
errors.push(
|
|
28
|
-
} else {
|
|
29
|
-
console.log(chalk.green('✓ CLAUDE.md exists'));
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
//
|
|
33
|
-
const stateFile = path.join(projectDir, '.aiflow', 'state.json');
|
|
34
|
-
if (!(await fs.pathExists(stateFile))) {
|
|
35
|
-
errors.push(
|
|
36
|
-
} else {
|
|
37
|
-
const state = await fs.readJson(stateFile);
|
|
38
|
-
console.log(chalk.green(`✓ Version tracking active (v${state.current_version})`));
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const { detectRtk, isRtkHookConfigured } = require('./init');
|
|
5
|
+
|
|
6
|
+
module.exports = async function doctor() {
|
|
7
|
+
const projectDir = process.cwd();
|
|
8
|
+
const errors = [];
|
|
9
|
+
const warnings = [];
|
|
10
|
+
|
|
11
|
+
console.log(chalk.blue('Running health check for AI Flow Kit...\n'));
|
|
12
|
+
|
|
13
|
+
// ── Core setup ─────────────────────────────────────────────
|
|
14
|
+
if (!(await fs.pathExists(path.join(projectDir, '.claude', 'skills')))) {
|
|
15
|
+
errors.push('Missing `.claude/skills` directory. Did you run `aiflow init`?');
|
|
16
|
+
} else {
|
|
17
|
+
console.log(chalk.green('✓ .claude/skills exists'));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!(await fs.pathExists(path.join(projectDir, '.rules')))) {
|
|
21
|
+
errors.push('Missing `.rules` directory.');
|
|
22
|
+
} else {
|
|
23
|
+
console.log(chalk.green('✓ .rules exists'));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!(await fs.pathExists(path.join(projectDir, 'CLAUDE.md')))) {
|
|
27
|
+
errors.push('Missing CLAUDE.md. AI context prompt is not set up.');
|
|
28
|
+
} else {
|
|
29
|
+
console.log(chalk.green('✓ CLAUDE.md exists'));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ── Version tracking ───────────────────────────────────────
|
|
33
|
+
const stateFile = path.join(projectDir, '.aiflow', 'state.json');
|
|
34
|
+
if (!(await fs.pathExists(stateFile))) {
|
|
35
|
+
errors.push('Missing `.aiflow/state.json`. Version tracking is broken.');
|
|
36
|
+
} else {
|
|
37
|
+
const state = await fs.readJson(stateFile);
|
|
38
|
+
console.log(chalk.green(`✓ Version tracking active (v${state.current_version})`));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ── SessionStart hook ──────────────────────────────────────
|
|
42
|
+
const settingsPath = path.join(projectDir, '.claude', 'settings.json');
|
|
43
|
+
if (await fs.pathExists(settingsPath)) {
|
|
44
|
+
const settings = await fs.readJson(settingsPath).catch(() => ({}));
|
|
45
|
+
const hookList = settings?.hooks?.SessionStart || [];
|
|
46
|
+
const hasSessionHook = hookList.some(h =>
|
|
47
|
+
(h.hooks || []).some(e => typeof e.command === 'string' && e.command.includes('session-start'))
|
|
48
|
+
);
|
|
49
|
+
if (hasSessionHook) {
|
|
50
|
+
console.log(chalk.green('✓ SessionStart hook configured'));
|
|
51
|
+
} else {
|
|
52
|
+
warnings.push('SessionStart hook not found in .claude/settings.json — run `aiflow init` to set it up.');
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
warnings.push('Missing .claude/settings.json — run `aiflow init`.');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ── RTK token compression ──────────────────────────────────
|
|
59
|
+
console.log('');
|
|
60
|
+
const rtk = await detectRtk();
|
|
61
|
+
if (rtk.installed) {
|
|
62
|
+
const hookOk = await isRtkHookConfigured(projectDir);
|
|
63
|
+
if (hookOk) {
|
|
64
|
+
console.log(chalk.green(`✓ RTK installed (${rtk.version}) — token compression active`));
|
|
65
|
+
} else {
|
|
66
|
+
console.log(chalk.green(`✓ RTK installed (${rtk.version})`));
|
|
67
|
+
warnings.push('RTK is installed but hook not configured. Run `aiflow init` to enable token compression.');
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
console.log(chalk.gray('○ RTK not installed (optional) — token compression inactive'));
|
|
71
|
+
console.log(chalk.gray(' Install: cargo install rtk | More: https://github.com/rtk-ai/rtk'));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ── Summary ────────────────────────────────────────────────
|
|
75
|
+
console.log('');
|
|
76
|
+
if (warnings.length > 0) {
|
|
77
|
+
console.log(chalk.yellow('Warnings:'));
|
|
78
|
+
warnings.forEach(w => console.log(chalk.yellow(` ⚠ ${w}`)));
|
|
79
|
+
console.log('');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (errors.length > 0) {
|
|
83
|
+
console.log(chalk.red('Issues found:'));
|
|
84
|
+
errors.forEach(e => console.log(chalk.red(` ✗ ${e}`)));
|
|
85
|
+
console.log(chalk.yellow('\nTry running `aiflow init` or `aiflow update` to fix these files.'));
|
|
86
|
+
} else {
|
|
87
|
+
console.log(chalk.green('✨ Everything looks healthy! You are ready to fly.'));
|
|
88
|
+
}
|
|
89
|
+
};
|
package/scripts/guide.js
CHANGED
|
@@ -113,17 +113,17 @@ function showWorkflow() {
|
|
|
113
113
|
|
|
114
114
|
// Step 2: Load context
|
|
115
115
|
console.log(chalk.bold.white(' Step 2 — Load context from ticket (per task)'));
|
|
116
|
-
console.log(chalk.green(' aiflow use PROJ-33
|
|
117
|
-
console.log(chalk.green(' aiflow
|
|
116
|
+
console.log(chalk.green(' aiflow use PROJ-33') + chalk.gray(' # Fast Mode (Default)'));
|
|
117
|
+
console.log(chalk.green(' aiflow use PROJ-33 --full') + chalk.gray(' # Full Mode (Optional)'));
|
|
118
118
|
console.log(chalk.green(' claude'));
|
|
119
119
|
console.log();
|
|
120
120
|
|
|
121
121
|
// Gate workflow
|
|
122
122
|
console.log(chalk.bold.white(' Step 3 — AI runs Strict Gate Workflow automatically'));
|
|
123
123
|
console.log();
|
|
124
|
-
console.log(chalk.yellow(' ⛩️ GATE 1') + chalk.gray(' — AI checks
|
|
125
|
-
console.log(chalk.gray('
|
|
126
|
-
console.log(chalk.gray(' Developer
|
|
124
|
+
console.log(chalk.yellow(' ⛩️ GATE 1') + chalk.gray(' — AI checks requirement and solution approach'));
|
|
125
|
+
console.log(chalk.gray(' Fast Mode (Default): Minimal Q&A, target < 5 min'));
|
|
126
|
+
console.log(chalk.gray(' Developer reviews requirement.md → "APPROVED"'));
|
|
127
127
|
console.log();
|
|
128
128
|
console.log(chalk.yellow(' ⛩️ GATE 2') + chalk.gray(' — AI generates detailed spec + plan'));
|
|
129
129
|
console.log(chalk.gray(' Developer reviews each section'));
|
|
@@ -158,8 +158,12 @@ function showCommands() {
|
|
|
158
158
|
group: 'Setup',
|
|
159
159
|
items: [
|
|
160
160
|
['aiflow init', '--framework <fw> --adapter <ad>', 'Initialize project'],
|
|
161
|
+
['aiflow init', '--env <tools>', 'AI tools: cursor,gemini,copilot'],
|
|
162
|
+
['aiflow init', '--with-rtk | --no-rtk', 'Enable/disable RTK compression'],
|
|
161
163
|
['aiflow doctor', '', 'Check setup health'],
|
|
164
|
+
['aiflow doctor', '--verbose', 'Detailed health check output'],
|
|
162
165
|
['aiflow update', '', 'Update to the latest version'],
|
|
166
|
+
['aiflow update', '--force', 'Force update even if already latest'],
|
|
163
167
|
['aiflow remove', '', 'Remove ai-flow-kit from project'],
|
|
164
168
|
['aiflow remove', '--version <ver>', 'Remove a cached version'],
|
|
165
169
|
['aiflow remove', '--global', 'Uninstall global package'],
|
|
@@ -169,16 +173,33 @@ function showCommands() {
|
|
|
169
173
|
group: 'Workflow (use per task)',
|
|
170
174
|
items: [
|
|
171
175
|
['aiflow use <TICKET-ID>', '', 'Load context from Backlog/Jira'],
|
|
176
|
+
['aiflow use', '--fast', 'Fast Mode: minimal Q&A (Default)'],
|
|
177
|
+
['aiflow use', '--full', 'Full Mode: deep analysis with Q&A'],
|
|
178
|
+
['aiflow use', '--file <path>', 'Load from local text/json file'],
|
|
172
179
|
['aiflow use', '--manual', 'Enter context manually'],
|
|
173
180
|
['aiflow use', '--with-comments', 'Include all comments'],
|
|
174
181
|
['aiflow use', '--comments-last <n>', 'Only get last N comments'],
|
|
182
|
+
['aiflow use', '--comments-from <n>', 'Get comments from index N onward'],
|
|
175
183
|
['aiflow prompt <type>', '', 'Generate AI prompt'],
|
|
176
184
|
['aiflow prompt', '--list', 'View all prompt types'],
|
|
177
185
|
['aiflow prompt', '--output <file>', 'Save prompt to file'],
|
|
178
186
|
['aiflow prompt', '--lang vietnamese', 'Prompt in Vietnamese'],
|
|
187
|
+
['aiflow prompt', '--detail <level>', 'Detail: minimal|standard|comprehensive'],
|
|
179
188
|
['aiflow detect "<description>"', '', 'Auto-detect task type'],
|
|
180
189
|
]
|
|
181
190
|
},
|
|
191
|
+
{
|
|
192
|
+
group: 'Task management',
|
|
193
|
+
items: [
|
|
194
|
+
['aiflow task status', '', 'Show active and pending tasks'],
|
|
195
|
+
['aiflow task list', '', 'List all saved tasks'],
|
|
196
|
+
['aiflow task pause', '--note <text>', 'Pause current task and save progress'],
|
|
197
|
+
['aiflow task switch <id>', '', 'Pause current and switch to another'],
|
|
198
|
+
['aiflow task resume <id>', '', 'Resume a paused task'],
|
|
199
|
+
['aiflow task reset <id>', '', 'Reset task to Gate 1 (keeps context)'],
|
|
200
|
+
['aiflow task remove <id>', '', 'Permanently delete all task data'],
|
|
201
|
+
]
|
|
202
|
+
},
|
|
182
203
|
{
|
|
183
204
|
group: 'Context management',
|
|
184
205
|
items: [
|
|
@@ -206,6 +227,7 @@ function showCommands() {
|
|
|
206
227
|
items: [
|
|
207
228
|
['aiflow validate <file>', '', 'Validate code against team rules'],
|
|
208
229
|
['aiflow validate <file>', '--fix', 'Auto-fix trailing whitespace'],
|
|
230
|
+
['aiflow validate <file>', '--verbose', 'Detailed validation report'],
|
|
209
231
|
['aiflow validate <file>', '-r lenient', 'Only report critical errors'],
|
|
210
232
|
['aiflow validate <file>', '-r strict', 'Report all warnings'],
|
|
211
233
|
]
|
|
@@ -224,6 +246,7 @@ function showCommands() {
|
|
|
224
246
|
items: [
|
|
225
247
|
['aiflow gate <n> start', '--ticket <id>', 'Log gate N started (AI calls this)'],
|
|
226
248
|
['aiflow gate <n> approved', '--ticket <id>', 'Log gate N approved (AI calls this)'],
|
|
249
|
+
['aiflow gate <n> start', '--ai-tool <tool>', 'Specify AI tool name'],
|
|
227
250
|
]
|
|
228
251
|
},
|
|
229
252
|
{
|
|
@@ -3,29 +3,28 @@
|
|
|
3
3
|
* SessionStart hook for ai-flow-kit
|
|
4
4
|
* Injects:
|
|
5
5
|
* 1. using-superpowers skill content
|
|
6
|
-
* 2. Active ticket context + gate workflow
|
|
7
|
-
* Cross-platform replacement for the bash session-start hook.
|
|
6
|
+
* 2. Active ticket context + gate-aware workflow instruction
|
|
8
7
|
*/
|
|
9
8
|
|
|
10
9
|
const fs = require('fs');
|
|
11
10
|
const path = require('path');
|
|
12
11
|
const { record } = require('../telemetry/record');
|
|
13
12
|
|
|
14
|
-
// Record session start telemetry
|
|
15
13
|
record('session.start');
|
|
16
14
|
|
|
17
15
|
// This script lives at .claude/hooks/session-start.js
|
|
18
|
-
// Project root is 2 levels up
|
|
16
|
+
// Project root is 2 levels up: .claude/hooks → .claude → project root
|
|
19
17
|
const projectRoot = path.resolve(__dirname, '..', '..');
|
|
20
|
-
const skillPath
|
|
18
|
+
const skillPath = path.join(projectRoot, '.claude', 'skills', 'using-superpowers', 'SKILL.md');
|
|
21
19
|
const contextPath = path.join(projectRoot, '.aiflow', 'context', 'current.json');
|
|
20
|
+
const tasksDir = path.join(projectRoot, '.aiflow', 'tasks');
|
|
22
21
|
|
|
23
22
|
// ── 1. Load superpowers skill ──────────────────────────────────
|
|
24
23
|
let skillContent = '';
|
|
25
24
|
try {
|
|
26
25
|
skillContent = fs.readFileSync(skillPath, 'utf-8');
|
|
27
26
|
} catch (_) {
|
|
28
|
-
|
|
27
|
+
process.stderr.write('[aiflow] WARNING: using-superpowers skill missing — run `aiflow update`\n');
|
|
29
28
|
}
|
|
30
29
|
|
|
31
30
|
// ── 2. Load active ticket context ──────────────────────────────
|
|
@@ -34,7 +33,8 @@ try {
|
|
|
34
33
|
if (fs.existsSync(contextPath)) {
|
|
35
34
|
const ctx = JSON.parse(fs.readFileSync(contextPath, 'utf-8'));
|
|
36
35
|
if (ctx.taskId && ctx.title) {
|
|
37
|
-
|
|
36
|
+
const taskState = loadTaskState(ctx.taskId);
|
|
37
|
+
contextBlock = buildContextPrompt(ctx, taskState);
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
} catch (_) {
|
|
@@ -54,7 +54,6 @@ if (contextBlock) {
|
|
|
54
54
|
|
|
55
55
|
const combined = parts.join('\n\n');
|
|
56
56
|
|
|
57
|
-
// Escape for JSON string embedding
|
|
58
57
|
const escaped = combined
|
|
59
58
|
.replace(/\\/g, '\\\\')
|
|
60
59
|
.replace(/"/g, '\\"')
|
|
@@ -71,15 +70,23 @@ process.stdout.write(JSON.stringify({
|
|
|
71
70
|
|
|
72
71
|
process.exit(0);
|
|
73
72
|
|
|
74
|
-
// ──
|
|
73
|
+
// ── Helpers ────────────────────────────────────────────────────
|
|
75
74
|
|
|
76
|
-
function
|
|
77
|
-
|
|
75
|
+
function loadTaskState(taskId) {
|
|
76
|
+
try {
|
|
77
|
+
const statePath = path.join(tasksDir, taskId, 'task-state.json');
|
|
78
|
+
if (fs.existsSync(statePath)) {
|
|
79
|
+
return JSON.parse(fs.readFileSync(statePath, 'utf-8'));
|
|
80
|
+
}
|
|
81
|
+
} catch (_) {}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
78
84
|
|
|
85
|
+
function buildContextPrompt(ctx, taskState) {
|
|
79
86
|
const lines = [];
|
|
80
87
|
lines.push('<ACTIVE_TASK>');
|
|
81
88
|
lines.push(`**Active Ticket:** ${ctx.taskId} — ${ctx.title}`);
|
|
82
|
-
lines.push(`**Type:** ${taskType}`);
|
|
89
|
+
lines.push(`**Type:** ${ctx.taskType || 'feature'}`);
|
|
83
90
|
lines.push(`**Status:** ${ctx.status || 'Unknown'}`);
|
|
84
91
|
lines.push(`**Assignee:** ${ctx.assignee || 'Unknown'}`);
|
|
85
92
|
lines.push('');
|
|
@@ -92,54 +99,146 @@ function buildContextPrompt(ctx) {
|
|
|
92
99
|
|
|
93
100
|
if (ctx.acceptanceCriteria && ctx.acceptanceCriteria.length > 0) {
|
|
94
101
|
lines.push('**Acceptance Criteria:**');
|
|
95
|
-
for (const c of ctx.acceptanceCriteria) {
|
|
96
|
-
lines.push(`- ${c}`);
|
|
97
|
-
}
|
|
102
|
+
for (const c of ctx.acceptanceCriteria) lines.push(`- ${c}`);
|
|
98
103
|
lines.push('');
|
|
99
104
|
}
|
|
100
105
|
|
|
101
106
|
if (ctx.comments && ctx.comments.length > 0) {
|
|
102
107
|
lines.push(`**Comments (${ctx.comments.length}):**`);
|
|
103
|
-
// Include last 5 comments to keep context manageable
|
|
104
108
|
const recent = ctx.comments.slice(-5);
|
|
105
|
-
for (const c of recent) {
|
|
106
|
-
lines.push(c);
|
|
107
|
-
lines.push('');
|
|
108
|
-
}
|
|
109
|
+
for (const c of recent) { lines.push(c); lines.push(''); }
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
if (ctx.context && ctx.context.files && ctx.context.files.length > 0) {
|
|
112
113
|
lines.push('**Related Files:**');
|
|
113
|
-
for (const f of ctx.context.files) {
|
|
114
|
-
lines.push(`- \`${f}\``);
|
|
115
|
-
}
|
|
114
|
+
for (const f of ctx.context.files) lines.push(`- \`${f}\``);
|
|
116
115
|
lines.push('');
|
|
117
116
|
}
|
|
118
117
|
|
|
119
|
-
// ── Gate workflow instruction ──
|
|
120
118
|
lines.push('---');
|
|
121
119
|
lines.push('');
|
|
122
|
-
lines.push('**AUTO-START GATE 1: You MUST begin analyzing this ticket immediately.**');
|
|
123
|
-
lines.push('');
|
|
124
|
-
lines.push(`INVOKE the \`read-study-requirement\` skill NOW for ticket ${ctx.taskId}.`);
|
|
125
|
-
lines.push('');
|
|
126
|
-
lines.push('Gate 1 process:');
|
|
127
|
-
lines.push('1. Read the ticket context above');
|
|
128
|
-
lines.push('2. Read CLAUDE.md — understand project architecture and conventions');
|
|
129
|
-
lines.push('3. Read source code — identify related files, data flow, patterns');
|
|
130
|
-
lines.push('4. If anything is unclear — ask ONE question at a time');
|
|
131
|
-
lines.push('5. Output plan/' + ctx.taskId + '/requirement.md with:');
|
|
132
|
-
lines.push(' - Requirements summary');
|
|
133
|
-
lines.push(' - Source code analysis');
|
|
134
|
-
lines.push(' - Proposed solution and approach');
|
|
135
|
-
lines.push(' - Impact analysis');
|
|
136
|
-
lines.push(' - Effort estimate');
|
|
137
|
-
lines.push(' - Testing plan');
|
|
138
|
-
lines.push('6. Display GATE 1 prompt and wait for APPROVED');
|
|
139
|
-
lines.push('');
|
|
140
|
-
lines.push('DO NOT wait for the developer to ask. START NOW.');
|
|
141
|
-
lines.push('If you have not started automatically, begin as soon as the developer types **"start"**.');
|
|
142
|
-
lines.push('</ACTIVE_TASK>');
|
|
143
120
|
|
|
121
|
+
// ── Gate-aware instruction ──────────────────────────────────
|
|
122
|
+
const mode = ctx.mode || 'fast';
|
|
123
|
+
const currentGate = taskState && taskState.currentGate ? taskState.currentGate : 1;
|
|
124
|
+
const isResume = taskState && taskState.pausedAt;
|
|
125
|
+
|
|
126
|
+
if (isResume && currentGate > 1) {
|
|
127
|
+
// Resuming a paused task — inject resume context
|
|
128
|
+
const gateApprovals = taskState.gateApprovals || {};
|
|
129
|
+
lines.push(`**RESUMING TASK: ${ctx.taskId} — Gate ${currentGate}**`);
|
|
130
|
+
lines.push('');
|
|
131
|
+
lines.push('This task was previously paused and is now being resumed.');
|
|
132
|
+
lines.push('');
|
|
133
|
+
lines.push('**Gate Progress:**');
|
|
134
|
+
for (let g = 1; g < currentGate; g++) {
|
|
135
|
+
const approvedAt = gateApprovals[String(g)];
|
|
136
|
+
const stamp = approvedAt ? ` — approved ${approvedAt}` : ' — approved';
|
|
137
|
+
lines.push(`- Gate ${g}: ✅ APPROVED${stamp}`);
|
|
138
|
+
}
|
|
139
|
+
lines.push(`- Gate ${currentGate}: 🔄 IN PROGRESS`);
|
|
140
|
+
lines.push('');
|
|
141
|
+
lines.push(`**DO NOT restart from Gate 1.** Resume directly at Gate ${currentGate} (${gateLabel(currentGate)}).`);
|
|
142
|
+
lines.push('');
|
|
143
|
+
|
|
144
|
+
if (currentGate === 2) {
|
|
145
|
+
lines.push('Gate 1 is already APPROVED. The requirement document is at:');
|
|
146
|
+
lines.push(` plan/${ctx.taskId}/requirement.md`);
|
|
147
|
+
lines.push('');
|
|
148
|
+
lines.push('Proceed to Gate 2: create the implementation plan.');
|
|
149
|
+
lines.push(buildModeInstruction(mode, 2));
|
|
150
|
+
} else if (currentGate === 3) {
|
|
151
|
+
lines.push('Gates 1 and 2 are already APPROVED. Plans are at:');
|
|
152
|
+
lines.push(` plan/${ctx.taskId}/requirement.md`);
|
|
153
|
+
lines.push(` plan/${ctx.taskId}/plan.md`);
|
|
154
|
+
lines.push('');
|
|
155
|
+
lines.push('Proceed to Gate 3: implement code following the plan (TDD).');
|
|
156
|
+
lines.push(buildModeInstruction(mode, 3));
|
|
157
|
+
} else if (currentGate === 4) {
|
|
158
|
+
lines.push('Gates 1–3 are APPROVED. Code has been implemented.');
|
|
159
|
+
lines.push('Proceed to Gate 4: run verification + impact-analysis + review checklist.');
|
|
160
|
+
lines.push(buildModeInstruction(mode, 4));
|
|
161
|
+
} else if (currentGate === 5) {
|
|
162
|
+
lines.push('Gates 1–4 are APPROVED. Self-review is done.');
|
|
163
|
+
lines.push('Proceed to Gate 5: guide the developer to create a Pull Request.');
|
|
164
|
+
}
|
|
165
|
+
if (taskState.notes) {
|
|
166
|
+
lines.push('');
|
|
167
|
+
lines.push(`**Note from developer:** ${taskState.notes}`);
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
// Fresh task — auto-start Gate 1
|
|
171
|
+
lines.push('**AUTO-START GATE 1: You MUST begin analyzing this ticket immediately.**');
|
|
172
|
+
lines.push('');
|
|
173
|
+
lines.push(`INVOKE the \`read-study-requirement\` skill NOW for ticket ${ctx.taskId}.`);
|
|
174
|
+
lines.push('');
|
|
175
|
+
|
|
176
|
+
if (mode === 'fast') {
|
|
177
|
+
lines.push('**MODE: fast** — Use the FAST TRACK path in the skill:');
|
|
178
|
+
lines.push('- Read ticket + identify changed files only (no deep source scan)');
|
|
179
|
+
lines.push('- Skip Q&A unless there is a critical blocking ambiguity');
|
|
180
|
+
lines.push('- Output a lite requirement doc (Sections 1, 3, 5 only)');
|
|
181
|
+
lines.push('- Target: Gate 1 complete in < 5 minutes');
|
|
182
|
+
} else {
|
|
183
|
+
lines.push('Gate 1 process:');
|
|
184
|
+
lines.push('1. Read the ticket context above');
|
|
185
|
+
lines.push('2. Read CLAUDE.md — understand project architecture and conventions');
|
|
186
|
+
lines.push('3. Read source code — identify related files, data flow, patterns');
|
|
187
|
+
lines.push('4. If anything is unclear — ask ONE question at a time');
|
|
188
|
+
lines.push('5. Output plan/' + ctx.taskId + '/requirement.md');
|
|
189
|
+
lines.push('6. Display GATE 1 prompt and wait for APPROVED');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
lines.push('');
|
|
193
|
+
lines.push('DO NOT wait for the developer to ask. START NOW.');
|
|
194
|
+
lines.push('If you have not started automatically, begin as soon as the developer types **"start"**.');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
lines.push('</ACTIVE_TASK>');
|
|
144
198
|
return lines.join('\n');
|
|
145
199
|
}
|
|
200
|
+
|
|
201
|
+
function gateLabel(n) {
|
|
202
|
+
const labels = {
|
|
203
|
+
1: 'AI Analyze Requirement',
|
|
204
|
+
2: 'Implementation Plan',
|
|
205
|
+
3: 'Code Generation',
|
|
206
|
+
4: 'AI Self-Review',
|
|
207
|
+
5: 'Peer Review & PR',
|
|
208
|
+
};
|
|
209
|
+
return labels[n] || `Gate ${n}`;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function buildModeInstruction(mode, gate) {
|
|
213
|
+
if (mode !== 'fast') return ''; // full mode: không inject gì thêm, AI chạy đầy đủ các bước
|
|
214
|
+
|
|
215
|
+
const instructions = {
|
|
216
|
+
2: [
|
|
217
|
+
'**MODE: fast** — Gate 2 fast track:',
|
|
218
|
+
'- Create a simple task list (task-level, NOT step-level)',
|
|
219
|
+
'- DO NOT invoke `superpowers:writing-plans`',
|
|
220
|
+
'- Format: numbered list of tasks, each with file + action',
|
|
221
|
+
'- No code snippets, no step-by-step TDD breakdown required',
|
|
222
|
+
],
|
|
223
|
+
3: [
|
|
224
|
+
'**MODE: fast** — Gate 3 fast track:',
|
|
225
|
+
'- DO NOT use `superpowers:subagent-driven-development`',
|
|
226
|
+
'- DO NOT spawn Spec Reviewer or Code Quality Reviewer subagents',
|
|
227
|
+
'- Implement ALL tasks directly in this session',
|
|
228
|
+
'- Write tests and implementation in the same pass per task',
|
|
229
|
+
'- Run the test suite ONCE at the end of all tasks (not after each step)',
|
|
230
|
+
'- If tests fail: fix inline, do not spawn a subagent',
|
|
231
|
+
],
|
|
232
|
+
4: [
|
|
233
|
+
'**MODE: fast** — Gate 4 fast track:',
|
|
234
|
+
'- Run the full test suite — this is MANDATORY',
|
|
235
|
+
'- Impact Analysis: quick scan only — reason from git diff and changed files.',
|
|
236
|
+
' DO NOT grep the entire codebase. List affected files and assess impact level.',
|
|
237
|
+
'- Review checklist (3 items only): tests pass ✅, no hardcoded secrets ✅, no SQL injection ✅',
|
|
238
|
+
'- summary.md: short format (Implementation table + Test result only)',
|
|
239
|
+
],
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const lines = instructions[gate];
|
|
243
|
+
return lines ? '\n' + lines.join('\n') : '';
|
|
244
|
+
}
|