@relipa/ai-flow-kit 0.0.6-beta.0 → 0.0.7-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.
Files changed (40) hide show
  1. package/README.md +33 -13
  2. package/bin/aiflow.js +58 -2
  3. package/custom/mcp-presets/gitnexus.json +8 -0
  4. package/custom/rules/java/spring-boot-examples.md +329 -329
  5. package/custom/skills/generate-spec/SKILL.md +7 -0
  6. package/custom/skills/impact-analysis/SKILL.md +10 -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/spring-boot.md +224 -224
  10. package/docs/common/AIFLOW.md +12 -6
  11. package/docs/common/CHANGELOG.md +49 -5
  12. package/docs/common/QUICK_START.md +13 -11
  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 +192 -89
  17. package/scripts/gitnexus-worker.js +94 -0
  18. package/scripts/guide.js +42 -51
  19. package/scripts/hooks/session-start.js +274 -244
  20. package/scripts/hooks/session-stop.js +55 -0
  21. package/scripts/init.js +293 -18
  22. package/scripts/prompt.js +2 -2
  23. package/scripts/remove.js +54 -0
  24. package/scripts/task.js +446 -384
  25. package/scripts/update.js +14 -4
  26. package/scripts/use.js +41 -0
  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
package/scripts/init.js CHANGED
@@ -8,6 +8,18 @@ const PKG_DIR = path.join(__dirname, '..');
8
8
  const GLOBAL_AIFLOW_DIR = path.join(os.homedir(), '.aiflow');
9
9
  const GLOBAL_CREDENTIALS_PATH = path.join(GLOBAL_AIFLOW_DIR, 'credentials.json');
10
10
  const PKG_VERSION = require('../package.json').version;
11
+ const { execSync, spawn } = require('child_process');
12
+
13
+ function commandExists(cmd) {
14
+ try {
15
+ const isWin = process.platform === 'win32';
16
+ const checkCmd = isWin ? `where ${cmd}` : `which ${cmd}`;
17
+ execSync(checkCmd, { stdio: 'ignore' });
18
+ return true;
19
+ } catch (_) {
20
+ return false;
21
+ }
22
+ }
11
23
 
12
24
  // Map framework → language family for picking the right rules
