@relipa/ai-flow-kit 0.0.5-beta.0 → 0.0.5

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 (35) hide show
  1. package/README.md +23 -17
  2. package/custom/skills/{validate-ticket → read-study-requirement}/SKILL.md +27 -17
  3. package/custom/skills/review-plan/SKILL.md +1 -1
  4. package/custom/templates/shared/gate-workflow.md +88 -88
  5. package/custom/templates/tools/claude.md +1 -1
  6. package/custom/templates/tools/copilot.md +1 -1
  7. package/custom/templates/tools/cursor.md +1 -1
  8. package/custom/templates/tools/gemini.md +1 -1
  9. package/custom/templates/tools/generic.md +1 -1
  10. package/docs/{AIFLOW.md → common/AIFLOW.md} +462 -463
  11. package/docs/{CHANGELOG.md → common/CHANGELOG.md} +132 -128
  12. package/docs/{cli-reference.md → common/cli-reference.md} +1 -1
  13. package/docs/project/ARCHITECTURE.md +28 -0
  14. package/package.json +3 -2
  15. package/scripts/context.js +1 -1
  16. package/scripts/hooks/session-start.js +145 -145
  17. package/scripts/init.js +154 -47
  18. package/scripts/prompt.js +431 -402
  19. package/scripts/telemetry/cli.js +6 -12
  20. package/scripts/telemetry/config.js +1 -4
  21. package/scripts/telemetry/flush.js +16 -13
  22. package/scripts/use.js +74 -31
  23. package/docs/IMPLEMENTATION_SUMMARY.md +0 -330
  24. package/docs/architecture.md +0 -394
  25. package/docs/developer-overview.md +0 -126
  26. /package/docs/{QUICK_START.md → common/QUICK_START.md} +0 -0
  27. /package/docs/{ai-integration.md → common/ai-integration.md} +0 -0
  28. /package/docs/{configuration.md → common/configuration.md} +0 -0
  29. /package/docs/{getting-started.md → common/getting-started.md} +0 -0
  30. /package/docs/{troubleshooting.md → common/troubleshooting.md} +0 -0
  31. /package/docs/{workflows → common/workflows}/bug-fix.md +0 -0
  32. /package/docs/{workflows → common/workflows}/feature.md +0 -0
  33. /package/docs/{workflows → common/workflows}/impact-analysis.md +0 -0
  34. /package/docs/{workflows → common/workflows}/investigation.md +0 -0
  35. /package/docs/{workflows → common/workflows}/refactor.md +0 -0
package/scripts/init.js CHANGED
@@ -1,9 +1,12 @@
1
1
  const fs = require('fs-extra');
2
2
  const path = require('path');
3
+ const os = require('os');
3
4
  const chalk = require('chalk');
4
- const { input } = require('@inquirer/prompts');
5
+ const { input, select, confirm } = require('@inquirer/prompts');
5
6
 
6
7
  const PKG_DIR = path.join(__dirname, '..');
8
+ const GLOBAL_AIFLOW_DIR = path.join(os.homedir(), '.aiflow');
9
+ const GLOBAL_CREDENTIALS_PATH = path.join(GLOBAL_AIFLOW_DIR, 'credentials.json');
7
10
  const PKG_VERSION = require('../package.json').version;
8
11
 
9
12
  // Map framework → language family for picking the right rules
@@ -20,43 +23,73 @@ const AI_TOOL_FILES = {
20
23
  'cursor': '.cursorrules',
21
24
  'gemini': 'GEMINI.md',
22
25
  'copilot': '.github/copilot-instructions.md',
23
- 'generic': 'AI_INSTRUCTIONS.md'
26
+ 'generic': '.aiflow/AI_INSTRUCTIONS.md'
24
27
  };
25
28
 
26
29
  /**
27
- * Copy documentation files and the docs/ folder to the project
30
+ * Copy only docs/common/ to .aiflow/docs in the project.
31
+ * Internal aiflow docs (changelog, architecture…) are never copied out.
28
32
  */
