@relipa/ai-flow-kit 0.0.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 (204) hide show
  1. package/AIFLOW.md +432 -0
  2. package/CHANGELOG.md +78 -0
  3. package/CONTRIBUTING.md +351 -0
  4. package/IMPLEMENTATION_SUMMARY.md +348 -0
  5. package/QUICK_START.md +392 -0
  6. package/README.md +323 -0
  7. package/bin/aiflow.js +178 -0
  8. package/custom/mcp-presets/README.md +138 -0
  9. package/custom/mcp-presets/backlog.json +15 -0
  10. package/custom/mcp-presets/figma.json +15 -0
  11. package/custom/mcp-presets/google-sheets.json +14 -0
  12. package/custom/mcp-presets/jira.json +16 -0
  13. package/custom/prompts/bug-fix.md +149 -0
  14. package/custom/prompts/feature.md +190 -0
  15. package/custom/prompts/investigation.md +164 -0
  16. package/custom/rules/code-style.md +7 -0
  17. package/custom/rules/java/code-style.md +11 -0
  18. package/custom/rules/java/naming.md +17 -0
  19. package/custom/rules/java/review-checklist.md +36 -0
  20. package/custom/rules/javascript/code-style.md +12 -0
  21. package/custom/rules/javascript/naming.md +14 -0
  22. package/custom/rules/naming.md +9 -0
  23. package/custom/rules/php/code-style.md +10 -0
  24. package/custom/rules/php/naming.md +15 -0
  25. package/custom/rules/review-checklist.md +9 -0
  26. package/custom/skills/figma-to-component/SKILL.md +207 -0
  27. package/custom/skills/generate-spec/SKILL.md +97 -0
  28. package/custom/skills/impact-analysis/SKILL.md +94 -0
  29. package/custom/skills/investigate-bug/SKILL.md +93 -0
  30. package/custom/skills/report-customer/SKILL.md +95 -0
  31. package/custom/skills/review-plan/SKILL.md +158 -0
  32. package/custom/skills/validate-ticket/SKILL.md +240 -0
  33. package/custom/templates/laravel.md +90 -0
  34. package/custom/templates/nextjs.md +89 -0
  35. package/custom/templates/reactjs.md +567 -0
  36. package/custom/templates/spring-boot.md +598 -0
  37. package/custom/templates/vue-nuxt.md +89 -0
  38. package/docs/architecture.md +395 -0
  39. package/docs/cli-reference.md +438 -0
  40. package/docs/configuration.md +412 -0
  41. package/docs/getting-started.md +248 -0
  42. package/docs/troubleshooting.md +587 -0
  43. package/docs/workflows/bug-fix.md +126 -0
  44. package/docs/workflows/feature.md +123 -0
  45. package/docs/workflows/impact-analysis.md +13 -0
  46. package/docs/workflows/investigation.md +13 -0
  47. package/docs/workflows/refactor.md +13 -0
  48. package/index.js +27 -0
  49. package/package.json +70 -0
  50. package/scripts/config.js +317 -0
  51. package/scripts/context.js +117 -0
  52. package/scripts/detect.js +239 -0
  53. package/scripts/doctor.js +48 -0
  54. package/scripts/guide.js +282 -0
  55. package/scripts/hooks/session-start.js +140 -0
  56. package/scripts/init.js +453 -0
  57. package/scripts/memory.js +247 -0
  58. package/scripts/prompt.js +392 -0
  59. package/scripts/remove.js +170 -0
  60. package/scripts/update.js +111 -0
  61. package/scripts/use.js +570 -0
  62. package/scripts/validate.js +303 -0
  63. package/upstream/.claude-plugin/marketplace.json +20 -0
  64. package/upstream/.claude-plugin/plugin.json +20 -0
  65. package/upstream/.codex/INSTALL.md +67 -0
  66. package/upstream/.cursor-plugin/plugin.json +25 -0
  67. package/upstream/.gitattributes +18 -0
  68. package/upstream/.github/FUNDING.yml +3 -0
  69. package/upstream/.github/ISSUE_TEMPLATE/bug_report.md +52 -0
  70. package/upstream/.github/ISSUE_TEMPLATE/config.yml +5 -0
  71. package/upstream/.github/ISSUE_TEMPLATE/feature_request.md +34 -0
  72. package/upstream/.github/ISSUE_TEMPLATE/platform_support.md +23 -0
  73. package/upstream/.github/PULL_REQUEST_TEMPLATE.md +87 -0
  74. package/upstream/.opencode/INSTALL.md +83 -0
  75. package/upstream/.opencode/plugins/superpowers.js +112 -0
  76. package/upstream/.version-bump.json +19 -0
  77. package/upstream/AGENTS.md +1 -0
  78. package/upstream/CHANGELOG.md +13 -0
  79. package/upstream/CLAUDE.md +85 -0
  80. package/upstream/CODE_OF_CONDUCT.md +128 -0
  81. package/upstream/GEMINI.md +2 -0
  82. package/upstream/LICENSE +21 -0
  83. package/upstream/README.md +190 -0
  84. package/upstream/RELEASE-NOTES.md +1096 -0
  85. package/upstream/agents/code-reviewer.md +48 -0
  86. package/upstream/commands/brainstorm.md +5 -0
  87. package/upstream/commands/execute-plan.md +5 -0
  88. package/upstream/commands/write-plan.md +5 -0
  89. package/upstream/docs/README.codex.md +126 -0
  90. package/upstream/docs/README.opencode.md +130 -0
  91. package/upstream/docs/plans/2025-11-22-opencode-support-design.md +294 -0
  92. package/upstream/docs/plans/2025-11-22-opencode-support-implementation.md +1095 -0
  93. package/upstream/docs/plans/2025-11-28-skills-improvements-from-user-feedback.md +711 -0
  94. package/upstream/docs/plans/2026-01-17-visual-brainstorming.md +571 -0
  95. package/upstream/docs/superpowers/plans/2026-01-22-document-review-system.md +301 -0
  96. package/upstream/docs/superpowers/plans/2026-02-19-visual-brainstorming-refactor.md +523 -0
  97. package/upstream/docs/superpowers/plans/2026-03-11-zero-dep-brainstorm-server.md +479 -0
  98. package/upstream/docs/superpowers/plans/2026-03-23-codex-app-compatibility.md +564 -0
  99. package/upstream/docs/superpowers/specs/2026-01-22-document-review-system-design.md +136 -0
  100. package/upstream/docs/superpowers/specs/2026-02-19-visual-brainstorming-refactor-design.md +162 -0
  101. package/upstream/docs/superpowers/specs/2026-03-11-zero-dep-brainstorm-server-design.md +118 -0
  102. package/upstream/docs/superpowers/specs/2026-03-23-codex-app-compatibility-design.md +244 -0
  103. package/upstream/docs/testing.md +303 -0
  104. package/upstream/docs/windows/polyglot-hooks.md +212 -0
  105. package/upstream/gemini-extension.json +6 -0
  106. package/upstream/hooks/hooks-cursor.json +10 -0
  107. package/upstream/hooks/hooks.json +16 -0
  108. package/upstream/hooks/run-hook.cmd +46 -0
  109. package/upstream/hooks/session-start +57 -0
  110. package/upstream/package.json +6 -0
  111. package/upstream/scripts/bump-version.sh +220 -0
  112. package/upstream/skills/brainstorming/SKILL.md +164 -0
  113. package/upstream/skills/brainstorming/scripts/frame-template.html +214 -0
  114. package/upstream/skills/brainstorming/scripts/helper.js +88 -0
  115. package/upstream/skills/brainstorming/scripts/server.cjs +354 -0
  116. package/upstream/skills/brainstorming/scripts/start-server.sh +148 -0
  117. package/upstream/skills/brainstorming/scripts/stop-server.sh +56 -0
  118. package/upstream/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
  119. package/upstream/skills/brainstorming/visual-companion.md +287 -0
  120. package/upstream/skills/dispatching-parallel-agents/SKILL.md +182 -0
  121. package/upstream/skills/executing-plans/SKILL.md +70 -0
  122. package/upstream/skills/finishing-a-development-branch/SKILL.md +200 -0
  123. package/upstream/skills/receiving-code-review/SKILL.md +213 -0
  124. package/upstream/skills/requesting-code-review/SKILL.md +105 -0
  125. package/upstream/skills/requesting-code-review/code-reviewer.md +146 -0
  126. package/upstream/skills/subagent-driven-development/SKILL.md +277 -0
  127. package/upstream/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
  128. package/upstream/skills/subagent-driven-development/implementer-prompt.md +113 -0
  129. package/upstream/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
  130. package/upstream/skills/systematic-debugging/CREATION-LOG.md +119 -0
  131. package/upstream/skills/systematic-debugging/SKILL.md +296 -0
  132. package/upstream/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
  133. package/upstream/skills/systematic-debugging/condition-based-waiting.md +115 -0
  134. package/upstream/skills/systematic-debugging/defense-in-depth.md +122 -0
  135. package/upstream/skills/systematic-debugging/find-polluter.sh +63 -0
  136. package/upstream/skills/systematic-debugging/root-cause-tracing.md +169 -0
  137. package/upstream/skills/systematic-debugging/test-academic.md +14 -0
  138. package/upstream/skills/systematic-debugging/test-pressure-1.md +58 -0
  139. package/upstream/skills/systematic-debugging/test-pressure-2.md +68 -0
  140. package/upstream/skills/systematic-debugging/test-pressure-3.md +69 -0
  141. package/upstream/skills/test-driven-development/SKILL.md +371 -0
  142. package/upstream/skills/test-driven-development/testing-anti-patterns.md +299 -0
  143. package/upstream/skills/using-git-worktrees/SKILL.md +218 -0
  144. package/upstream/skills/using-superpowers/SKILL.md +117 -0
  145. package/upstream/skills/using-superpowers/references/codex-tools.md +100 -0
  146. package/upstream/skills/using-superpowers/references/copilot-tools.md +52 -0
  147. package/upstream/skills/using-superpowers/references/gemini-tools.md +33 -0
  148. package/upstream/skills/verification-before-completion/SKILL.md +139 -0
  149. package/upstream/skills/writing-plans/SKILL.md +152 -0
  150. package/upstream/skills/writing-plans/plan-document-reviewer-prompt.md +49 -0
  151. package/upstream/skills/writing-skills/SKILL.md +655 -0
  152. package/upstream/skills/writing-skills/anthropic-best-practices.md +1150 -0
  153. package/upstream/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
  154. package/upstream/skills/writing-skills/graphviz-conventions.dot +172 -0
  155. package/upstream/skills/writing-skills/persuasion-principles.md +187 -0
  156. package/upstream/skills/writing-skills/render-graphs.js +168 -0
  157. package/upstream/skills/writing-skills/testing-skills-with-subagents.md +384 -0
  158. package/upstream/tests/brainstorm-server/package-lock.json +36 -0
  159. package/upstream/tests/brainstorm-server/package.json +10 -0
  160. package/upstream/tests/brainstorm-server/server.test.js +427 -0
  161. package/upstream/tests/brainstorm-server/windows-lifecycle.test.sh +351 -0
  162. package/upstream/tests/brainstorm-server/ws-protocol.test.js +392 -0
  163. package/upstream/tests/claude-code/README.md +158 -0
  164. package/upstream/tests/claude-code/analyze-token-usage.py +168 -0
  165. package/upstream/tests/claude-code/run-skill-tests.sh +187 -0
  166. package/upstream/tests/claude-code/test-document-review-system.sh +177 -0
  167. package/upstream/tests/claude-code/test-helpers.sh +202 -0
  168. package/upstream/tests/claude-code/test-subagent-driven-development-integration.sh +314 -0
  169. package/upstream/tests/claude-code/test-subagent-driven-development.sh +165 -0
  170. package/upstream/tests/explicit-skill-requests/prompts/action-oriented.txt +3 -0
  171. package/upstream/tests/explicit-skill-requests/prompts/after-planning-flow.txt +17 -0
  172. package/upstream/tests/explicit-skill-requests/prompts/claude-suggested-it.txt +11 -0
  173. package/upstream/tests/explicit-skill-requests/prompts/i-know-what-sdd-means.txt +8 -0
  174. package/upstream/tests/explicit-skill-requests/prompts/mid-conversation-execute-plan.txt +3 -0
  175. package/upstream/tests/explicit-skill-requests/prompts/please-use-brainstorming.txt +1 -0
  176. package/upstream/tests/explicit-skill-requests/prompts/skip-formalities.txt +3 -0
  177. package/upstream/tests/explicit-skill-requests/prompts/subagent-driven-development-please.txt +1 -0
  178. package/upstream/tests/explicit-skill-requests/prompts/use-systematic-debugging.txt +1 -0
  179. package/upstream/tests/explicit-skill-requests/run-all.sh +70 -0
  180. package/upstream/tests/explicit-skill-requests/run-claude-describes-sdd.sh +100 -0
  181. package/upstream/tests/explicit-skill-requests/run-extended-multiturn-test.sh +113 -0
  182. package/upstream/tests/explicit-skill-requests/run-haiku-test.sh +144 -0
  183. package/upstream/tests/explicit-skill-requests/run-multiturn-test.sh +143 -0
  184. package/upstream/tests/explicit-skill-requests/run-test.sh +136 -0
  185. package/upstream/tests/opencode/run-tests.sh +163 -0
  186. package/upstream/tests/opencode/setup.sh +88 -0
  187. package/upstream/tests/opencode/test-plugin-loading.sh +82 -0
  188. package/upstream/tests/opencode/test-priority.sh +198 -0
  189. package/upstream/tests/opencode/test-tools.sh +104 -0
  190. package/upstream/tests/skill-triggering/prompts/dispatching-parallel-agents.txt +8 -0
  191. package/upstream/tests/skill-triggering/prompts/executing-plans.txt +1 -0
  192. package/upstream/tests/skill-triggering/prompts/requesting-code-review.txt +3 -0
  193. package/upstream/tests/skill-triggering/prompts/systematic-debugging.txt +11 -0
  194. package/upstream/tests/skill-triggering/prompts/test-driven-development.txt +7 -0
  195. package/upstream/tests/skill-triggering/prompts/writing-plans.txt +10 -0
  196. package/upstream/tests/skill-triggering/run-all.sh +60 -0
  197. package/upstream/tests/skill-triggering/run-test.sh +88 -0
  198. package/upstream/tests/subagent-driven-dev/go-fractals/design.md +81 -0
  199. package/upstream/tests/subagent-driven-dev/go-fractals/plan.md +172 -0
  200. package/upstream/tests/subagent-driven-dev/go-fractals/scaffold.sh +45 -0
  201. package/upstream/tests/subagent-driven-dev/run-test.sh +106 -0
  202. package/upstream/tests/subagent-driven-dev/svelte-todo/design.md +70 -0
  203. package/upstream/tests/subagent-driven-dev/svelte-todo/plan.md +222 -0
  204. package/upstream/tests/subagent-driven-dev/svelte-todo/scaffold.sh +46 -0
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * SessionStart hook for ai-flow-kit
4
+ * Injects:
5
+ * 1. using-superpowers skill content
6
+ * 2. Active ticket context + gate workflow (if context exists)
7
+ * Cross-platform replacement for the bash session-start hook.
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+
13
+ // This script lives at .claude/hooks/session-start.js
14
+ // Project root is 3 levels up
15
+ const projectRoot = path.resolve(__dirname, '..', '..', '..');
16
+ const skillPath = path.join(projectRoot, '.claude', 'skills', 'using-superpowers', 'SKILL.md');
17
+ const contextPath = path.join(projectRoot, '.claude', 'context', 'current.json');
18
+
19
+ // ── 1. Load superpowers skill ──────────────────────────────────
20
+ let skillContent = '';
21
+ try {
22
+ skillContent = fs.readFileSync(skillPath, 'utf-8');
23
+ } catch (_) {
24
+ // Skill file missing — continue without it
25
+ }
26
+
27
+ // ── 2. Load active ticket context ──────────────────────────────
28
+ let contextBlock = '';
29
+ try {
30
+ if (fs.existsSync(contextPath)) {
31
+ const ctx = JSON.parse(fs.readFileSync(contextPath, 'utf-8'));
32
+ if (ctx.taskId && ctx.title) {
33
+ contextBlock = buildContextPrompt(ctx);
34
+ }
35
+ }
36
+ } catch (_) {
37
+ // Context missing or invalid — continue without it
38
+ }
39
+
40
+ // ── 3. Combine and output ──────────────────────────────────────
41
+ const parts = [];
42
+
43
+ if (skillContent) {
44
+ parts.push(`<EXTREMELY_IMPORTANT>\nYou have superpowers.\n\n**Below is the full content of your 'superpowers:using-superpowers' skill - your introduction to using skills. For all other skills, use the 'Skill' tool:**\n\n${skillContent}\n\n</EXTREMELY_IMPORTANT>`);
45
+ }
46
+
47
+ if (contextBlock) {
48
+ parts.push(contextBlock);
49
+ }
50
+
51
+ const combined = parts.join('\n\n');
52
+
53
+ // Escape for JSON string embedding
54
+ const escaped = combined
55
+ .replace(/\\/g, '\\\\')
56
+ .replace(/"/g, '\\"')
57
+ .replace(/\n/g, '\\n')
58
+ .replace(/\r/g, '\\r')
59
+ .replace(/\t/g, '\\t');
60
+
61
+ process.stdout.write(JSON.stringify({
62
+ hookSpecificOutput: {
63
+ hookEventName: 'SessionStart',
64
+ additionalContext: escaped
65
+ }
66
+ }));
67
+
68
+ process.exit(0);
69
+
70
+ // ── Helper ─────────────────────────────────────────────────────
71
+
72
+ function buildContextPrompt(ctx) {
73
+ const taskType = ctx.taskType || 'feature';
74
+
75
+ const lines = [];
76
+ lines.push('<ACTIVE_TASK>');
77
+ lines.push(`**Active Ticket:** ${ctx.taskId} — ${ctx.title}`);
78
+ lines.push(`**Type:** ${taskType}`);
79
+ lines.push(`**Status:** ${ctx.status || 'Unknown'}`);
80
+ lines.push(`**Assignee:** ${ctx.assignee || 'Unknown'}`);
81
+ lines.push('');
82
+
83
+ if (ctx.description) {
84
+ lines.push('**Description:**');
85
+ lines.push(ctx.description.substring(0, 2000));
86
+ lines.push('');
87
+ }
88
+
89
+ if (ctx.acceptanceCriteria && ctx.acceptanceCriteria.length > 0) {
90
+ lines.push('**Acceptance Criteria:**');
91
+ for (const c of ctx.acceptanceCriteria) {
92
+ lines.push(`- ${c}`);
93
+ }
94
+ lines.push('');
95
+ }
96
+
97
+ if (ctx.comments && ctx.comments.length > 0) {
98
+ lines.push(`**Comments (${ctx.comments.length}):**`);
99
+ // Include last 5 comments to keep context manageable
100
+ const recent = ctx.comments.slice(-5);
101
+ for (const c of recent) {
102
+ lines.push(c);
103
+ lines.push('');
104
+ }
105
+ }
106
+
107
+ if (ctx.context && ctx.context.files && ctx.context.files.length > 0) {
108
+ lines.push('**Related Files:**');
109
+ for (const f of ctx.context.files) {
110
+ lines.push(`- \`${f}\``);
111
+ }
112
+ lines.push('');
113
+ }
114
+
115
+ // ── Gate workflow instruction ──
116
+ lines.push('---');
117
+ lines.push('');
118
+ lines.push('**AUTO-START GATE 1: You MUST begin analyzing this ticket immediately.**');
119
+ lines.push('');
120
+ lines.push(`INVOKE the \`validate-ticket\` skill NOW for ticket ${ctx.taskId}.`);
121
+ lines.push('');
122
+ lines.push('Gate 1 process:');
123
+ lines.push('1. Read the ticket context above');
124
+ lines.push('2. Read CLAUDE.md — understand project architecture and conventions');
125
+ lines.push('3. Read source code — identify related files, data flow, patterns');
126
+ lines.push('4. If anything is unclear — ask ONE question at a time');
127
+ lines.push('5. Output plan/' + ctx.taskId + '/requirement.md with:');
128
+ lines.push(' - Requirements summary');
129
+ lines.push(' - Source code analysis');
130
+ lines.push(' - Proposed solution and approach');
131
+ lines.push(' - Impact analysis');
132
+ lines.push(' - Effort estimate');
133
+ lines.push(' - Testing plan');
134
+ lines.push('6. Display GATE 1 prompt and wait for APPROVED');
135
+ lines.push('');
136
+ lines.push('DO NOT wait for the developer to ask. START NOW.');
137
+ lines.push('</ACTIVE_TASK>');
138
+
139
+ return lines.join('\n');
140
+ }
@@ -0,0 +1,453 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+ const { input } = require('@inquirer/prompts');
5
+
6
+ const PKG_DIR = path.join(__dirname, '..');
7
+ const PKG_VERSION = require('../package.json').version;
8
+
9
+ // Map framework → language family for picking the right rules
10
+ const FRAMEWORK_LANGUAGE = {
11
+ 'laravel': 'php',
12
+ 'spring-boot': 'java',
13
+ 'reactjs': 'javascript',
14
+ 'nextjs': 'javascript',
15
+ 'vue-nuxt': 'javascript',
16
+ };
17
+
18
+ async function copyAssetsToVersion(versionDir, framework) {
19
+ // ── Skills: merge upstream + custom ──────────────────────────────
20
+ const upstreamSkills = path.join(PKG_DIR, 'upstream', 'skills');
21
+ const customSkills = path.join(PKG_DIR, 'custom', 'skills');
22
+
23
+ await fs.ensureDir(path.join(versionDir, 'skills'));
24
+ if (await fs.pathExists(upstreamSkills)) {
25
+ await fs.copy(upstreamSkills, path.join(versionDir, 'skills'), { overwrite: true });
26
+ }
27
+ if (await fs.pathExists(customSkills)) {
28
+ await fs.copy(customSkills, path.join(versionDir, 'skills'), { overwrite: true });
29
+ }
30
+
31
+ // ── Rules: common first, then language-specific override ─────────
32
+ const rulesTarget = path.join(versionDir, 'rules');
33
+ const commonRules = path.join(PKG_DIR, 'custom', 'rules', 'common');
34
+ const allRules = path.join(PKG_DIR, 'custom', 'rules');
35
+
36
+ await fs.ensureDir(rulesTarget);
37
+
38
+ // Copy common rules (language-agnostic) if the folder exists,
39
+ // otherwise fall back to copying everything at root level
40
+ if (await fs.pathExists(commonRules)) {
41
+ await fs.copy(commonRules, rulesTarget, { overwrite: true });
42
+ } else {
43
+ // Copy only .md files at root (skip sub-folders like java/, php/, javascript/)
44
+ const entries = await fs.readdir(allRules, { withFileTypes: true });
45
+ for (const entry of entries) {
46
+ if (entry.isFile() && entry.name.endsWith('.md')) {
47
+ await fs.copy(
48
+ path.join(allRules, entry.name),
49
+ path.join(rulesTarget, entry.name),
50
+ { overwrite: true }
51
+ );
52
+ }
53
+ }
54
+ }
55
+
56
+ // Overlay language-specific rules on top
57
+ if (framework) {
58
+ const lang = FRAMEWORK_LANGUAGE[framework];
59
+ if (lang) {
60
+ const langRules = path.join(PKG_DIR, 'custom', 'rules', lang);
61
+ if (await fs.pathExists(langRules)) {
62
+ await fs.copy(langRules, rulesTarget, { overwrite: true });
63
+ console.log(chalk.gray(` ✓ Applied ${lang} rules`));
64
+ }
65
+ }
66
+ }
67
+ }
68
+
69
+ async function updateSymlinks(projectDir, versionDir) {
70
+ const claudeDir = path.join(projectDir, '.claude');
71
+ await fs.emptyDir(claudeDir);
72
+ await fs.copy(path.join(versionDir, 'skills'), path.join(claudeDir, 'skills'), { overwrite: true });
73
+
74
+ const rulesDir = path.join(projectDir, '.rules');
75
+ await fs.emptyDir(rulesDir);
76
+ await fs.copy(path.join(versionDir, 'rules'), rulesDir, { overwrite: true });
77
+
78
+ // Setup SessionStart hook so superpowers auto-triggers on every session
79
+ await setupSuperpowersHook(projectDir, claudeDir);
80
+
81
+ await fs.writeJson(path.join(projectDir, '.aiflow', 'state.json'), {
82
+ current_version: PKG_VERSION,
83
+ framework: null, // will be set after setupFramework
84
+ });
85
+ }
86
+
87
+ async function setupSuperpowersHook(_projectDir, claudeDir) {
88
+ // 1. Copy hook script into .claude/hooks/
89
+ const hooksDir = path.join(claudeDir, 'hooks');
90
+ await fs.ensureDir(hooksDir);
91
+
92
+ const hookSrc = path.join(PKG_DIR, 'scripts', 'hooks', 'session-start.js');
93
+ const hookDest = path.join(hooksDir, 'session-start.js');
94
+ await fs.copy(hookSrc, hookDest, { overwrite: true });
95
+
96
+ // 2. Merge SessionStart hook into .claude/settings.json
97
+ const settingsFile = path.join(claudeDir, 'settings.json');
98
+ let settings = {};
99
+ if (await fs.pathExists(settingsFile)) {
100
+ settings = await fs.readJson(settingsFile).catch(() => ({}));
101
+ }
102
+
103
+ if (!settings.hooks) settings.hooks = {};
104
+ if (!settings.hooks.SessionStart) settings.hooks.SessionStart = [];
105
+
106
+ // Remove any stale ai-flow-kit hook entry, then add fresh one
107
+ settings.hooks.SessionStart = settings.hooks.SessionStart.filter(
108
+ h => !(h._aiflowKit)
109
+ );
110
+ settings.hooks.SessionStart.push({
111
+ _aiflowKit: true, // marker so we can update it cleanly on re-init
112
+ hooks: [
113
+ {
114
+ type: 'command',
115
+ command: `node "${hookDest.replace(/\\/g, '/')}"`,
116
+ }
117
+ ]
118
+ });
119
+
120
+ await fs.writeJson(settingsFile, settings, { spaces: 2 });
121
+ console.log(chalk.green(`✓ Superpowers SessionStart hook configured`));
122
+ }
123
+
124
+ /**
125
+ * Verify superpowers skills are present — auto-copied from upstream.
126
+ * Developer does NOT need to install superpowers separately.
127
+ */
128
+ async function verifySuperpowersSkills(versionDir) {
129
+ const usingSuperpowers = path.join(versionDir, 'skills', 'using-superpowers', 'SKILL.md');
130
+ if (await fs.pathExists(usingSuperpowers)) {
131
+ console.log(chalk.green(`✓ Superpowers skills ready (built-in)`));
132
+ } else {
133
+ console.log(chalk.yellow(`! Superpowers skills missing — upstream folder may be empty`));
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Setup CLAUDE.md for a framework.
139
+ * If multi=true (multiple frameworks), append instead of overwrite.
140
+ */
141
+ async function setupFramework(projectDir, framework, multi = false) {
142
+ if (!framework) return;
143
+ const templatePath = path.join(PKG_DIR, 'custom', 'templates', `${framework}.md`);
144
+ if (!(await fs.pathExists(templatePath))) {
145
+ console.log(chalk.yellow(`! No template found for framework: ${framework}`));
146
+ return;
147
+ }
148
+
149
+ const claudeMdPath = path.join(projectDir, 'CLAUDE.md');
150
+
151
+ if (!multi) {
152
+ // Single framework → overwrite
153
+ await fs.copy(templatePath, claudeMdPath);
154
+ } else {
155
+ // Multiple frameworks → append with separator
156
+ const content = await fs.readFile(templatePath, 'utf-8');
157
+ const separator = `\n\n---\n\n`;
158
+ if (await fs.pathExists(claudeMdPath)) {
159
+ await fs.appendFile(claudeMdPath, separator + content);
160
+ } else {
161
+ await fs.writeFile(claudeMdPath, content);
162
+ }
163
+ }
164
+
165
+ console.log(chalk.green(`✓ CLAUDE.md updated for framework: ${framework}`));
166
+ }
167
+
168
+ async function setupAdapter(projectDir, adapter) {
169
+ if (!adapter) return;
170
+ const presetPath = path.join(PKG_DIR, 'custom', 'mcp-presets', `${adapter}.json`);
171
+ if (!(await fs.pathExists(presetPath))) {
172
+ console.log(chalk.yellow(`! No MCP preset found for adapter: ${adapter}`));
173
+ return;
174
+ }
175
+
176
+ const { mcpServers } = await fs.readJson(presetPath);
177
+ const serverKey = Object.keys(mcpServers)[0];
178
+ const serverConfig = mcpServers[serverKey];
179
+
180
+ // ── Load existing credentials (same pattern as `aws configure`) ──
181
+ const credsFile = path.join(projectDir, '.aiflow', 'credentials.json');
182
+ let existingCreds = {};
183
+ if (await fs.pathExists(credsFile)) {
184
+ existingCreds = await fs.readJson(credsFile).catch(() => ({}));
185
+ }
186
+
187
+ console.log(chalk.cyan(`\nSetup MCP adapter for ${adapter}:`));
188
+ console.log(chalk.gray('Press Enter to keep existing value shown in [brackets]\n'));
189
+
190
+ const credentials = {};
191
+ for (const [envKey] of Object.entries(serverConfig.env)) {
192
+ const current = existingCreds[envKey];
193
+
194
+ // Show masked current value as default hint (like aws configure)
195
+ const hint = current
196
+ ? `[${maskSecret(current)}]`
197
+ : chalk.gray('[not set — required]');
198
+
199
+ let resolved = '';
200
+ while (true) {
201
+ const value = await input({
202
+ message: `${envKey} ${hint}:`,
203
+ default: '',
204
+ });
205
+
206
+ resolved = value.trim() || current || '';
207
+
208
+ if (resolved) break;
209
+
210
+ // Empty and no existing value → reject
211
+ console.log(chalk.red(` ✗ ${envKey} is required. Please enter a value.`));
212
+ }
213
+
214
+ serverConfig.env[envKey] = resolved;
215
+ credentials[envKey] = resolved;
216
+ }
217
+
218
+ // ── Verify credentials against real API ──────────────────────────
219
+ console.log(chalk.cyan(`\nVerifying ${adapter} credentials...`));
220
+ const verifyResult = await verifyAdapterCredentials(adapter, credentials);
221
+ if (!verifyResult.ok) {
222
+ console.log(chalk.red(`✗ Verification failed: ${verifyResult.error}`));
223
+ console.log(chalk.yellow(' Credentials saved but may not work. Re-run `aiflow init --adapter ' + adapter + '` to update.'));
224
+ } else {
225
+ console.log(chalk.green(`✓ Credentials verified successfully`));
226
+ }
227
+
228
+ // ── 1. Save to Claude Desktop MCP config ─────────────────────────
229
+ const claudeConfigDir = path.join(
230
+ process.env.APPDATA ||
231
+ (process.platform === 'darwin'
232
+ ? process.env.HOME + '/Library/Application Support'
233
+ : process.env.HOME + '/.config'),
234
+ 'Claude'
235
+ );
236
+ const mcpConfigFile = path.join(claudeConfigDir, 'claude_desktop_config.json');
237
+ let existingConfig = { mcpServers: {} };
238
+ if (await fs.pathExists(mcpConfigFile)) {
239
+ existingConfig = await fs.readJson(mcpConfigFile).catch(() => ({ mcpServers: {} }));
240
+ }
241
+ if (!existingConfig.mcpServers) existingConfig.mcpServers = {};
242
+ existingConfig.mcpServers[serverKey] = serverConfig;
243
+ await fs.ensureDir(path.dirname(mcpConfigFile));
244
+ await fs.writeJson(mcpConfigFile, existingConfig, { spaces: 2 });
245
+
246
+ // ── 2. Save locally as .mcp.json ─────────────────────────────────
247
+ await fs.writeJson(
248
+ path.join(projectDir, '.mcp.json'),
249
+ { mcpServers: { [serverKey]: serverConfig } },
250
+ { spaces: 2 }
251
+ );
252
+
253
+ // ── 3. Save credentials to .aiflow/credentials.json ──────────────
254
+ // use.js will read from here when env vars are not set
255
+ Object.assign(existingCreds, credentials);
256
+ await fs.writeJson(credsFile, existingCreds, { spaces: 2 });
257
+
258
+ // ── 4. Ensure credentials.json is gitignored ──────────────────────
259
+ await ensureCredentialsGitignored(projectDir);
260
+
261
+ console.log(chalk.green(`✓ MCP configuration saved (global + local + credentials).`));
262
+ }
263
+
264
+ /**
265
+ * Verify adapter credentials by making a lightweight API call
266
+ */
267
+ async function verifyAdapterCredentials(adapter, credentials) {
268
+ try {
269
+ if (adapter === 'backlog') {
270
+ return await verifyBacklog(credentials);
271
+ }
272
+ if (adapter === 'jira') {
273
+ return await verifyJira(credentials);
274
+ }
275
+ if (adapter === 'figma') {
276
+ return await verifyFigma(credentials);
277
+ }
278
+ // Unknown adapter — skip verification
279
+ return { ok: true };
280
+ } catch (err) {
281
+ return { ok: false, error: err.message };
282
+ }
283
+ }
284
+
285
+ function verifyBacklog(credentials) {
286
+ const https = require('https');
287
+ const apiKey = credentials.BACKLOG_API_KEY;
288
+ const spaceKey = credentials.BACKLOG_SPACE_KEY;
289
+ const domain = spaceKey.includes('.') ? spaceKey : `${spaceKey}.backlog.com`;
290
+
291
+ return new Promise((resolve) => {
292
+ const url = `https://${domain}/api/v2/users/myself?apiKey=${apiKey}`;
293
+ const req = https.get(url, (res) => {
294
+ let data = '';
295
+ res.on('data', chunk => data += chunk);
296
+ res.on('end', () => {
297
+ if (res.statusCode === 200) {
298
+ const user = JSON.parse(data);
299
+ resolve({ ok: true, user: user.name });
300
+ } else if (res.statusCode === 401) {
301
+ resolve({ ok: false, error: 'Invalid API key' });
302
+ } else if (res.statusCode === 404) {
303
+ resolve({ ok: false, error: `Space "${spaceKey}" not found` });
304
+ } else {
305
+ resolve({ ok: false, error: `HTTP ${res.statusCode}` });
306
+ }
307
+ });
308
+ });
309
+ req.on('error', (err) => resolve({ ok: false, error: err.message }));
310
+ req.setTimeout(8000, () => { req.destroy(); resolve({ ok: false, error: 'Connection timeout' }); });
311
+ });
312
+ }
313
+
314
+ function verifyJira(credentials) {
315
+ const https = require('https');
316
+ const { JIRA_API_TOKEN, JIRA_EMAIL, JIRA_DOMAIN } = credentials;
317
+ const auth = Buffer.from(`${JIRA_EMAIL}:${JIRA_API_TOKEN}`).toString('base64');
318
+
319
+ return new Promise((resolve) => {
320
+ const options = {
321
+ hostname: `${JIRA_DOMAIN}.atlassian.net`,
322
+ path: '/rest/api/3/myself',
323
+ headers: { 'Authorization': `Basic ${auth}`, 'Accept': 'application/json' },
324
+ };
325
+ const req = https.get(options, (res) => {
326
+ let data = '';
327
+ res.on('data', chunk => data += chunk);
328
+ res.on('end', () => {
329
+ if (res.statusCode === 200) {
330
+ resolve({ ok: true });
331
+ } else if (res.statusCode === 401) {
332
+ resolve({ ok: false, error: 'Invalid email or API token' });
333
+ } else {
334
+ resolve({ ok: false, error: `HTTP ${res.statusCode}` });
335
+ }
336
+ });
337
+ });
338
+ req.on('error', (err) => resolve({ ok: false, error: err.message }));
339
+ req.setTimeout(8000, () => { req.destroy(); resolve({ ok: false, error: 'Connection timeout' }); });
340
+ });
341
+ }
342
+
343
+ function verifyFigma(credentials) {
344
+ const https = require('https');
345
+ const { FIGMA_API_TOKEN } = credentials;
346
+
347
+ return new Promise((resolve) => {
348
+ const options = {
349
+ hostname: 'api.figma.com',
350
+ path: '/v1/me',
351
+ headers: { 'Authorization': `Bearer ${FIGMA_API_TOKEN}` },
352
+ };
353
+ const req = https.get(options, (res) => {
354
+ let data = '';
355
+ res.on('data', chunk => data += chunk);
356
+ res.on('end', () => {
357
+ if (res.statusCode === 200) {
358
+ const user = JSON.parse(data);
359
+ resolve({ ok: true, user: user.name || user.email });
360
+ } else if (res.statusCode === 403 || res.statusCode === 401) {
361
+ resolve({ ok: false, error: 'Invalid Figma API token' });
362
+ } else {
363
+ resolve({ ok: false, error: `HTTP ${res.statusCode}` });
364
+ }
365
+ });
366
+ });
367
+ req.on('error', (err) => resolve({ ok: false, error: err.message }));
368
+ req.setTimeout(8000, () => { req.destroy(); resolve({ ok: false, error: 'Connection timeout' }); });
369
+ });
370
+ }
371
+
372
+ /**
373
+ * Ensure `.aiflow/credentials.json` is listed in the project's .gitignore.
374
+ * Appends the entry if missing, creates .gitignore if it doesn't exist.
375
+ */
376
+ async function ensureCredentialsGitignored(projectDir) {
377
+ const gitignorePath = path.join(projectDir, '.gitignore');
378
+ const entry = '.aiflow/credentials.json';
379
+
380
+ let content = '';
381
+ if (await fs.pathExists(gitignorePath)) {
382
+ content = await fs.readFile(gitignorePath, 'utf-8');
383
+ }
384
+
385
+ // Check if already ignored (exact line match)
386
+ const lines = content.split('\n').map(l => l.trim());
387
+ if (lines.includes(entry)) return;
388
+
389
+ // Append with a trailing newline
390
+ const separator = content.length > 0 && !content.endsWith('\n') ? '\n' : '';
391
+ await fs.appendFile(gitignorePath, `${separator}${entry}\n`);
392
+ console.log(chalk.yellow(` ⚠ Added ${entry} to .gitignore (contains API keys)`));
393
+ }
394
+
395
+ /**
396
+ * Mask a secret for display: show first 4 chars + *** + last 4 chars
397
+ * e.g. "sAgkcqW5Bq...K5xnFvkQ7" → "sAgk***vkQ7"
398
+ */
399
+ function maskSecret(value) {
400
+ if (!value || value.length <= 8) return '***';
401
+ return `${value.slice(0, 4)}***${value.slice(-4)}`;
402
+ }
403
+
404
+ module.exports = async function init(options) {
405
+ const projectDir = process.cwd();
406
+ const aiflowDir = path.join(projectDir, '.aiflow');
407
+ // Use primary (first) framework for language-specific rules selection
408
+ const frameworks = options.frameworks || (options.framework ? [options.framework] : []);
409
+ const adapters = options.adapters || (options.adapter ? [options.adapter] : []);
410
+ const primaryFramework = frameworks[0] || null;
411
+ const versionDir = path.join(aiflowDir, 'versions', PKG_VERSION);
412
+
413
+ try {
414
+ console.log(chalk.blue(`Initializing ai-flow-kit (v${PKG_VERSION})...`));
415
+ if (frameworks.length) console.log(chalk.gray(` Frameworks: ${frameworks.join(', ')}`));
416
+ if (adapters.length) console.log(chalk.gray(` Adapters: ${adapters.join(', ')}`));
417
+
418
+ await fs.ensureDir(versionDir);
419
+ await copyAssetsToVersion(versionDir, primaryFramework);
420
+ await verifySuperpowersSkills(versionDir);
421
+ await updateSymlinks(projectDir, versionDir);
422
+
423
+ // Setup each framework (CLAUDE.md = last one wins, or merged)
424
+ for (const fw of frameworks) {
425
+ await setupFramework(projectDir, fw, frameworks.length > 1);
426
+ }
427
+
428
+ // Setup each adapter
429
+ for (const adapter of adapters) {
430
+ await setupAdapter(projectDir, adapter);
431
+ }
432
+
433
+ // Persist all frameworks + adapters in state
434
+ const stateFile = path.join(projectDir, '.aiflow', 'state.json');
435
+ if (await fs.pathExists(stateFile)) {
436
+ const state = await fs.readJson(stateFile);
437
+ state.frameworks = frameworks;
438
+ state.adapters = adapters;
439
+ await fs.writeJson(stateFile, state);
440
+ }
441
+
442
+ console.log(chalk.green('\n✨ Initialized AI Flow Kit successfully!'));
443
+ console.log(chalk.blue('\n' + '─'.repeat(62)));
444
+ console.log(chalk.bold.white(' Next steps:'));
445
+ console.log(` ${chalk.green('aiflow use PROJ-33')} ${chalk.gray('← load ticket context')}`);
446
+ console.log(` ${chalk.green('claude')} ${chalk.gray('← AI auto-starts analyzing requirement')}`);
447
+ console.log(chalk.blue('─'.repeat(62)));
448
+ console.log(chalk.gray(` Full guide: ${chalk.green('aiflow guide')} | Command list: ${chalk.green('aiflow guide --commands')}`));
449
+ console.log(chalk.blue('─'.repeat(62)) + '\n');
450
+ } catch (err) {
451
+ console.error(chalk.red(`Error during initialization: ${err.message}`));
452
+ }
453
+ };