@relipa/ai-flow-kit 0.0.6 → 0.0.7-beta.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.
Files changed (40) hide show
  1. package/README.md +41 -16
  2. package/bin/aiflow.js +65 -2
  3. package/custom/mcp-presets/gitnexus.json +8 -0
  4. package/custom/skills/generate-spec/SKILL.md +7 -0
  5. package/custom/skills/impact-analysis/SKILL.md +10 -0
  6. package/custom/skills/investigate-bug/SKILL.md +17 -0
  7. package/custom/skills/read-study-requirement/SKILL.md +11 -0
  8. package/custom/skills/review-plan/SKILL.md +15 -0
  9. package/custom/templates/php-plain.md +261 -0
  10. package/docs/common/AIFLOW.md +17 -6
  11. package/docs/common/CHANGELOG.md +63 -5
  12. package/docs/common/QUICK_START.md +33 -13
  13. package/docs/common/cli-reference.md +23 -0
  14. package/package.json +2 -7
  15. package/scripts/checkpoint.js +46 -0
  16. package/scripts/doctor.js +111 -8
  17. package/scripts/gitnexus-worker.js +94 -0
  18. package/scripts/guide.js +42 -51
  19. package/scripts/hooks/session-start.js +35 -5
  20. package/scripts/hooks/session-stop.js +55 -0
  21. package/scripts/init.js +299 -19
  22. package/scripts/prompt.js +2 -2
  23. package/scripts/remove.js +54 -0
  24. package/scripts/task.js +101 -0
  25. package/scripts/update.js +14 -4
  26. package/scripts/use.js +78 -6
  27. package/upstream/.claude-plugin/marketplace.json +4 -4
  28. package/upstream/.claude-plugin/plugin.json +2 -4
  29. package/upstream/.cursor-plugin/plugin.json +2 -4
  30. package/upstream/docs/plans/2025-11-22-opencode-support-design.md +1 -1
  31. package/upstream/docs/plans/2025-11-28-skills-improvements-from-user-feedback.md +2 -2
  32. package/upstream/docs/testing.md +2 -2
  33. package/upstream/skills/subagent-driven-development/SKILL.md +5 -6
  34. package/upstream/skills/subagent-driven-development/implementer-prompt.md +2 -3
  35. package/upstream/skills/systematic-debugging/CREATION-LOG.md +1 -1
  36. package/upstream/skills/systematic-debugging/root-cause-tracing.md +1 -1
  37. package/upstream/skills/tdd-lean/SKILL.md +127 -0
  38. package/upstream/skills/using-git-worktrees/SKILL.md +4 -5
  39. package/upstream/skills/writing-plans/SKILL.md +3 -9
  40. package/upstream/tests/brainstorm-server/package-lock.json +36 -0
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Background worker: runs `npx gitnexus analyze` and writes result to
4
+ * .aiflow/gitnexus-status.json so session-start.js / use.js can notify the developer.
5
+ *
6
+ * Spawned as a detached process by init.js — never run directly.
7
+ * Usage: node gitnexus-worker.js <projectDir>
8
+ */
9
+
10
+ const { spawn, execSync } = require('child_process');
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+
14
+ function commandExists(cmd) {
15
+ try {
16
+ const isWin = process.platform === 'win32';
17
+ const checkCmd = isWin ? `where ${cmd}` : `which ${cmd}`;
18
+ execSync(checkCmd, { stdio: 'ignore' });
19
+ return true;
20
+ } catch (_) {
21
+ return false;
22
+ }
23
+ }
24
+
25
+ const projectDir = process.argv[2];
26
+ if (!projectDir) process.exit(1);
27
+
28
+ const statusPath = path.join(projectDir, '.aiflow', 'gitnexus-status.json');
29
+ const logPath = path.join(projectDir, '.aiflow', 'gitnexus-analyze.log');
30
+
31
+ // Preserve startedAt written by init.js
32
+ let startedAt = new Date().toISOString();
33
+ try {
34
+ if (fs.existsSync(statusPath)) {
35
+ const existing = JSON.parse(fs.readFileSync(statusPath, 'utf-8'));
36
+ if (existing.startedAt) startedAt = existing.startedAt;
37
+ }
38
+ } catch (_) {}
39
+
40
+ // gitnexus requires Node >=20
41
+ const nodeMajor = parseInt(process.version.slice(1).split('.')[0], 10);
42
+ if (nodeMajor < 20) {
43
+ try {
44
+ fs.writeFileSync(statusPath, JSON.stringify({
45
+ status: 'error',
46
+ startedAt,
47
+ completedAt: new Date().toISOString(),
48
+ error: `Node.js version >=20 required (current: ${process.version})`,
49
+ }, null, 2));
50
+ } catch (_) {}
51
+ process.exit(0);
52
+ }
53
+
54
+ // Status path and log path already defined above
55
+
56
+ const logStream = fs.createWriteStream(logPath, { flags: 'w' });
57
+
58
+ // On Windows use shell:true — avoids EINVAL when spawning .cmd scripts
59
+ const isWin = process.platform === 'win32';
60
+
61
+ const hasGlobalGitNexus = commandExists('gitnexus');
62
+ const cmd = hasGlobalGitNexus ? 'gitnexus' : 'npx';
63
+ const args = hasGlobalGitNexus ? ['analyze'] : ['-y', 'gitnexus@latest', 'analyze'];
64
+
65
+ const child = spawn(cmd, args, {
66
+ cwd: projectDir,
67
+ shell: isWin,
68
+ windowsHide: true,
69
+ stdio: ['ignore', 'pipe', 'pipe'],
70
+ });
71
+
72
+ child.stdout.pipe(logStream);
73
+ child.stderr.pipe(logStream);
74
+
75
+ child.on('close', (code) => {
76
+ const status = code === 0
77
+ ? { status: 'done', startedAt, completedAt: new Date().toISOString() }
78
+ : { status: 'error', startedAt, completedAt: new Date().toISOString(), exitCode: code };
79
+ try {
80
+ fs.writeFileSync(statusPath, JSON.stringify(status, null, 2));
81
+ } catch (_) {}
82
+ });
83
+
84
+ child.on('error', (err) => {
85
+ try {
86
+ fs.writeFileSync(statusPath, JSON.stringify({
87
+ status: 'error',
88
+ startedAt,
89
+ completedAt: new Date().toISOString(),
90
+ exitCode: null,
91
+ error: err.message,
92
+ }, null, 2));
93
+ } catch (_) {}
94
+ });
package/scripts/guide.js CHANGED
@@ -1,3 +1,5 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
1
3
  const chalk = require('chalk');