13
25
  const FRAMEWORK_LANGUAGE = {
@@ -157,9 +169,13 @@ async function setupSuperpowersHook(_projectDir, claudeDir) {
157
169
  const hooksDir = path.join(claudeDir, 'hooks');
158
170
  await fs.ensureDir(hooksDir);
159
171
 
160
- const hookSrc = path.join(PKG_DIR, 'scripts', 'hooks', 'session-start.js');
161
- const hookDest = path.join(hooksDir, 'session-start.js');
162
- await fs.copy(hookSrc, hookDest, { overwrite: true });
172
+ const hookStartSrc = path.join(PKG_DIR, 'scripts', 'hooks', 'session-start.js');
173
+ const hookStartDest = path.join(hooksDir, 'session-start.js');
174
+ await fs.copy(hookStartSrc, hookStartDest, { overwrite: true });
175
+
176
+ const hookStopSrc = path.join(PKG_DIR, 'scripts', 'hooks', 'session-stop.js');
177
+ const hookStopDest = path.join(hooksDir, 'session-stop.js');
178
+ await fs.copy(hookStopSrc, hookStopDest, { overwrite: true });
163
179
 
164
180
  const settingsFile = path.join(claudeDir, 'settings.json');
165
181
  let settings = {};
@@ -169,6 +185,7 @@ async function setupSuperpowersHook(_projectDir, claudeDir) {
169
185
 
170
186
  if (!settings.hooks) settings.hooks = {};
171
187
  if (!settings.hooks.SessionStart) settings.hooks.SessionStart = [];
188
+ if (!settings.hooks.SessionStop) settings.hooks.SessionStop = [];
172
189
 
173
190
  settings.hooks.SessionStart = settings.hooks.SessionStart.filter(
174
191
  h => !(h._aiflowKit)
@@ -178,13 +195,26 @@ async function setupSuperpowersHook(_projectDir, claudeDir) {
178
195
  hooks: [
179
196
  {
180
197
  type: 'command',
181
- command: `node "${hookDest.replace(/\\/g, '/')}"`,
198
+ command: `node "${hookStartDest.replace(/\\/g, '/')}"`,
199
+ }
200
+ ]
201
+ });
202
+
203
+ settings.hooks.SessionStop = settings.hooks.SessionStop.filter(
204
+ h => !(h._aiflowKit)
205
+ );
206
+ settings.hooks.SessionStop.push({
207
+ _aiflowKit: true,
208
+ hooks: [
209
+ {
210
+ type: 'command',
211
+ command: `node "${hookStopDest.replace(/\\/g, '/')}"`,
182
212
  }
183
213
  ]
184
214
  });
185
215
 
186
216
  await fs.writeJson(settingsFile, settings, { spaces: 2 });
187
- console.log(chalk.green(`✓ Superpowers SessionStart hook configured`));
217
+ console.log(chalk.green(`✓ Superpowers hooks (SessionStart, SessionStop) configured`));
188
218
  }
189
219
 
190
220
  async function verifySuperpowersSkills(versionDir) {
@@ -273,24 +303,66 @@ async function setupFramework(projectDir, framework, multi = false, selectedTool
273
303
  finalContent += workflowContent + '\n\n';
274
304
  }
275
305
 
276
- finalContent += frameworkContent;
306
+ const markerStart = '<!-- aiflow-kit-start -->';
307
+ const markerEnd = '<!-- aiflow-kit-end -->';
308
+
309
+ finalContent = `${markerStart}\n${finalContent.trim()}\n${markerEnd}`;
277
310
 
278
311
  const fileExists = await fs.pathExists(targetPath);
279
312
 
280
313
  if (!multi) {
281
314
  const relPath = path.relative(projectDir, targetPath).replace(/\\/g, '/');
282
315
  if (fileExists) {
283
- // File already exists — save aiflow template to .aiflow/reference/ for comparison.
284
- // Developer owns the file in root; we never overwrite without explicit action.
285
- const refPath = path.join(projectDir, '.aiflow', 'reference', path.basename(targetPath));
286
- await fs.ensureDir(path.dirname(refPath));
287
- await fs.writeFile(refPath, finalContent);
288
- const refRel = path.relative(projectDir, refPath).replace(/\\/g, '/');
289
- console.log(chalk.gray(` ${relPath} exists aiflow template saved to ${refRel}`));
290
- skipped.push(relPath);
316
+ const existingContent = await fs.readFile(targetPath, 'utf-8');
317
+ const hasStart = existingContent.includes(markerStart);
318
+ const hasEnd = existingContent.includes(markerEnd);
319
+
320
+ if (hasStart && hasEnd) {
321
+ const doUpdate = await confirm({
322
+ message: `File ${relPath} already contains an AI Flow block. Do you want to update this block? (Your custom rules will not be affected)`,
323
+ default: true
324
+ });
325
+ if (doUpdate) {
326
+ const regex = new RegExp(`${markerStart}[\\s\\S]*?${markerEnd}`, 'g');
327
+ const updatedContent = existingContent.replace(regex, finalContent);
328
+ await fs.writeFile(targetPath, updatedContent);
329
+ console.log(chalk.green(` ✓ Updated AI Flow block in ${relPath}`));
330
+ written.push(AI_TOOL_FILES[tool]);
331
+ } else {
332
+ const refPath = path.join(projectDir, '.aiflow', 'reference', path.basename(targetPath));
333
+ await fs.ensureDir(path.dirname(refPath));
334
+ await fs.writeFile(refPath, finalContent);
335
+ const refRel = path.relative(projectDir, refPath).replace(/\\/g, '/');
336
+ console.log(chalk.gray(` ✓ Skipped ${relPath} — aiflow template saved to ${refRel}`));
337
+ skipped.push(relPath);
338
+ }
339
+ } else {
340
+ const doOverwrite = await confirm({
341
+ message: `File ${relPath} is missing the new AI Flow marker block. Do you want to OVERWRITE it completely with the latest template?`,
342
+ default: true
343
+ });
344
+ if (doOverwrite) {
345
+ await fs.writeFile(targetPath, finalContent);
346
+ console.log(chalk.green(` ✓ Overwrote ${relPath} with latest AI Flow template`));
347
+ written.push(AI_TOOL_FILES[tool]);
348
+ } else {
349
+ const refPath = path.join(projectDir, '.aiflow', 'reference', path.basename(targetPath));
350
+ await fs.ensureDir(path.dirname(refPath));
351
+ await fs.writeFile(refPath, finalContent);
352
+ const refRel = path.relative(projectDir, refPath).replace(/\\/g, '/');
353
+ console.log(chalk.gray(` ✓ Skipped ${relPath} — aiflow template saved to ${refRel}`));
354
+ skipped.push(relPath);
355
+ }
356
+ }
291
357
  } else {
292
- await fs.writeFile(targetPath, finalContent);
293
- written.push(AI_TOOL_FILES[tool]);
358
+ const doCreate = await confirm({
359
+ message: `File ${relPath} does not exist. Do you want to create it using the latest AI Flow template?`,
360
+ default: true
361
+ });
362
+ if (doCreate) {
363
+ await fs.writeFile(targetPath, finalContent);
364
+ written.push(AI_TOOL_FILES[tool]);
365
+ }
294
366
  }
295
367
  } else {
296
368
  if (fileExists) {
@@ -494,9 +566,19 @@ function verifyFigma(credentials) {
494
566
  });
495
567
  }
496
568
 
497
- async function ensureCredentialsGitignored(projectDir) {
569
+ async function ensureAiflowGitignored(projectDir) {
498
570
  const gitignorePath = path.join(projectDir, '.gitignore');
499
- const entries = ['.aiflow/no-telemetry'];
571
+ const entries = [
572
+ '.aiflow/',
573
+ 'plan/',
574
+ '.claude/',
575
+ '.rules/',
576
+ '.mcp.json',
577
+ 'CLAUDE.md',
578
+ 'GEMINI.md',
579
+ '.cursorrules',
580
+ '.github/copilot-instructions.md'
581
+ ];
500
582
  let content = '';
501
583
  if (await fs.pathExists(gitignorePath)) {
502
584
  content = await fs.readFile(gitignorePath, 'utf-8');
@@ -523,6 +605,190 @@ function maskSecret(value) {
523
605
  return `${value.slice(0, 4)}***${value.slice(-4)}`;
524
606
  }
525
607
 
608
+ // ── GitNexus (code intelligence) helpers ─────────────────────────
609
+
610
+ const IGNORE_DIRS = new Set([
611
+ '.git', 'node_modules', 'target', 'build', '.aiflow',
612
+ 'dist', 'out', '.next', '.nuxt', 'vendor', 'coverage', '.gradle',
613
+ ]);
614
+ const SOURCE_EXT = /\.(java|kt|groovy|js|ts|jsx|tsx|mjs|cjs|php|py|go|rb|cs|cpp|c|h|rs|vue|svelte|xml|yml|yaml|sql|properties)$/i;
615
+ async function countSourceFiles(dir) {
616
+ let count = 0;
617
+ const scan = async (d) => {
618
+ let entries;
619
+ try { entries = await fs.readdir(d, { withFileTypes: true }); } catch (_) { return; }
620
+ for (const e of entries) {
621
+ if (e.isDirectory()) {
622
+ if (!IGNORE_DIRS.has(e.name) && !e.name.startsWith('.')) await scan(path.join(d, e.name));
623
+ } else if (SOURCE_EXT.test(e.name)) {
624
+ count++;
625
+ }
626
+ }
627
+ };
628
+ await scan(dir);
629
+ return count;
630
+ }
631
+
632
+ async function isGitNexusConfigured(projectDir) {
633
+ const mcpPath = path.join(projectDir, '.mcp.json');
634
+ if (!(await fs.pathExists(mcpPath))) return false;
635
+ const mcp = await fs.readJson(mcpPath).catch(() => ({}));
636
+ return !!(mcp.mcpServers && mcp.mcpServers.gitnexus);
637
+ }
638
+
639
+ async function setupGitNexus(projectDir, wait = false) {
640
+ const { spawn } = require('child_process');
641
+ // On Windows, use shell:true so npm scripts (.cmd) resolve correctly — avoids EINVAL
642
+ const isWin = process.platform === 'win32';
643
+ const npxCmd = 'npx';
644
+ const spawnOpts = (extra = {}) => ({ shell: isWin, windowsHide: true, ...extra });
645
+
646
+ // 1. Inject MCP server config into .mcp.json (merge, don't overwrite)
647
+ const mcpPath = path.join(projectDir, '.mcp.json');
648
+ let existing = {};
649
+ if (await fs.pathExists(mcpPath)) {
650
+ existing = await fs.readJson(mcpPath).catch(() => ({}));
651
+ }
652
+ if (!existing.mcpServers) existing.mcpServers = {};
653
+ const mcpAlreadyPresent = !!(existing.mcpServers.gitnexus);
654
+
655
+ // 3. Detect if gitnexus is already available in PATH (preferred over npx)
656
+ const hasGlobalGitNexus = commandExists('gitnexus');
657
+ if (hasGlobalGitNexus) {
658
+ existing.mcpServers.gitnexus = { command: 'gitnexus', args: ['mcp'] };
659
+ console.log(chalk.gray(' ✓ Global gitnexus found — using it for MCP and indexing.'));
660
+ } else {
661
+ existing.mcpServers.gitnexus = { command: 'npx', args: ['-y', 'gitnexus@latest', 'mcp'] };
662
+ }
663
+ await fs.writeJson(mcpPath, existing, { spaces: 2 });
664
+ if (!mcpAlreadyPresent) {
665
+ console.log(chalk.green('✓ GitNexus MCP server configured (.mcp.json)'));
666
+ }
667
+
668
+ const nodeMajor = parseInt(process.version.slice(1).split('.')[0], 10);
669
+ if (nodeMajor < 20) {
670
+ console.log(chalk.yellow(`⚠ GitNexus indexing skipped — requires Node.js >=20.0.0 (current: ${process.version}).`));
671
+ console.log(chalk.gray(' MCP config saved. After upgrading Node.js, run: npx gitnexus analyze'));
672
+ return;
673
+ }
674
+
675
+ const aiflowDir = path.join(projectDir, '.aiflow');
676
+ await fs.ensureDir(aiflowDir);
677
+ const statusPath = path.join(aiflowDir, 'gitnexus-status.json');
678
+ const startedAt = new Date().toISOString();
679
+ await fs.writeJson(statusPath, { status: 'running', startedAt });
680
+
681
+ if (!wait) {
682
+ const workerPath = path.join(__dirname, 'gitnexus-worker.js');
683
+ const child = spawn(process.execPath, [workerPath, projectDir],
684
+ spawnOpts({ detached: true, stdio: 'ignore' }));
685
+ child.unref();
686
+ console.log(chalk.cyan('⟳ GitNexus: indexing codebase in background...'));
687
+ console.log(chalk.gray(' Notification will appear when you run `aiflow use <TICKET>` next time.'));
688
+ return;
689
+ }
690
+
691
+ const fileCount = await countSourceFiles(projectDir);
692
+ console.log(chalk.cyan(`⟳ GitNexus: indexing ${fileCount} source files (this may take 10–30 min on first run)...`));
693
+ console.log(chalk.gray(' Remove --wait to index in background instead.'));
694
+
695
+ await new Promise((resolve) => {
696
+ const runIndexing = (withMoreRam = false) => {
697
+ const currentCmd = hasGlobalGitNexus ? 'gitnexus' : 'npx';
698
+ const currentArgs = hasGlobalGitNexus ? ['analyze'] : ['-y', 'gitnexus@latest', 'analyze'];
699
+ const env = { ...process.env };
700
+ if (withMoreRam) {
701
+ const ramSize = withMoreRam === '8g' ? '8192' : '4096';
702
+ env.NODE_OPTIONS = `--max-old-space-size=${ramSize}`;
703
+ }
704
+
705
+ const child = spawn(currentCmd, currentArgs,
706
+ spawnOpts({ cwd: projectDir, stdio: ['ignore', 'inherit', 'inherit'], env }));
707
+
708
+ child.on('close', async (code) => {
709
+ const completedAt = new Date().toISOString();
710
+ if (code === 0) {
711
+ await fs.writeJson(statusPath, { status: 'done', startedAt, completedAt, notified: true });
712
+ console.log(chalk.green(`✓ GitNexus: indexed ${fileCount} files — code intelligence is ready.`));
713
+ resolve();
714
+ } else {
715
+ console.log(chalk.red(`✗ GitNexus: indexing failed (exit ${code}).`));
716
+
717
+ // Detection of Segmentation Fault / Crash (139 on Linux/Mac, 3221225477 on Windows)
718
+ const isCrash = (code === 139 || code === 3221225477);
719
+ if (isCrash) {
720
+ console.log(chalk.yellow(`\n⚠ Indexing crashed (Segmentation Fault) at ${withMoreRam ? (withMoreRam === '8g' ? '8GB' : '4GB') : 'default'} RAM.`));
721
+
722
+ const nextRam = !withMoreRam ? '4g' : (withMoreRam === '4g' ? '8g' : null);
723
+ if (nextRam) {
724
+ const retry = await confirm({
725
+ message: `Would you like to retry indexing with increased memory (${nextRam.toUpperCase()})?`,
726
+ default: true
727
+ });
728
+ if (retry) {
729
+ return runIndexing(nextRam); // Recursion, don't resolve yet
730
+ }
731
+ } else {
732
+ console.log(chalk.red('⚠ Already tried with 8GB RAM. Please check if there are any extremely large files or corrupted database.'));
733
+ console.log(chalk.gray(' Try: rm -rf .aiflow/ladybug.db (if exists) or check your .gitignore'));
734
+ }
735
+ }
736
+
737
+ // If npx failed and gitnexus isn't global, offer to install it global (only if not a crash or if retry was rejected)
738
+ if (!hasGlobalGitNexus && !isCrash) {
739
+ console.log(chalk.yellow('\nTip: npx failed. Installing gitnexus globally often fixes environment issues.'));
740
+ const install = await confirm({ message: 'Would you like me to try installing gitnexus globally for you?', default: true });
741
+ if (install) {
742
+ try {
743
+ console.log(chalk.blue('Running: npm install -g gitnexus ...'));
744
+ execSync('npm install -g gitnexus', { stdio: 'inherit' });
745
+ console.log(chalk.green('✓ GitNexus installed globally. Please run `aiflow init` again to retry indexing.'));
746
+ } catch (err) {
747
+ console.log(chalk.red('Failed to install globally. Please try manually: npm install -g gitnexus'));
748
+ }
749
+ }
750
+ }
751
+
752
+ await fs.writeJson(statusPath, { status: 'error', startedAt, completedAt, exitCode: code, notified: true });
753
+ resolve();
754
+ }
755
+ });
756
+
757
+ child.on('error', async (err) => {
758
+ await fs.writeJson(statusPath, {
759
+ status: 'error', startedAt, completedAt: new Date().toISOString(),
760
+ exitCode: null, error: err.message, notified: true,
761
+ });
762
+ console.log(chalk.red(`✗ GitNexus: failed to start — ${err.message}`));
763
+ resolve();
764
+ });
765
+ };
766
+
767
+ runIndexing();
768
+ });
769
+ }
770
+
771
+ async function maybeSetupGitNexus(projectDir, withGitNexus, wait = false) {
772
+ if (!withGitNexus) return;
773
+
774
+ // Check if indexing has already completed successfully (not stale)
775
+ const STALE_MS = 3 * 60 * 60 * 1000;
776
+ const statusPath = path.join(projectDir, '.aiflow', 'gitnexus-status.json');
777
+ let indexDone = false;
778
+ try {
779
+ const gn = await fs.readJson(statusPath);
780
+ const age = gn.startedAt ? Date.now() - new Date(gn.startedAt).getTime() : Infinity;
781
+ const staleRunning = gn.status === 'running' && age > STALE_MS;
782
+ indexDone = gn.status === 'done' && !staleRunning;
783
+ } catch (_) {}
784
+
785
+ if ((await isGitNexusConfigured(projectDir)) && indexDone) {
786
+ console.log(chalk.gray(' GitNexus already configured and indexed — skipping.'));
787
+ return;
788
+ }
789
+ await setupGitNexus(projectDir, wait);
790
+ }
791
+
526
792
  // ── RTK (token compression) helpers ──────────────────────────────
527
793
 
528
794
  async function detectRtk() {
@@ -657,6 +923,12 @@ async function init(options) {
657
923
  await maybeSetupRtk(projectDir, options.withRtk || false);
658
924
  }
659
925
 
926
+ // ── Optional: GitNexus code intelligence ─────────────────────
927
+ await maybeSetupGitNexus(projectDir, options.withGitnexus || false, options.wait || false);
928
+
929
+ // ── Ensure all aiflow files are gitignored ───────────────────
930
+ await ensureAiflowGitignored(projectDir);
931
+
660
932
  console.log(chalk.green('\n✨ Initialized AI Flow Kit successfully!'));
661
933
  console.log(chalk.blue('\n' + '─'.repeat(62)));
662
934
  console.log(chalk.bold.white(' Next steps:'));
@@ -682,3 +954,6 @@ module.exports = init;
682
954
  module.exports.AI_TOOL_FILES = AI_TOOL_FILES;
683
955
  module.exports.detectRtk = detectRtk;
684
956
  module.exports.isRtkHookConfigured = isRtkHookConfigured;
957
+ module.exports.isGitNexusConfigured = isGitNexusConfigured;
958
+ module.exports.setupFramework = setupFramework;
959
+ module.exports.ensureAiflowGitignored = ensureAiflowGitignored;
package/scripts/prompt.js CHANGED
@@ -96,7 +96,7 @@ AI must:
96
96
  Only runs after Gate 2 APPROVED.
97
97
  1. Complex feature (3+ files): **INVOKE:** \`superpowers:subagent-driven-development\`
98
98
  2. **INVOKE:** \`superpowers:test-driven-development\` — write failing test first
99
- 3. Implement to make tests pass — commit each small step
99
+ 3. Implement to make tests pass — verify each small step
100
100
 
101
101
  ### GATE 4 — AI Self-Review (wait for APPROVED)
102
102
  **INVOKE:** \`review-plan\` skill
@@ -174,7 +174,7 @@ AI must:
174
174
  Only runs after Gate 2 APPROVED.
175
175
  1. **INVOKE:** \`superpowers:test-driven-development\` — ensure tests cover CURRENT behavior
176
176
  2. Refactor incrementally — keep tests green at each step
177
- 3. Commit each step
177
+ 3. Commit after manual verification
178
178
 
179
179
  ### GATE 4 — AI Self-Review (wait for APPROVED)
180
180
  **INVOKE:** \`review-plan\` skill
package/scripts/remove.js CHANGED
@@ -89,6 +89,60 @@ async function removeFromProject() {
89
89
  console.log(chalk.green('✓ Removed .rules/'));
90
90
  }
91
91
 
92
+ // Clean up AI Tool files (CLAUDE.md, .cursorrules, etc.)
93
+ const { AI_TOOL_FILES } = require('./init');
94
+ const markerStart = '<!-- aiflow-kit-start -->';
95
+ const markerEnd = '<!-- aiflow-kit-end -->';
96
+
97
+ for (const tool in AI_TOOL_FILES) {
98
+ const filePath = path.join(PROJECT_DIR, AI_TOOL_FILES[tool]);
99
+ if (!(await fs.pathExists(filePath))) continue;
100
+
101
+ let content = await fs.readFile(filePath, 'utf-8');
102
+ let cleaned = false;
103
+
104
+ if (content.includes(markerStart) && content.includes(markerEnd)) {
105
+ // Precise removal using markers
106
+ const startIndex = content.indexOf(markerStart);
107
+ const endIndex = content.indexOf(markerEnd) + markerEnd.length;
108
+ content = content.slice(0, startIndex).trim() + '\n' + content.slice(endIndex).trim();
109
+ content = content.trim();
110
+ cleaned = true;
111
+ } else if (content.includes('## AI Skill Registry (Superpowers)')) {
112
+ // Fallback for legacy installs: remove sections by headers
113
+ console.log(chalk.yellow(` ⚠ Found legacy aiflow sections in ${AI_TOOL_FILES[tool]}`));
114
+ const ok = await confirm({ message: `Remove ai-flow-kit sections from ${AI_TOOL_FILES[tool]}?`, default: true });
115
+ if (ok) {
116
+ // Heuristic: remove from "## AI Skill Registry" or "# Claude AI System Instructions"
117
+ // This is simple and covers most cases. For complex cases, user should manual clean.
118
+ const headers = [
119
+ '# Claude AI System Instructions',
120
+ '## AI Skill Registry (Superpowers)',
121
+ '## MANDATORY: Strict Gate Workflow'
122
+ ];
123
+ let minIndex = content.length;
124
+ for (const h of headers) {
125
+ const idx = content.indexOf(h);
126
+ if (idx !== -1 && idx < minIndex) minIndex = idx;
127
+ }
128
+ if (minIndex < content.length) {
129
+ content = content.slice(0, minIndex).trim();
130
+ cleaned = true;
131
+ }
132
+ }
133
+ }
134
+
135
+ if (cleaned) {
136
+ if (content.length === 0) {
137
+ await fs.remove(filePath);
138
+ console.log(chalk.green(`✓ Deleted ${AI_TOOL_FILES[tool]} (it was aiflow-only)`));
139
+ } else {
140
+ await fs.writeFile(filePath, content);
141
+ console.log(chalk.green(`✓ Cleaned up ai-flow-kit instructions from ${AI_TOOL_FILES[tool]}`));
142
+ }
143
+ }
144
+ }
145
+
92
146
  console.log(chalk.green('\n✨ ai-flow-kit removed from project.'));
93
147
  console.log(chalk.gray('To reinstall: aiflow init'));
94
148
  }