@relipa/ai-flow-kit 0.0.5 → 0.0.6
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 +3 -302
- 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 +52 -11
- package/scripts/guide.js +28 -5
- package/scripts/hooks/session-start.js +244 -145
- 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/init.js
CHANGED
|
@@ -2,7 +2,7 @@ const fs = require('fs-extra');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const os = require('os');
|
|
4
4
|
const chalk = require('chalk');
|
|
5
|
-
const { input,
|
|
5
|
+
const { input, checkbox, confirm } = require('@inquirer/prompts');
|
|
6
6
|
|
|
7
7
|
const PKG_DIR = path.join(__dirname, '..');
|
|
8
8
|
const GLOBAL_AIFLOW_DIR = path.join(os.homedir(), '.aiflow');
|
|
@@ -23,7 +23,7 @@ const AI_TOOL_FILES = {
|
|
|
23
23
|
'cursor': '.cursorrules',
|
|
24
24
|
'gemini': 'GEMINI.md',
|
|
25
25
|
'copilot': '.github/copilot-instructions.md',
|
|
26
|
-
'generic': '.aiflow/AI_INSTRUCTIONS.md'
|
|
26
|
+
'generic': '.aiflow/docs/AI_INSTRUCTIONS.md',
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -79,15 +79,14 @@ async function detectFramework(projectDir) {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
const FRAMEWORK_CHOICES = [
|
|
82
|
-
{ name: 'Spring Boot (Java)',
|
|
83
|
-
{ name: 'React',
|
|
84
|
-
{ name: 'Next.js',
|
|
85
|
-
{ name: 'Vue / Nuxt',
|
|
86
|
-
{ name: 'Laravel (PHP)',
|
|
87
|
-
{ name: 'Node.js / Express',
|
|
88
|
-
{ name: 'Python Django',
|
|
89
|
-
{ name: 'Python FastAPI',
|
|
90
|
-
{ name: 'Skip (no framework)', value: null },
|
|
82
|
+
{ name: 'Spring Boot (Java)', value: 'spring-boot' },
|
|
83
|
+
{ name: 'React', value: 'reactjs' },
|
|
84
|
+
{ name: 'Next.js', value: 'nextjs' },
|
|
85
|
+
{ name: 'Vue / Nuxt', value: 'vue-nuxt' },
|
|
86
|
+
{ name: 'Laravel (PHP)', value: 'laravel' },
|
|
87
|
+
{ name: 'Node.js / Express', value: 'nodejs-express' },
|
|
88
|
+
{ name: 'Python Django', value: 'python-django' },
|
|
89
|
+
{ name: 'Python FastAPI', value: 'python-fastapi' },
|
|
91
90
|
];
|
|
92
91
|
|
|
93
92
|
async function copyAssetsToVersion(versionDir, framework) {
|
|
@@ -279,32 +278,20 @@ async function setupFramework(projectDir, framework, multi = false, selectedTool
|
|
|
279
278
|
const fileExists = await fs.pathExists(targetPath);
|
|
280
279
|
|
|
281
280
|
if (!multi) {
|
|
281
|
+
const relPath = path.relative(projectDir, targetPath).replace(/\\/g, '/');
|
|
282
282
|
if (fileExists) {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const refRel = path.relative(projectDir, refPath).replace(/\\/g, '/');
|
|
295
|
-
skipped.push(relPath);
|
|
296
|
-
console.log(chalk.gray(` aiflow template saved to ${refRel} for reference`));
|
|
297
|
-
continue;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// Backup existing file before overwriting
|
|
301
|
-
const backupPath = path.join(projectDir, '.aiflow', 'backup', path.basename(targetPath));
|
|
302
|
-
await fs.ensureDir(path.dirname(backupPath));
|
|
303
|
-
await fs.copy(targetPath, backupPath, { overwrite: true });
|
|
304
|
-
console.log(chalk.gray(` Backed up ${relPath} → .aiflow/backup/${path.basename(targetPath)}`));
|
|
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);
|
|
291
|
+
} else {
|
|
292
|
+
await fs.writeFile(targetPath, finalContent);
|
|
293
|
+
written.push(AI_TOOL_FILES[tool]);
|
|
305
294
|
}
|
|
306
|
-
await fs.writeFile(targetPath, finalContent);
|
|
307
|
-
written.push(AI_TOOL_FILES[tool]);
|
|
308
295
|
} else {
|
|
309
296
|
if (fileExists) {
|
|
310
297
|
await fs.appendFile(targetPath, separator + frameworkContent);
|
|
@@ -536,6 +523,74 @@ function maskSecret(value) {
|
|
|
536
523
|
return `${value.slice(0, 4)}***${value.slice(-4)}`;
|
|
537
524
|
}
|
|
538
525
|
|
|
526
|
+
// ── RTK (token compression) helpers ──────────────────────────────
|
|
527
|
+
|
|
528
|
+
async function detectRtk() {
|
|
529
|
+
const { execSync } = require('child_process');
|
|
530
|
+
try {
|
|
531
|
+
const out = execSync('rtk --version', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
|
|
532
|
+
return { installed: true, version: out.replace(/^rtk\s*/i, '') };
|
|
533
|
+
} catch (_) {
|
|
534
|
+
return { installed: false };
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
async function isRtkHookConfigured(projectDir) {
|
|
539
|
+
const settingsPath = path.join(projectDir, '.claude', 'settings.json');
|
|
540
|
+
if (!(await fs.pathExists(settingsPath))) return false;
|
|
541
|
+
const settings = await fs.readJson(settingsPath).catch(() => ({}));
|
|
542
|
+
const preHooks = settings?.hooks?.PreToolUse || [];
|
|
543
|
+
return preHooks.some(h =>
|
|
544
|
+
(h.hooks || []).some(entry =>
|
|
545
|
+
typeof entry.command === 'string' && entry.command.includes('rtk')
|
|
546
|
+
)
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
async function setupRtkHook(projectDir) {
|
|
551
|
+
const settingsPath = path.join(projectDir, '.claude', 'settings.json');
|
|
552
|
+
let settings = {};
|
|
553
|
+
if (await fs.pathExists(settingsPath)) {
|
|
554
|
+
settings = await fs.readJson(settingsPath).catch(() => ({}));
|
|
555
|
+
}
|
|
556
|
+
if (!settings.hooks) settings.hooks = {};
|
|
557
|
+
if (!settings.hooks.PreToolUse) settings.hooks.PreToolUse = [];
|
|
558
|
+
|
|
559
|
+
const alreadySet = settings.hooks.PreToolUse.some(h =>
|
|
560
|
+
(h.hooks || []).some(e => typeof e.command === 'string' && e.command.includes('rtk'))
|
|
561
|
+
);
|
|
562
|
+
if (alreadySet) return;
|
|
563
|
+
|
|
564
|
+
settings.hooks.PreToolUse.push({
|
|
565
|
+
matcher: 'Bash',
|
|
566
|
+
hooks: [{ type: 'command', command: 'rtk rewrite' }],
|
|
567
|
+
});
|
|
568
|
+
await fs.ensureDir(path.join(projectDir, '.claude'));
|
|
569
|
+
await fs.writeJson(settingsPath, settings, { spaces: 2 });
|
|
570
|
+
console.log(chalk.green('✓ RTK token compression hook configured (.claude/settings.json)'));
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
async function maybeSetupRtk(projectDir, forceSetup) {
|
|
574
|
+
const rtk = await detectRtk();
|
|
575
|
+
if (!rtk.installed && !forceSetup) return;
|
|
576
|
+
|
|
577
|
+
if (forceSetup && !rtk.installed) {
|
|
578
|
+
console.log(chalk.yellow('⚠ --with-rtk specified but rtk binary not found. Install: cargo install rtk'));
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
if (await isRtkHookConfigured(projectDir)) {
|
|
583
|
+
console.log(chalk.gray(' RTK hook already configured — skipping.'));
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
const enable = forceSetup || await confirm({
|
|
588
|
+
message: `RTK detected (${rtk.version}). Enable token compression for Claude Code?`,
|
|
589
|
+
default: true,
|
|
590
|
+
});
|
|
591
|
+
if (enable) await setupRtkHook(projectDir);
|
|
592
|
+
}
|
|
593
|
+
|
|
539
594
|
async function init(options) {
|
|
540
595
|
const projectDir = process.cwd();
|
|
541
596
|
const aiflowDir = path.join(projectDir, '.aiflow');
|
|
@@ -548,27 +603,23 @@ async function init(options) {
|
|
|
548
603
|
// ── Auto-detect or prompt for framework if not supplied ──────
|
|
549
604
|
if (frameworks.length === 0) {
|
|
550
605
|
const detected = await detectFramework(projectDir);
|
|
606
|
+
const hint = chalk.gray(' Space to select/unselect · Enter to confirm · Leave empty to skip');
|
|
551
607
|
if (detected) {
|
|
552
608
|
const label = FRAMEWORK_CHOICES.find(c => c.value === detected)?.name || detected;
|
|
553
609
|
console.log(chalk.cyan(`\n Detected framework: ${chalk.bold(label)}`));
|
|
554
|
-
|
|
555
|
-
if (useDetected) {
|
|
556
|
-
frameworks = [detected];
|
|
557
|
-
} else {
|
|
558
|
-
const chosen = await select({
|
|
559
|
-
message: 'Select framework:',
|
|
560
|
-
choices: FRAMEWORK_CHOICES,
|
|
561
|
-
});
|
|
562
|
-
if (chosen) frameworks = [chosen];
|
|
563
|
-
}
|
|
610
|
+
console.log(hint);
|
|
564
611
|
} else {
|
|
565
612
|
console.log(chalk.gray('\n No framework detected.'));
|
|
566
|
-
|
|
567
|
-
message: 'Select framework (or skip):',
|
|
568
|
-
choices: FRAMEWORK_CHOICES,
|
|
569
|
-
});
|
|
570
|
-
if (chosen) frameworks = [chosen];
|
|
613
|
+
console.log(hint);
|
|
571
614
|
}
|
|
615
|
+
const chosen = await checkbox({
|
|
616
|
+
message: 'Select framework(s):',
|
|
617
|
+
choices: FRAMEWORK_CHOICES.map(c => ({
|
|
618
|
+
...c,
|
|
619
|
+
checked: c.value === detected,
|
|
620
|
+
})),
|
|
621
|
+
});
|
|
622
|
+
frameworks = chosen;
|
|
572
623
|
}
|
|
573
624
|
|
|
574
625
|
const primaryFramework = frameworks[0] || null;
|
|
@@ -600,6 +651,12 @@ async function init(options) {
|
|
|
600
651
|
await fs.writeJson(stateFile, state);
|
|
601
652
|
}
|
|
602
653
|
|
|
654
|
+
// ── Optional: RTK token compression ─────────────────────────
|
|
655
|
+
// Commander sets options.rtk=false when --no-rtk is passed
|
|
656
|
+
if (options.rtk !== false) {
|
|
657
|
+
await maybeSetupRtk(projectDir, options.withRtk || false);
|
|
658
|
+
}
|
|
659
|
+
|
|
603
660
|
console.log(chalk.green('\n✨ Initialized AI Flow Kit successfully!'));
|
|
604
661
|
console.log(chalk.blue('\n' + '─'.repeat(62)));
|
|
605
662
|
console.log(chalk.bold.white(' Next steps:'));
|
|
@@ -623,3 +680,5 @@ async function init(options) {
|
|
|
623
680
|
|
|
624
681
|
module.exports = init;
|
|
625
682
|
module.exports.AI_TOOL_FILES = AI_TOOL_FILES;
|
|
683
|
+
module.exports.detectRtk = detectRtk;
|
|
684
|
+
module.exports.isRtkHookConfigured = isRtkHookConfigured;
|