2
4
 
3
5
  // ──────────────────────────────────────────────────────────────
@@ -40,56 +42,42 @@ function showHeader() {
40
42
  // ──────────────────────────────────────────────────────────────
41
43
 
42
44
  function showFlow() {
43
- console.log(chalk.bold.cyan(' 📐 ARCHITECTURE & FLOW\n'));
45
+ console.log(chalk.bold.cyan(' ⛩️ WORKFLOW OVERVIEW (AIFLOW.md)\n'));
44
46
 
45
- console.log(chalk.gray(' ┌──────────────────────────────────────────────────────┐'));
46
- console.log(chalk.gray(' │') + chalk.bold.yellow(' TECH LEAD (One-time setup)') + chalk.gray(' │'));
47
- console.log(chalk.gray(' │') + chalk.white(' 1. Configure custom/rules/, custom/templates/') + chalk.gray(' │'));
48
- console.log(chalk.gray(' │') + chalk.white(' 2. npm publish → developer: npm install -g pkg') + chalk.gray(' │'));
49
- console.log(chalk.gray(' └─────────────────────────┬────────────────────────────┘'));
50
- console.log(chalk.gray(' │'));
51
- console.log(chalk.gray(' ┌─────────────▼──────────────┐'));
52
- console.log(chalk.gray(' │') + chalk.bold.green(' aiflow init') + chalk.gray(' │'));
53
- console.log(chalk.gray(' │') + chalk.white(' --framework spring-boot') + chalk.gray(''));
54
- console.log(chalk.gray(' │') + chalk.white(' --adapter backlog') + chalk.gray(' │'));
55
- console.log(chalk.gray(' └─────────────┬──────────────┘'));
56
- console.log(chalk.gray(' │ generates'));
57
- console.log(chalk.gray(' ┌─────────────▼──────────────────────────┐'));
58
- console.log(chalk.gray(' │') + chalk.white(' .claude/skills/ ← superpowers + custom') + chalk.gray(' │'));
59
- console.log(chalk.gray(' │') + chalk.white(' .claude/hooks/ ← SessionStart hook') + chalk.gray(' │'));
60
- console.log(chalk.gray(' │') + chalk.white(' .rules/ ← team coding rules') + chalk.gray(' │'));
61
- console.log(chalk.gray(' │') + chalk.white(' CLAUDE.md ← framework AI prompt') + chalk.gray(' │'));
62
- console.log(chalk.gray(' │') + chalk.white(' .mcp.json ← MCP adapter config') + chalk.gray(' │'));
63
- console.log(chalk.gray(' └─────────────┬──────────────────────────┘'));
64
- console.log(chalk.gray(''));
65
- console.log(chalk.gray(' ┌──────────────────▼───────────────────┐'));
66
- console.log(chalk.gray(' │') + chalk.bold.green(' Developer Workflow (per task)') + chalk.gray(' │'));
67
- console.log(chalk.gray(' │') + chalk.white(' │'));
68
- console.log(chalk.gray(' │') + chalk.cyan(' 1. aiflow use PROJ-33') + chalk.gray(' │'));
69
- console.log(chalk.gray(' │') + chalk.gray(' └─ pull title, desc, comments from │'));
70
- console.log(chalk.gray(' │') + chalk.gray(' Backlog/Jira → current.json │'));
71
- console.log(chalk.gray(' │') + chalk.white(' │'));
72
- console.log(chalk.gray(' │') + chalk.cyan(' 2. aiflow prompt bug-fix') + chalk.gray(' │'));
73
- console.log(chalk.gray(' │') + chalk.gray(' └─ generate AI prompt from context │'));
74
- console.log(chalk.gray(' │') + chalk.gray(' + team rules paste into Claude │'));
75
- console.log(chalk.gray(' │') + chalk.white(' │'));
76
- console.log(chalk.gray(' │') + chalk.cyan(' 3. claude (open Claude terminal)') + chalk.gray(' │'));
77
- console.log(chalk.gray(' │') + chalk.gray(' └─ "Fix bug PROJ-33" (1 line) │'));
78
- console.log(chalk.gray(' └──────────────────┬───────────────────┘'));
79
- console.log(chalk.gray(' │'));
80
- console.log(chalk.gray(' ┌──────────────────▼───────────────────┐'));
81
- console.log(chalk.gray(' │') + chalk.bold.magenta(' Claude AI (automatic)') + chalk.gray(' │'));
82
- console.log(chalk.gray(' │') + chalk.white(' │'));
83
- console.log(chalk.gray(' │') + chalk.gray(' SessionStart hook injected superpowers │'));
84
- console.log(chalk.gray(' │') + chalk.gray(' ↓ │'));
85
- console.log(chalk.gray(' │') + chalk.gray(' Read CLAUDE.md (framework rules) │'));
86
- console.log(chalk.gray(' │') + chalk.gray(' ↓ │'));
87
- console.log(chalk.gray(' │') + chalk.gray(' Read context/current.json (ticket) │'));
88
- console.log(chalk.gray(' │') + chalk.gray(' ↓ │'));
89
- console.log(chalk.gray(' │') + chalk.gray(' Auto-detect task type → select skill │'));
90
- console.log(chalk.gray(' │') + chalk.gray(' ↓ │'));
91
- console.log(chalk.gray(' │') + chalk.gray(' Implement with team rules │'));
92
- console.log(chalk.gray(' └────────────────────────────────────────┘'));
47
+ try {
48
+ const aiflowPath = path.join(__dirname, '../docs/common/AIFLOW.md');
49
+ if (!fs.existsSync(aiflowPath)) {
50
+ console.log(chalk.red(' Error: AIFLOW.md not found.'));
51
+ return;
52
+ }
53
+
54
+ const content = fs.readFileSync(aiflowPath, 'utf8');
55
+ const lines = content.split('\n');
56
+
57
+ let inSection = false;
58
+ let sectionLines = [];
59
+
60
+ for (const line of lines) {
61
+ if (line.trim() === '## Workflow Overview') {
62
+ inSection = true;
63
+ continue;
64
+ }
65
+ if (inSection) {
66
+ if (line.startsWith('## ') || line.trim() === '---') {
67
+ break;
68
+ }
69
+ sectionLines.push(line);
70
+ }
71
+ }
72
+
73
+ if (sectionLines.length > 0) {
74
+ console.log(chalk.white(sectionLines.join('\n')));
75
+ } else {
76
+ console.log(chalk.yellow(' (Workflow Overview section not found in AIFLOW.md)'));
77
+ }
78
+ } catch (error) {
79
+ console.log(chalk.red(' Error reading AIFLOW.md: ' + error.message));
80
+ }
93
81
  console.log();
94
82
  }
95
83
 
@@ -159,11 +147,14 @@ function showCommands() {
159
147
  items: [
160
148
  ['aiflow init', '--framework <fw> --adapter <ad>', 'Initialize project'],
161
149
  ['aiflow init', '--env <tools>', 'AI tools: cursor,gemini,copilot'],
162
- ['aiflow init', '--with-rtk | --no-rtk', 'Enable/disable RTK compression'],
163
- ['aiflow doctor', '', 'Check setup health'],
150
+ ['aiflow init', '--with-rtk | --no-rtk', 'RTK bash compression (60–90% saving)'],
151
+ // ['aiflow init', '--with-gitnexus', 'GitNexus code intelligence (waits for index)'],
152
+ // ['aiflow init', '--with-gitnexus --no-wait', 'GitNexus: index in background'],
153
+ ['aiflow doctor', '', 'Check setup health + token savings status'],
164
154
  ['aiflow doctor', '--verbose', 'Detailed health check output'],
165
155
  ['aiflow update', '', 'Update to the latest version'],
166
156
  ['aiflow update', '--force', 'Force update even if already latest'],
157
+ ['aiflow sync-skills', '', 'Manually sync AI Instruction files'],
167
158
  ['aiflow remove', '', 'Remove ai-flow-kit from project'],
168
159
  ['aiflow remove', '--version <ver>', 'Remove a cached version'],
169
160
  ['aiflow remove', '--global', 'Uninstall global package'],
@@ -41,7 +41,37 @@ try {
41
41
  // Context missing or invalid — continue without it
42
42
  }
43
43
 
44
- // ── 3. Combine and output ──────────────────────────────────────
44
+ // ── 3. Check GitNexus background indexing status ───────────────
45
+ const GITNEXUS_STALE_MS = 3 * 60 * 60 * 1000; // 3 hours
46
+ const gitNexusStatusPath = path.join(projectRoot, '.aiflow', 'gitnexus-status.json');
47
+ try {
48
+ if (fs.existsSync(gitNexusStatusPath)) {
49
+ const gn = JSON.parse(fs.readFileSync(gitNexusStatusPath, 'utf-8'));
50
+ if (!gn.notified) {
51
+ // Detect stale "running" — process was killed without updating status
52
+ if (gn.status === 'running' && gn.startedAt) {
53
+ const age = Date.now() - new Date(gn.startedAt).getTime();
54
+ if (age > GITNEXUS_STALE_MS) {
55
+ gn.status = 'error';
56
+ gn.error = 'timed out — process may have been killed or ran out of memory';
57
+ }
58
+ }
59
+ if (gn.status === 'done') {
60
+ process.stderr.write('[aiflow] ✓ GitNexus indexing complete — code intelligence tools are ready.\n');
61
+ } else if (gn.status === 'error') {
62
+ const detail = gn.error ? ` (${gn.error})` : gn.exitCode != null ? ` (exit ${gn.exitCode})` : '';
63
+ process.stderr.write(`[aiflow] ✗ GitNexus indexing failed${detail}.\n`);
64
+ // process.stderr.write('[aiflow] Retry: aiflow init --with-gitnexus\n');
65
+ }
66
+ if (gn.status === 'done' || gn.status === 'error') {
67
+ gn.notified = true;
68
+ fs.writeFileSync(gitNexusStatusPath, JSON.stringify(gn, null, 2));
69
+ }
70
+ }
71
+ }
72
+ } catch (_) {}
73
+
74
+ // ── 4. Combine and output ──────────────────────────────────────
45
75
  const parts = [];
46
76
 
47
77
  if (skillContent) {
@@ -222,12 +252,12 @@ function buildModeInstruction(mode, gate) {
222
252
  ],
223
253
  3: [
224
254
  '**MODE: fast** — Gate 3 fast track:',
255
+ '- INVOKE `tdd-lean` skill (NOT superpowers:test-driven-development)',
256
+ '- tdd-lean uses Batch Red-Green-Refactor: 2–3 test runs total, never per-test',
257
+ '- Test Output Discipline: keep only summary line after each run, discard stack traces',
225
258
  '- DO NOT use `superpowers:subagent-driven-development`',
226
259
  '- 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',
260
+ '- Call `aiflow checkpoint` at each milestone (tests-written, code-done, tests-pass)',
231
261
  ],
232
262
  4: [
233
263
  '**MODE: fast** — Gate 4 fast track:',
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * SessionStop hook for ai-flow-kit
4
+ * Reads checkpoints.json for the active task and prints a token usage summary.
5
+ */
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const { record } = require('../telemetry/record');
10
+
11
+ record('session.stop');
12
+
13
+ const projectRoot = path.resolve(__dirname, '..', '..');
14
+ const contextFile = path.join(projectRoot, '.aiflow', 'context', 'current.json');
15
+ const tasksDir = path.join(projectRoot, '.aiflow', 'tasks');
16
+
17
+ const GATE_LABELS = { 1: 'Analyze', 2: 'Plan', 3: 'Code', 4: 'Review', 5: 'PR' };
18
+
19
+ try {
20
+ if (!fs.existsSync(contextFile)) process.exit(0);
21
+ const ctx = JSON.parse(fs.readFileSync(contextFile, 'utf-8'));
22
+ const ticketId = ctx && ctx.taskId;
23
+ if (!ticketId) process.exit(0);
24
+
25
+ const checkpointPath = path.join(tasksDir, ticketId, 'checkpoints.json');
26
+ if (!fs.existsSync(checkpointPath)) process.exit(0);
27
+
28
+ const data = JSON.parse(fs.readFileSync(checkpointPath, 'utf-8'));
29
+ const checkpoints = data.checkpoints || [];
30
+ if (checkpoints.length === 0) process.exit(0);
31
+
32
+ // Aggregate latest token value per gate
33
+ const gateTokens = {};
34
+ for (const cp of checkpoints) {
35
+ const g = cp.gate;
36
+ if (!gateTokens[g] || cp.tokens > gateTokens[g]) {
37
+ gateTokens[g] = cp.tokens;
38
+ }
39
+ }
40
+
41
+ const gates = Object.keys(gateTokens).sort();
42
+ const total = gates.reduce((sum, g) => sum + gateTokens[g], 0);
43
+
44
+ process.stderr.write('\n[aiflow] Token usage this session:\n');
45
+ for (const g of gates) {
46
+ const label = GATE_LABELS[parseInt(g)] || `Gate ${g}`;
47
+ const t = gateTokens[g];
48
+ const bar = '█'.repeat(Math.round((t / total) * 20)).padEnd(20, '░');
49
+ const pct = Math.round((t / total) * 100);
50
+ process.stderr.write(`[aiflow] Gate ${g} ${label.padEnd(8)} ~${t.toLocaleString().padStart(6)} ${bar} ${pct}%\n`);
51
+ }
52
+ process.stderr.write(`[aiflow] Total estimated: ~${total.toLocaleString()} tokens\n\n`);
53
+ } catch (_) {}
54
+
55
+ process.exit(0);