@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.
- package/AIFLOW.md +432 -0
- package/CHANGELOG.md +78 -0
- package/CONTRIBUTING.md +351 -0
- package/IMPLEMENTATION_SUMMARY.md +348 -0
- package/QUICK_START.md +392 -0
- package/README.md +323 -0
- package/bin/aiflow.js +178 -0
- package/custom/mcp-presets/README.md +138 -0
- package/custom/mcp-presets/backlog.json +15 -0
- package/custom/mcp-presets/figma.json +15 -0
- package/custom/mcp-presets/google-sheets.json +14 -0
- package/custom/mcp-presets/jira.json +16 -0
- package/custom/prompts/bug-fix.md +149 -0
- package/custom/prompts/feature.md +190 -0
- package/custom/prompts/investigation.md +164 -0
- package/custom/rules/code-style.md +7 -0
- package/custom/rules/java/code-style.md +11 -0
- package/custom/rules/java/naming.md +17 -0
- package/custom/rules/java/review-checklist.md +36 -0
- package/custom/rules/javascript/code-style.md +12 -0
- package/custom/rules/javascript/naming.md +14 -0
- package/custom/rules/naming.md +9 -0
- package/custom/rules/php/code-style.md +10 -0
- package/custom/rules/php/naming.md +15 -0
- package/custom/rules/review-checklist.md +9 -0
- package/custom/skills/figma-to-component/SKILL.md +207 -0
- package/custom/skills/generate-spec/SKILL.md +97 -0
- package/custom/skills/impact-analysis/SKILL.md +94 -0
- package/custom/skills/investigate-bug/SKILL.md +93 -0
- package/custom/skills/report-customer/SKILL.md +95 -0
- package/custom/skills/review-plan/SKILL.md +158 -0
- package/custom/skills/validate-ticket/SKILL.md +240 -0
- package/custom/templates/laravel.md +90 -0
- package/custom/templates/nextjs.md +89 -0
- package/custom/templates/reactjs.md +567 -0
- package/custom/templates/spring-boot.md +598 -0
- package/custom/templates/vue-nuxt.md +89 -0
- package/docs/architecture.md +395 -0
- package/docs/cli-reference.md +438 -0
- package/docs/configuration.md +412 -0
- package/docs/getting-started.md +248 -0
- package/docs/troubleshooting.md +587 -0
- package/docs/workflows/bug-fix.md +126 -0
- package/docs/workflows/feature.md +123 -0
- package/docs/workflows/impact-analysis.md +13 -0
- package/docs/workflows/investigation.md +13 -0
- package/docs/workflows/refactor.md +13 -0
- package/index.js +27 -0
- package/package.json +70 -0
- package/scripts/config.js +317 -0
- package/scripts/context.js +117 -0
- package/scripts/detect.js +239 -0
- package/scripts/doctor.js +48 -0
- package/scripts/guide.js +282 -0
- package/scripts/hooks/session-start.js +140 -0
- package/scripts/init.js +453 -0
- package/scripts/memory.js +247 -0
- package/scripts/prompt.js +392 -0
- package/scripts/remove.js +170 -0
- package/scripts/update.js +111 -0
- package/scripts/use.js +570 -0
- package/scripts/validate.js +303 -0
- package/upstream/.claude-plugin/marketplace.json +20 -0
- package/upstream/.claude-plugin/plugin.json +20 -0
- package/upstream/.codex/INSTALL.md +67 -0
- package/upstream/.cursor-plugin/plugin.json +25 -0
- package/upstream/.gitattributes +18 -0
- package/upstream/.github/FUNDING.yml +3 -0
- package/upstream/.github/ISSUE_TEMPLATE/bug_report.md +52 -0
- package/upstream/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/upstream/.github/ISSUE_TEMPLATE/feature_request.md +34 -0
- package/upstream/.github/ISSUE_TEMPLATE/platform_support.md +23 -0
- package/upstream/.github/PULL_REQUEST_TEMPLATE.md +87 -0
- package/upstream/.opencode/INSTALL.md +83 -0
- package/upstream/.opencode/plugins/superpowers.js +112 -0
- package/upstream/.version-bump.json +19 -0
- package/upstream/AGENTS.md +1 -0
- package/upstream/CHANGELOG.md +13 -0
- package/upstream/CLAUDE.md +85 -0
- package/upstream/CODE_OF_CONDUCT.md +128 -0
- package/upstream/GEMINI.md +2 -0
- package/upstream/LICENSE +21 -0
- package/upstream/README.md +190 -0
- package/upstream/RELEASE-NOTES.md +1096 -0
- package/upstream/agents/code-reviewer.md +48 -0
- package/upstream/commands/brainstorm.md +5 -0
- package/upstream/commands/execute-plan.md +5 -0
- package/upstream/commands/write-plan.md +5 -0
- package/upstream/docs/README.codex.md +126 -0
- package/upstream/docs/README.opencode.md +130 -0
- package/upstream/docs/plans/2025-11-22-opencode-support-design.md +294 -0
- package/upstream/docs/plans/2025-11-22-opencode-support-implementation.md +1095 -0
- package/upstream/docs/plans/2025-11-28-skills-improvements-from-user-feedback.md +711 -0
- package/upstream/docs/plans/2026-01-17-visual-brainstorming.md +571 -0
- package/upstream/docs/superpowers/plans/2026-01-22-document-review-system.md +301 -0
- package/upstream/docs/superpowers/plans/2026-02-19-visual-brainstorming-refactor.md +523 -0
- package/upstream/docs/superpowers/plans/2026-03-11-zero-dep-brainstorm-server.md +479 -0
- package/upstream/docs/superpowers/plans/2026-03-23-codex-app-compatibility.md +564 -0
- package/upstream/docs/superpowers/specs/2026-01-22-document-review-system-design.md +136 -0
- package/upstream/docs/superpowers/specs/2026-02-19-visual-brainstorming-refactor-design.md +162 -0
- package/upstream/docs/superpowers/specs/2026-03-11-zero-dep-brainstorm-server-design.md +118 -0
- package/upstream/docs/superpowers/specs/2026-03-23-codex-app-compatibility-design.md +244 -0
- package/upstream/docs/testing.md +303 -0
- package/upstream/docs/windows/polyglot-hooks.md +212 -0
- package/upstream/gemini-extension.json +6 -0
- package/upstream/hooks/hooks-cursor.json +10 -0
- package/upstream/hooks/hooks.json +16 -0
- package/upstream/hooks/run-hook.cmd +46 -0
- package/upstream/hooks/session-start +57 -0
- package/upstream/package.json +6 -0
- package/upstream/scripts/bump-version.sh +220 -0
- package/upstream/skills/brainstorming/SKILL.md +164 -0
- package/upstream/skills/brainstorming/scripts/frame-template.html +214 -0
- package/upstream/skills/brainstorming/scripts/helper.js +88 -0
- package/upstream/skills/brainstorming/scripts/server.cjs +354 -0
- package/upstream/skills/brainstorming/scripts/start-server.sh +148 -0
- package/upstream/skills/brainstorming/scripts/stop-server.sh +56 -0
- package/upstream/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
- package/upstream/skills/brainstorming/visual-companion.md +287 -0
- package/upstream/skills/dispatching-parallel-agents/SKILL.md +182 -0
- package/upstream/skills/executing-plans/SKILL.md +70 -0
- package/upstream/skills/finishing-a-development-branch/SKILL.md +200 -0
- package/upstream/skills/receiving-code-review/SKILL.md +213 -0
- package/upstream/skills/requesting-code-review/SKILL.md +105 -0
- package/upstream/skills/requesting-code-review/code-reviewer.md +146 -0
- package/upstream/skills/subagent-driven-development/SKILL.md +277 -0
- package/upstream/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
- package/upstream/skills/subagent-driven-development/implementer-prompt.md +113 -0
- package/upstream/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
- package/upstream/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/upstream/skills/systematic-debugging/SKILL.md +296 -0
- package/upstream/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/upstream/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/upstream/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/upstream/skills/systematic-debugging/find-polluter.sh +63 -0
- package/upstream/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/upstream/skills/systematic-debugging/test-academic.md +14 -0
- package/upstream/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/upstream/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/upstream/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/upstream/skills/test-driven-development/SKILL.md +371 -0
- package/upstream/skills/test-driven-development/testing-anti-patterns.md +299 -0
- package/upstream/skills/using-git-worktrees/SKILL.md +218 -0
- package/upstream/skills/using-superpowers/SKILL.md +117 -0
- package/upstream/skills/using-superpowers/references/codex-tools.md +100 -0
- package/upstream/skills/using-superpowers/references/copilot-tools.md +52 -0
- package/upstream/skills/using-superpowers/references/gemini-tools.md +33 -0
- package/upstream/skills/verification-before-completion/SKILL.md +139 -0
- package/upstream/skills/writing-plans/SKILL.md +152 -0
- package/upstream/skills/writing-plans/plan-document-reviewer-prompt.md +49 -0
- package/upstream/skills/writing-skills/SKILL.md +655 -0
- package/upstream/skills/writing-skills/anthropic-best-practices.md +1150 -0
- package/upstream/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/upstream/skills/writing-skills/graphviz-conventions.dot +172 -0
- package/upstream/skills/writing-skills/persuasion-principles.md +187 -0
- package/upstream/skills/writing-skills/render-graphs.js +168 -0
- package/upstream/skills/writing-skills/testing-skills-with-subagents.md +384 -0
- package/upstream/tests/brainstorm-server/package-lock.json +36 -0
- package/upstream/tests/brainstorm-server/package.json +10 -0
- package/upstream/tests/brainstorm-server/server.test.js +427 -0
- package/upstream/tests/brainstorm-server/windows-lifecycle.test.sh +351 -0
- package/upstream/tests/brainstorm-server/ws-protocol.test.js +392 -0
- package/upstream/tests/claude-code/README.md +158 -0
- package/upstream/tests/claude-code/analyze-token-usage.py +168 -0
- package/upstream/tests/claude-code/run-skill-tests.sh +187 -0
- package/upstream/tests/claude-code/test-document-review-system.sh +177 -0
- package/upstream/tests/claude-code/test-helpers.sh +202 -0
- package/upstream/tests/claude-code/test-subagent-driven-development-integration.sh +314 -0
- package/upstream/tests/claude-code/test-subagent-driven-development.sh +165 -0
- package/upstream/tests/explicit-skill-requests/prompts/action-oriented.txt +3 -0
- package/upstream/tests/explicit-skill-requests/prompts/after-planning-flow.txt +17 -0
- package/upstream/tests/explicit-skill-requests/prompts/claude-suggested-it.txt +11 -0
- package/upstream/tests/explicit-skill-requests/prompts/i-know-what-sdd-means.txt +8 -0
- package/upstream/tests/explicit-skill-requests/prompts/mid-conversation-execute-plan.txt +3 -0
- package/upstream/tests/explicit-skill-requests/prompts/please-use-brainstorming.txt +1 -0
- package/upstream/tests/explicit-skill-requests/prompts/skip-formalities.txt +3 -0
- package/upstream/tests/explicit-skill-requests/prompts/subagent-driven-development-please.txt +1 -0
- package/upstream/tests/explicit-skill-requests/prompts/use-systematic-debugging.txt +1 -0
- package/upstream/tests/explicit-skill-requests/run-all.sh +70 -0
- package/upstream/tests/explicit-skill-requests/run-claude-describes-sdd.sh +100 -0
- package/upstream/tests/explicit-skill-requests/run-extended-multiturn-test.sh +113 -0
- package/upstream/tests/explicit-skill-requests/run-haiku-test.sh +144 -0
- package/upstream/tests/explicit-skill-requests/run-multiturn-test.sh +143 -0
- package/upstream/tests/explicit-skill-requests/run-test.sh +136 -0
- package/upstream/tests/opencode/run-tests.sh +163 -0
- package/upstream/tests/opencode/setup.sh +88 -0
- package/upstream/tests/opencode/test-plugin-loading.sh +82 -0
- package/upstream/tests/opencode/test-priority.sh +198 -0
- package/upstream/tests/opencode/test-tools.sh +104 -0
- package/upstream/tests/skill-triggering/prompts/dispatching-parallel-agents.txt +8 -0
- package/upstream/tests/skill-triggering/prompts/executing-plans.txt +1 -0
- package/upstream/tests/skill-triggering/prompts/requesting-code-review.txt +3 -0
- package/upstream/tests/skill-triggering/prompts/systematic-debugging.txt +11 -0
- package/upstream/tests/skill-triggering/prompts/test-driven-development.txt +7 -0
- package/upstream/tests/skill-triggering/prompts/writing-plans.txt +10 -0
- package/upstream/tests/skill-triggering/run-all.sh +60 -0
- package/upstream/tests/skill-triggering/run-test.sh +88 -0
- package/upstream/tests/subagent-driven-dev/go-fractals/design.md +81 -0
- package/upstream/tests/subagent-driven-dev/go-fractals/plan.md +172 -0
- package/upstream/tests/subagent-driven-dev/go-fractals/scaffold.sh +45 -0
- package/upstream/tests/subagent-driven-dev/run-test.sh +106 -0
- package/upstream/tests/subagent-driven-dev/svelte-todo/design.md +70 -0
- package/upstream/tests/subagent-driven-dev/svelte-todo/plan.md +222 -0
- 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
|
+
}
|
package/scripts/init.js
ADDED
|
@@ -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
|
+
};
|