29
33
  async function copyDocsToProject(projectDir) {
30
- const aiflowDocsDir = path.join(projectDir, '.aiflow', 'docs');
31
- const srcDocsDir = path.join(PKG_DIR, 'docs');
32
-
33
- // 1. Copy the entire docs/ folder to .aiflow/docs
34
- if (await fs.pathExists(srcDocsDir)) {
35
- await fs.ensureDir(aiflowDocsDir);
36
- await fs.copy(srcDocsDir, aiflowDocsDir, { overwrite: true });
37
-
38
- // Ensure README.md is pulled into docs folder for developer visibility
39
- const srcReadme = path.join(PKG_DIR, 'README.md');
40
- if (await fs.pathExists(srcReadme)) {
41
- await fs.copy(srcReadme, path.join(aiflowDocsDir, 'README.md'), { overwrite: true });
42
- }
43
-
44
- console.log(chalk.green(`✓ Documentation folder copied to .aiflow/docs`));
45
- }
34
+ const srcCommonDir = path.join(PKG_DIR, 'docs', 'common');
35
+ if (!(await fs.pathExists(srcCommonDir))) return;
46
36
 
47
- // 2. Copy key markdown files to project root for easy access
48
- const keyFiles = ['QUICK_START.md', 'AIFLOW.md'];
49
- for (const file of keyFiles) {
50
- const srcPath = path.join(srcDocsDir, file);
51
- const destPath = path.join(projectDir, file);
52
- if (await fs.pathExists(srcPath)) {
53
- // We overwrite these to ensure they are up to date with the installed version
54
- await fs.copy(srcPath, destPath, { overwrite: true });
55
- console.log(chalk.gray(` ✓ ${file} updated in project root`));
56
- }
37
+ const destDir = path.join(projectDir, '.aiflow', 'docs');
38
+ await fs.ensureDir(destDir);
39
+ await fs.copy(srcCommonDir, destDir, { overwrite: true });
40
+ console.log(chalk.green(`✓ Documentation copied to .aiflow/docs`));
41
+ }
42
+
43
+ /**
44
+ * Scan the project directory to guess the framework.
45
+ * Returns a framework key or null if undetected.
46
+ */
47
+ async function detectFramework(projectDir) {
48
+ // JS / TS — check package.json dependencies
49
+ const pkgPath = path.join(projectDir, 'package.json');
50
+ if (await fs.pathExists(pkgPath)) {
51
+ const pkg = await fs.readJson(pkgPath).catch(() => ({}));
52
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
53
+ if (deps['next']) return 'nextjs';
54
+ if (deps['nuxt']) return 'vue-nuxt';
55
+ if (deps['vue']) return 'vue-nuxt';
56
+ if (deps['@nestjs/core']) return 'nodejs-express';
57
+ if (deps['react']) return 'reactjs';
58
+ if (deps['express'] || deps['fastify']) return 'nodejs-express';
59
+ }
60
+ // Java — pom.xml / Gradle
61
+ if (await fs.pathExists(path.join(projectDir, 'pom.xml'))) return 'spring-boot';
62
+ if (await fs.pathExists(path.join(projectDir, 'build.gradle'))) return 'spring-boot';
63
+ if (await fs.pathExists(path.join(projectDir, 'build.gradle.kts'))) return 'spring-boot';
64
+ // PHP — composer.json
65
+ const composerPath = path.join(projectDir, 'composer.json');
66
+ if (await fs.pathExists(composerPath)) {
67
+ const composer = await fs.readJson(composerPath).catch(() => ({}));
68
+ if ((composer.require || {})['laravel/framework']) return 'laravel';
57
69
  }
70
+ // Python
71
+ if (await fs.pathExists(path.join(projectDir, 'manage.py'))) return 'python-django';
72
+ const reqPath = path.join(projectDir, 'requirements.txt');
73
+ if (await fs.pathExists(reqPath)) {
74
+ const reqs = (await fs.readFile(reqPath, 'utf-8').catch(() => '')).toLowerCase();
75
+ if (reqs.includes('fastapi')) return 'python-fastapi';
76
+ if (reqs.includes('django')) return 'python-django';
77
+ }
78
+ return null;
58
79
  }
59
80
 
81
+ const FRAMEWORK_CHOICES = [
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' },
90
+ { name: 'Skip (no framework)', value: null },
91
+ ];
92
+
60
93
  async function copyAssetsToVersion(versionDir, framework) {
61
94
  // ── Skills: merge upstream + custom ──────────────────────────────
62
95
  const upstreamSkills = path.join(PKG_DIR, 'upstream', 'skills');
@@ -217,6 +250,9 @@ async function setupFramework(projectDir, framework, multi = false, selectedTool
217
250
  const separator = `\n\n---\n\n`;
218
251
  const skillRegistry = await generateSkillRegistry(projectDir);
219
252
 
253
+ const written = [];
254
+ const skipped = [];
255
+
220
256
  for (const tool of selectedTools) {
221
257
  const targetPath = path.join(projectDir, AI_TOOL_FILES[tool]);
222
258
  if (!targetPath) continue;
@@ -240,18 +276,51 @@ async function setupFramework(projectDir, framework, multi = false, selectedTool
240
276
 
241
277
  finalContent += frameworkContent;
242
278
 
279
+ const fileExists = await fs.pathExists(targetPath);
280
+
243
281
  if (!multi) {
282
+ if (fileExists) {
283
+ const relPath = path.relative(projectDir, targetPath).replace(/\\/g, '/');
284
+ const overwrite = await confirm({
285
+ message: ` ${relPath} already exists — overwrite with aiflow template?`,
286
+ default: false
287
+ });
288
+
289
+ if (!overwrite) {
290
+ // Save aiflow template to .aiflow/reference/ so dev can compare/merge manually
291
+ const refPath = path.join(projectDir, '.aiflow', 'reference', path.basename(targetPath));
292
+ await fs.ensureDir(path.dirname(refPath));
293
+ await fs.writeFile(refPath, finalContent);
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)}`));
305
+ }
244
306
  await fs.writeFile(targetPath, finalContent);
307
+ written.push(AI_TOOL_FILES[tool]);
245
308
  } else {
246
- if (await fs.pathExists(targetPath)) {
309
+ if (fileExists) {
247
310
  await fs.appendFile(targetPath, separator + frameworkContent);
248
311
  } else {
249
312
  await fs.writeFile(targetPath, finalContent);
250
313
  }
314
+ written.push(AI_TOOL_FILES[tool]);
251
315
  }
252
316
  }
253
317
 
254
- console.log(chalk.green(`✓ AI instruction files updated for framework: ${framework} (${selectedTools.join(', ')})`));
318
+ if (written.length) {
319
+ console.log(chalk.green(`✓ AI instruction files updated for framework: ${framework} (${written.join(', ')})`));
320
+ }
321
+ if (skipped.length) {
322
+ console.log(chalk.yellow(` Skipped (kept existing): ${skipped.join(', ')}`));
323
+ }
255
324
  }
256
325
 
257
326
  async function setupAdapter(projectDir, adapter) {
@@ -266,10 +335,10 @@ async function setupAdapter(projectDir, adapter) {
266
335
  const serverKey = Object.keys(mcpServers)[0];
267
336
  const serverConfig = mcpServers[serverKey];
268
337
 
269
- const credsFile = path.join(projectDir, '.aiflow', 'credentials.json');
270
338
  let existingCreds = {};
271
- if (await fs.pathExists(credsFile)) {
272
- existingCreds = await fs.readJson(credsFile).catch(() => ({}));
339
+ if (await fs.pathExists(GLOBAL_CREDENTIALS_PATH)) {
340
+ const globalData = await fs.readJson(GLOBAL_CREDENTIALS_PATH).catch(() => ({}));
341
+ existingCreds = globalData.mcp || {};
273
342
  }
274
343
 
275
344
  console.log(chalk.cyan(`\nSetup MCP adapter for ${adapter}:`));
@@ -328,8 +397,13 @@ async function setupAdapter(projectDir, adapter) {
328
397
  { spaces: 2 }
329
398
  );
330
399
 
331
- Object.assign(existingCreds, credentials);
332
- await fs.writeJson(credsFile, existingCreds, { spaces: 2 });
400
+ await fs.ensureDir(GLOBAL_AIFLOW_DIR);
401
+ let globalData = {};
402
+ if (await fs.pathExists(GLOBAL_CREDENTIALS_PATH)) {
403
+ globalData = await fs.readJson(GLOBAL_CREDENTIALS_PATH).catch(() => ({}));
404
+ }
405
+ globalData.mcp = { ...(globalData.mcp || {}), ...credentials };
406
+ await fs.writeJson(GLOBAL_CREDENTIALS_PATH, globalData, { spaces: 2 });
333
407
  await ensureCredentialsGitignored(projectDir);
334
408
 
335
409
  console.log(chalk.green(`✓ MCP configuration saved (global + local + credentials).`));
@@ -435,7 +509,7 @@ function verifyFigma(credentials) {
435
509
 
436
510
  async function ensureCredentialsGitignored(projectDir) {
437
511
  const gitignorePath = path.join(projectDir, '.gitignore');
438
- const entries = ['.aiflow/credentials.json', '.aiflow/no-telemetry'];
512
+ const entries = ['.aiflow/no-telemetry'];
439
513
  let content = '';
440
514
  if (await fs.pathExists(gitignorePath)) {
441
515
  content = await fs.readFile(gitignorePath, 'utf-8');
@@ -465,13 +539,41 @@ function maskSecret(value) {
465
539
  async function init(options) {
466
540
  const projectDir = process.cwd();
467
541
  const aiflowDir = path.join(projectDir, '.aiflow');
468
- const frameworks = options.frameworks || (options.framework ? [options.framework] : []);
469
- const adapters = options.adapters || (options.adapter ? [options.adapter] : []);
470
- const primaryFramework = frameworks[0] || null;
471
- const versionDir = path.join(aiflowDir, 'versions', PKG_VERSION);
542
+ let frameworks = options.frameworks || (options.framework ? [options.framework] : []);
543
+ const adapters = options.adapters || (options.adapter ? [options.adapter] : []);
472
544
 
473
545
  try {
474
546
  console.log(chalk.blue(`Initializing ai-flow-kit (v${PKG_VERSION})...`));
547
+
548
+ // ── Auto-detect or prompt for framework if not supplied ──────
549
+ if (frameworks.length === 0) {
550
+ const detected = await detectFramework(projectDir);
551
+ if (detected) {
552
+ const label = FRAMEWORK_CHOICES.find(c => c.value === detected)?.name || detected;
553
+ console.log(chalk.cyan(`\n Detected framework: ${chalk.bold(label)}`));
554
+ const useDetected = await confirm({ message: `Use "${label}"?`, default: true });
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
+ }
564
+ } else {
565
+ console.log(chalk.gray('\n No framework detected.'));
566
+ const chosen = await select({
567
+ message: 'Select framework (or skip):',
568
+ choices: FRAMEWORK_CHOICES,
569
+ });
570
+ if (chosen) frameworks = [chosen];
571
+ }
572
+ }
573
+
574
+ const primaryFramework = frameworks[0] || null;
575
+ const versionDir = path.join(aiflowDir, 'versions', PKG_VERSION);
576
+
475
577
  if (frameworks.length) console.log(chalk.gray(` Frameworks: ${frameworks.join(', ')}`));
476
578
  if (adapters.length) console.log(chalk.gray(` Adapters: ${adapters.join(', ')}`));
477
579
 
@@ -479,8 +581,6 @@ async function init(options) {
479
581
  await copyAssetsToVersion(versionDir, primaryFramework);
480
582
  await verifySuperpowersSkills(versionDir);
481
583
  await updateSymlinks(projectDir, versionDir);
482
-
483
- // --- Added: Copy docs to project ---
484
584
  await copyDocsToProject(projectDir);
485
585
 
486
586
  const selectedTools = options.aiTools || Object.keys(AI_TOOL_FILES);
@@ -503,11 +603,18 @@ async function init(options) {
503
603
  console.log(chalk.green('\n✨ Initialized AI Flow Kit successfully!'));
504
604
  console.log(chalk.blue('\n' + '─'.repeat(62)));
505
605
  console.log(chalk.bold.white(' Next steps:'));
506
- console.log(` ${chalk.green('aiflow use PROJ-33')} ${chalk.gray('← load ticket context')}`);
507
- console.log(` ${chalk.green('claude')} ${chalk.gray('← Use Claude Code CLI (recommended)')}`);
508
- console.log(` ${chalk.green('Cursor / Gemini')} ${chalk.gray('← Instructions loaded in .cursorrules / GEMINI.md')}`);
606
+
607
+ if (adapters.length > 0) {
608
+ console.log(` ${chalk.green('aiflow use PROJ-33')} ${chalk.gray('← load ticket from ' + adapters.join('/'))} `);
609
+ } else {
610
+ console.log(` ${chalk.green('aiflow use --manual')} ${chalk.gray('← input ticket content in terminal')}`);
611
+ console.log(` ${chalk.green('aiflow use --file req.md')} ${chalk.gray('← load from local file')}`);
612
+ }
613
+
614
+ console.log(` ${chalk.green('claude')} ${chalk.gray('← Claude Code CLI (recommended)')}`);
615
+ console.log(` ${chalk.green('Cursor / Gemini')} ${chalk.gray('← Instructions in .cursorrules / GEMINI.md')}`);
509
616
  console.log(chalk.blue('─'.repeat(62)));
510
- console.log(chalk.gray(` Full guide: ${chalk.green('aiflow guide')} | Command list: ${chalk.green('aiflow guide --commands')}`));
617
+ console.log(chalk.gray(` Full guide: ${chalk.green('aiflow guide')} | Commands: ${chalk.green('aiflow guide --commands')}`));
511
618
  console.log(chalk.blue('─'.repeat(62)) + '\n');
512
619
  } catch (err) {
513
620
  console.error(chalk.red(`Error during initialization: ${err.message}`));