@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.
- package/README.md +33 -13
- package/bin/aiflow.js +58 -2
- package/custom/mcp-presets/gitnexus.json +8 -0
- package/custom/rules/java/spring-boot-examples.md +329 -329
- package/custom/skills/generate-spec/SKILL.md +7 -0
- package/custom/skills/impact-analysis/SKILL.md +10 -0
- package/custom/skills/read-study-requirement/SKILL.md +11 -0
- package/custom/skills/review-plan/SKILL.md +15 -0
- package/custom/templates/spring-boot.md +224 -224
- package/docs/common/AIFLOW.md +12 -6
- package/docs/common/CHANGELOG.md +49 -5
- package/docs/common/QUICK_START.md +13 -11
- package/docs/common/cli-reference.md +23 -0
- package/package.json +2 -7
- package/scripts/checkpoint.js +46 -0
- package/scripts/doctor.js +192 -89
- package/scripts/gitnexus-worker.js +94 -0
- package/scripts/guide.js +42 -51
- package/scripts/hooks/session-start.js +274 -244
- package/scripts/hooks/session-stop.js +55 -0
- package/scripts/init.js +293 -18
- package/scripts/prompt.js +2 -2
- package/scripts/remove.js +54 -0
- package/scripts/task.js +446 -384
- package/scripts/update.js +14 -4
- package/scripts/use.js +41 -0
- package/upstream/.claude-plugin/marketplace.json +4 -4
- package/upstream/.claude-plugin/plugin.json +2 -4
- package/upstream/.cursor-plugin/plugin.json +2 -4
- package/upstream/docs/plans/2025-11-22-opencode-support-design.md +1 -1
- package/upstream/docs/plans/2025-11-28-skills-improvements-from-user-feedback.md +2 -2
- package/upstream/docs/testing.md +2 -2
- package/upstream/skills/subagent-driven-development/SKILL.md +5 -6
- package/upstream/skills/subagent-driven-development/implementer-prompt.md +2 -3
- package/upstream/skills/systematic-debugging/CREATION-LOG.md +1 -1
- package/upstream/skills/systematic-debugging/root-cause-tracing.md +1 -1
- package/upstream/skills/tdd-lean/SKILL.md +127 -0
- package/upstream/skills/using-git-worktrees/SKILL.md +4 -5
- package/upstream/skills/writing-plans/SKILL.md +3 -9
- 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
|
|
161
|
-
const
|
|
162
|
-
await fs.copy(
|
|
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 "${
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
284
|
-
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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
|
|
293
|
-
|
|
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
|
|
569
|
+
async function ensureAiflowGitignored(projectDir) {
|
|
498
570
|
const gitignorePath = path.join(projectDir, '.gitignore');
|
|
499
|
-
const entries = [
|
|
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 —
|
|
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
|
|
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
|
}
|