ai-flow-dev 2.2.1 → 2.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -10
- package/dist/cli.js +221 -417
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/prompts/backend/flow-build.md +2 -0
- package/prompts/backend/flow-check.md +2 -0
- package/prompts/backend/flow-commit.md +2 -0
- package/prompts/backend/flow-docs-sync.md +2 -0
- package/prompts/backend/flow-work.md +2 -0
- package/prompts/frontend/flow-build.md +2 -0
- package/prompts/frontend/flow-check.md +2 -0
- package/prompts/frontend/flow-commit.md +2 -0
- package/prompts/frontend/flow-docs-sync.md +3 -1
- package/prompts/frontend/flow-work.md +2 -0
- package/prompts/mobile/flow-build.md +2 -0
- package/prompts/mobile/flow-check.md +2 -0
- package/prompts/mobile/flow-commit.md +2 -0
- package/prompts/mobile/flow-docs-sync.md +2 -0
- package/prompts/mobile/flow-work.md +2 -0
- package/templates/AGENT.template.md +13 -3
- package/templates/backend/.clauderules.template +5 -4
- package/templates/backend/.cursorrules.template +1 -1
package/dist/cli.js
CHANGED
|
@@ -13,40 +13,73 @@ const __dirname = dirname(__filename);
|
|
|
13
13
|
const ROOT_DIR = path.resolve(__dirname, '..');
|
|
14
14
|
const program = new Command();
|
|
15
15
|
const AI_TOOLS = [
|
|
16
|
-
{
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
{
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
16
|
+
{ name: 'GitHub Copilot', value: 'copilot', description: '' },
|
|
17
|
+
{ name: 'Claude Code', value: 'claude', description: '' },
|
|
18
|
+
{ name: 'Cursor', value: 'cursor', description: '' },
|
|
19
|
+
{ name: 'Gemini', value: 'gemini', description: '' },
|
|
20
|
+
{ name: 'Antigravity', value: 'antigravity', description: '' },
|
|
21
|
+
{ name: 'All AI Tools', value: 'all', description: '' },
|
|
22
|
+
];
|
|
23
|
+
const SLASH_COMMANDS = [
|
|
24
|
+
{ name: '/flow-build', desc: 'Full documentation flow' },
|
|
25
|
+
{ name: '/flow-work', desc: 'Development orchestrator (feature, refactor, fix, resume)' },
|
|
26
|
+
{ name: '/flow-check', desc: 'Combined code review & testing workflow' },
|
|
27
|
+
{ name: '/flow-commit', desc: 'Atomic commits (Conventional Commits)' },
|
|
28
|
+
{ name: '/flow-docs-sync', desc: 'Sincronización de documentación' },
|
|
29
|
+
];
|
|
30
|
+
const PROJECT_PHASES = {
|
|
31
|
+
backend: {
|
|
32
|
+
label: 'Backend',
|
|
33
|
+
phases: [
|
|
34
|
+
'fase 0: Context Discovery (proyectos existentes)',
|
|
35
|
+
'fase 1: Discovery & Business',
|
|
36
|
+
'fase 2: Data Architecture',
|
|
37
|
+
'fase 3: System Architecture',
|
|
38
|
+
'fase 4: Security & Auth',
|
|
39
|
+
'fase 5: Code Standards',
|
|
40
|
+
'fase 6: Testing',
|
|
41
|
+
'fase 7: Operations + Tools',
|
|
42
|
+
'fase 8: Project Setup & Final Docs',
|
|
43
|
+
'fase 9: Implementation Roadmap (opcional)',
|
|
44
|
+
'fase 10: User Stories Generation (opcional)',
|
|
45
|
+
],
|
|
35
46
|
},
|
|
36
|
-
{
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
47
|
+
frontend: {
|
|
48
|
+
label: 'Frontend',
|
|
49
|
+
phases: [
|
|
50
|
+
'fase 0: Context Discovery (proyectos existentes)',
|
|
51
|
+
'fase 1: Discovery & UX',
|
|
52
|
+
'fase 2: Components & Framework',
|
|
53
|
+
'fase 3: State Management',
|
|
54
|
+
'fase 4: Styling & Design',
|
|
55
|
+
'fase 5: Code Standards',
|
|
56
|
+
'fase 6: Testing',
|
|
57
|
+
'fase 7: Deployment',
|
|
58
|
+
'fase 8: Project Setup & Final Docs',
|
|
59
|
+
'fase 9: Implementation Roadmap (opcional)',
|
|
60
|
+
'fase 10: User Stories Generation (opcional)',
|
|
61
|
+
],
|
|
40
62
|
},
|
|
41
|
-
{
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
63
|
+
mobile: {
|
|
64
|
+
label: 'Mobile',
|
|
65
|
+
phases: [
|
|
66
|
+
'fase 0: Context Discovery (proyectos existentes)',
|
|
67
|
+
'fase 1: Platform & Framework Selection',
|
|
68
|
+
'fase 2: Navigation & Architecture',
|
|
69
|
+
'fase 3: State & Data Management',
|
|
70
|
+
'fase 4: Permissions & Native Features',
|
|
71
|
+
'fase 5: Code Standards',
|
|
72
|
+
'fase 6: Testing Strategy',
|
|
73
|
+
'fase 7: Store Deployment',
|
|
74
|
+
'fase 8: Project Setup & Final Documentation',
|
|
75
|
+
'fase 9: Implementation Roadmap (opcional)',
|
|
76
|
+
'fase 10: User Stories Generation (opcional)',
|
|
77
|
+
],
|
|
45
78
|
},
|
|
46
|
-
|
|
47
|
-
// Read package.json for version
|
|
48
|
-
const packageJsonPath = path.join(
|
|
49
|
-
const packageJson = JSON.parse(
|
|
79
|
+
};
|
|
80
|
+
// Read package.json for version (Sync for top-level usage)
|
|
81
|
+
const packageJsonPath = path.join(ROOT_DIR, 'package.json');
|
|
82
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
50
83
|
const PKG_VERSION = packageJson.version;
|
|
51
84
|
const EXIT = {
|
|
52
85
|
OK: 0,
|
|
@@ -75,23 +108,21 @@ function fsErrorMessage(e) {
|
|
|
75
108
|
const msg = anyErr && anyErr.message ? String(anyErr.message) : String(e);
|
|
76
109
|
return `${code}: ${msg}`;
|
|
77
110
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
// If no TTY available (non-interactive mode, e.g., in tests), default to copilot
|
|
91
|
-
if (!process.stdin.isTTY) {
|
|
92
|
-
return ['copilot'];
|
|
111
|
+
function getProjectTypeLabel(type) {
|
|
112
|
+
switch (type) {
|
|
113
|
+
case 'backend':
|
|
114
|
+
return 'Backend';
|
|
115
|
+
case 'frontend':
|
|
116
|
+
return 'Frontend';
|
|
117
|
+
case 'fullstack':
|
|
118
|
+
return 'Full Stack';
|
|
119
|
+
case 'mobile':
|
|
120
|
+
return 'Mobile';
|
|
121
|
+
default:
|
|
122
|
+
return type;
|
|
93
123
|
}
|
|
94
|
-
|
|
124
|
+
}
|
|
125
|
+
function printBanner() {
|
|
95
126
|
console.log('\n');
|
|
96
127
|
console.log(chalk.cyan(' ╔═══════════════════════════════════════════════════════════════════╗'));
|
|
97
128
|
console.log(chalk.cyan(' ║') +
|
|
@@ -134,19 +165,75 @@ async function selectAITool(providedTool) {
|
|
|
134
165
|
chalk.cyan('║'));
|
|
135
166
|
console.log(chalk.cyan(' ╚═══════════════════════════════════════════════════════════════════╝'));
|
|
136
167
|
console.log('\n');
|
|
137
|
-
console.log(chalk.white(' 📂 Project
|
|
168
|
+
console.log(chalk.white(' 📂 Project Context'));
|
|
138
169
|
console.log(chalk.gray(' ────────────────────────────────────────────────────────────────────'));
|
|
139
170
|
console.log(chalk.gray(` Working Directory: ${process.cwd()}`));
|
|
140
|
-
console.log(chalk.gray(
|
|
171
|
+
console.log(chalk.gray(` Version: ${PKG_VERSION}`));
|
|
141
172
|
console.log('\n');
|
|
173
|
+
}
|
|
174
|
+
function printAvailableCommands(projectType) {
|
|
175
|
+
console.log(chalk.white('Available slash commands:'));
|
|
176
|
+
for (const cmd of SLASH_COMMANDS) {
|
|
177
|
+
console.log(chalk.gray(` ${cmd.name.padEnd(25)} - ${cmd.desc}`));
|
|
178
|
+
}
|
|
179
|
+
const phasesInfo = PROJECT_PHASES[projectType === 'fullstack' ? 'backend' : projectType];
|
|
180
|
+
if (phasesInfo) {
|
|
181
|
+
console.log(chalk.gray(`\n Fases disponibles (${phasesInfo.label}):`));
|
|
182
|
+
for (const phase of phasesInfo.phases) {
|
|
183
|
+
console.log(chalk.gray(` ${phase}`));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function printNextSteps(config) {
|
|
188
|
+
const toolsText = config.aiTools.length === 1
|
|
189
|
+
? config.aiTools[0]
|
|
190
|
+
: `${config.aiTools.slice(0, -1).join(', ')} and ${config.aiTools[config.aiTools.length - 1]}`;
|
|
191
|
+
console.log(chalk.white('\nNext steps:'));
|
|
192
|
+
if (config.projectType === 'fullstack') {
|
|
193
|
+
const aiToolName = config.aiTools.includes('claude')
|
|
194
|
+
? 'Claude Code'
|
|
195
|
+
: config.aiTools.includes('cursor')
|
|
196
|
+
? 'Cursor'
|
|
197
|
+
: toolsText;
|
|
198
|
+
console.log(chalk.cyan(` 1. Open your AI tool (${aiToolName})`));
|
|
199
|
+
console.log(chalk.cyan(' 2. Backend Prompts: /backend-flow-build'));
|
|
200
|
+
console.log(chalk.cyan(' 3. Frontend Prompts: /frontend-flow-build'));
|
|
201
|
+
console.log(chalk.gray(' Each will guide you through up to 11 phases (0-10)\n'));
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
const aiToolName = config.aiTools.includes('claude')
|
|
205
|
+
? 'Claude Code'
|
|
206
|
+
: config.aiTools.includes('cursor')
|
|
207
|
+
? 'Cursor'
|
|
208
|
+
: config.aiTools.includes('antigravity')
|
|
209
|
+
? 'Antigravity'
|
|
210
|
+
: toolsText;
|
|
211
|
+
console.log(chalk.cyan(` 1. Open ${aiToolName}`));
|
|
212
|
+
console.log(chalk.cyan(' 2. Run: /flow-build'));
|
|
213
|
+
console.log(chalk.gray(' This will guide you through up to 11 phases (0-10)\n'));
|
|
214
|
+
}
|
|
215
|
+
printAvailableCommands(config.projectType);
|
|
216
|
+
}
|
|
217
|
+
async function selectAITool(providedTool) {
|
|
218
|
+
if (providedTool) {
|
|
219
|
+
const tool = AI_TOOLS.find((t) => t.value === providedTool);
|
|
220
|
+
if (!tool) {
|
|
221
|
+
console.error(chalk.red(`❌ Invalid AI tool: ${providedTool}`));
|
|
222
|
+
console.log(chalk.yellow('Available options: claude, cursor, copilot, gemini, antigravity, all'));
|
|
223
|
+
process.exit(EXIT.INVALID_ARGS);
|
|
224
|
+
}
|
|
225
|
+
return providedTool === 'all'
|
|
226
|
+
? ['claude', 'cursor', 'copilot', 'gemini', 'antigravity']
|
|
227
|
+
: [providedTool];
|
|
228
|
+
}
|
|
229
|
+
if (!process.stdin.isTTY)
|
|
230
|
+
return ['copilot'];
|
|
231
|
+
printBanner();
|
|
142
232
|
console.log(chalk.white(' 🤖 Select your AI development tool:'));
|
|
143
233
|
console.log(chalk.gray(' ────────────────────────────────────────────────────────────────────'));
|
|
144
234
|
const selectedTool = await select({
|
|
145
235
|
message: 'Select your AI tool:',
|
|
146
|
-
choices: AI_TOOLS.map((tool) => ({
|
|
147
|
-
name: tool.name,
|
|
148
|
-
value: tool.value,
|
|
149
|
-
})),
|
|
236
|
+
choices: AI_TOOLS.map((tool) => ({ name: tool.name, value: tool.value })),
|
|
150
237
|
default: 'copilot',
|
|
151
238
|
});
|
|
152
239
|
return selectedTool === 'all'
|
|
@@ -154,7 +241,6 @@ async function selectAITool(providedTool) {
|
|
|
154
241
|
: [selectedTool];
|
|
155
242
|
}
|
|
156
243
|
async function selectProjectType(providedType) {
|
|
157
|
-
// v1.4.0: Backend, Frontend, Fullstack, and Mobile supported
|
|
158
244
|
if (providedType) {
|
|
159
245
|
const valid = ['backend', 'frontend', 'fullstack', 'mobile'];
|
|
160
246
|
if (!valid.includes(providedType)) {
|
|
@@ -164,11 +250,8 @@ async function selectProjectType(providedType) {
|
|
|
164
250
|
}
|
|
165
251
|
return providedType;
|
|
166
252
|
}
|
|
167
|
-
|
|
168
|
-
if (!process.stdin.isTTY) {
|
|
253
|
+
if (!process.stdin.isTTY)
|
|
169
254
|
return 'backend';
|
|
170
|
-
}
|
|
171
|
-
// v1.4.0: Interactive selection for backend/frontend/fullstack/mobile
|
|
172
255
|
const projectType = await select({
|
|
173
256
|
message: 'What type of project are you building?',
|
|
174
257
|
choices: [
|
|
@@ -182,14 +265,12 @@ async function selectProjectType(providedType) {
|
|
|
182
265
|
return projectType;
|
|
183
266
|
}
|
|
184
267
|
async function checkIfInitialized(targetPath) {
|
|
185
|
-
|
|
186
|
-
return await fs.pathExists(bootstrapPath);
|
|
268
|
+
return await fs.pathExists(path.join(targetPath, '.ai-flow'));
|
|
187
269
|
}
|
|
188
270
|
async function createBootstrapStructure(targetPath, aiTools, projectType = 'backend', dryRun, verbose) {
|
|
189
271
|
const spinner = ora('Creating .ai-flow structure...').start();
|
|
190
272
|
try {
|
|
191
273
|
const bootstrapPath = path.join(targetPath, '.ai-flow');
|
|
192
|
-
// Create core directories
|
|
193
274
|
if (dryRun) {
|
|
194
275
|
spinner.succeed('Created .ai-flow structure (dry-run)');
|
|
195
276
|
return;
|
|
@@ -199,19 +280,16 @@ async function createBootstrapStructure(targetPath, aiTools, projectType = 'back
|
|
|
199
280
|
await fs.ensureDir(path.join(bootstrapPath, 'prompts'));
|
|
200
281
|
await fs.ensureDir(path.join(bootstrapPath, 'templates', 'docs'));
|
|
201
282
|
await fs.ensureDir(path.join(bootstrapPath, 'templates', 'specs'));
|
|
202
|
-
// Create config file with new projectType field
|
|
203
283
|
const config = {
|
|
204
284
|
version: PKG_VERSION,
|
|
205
|
-
aiTools
|
|
285
|
+
aiTools,
|
|
206
286
|
createdAt: new Date().toISOString(),
|
|
207
|
-
projectType
|
|
208
|
-
// Deprecated fields for backward compatibility
|
|
287
|
+
projectType,
|
|
209
288
|
backend: projectType === 'backend' || projectType === 'fullstack',
|
|
210
289
|
frontend: projectType === 'frontend' || projectType === 'fullstack',
|
|
211
290
|
mobile: projectType === 'mobile',
|
|
212
291
|
};
|
|
213
292
|
await fs.writeJSON(path.join(bootstrapPath, 'core', 'config.json'), config, { spaces: 2 });
|
|
214
|
-
logVerbose(`Wrote ${path.join(bootstrapPath, 'core', 'config.json')}`, verbose);
|
|
215
293
|
spinner.succeed('Created .ai-flow structure');
|
|
216
294
|
}
|
|
217
295
|
catch (error) {
|
|
@@ -222,109 +300,80 @@ async function createBootstrapStructure(targetPath, aiTools, projectType = 'back
|
|
|
222
300
|
async function copyTemplates(targetPath, projectType = 'backend', aiTools = [], dryRun, verbose) {
|
|
223
301
|
const spinner = ora('Copying templates to .ai-flow/templates/...').start();
|
|
224
302
|
try {
|
|
225
|
-
// Templates are copied WITHOUT rendering to .ai-flow/templates/
|
|
226
|
-
// Phase 8 will render them to project root
|
|
227
303
|
if (dryRun) {
|
|
228
304
|
spinner.succeed('Templates copied (dry-run)');
|
|
229
305
|
return;
|
|
230
306
|
}
|
|
231
|
-
await assertDirWritable(targetPath);
|
|
232
307
|
const destTemplatesPath = path.join(targetPath, '.ai-flow', 'templates');
|
|
233
|
-
// Find all .template.md and .template files in a directory and subfolders
|
|
234
308
|
const walk = async (dir) => {
|
|
235
309
|
let files = [];
|
|
236
310
|
for (const entry of await fs.readdir(dir)) {
|
|
237
311
|
const fullPath = path.join(dir, entry);
|
|
238
312
|
const stat = await fs.stat(fullPath);
|
|
239
|
-
if (stat.isDirectory())
|
|
313
|
+
if (stat.isDirectory())
|
|
240
314
|
files = files.concat(await walk(fullPath));
|
|
241
|
-
|
|
242
|
-
else if (entry.endsWith('.template.md') || entry.endsWith('.template')) {
|
|
315
|
+
else if (entry.endsWith('.template.md') || entry.endsWith('.template'))
|
|
243
316
|
files.push(fullPath);
|
|
244
|
-
}
|
|
245
317
|
}
|
|
246
318
|
return files;
|
|
247
319
|
};
|
|
248
|
-
// Collect template files from project-type-specific directories
|
|
249
320
|
const templateSources = [];
|
|
250
|
-
// Always include root templates (AGENT.template.md)
|
|
251
|
-
const rootTemplatesSource = path.join(ROOT_DIR, 'templates');
|
|
252
321
|
const processedFiles = new Map();
|
|
253
|
-
|
|
254
|
-
const
|
|
255
|
-
for (const item of allRootItems) {
|
|
322
|
+
const rootTemplatesSource = path.join(ROOT_DIR, 'templates');
|
|
323
|
+
for (const item of await fs.readdir(rootTemplatesSource)) {
|
|
256
324
|
const fullPath = path.join(rootTemplatesSource, item);
|
|
257
|
-
|
|
258
|
-
if (stat.isFile() && item.endsWith('.template.md')) {
|
|
325
|
+
if ((await fs.stat(fullPath)).isFile() && item.endsWith('.template.md')) {
|
|
259
326
|
processedFiles.set(item, { file: fullPath, base: rootTemplatesSource });
|
|
260
327
|
}
|
|
261
328
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
else if (projectType === 'frontend')
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
329
|
+
if (projectType === 'backend')
|
|
330
|
+
templateSources.push({
|
|
331
|
+
source: path.join(ROOT_DIR, 'templates', 'backend'),
|
|
332
|
+
base: path.join(ROOT_DIR, 'templates', 'backend'),
|
|
333
|
+
});
|
|
334
|
+
else if (projectType === 'frontend')
|
|
335
|
+
templateSources.push({
|
|
336
|
+
source: path.join(ROOT_DIR, 'templates', 'frontend'),
|
|
337
|
+
base: path.join(ROOT_DIR, 'templates', 'frontend'),
|
|
338
|
+
});
|
|
271
339
|
else if (projectType === 'fullstack') {
|
|
272
|
-
// v1.3.0: Copy both backend and frontend templates
|
|
273
|
-
// Priority: fullstack-specific templates > backend templates > frontend templates
|
|
274
340
|
const fullstackSource = path.join(ROOT_DIR, 'templates', 'fullstack');
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
// Backend templates (used as base for conflicts)
|
|
286
|
-
templateSources.push({ source: backendSource, base: backendSource });
|
|
287
|
-
// Frontend templates (will overwrite only if not in fullstack and not conflicting with backend)
|
|
288
|
-
templateSources.push({ source: frontendSource, base: frontendSource });
|
|
289
|
-
}
|
|
290
|
-
else if (projectType === 'mobile') {
|
|
291
|
-
// v1.4.0: Copy mobile templates
|
|
292
|
-
const mobileSource = path.join(ROOT_DIR, 'templates', 'mobile');
|
|
293
|
-
templateSources.push({ source: mobileSource, base: mobileSource });
|
|
341
|
+
if (await fs.pathExists(fullstackSource))
|
|
342
|
+
templateSources.push({ source: fullstackSource, base: fullstackSource });
|
|
343
|
+
templateSources.push({
|
|
344
|
+
source: path.join(ROOT_DIR, 'templates', 'backend'),
|
|
345
|
+
base: path.join(ROOT_DIR, 'templates', 'backend'),
|
|
346
|
+
});
|
|
347
|
+
templateSources.push({
|
|
348
|
+
source: path.join(ROOT_DIR, 'templates', 'frontend'),
|
|
349
|
+
base: path.join(ROOT_DIR, 'templates', 'frontend'),
|
|
350
|
+
});
|
|
294
351
|
}
|
|
295
|
-
|
|
296
|
-
|
|
352
|
+
else if (projectType === 'mobile')
|
|
353
|
+
templateSources.push({
|
|
354
|
+
source: path.join(ROOT_DIR, 'templates', 'mobile'),
|
|
355
|
+
base: path.join(ROOT_DIR, 'templates', 'mobile'),
|
|
356
|
+
});
|
|
297
357
|
for (const { source, base } of templateSources) {
|
|
298
|
-
const
|
|
299
|
-
for (const file of files) {
|
|
358
|
+
for (const file of await walk(source)) {
|
|
300
359
|
const relPath = path.relative(base, file);
|
|
301
|
-
|
|
302
|
-
if (!processedFiles.has(relPath)) {
|
|
360
|
+
if (!processedFiles.has(relPath))
|
|
303
361
|
processedFiles.set(relPath, { file, base });
|
|
304
|
-
}
|
|
305
362
|
}
|
|
306
363
|
}
|
|
307
|
-
// Copy each template WITHOUT rendering to .ai-flow/templates/
|
|
308
364
|
for (const [relPath, { file: templateFile }] of processedFiles) {
|
|
309
|
-
// Skip AI tool-specific config files if the tool is not selected
|
|
310
365
|
const fileName = path.basename(relPath);
|
|
311
366
|
if (fileName === '.clauderules.template' &&
|
|
312
367
|
!aiTools.includes('claude') &&
|
|
313
|
-
!aiTools.includes('all'))
|
|
314
|
-
logVerbose(`Skipping ${relPath} (Claude not selected)`, verbose);
|
|
368
|
+
!aiTools.includes('all'))
|
|
315
369
|
continue;
|
|
316
|
-
}
|
|
317
370
|
if (fileName === '.cursorrules.template' &&
|
|
318
371
|
!aiTools.includes('cursor') &&
|
|
319
|
-
!aiTools.includes('all'))
|
|
320
|
-
logVerbose(`Skipping ${relPath} (Cursor not selected)`, verbose);
|
|
372
|
+
!aiTools.includes('all'))
|
|
321
373
|
continue;
|
|
322
|
-
}
|
|
323
|
-
// Preserve original file structure with .template extension
|
|
324
374
|
const destPath = path.join(destTemplatesPath, relPath);
|
|
325
375
|
await fs.ensureDir(path.dirname(destPath));
|
|
326
376
|
await fs.copy(templateFile, destPath);
|
|
327
|
-
logVerbose(`Copied ${relPath}`, verbose);
|
|
328
377
|
}
|
|
329
378
|
spinner.succeed('Templates copied');
|
|
330
379
|
}
|
|
@@ -344,7 +393,6 @@ async function copyPrompts(targetPath, dryRun, verbose) {
|
|
|
344
393
|
}
|
|
345
394
|
await assertDirWritable(promptsTarget);
|
|
346
395
|
await fs.copy(promptsSource, promptsTarget);
|
|
347
|
-
logVerbose(`Copied prompts to ${promptsTarget}`, verbose);
|
|
348
396
|
spinner.succeed('Master prompts copied');
|
|
349
397
|
}
|
|
350
398
|
catch (error) {
|
|
@@ -355,16 +403,12 @@ async function copyPrompts(targetPath, dryRun, verbose) {
|
|
|
355
403
|
async function setupSlashCommands(targetPath, aiTools, projectType = 'backend', dryRun, verbose) {
|
|
356
404
|
const spinner = ora('Setting up slash commands...').start();
|
|
357
405
|
try {
|
|
358
|
-
// Determine which prompt directories to copy from
|
|
359
406
|
const promptSources = [];
|
|
360
|
-
if (projectType === 'backend')
|
|
407
|
+
if (projectType === 'backend')
|
|
361
408
|
promptSources.push({ dir: 'backend' });
|
|
362
|
-
|
|
363
|
-
else if (projectType === 'frontend') {
|
|
409
|
+
else if (projectType === 'frontend')
|
|
364
410
|
promptSources.push({ dir: 'frontend' });
|
|
365
|
-
}
|
|
366
411
|
else if (projectType === 'fullstack') {
|
|
367
|
-
// For fullstack, copy both with prefixes
|
|
368
412
|
promptSources.push({ dir: 'backend', prefix: 'backend-' });
|
|
369
413
|
promptSources.push({ dir: 'frontend', prefix: 'frontend-' });
|
|
370
414
|
}
|
|
@@ -374,7 +418,6 @@ async function setupSlashCommands(targetPath, aiTools, projectType = 'backend',
|
|
|
374
418
|
for (const { dir, prefix } of promptSources) {
|
|
375
419
|
const promptsSource = path.join(ROOT_DIR, 'prompts', dir);
|
|
376
420
|
const allFiles = await fs.readdir(promptsSource);
|
|
377
|
-
// Filter markdown files, excluding internal logic files (they are loaded by main commands)
|
|
378
421
|
const files = allFiles.filter((file) => {
|
|
379
422
|
const isMarkdown = file.endsWith('.md');
|
|
380
423
|
const isInternalPhase = file.match(/^flow-build-phase-\d+.*\.md$/);
|
|
@@ -383,82 +426,31 @@ async function setupSlashCommands(targetPath, aiTools, projectType = 'backend',
|
|
|
383
426
|
return isMarkdown && !isInternalPhase && !isInternalWork && !isInternalCheck;
|
|
384
427
|
});
|
|
385
428
|
for (const tool of aiTools) {
|
|
429
|
+
let commandsTarget = '';
|
|
430
|
+
let extension = '.md';
|
|
386
431
|
if (tool === 'copilot') {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
if (!dryRun) {
|
|
390
|
-
await assertDirWritable(promptsTarget);
|
|
391
|
-
await fs.ensureDir(promptsTarget);
|
|
392
|
-
}
|
|
393
|
-
for (const file of files) {
|
|
394
|
-
const srcFile = path.join(promptsSource, file);
|
|
395
|
-
const base = file.replace(/\.md$/, '');
|
|
396
|
-
const destName = prefix ? `${prefix}${base}.prompt.md` : `${base}.prompt.md`;
|
|
397
|
-
const destFile = path.join(promptsTarget, destName);
|
|
398
|
-
if (!dryRun)
|
|
399
|
-
await fs.copyFile(srcFile, destFile);
|
|
400
|
-
logVerbose(`Installed ${destFile}`, verbose);
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
else if (tool === 'claude') {
|
|
404
|
-
const commandsTarget = path.join(targetPath, '.claude', 'commands');
|
|
405
|
-
if (!dryRun) {
|
|
406
|
-
await assertDirWritable(commandsTarget);
|
|
407
|
-
await fs.ensureDir(commandsTarget);
|
|
408
|
-
}
|
|
409
|
-
for (const file of files) {
|
|
410
|
-
const srcFile = path.join(promptsSource, file);
|
|
411
|
-
const destName = prefix ? `${prefix}${file}` : file;
|
|
412
|
-
const destFile = path.join(commandsTarget, destName);
|
|
413
|
-
if (!dryRun)
|
|
414
|
-
await fs.copyFile(srcFile, destFile);
|
|
415
|
-
logVerbose(`Installed ${destFile}`, verbose);
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
else if (tool === 'cursor') {
|
|
419
|
-
const commandsTarget = path.join(targetPath, '.cursor', 'commands');
|
|
420
|
-
if (!dryRun) {
|
|
421
|
-
await assertDirWritable(commandsTarget);
|
|
422
|
-
await fs.ensureDir(commandsTarget);
|
|
423
|
-
}
|
|
424
|
-
for (const file of files) {
|
|
425
|
-
const srcFile = path.join(promptsSource, file);
|
|
426
|
-
const destName = prefix ? `${prefix}${file}` : file;
|
|
427
|
-
const destFile = path.join(commandsTarget, destName);
|
|
428
|
-
if (!dryRun)
|
|
429
|
-
await fs.copyFile(srcFile, destFile);
|
|
430
|
-
logVerbose(`Installed ${destFile}`, verbose);
|
|
431
|
-
}
|
|
432
|
+
commandsTarget = path.join(targetPath, '.github', 'prompts');
|
|
433
|
+
extension = '.prompt.md';
|
|
432
434
|
}
|
|
433
|
-
else if (tool === '
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
await fs.copyFile(srcFile, destFile);
|
|
445
|
-
logVerbose(`Installed ${destFile}`, verbose);
|
|
446
|
-
}
|
|
435
|
+
else if (tool === 'claude')
|
|
436
|
+
commandsTarget = path.join(targetPath, '.claude', 'commands');
|
|
437
|
+
else if (tool === 'cursor')
|
|
438
|
+
commandsTarget = path.join(targetPath, '.cursor', 'commands');
|
|
439
|
+
else if (tool === 'gemini')
|
|
440
|
+
commandsTarget = path.join(targetPath, '.gemini', 'commands');
|
|
441
|
+
else if (tool === 'antigravity')
|
|
442
|
+
commandsTarget = path.join(targetPath, '.agent', 'workflows');
|
|
443
|
+
if (!dryRun && commandsTarget) {
|
|
444
|
+
await assertDirWritable(commandsTarget);
|
|
445
|
+
await fs.ensureDir(commandsTarget);
|
|
447
446
|
}
|
|
448
|
-
|
|
449
|
-
const
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
const srcFile = path.join(promptsSource, file);
|
|
456
|
-
const destName = prefix ? `${prefix}${file}` : file;
|
|
457
|
-
const destFile = path.join(workflowsTarget, destName);
|
|
458
|
-
if (!dryRun)
|
|
459
|
-
await fs.copyFile(srcFile, destFile);
|
|
460
|
-
logVerbose(`Installed ${destFile}`, verbose);
|
|
461
|
-
}
|
|
447
|
+
for (const file of files) {
|
|
448
|
+
const srcFile = path.join(promptsSource, file);
|
|
449
|
+
const base = file.replace(/\.md$/, '');
|
|
450
|
+
const destName = prefix ? `${prefix}${base}${extension}` : `${base}${extension}`;
|
|
451
|
+
const destFile = path.join(commandsTarget, destName);
|
|
452
|
+
if (!dryRun)
|
|
453
|
+
await fs.copyFile(srcFile, destFile);
|
|
462
454
|
}
|
|
463
455
|
}
|
|
464
456
|
}
|
|
@@ -471,41 +463,27 @@ async function setupSlashCommands(targetPath, aiTools, projectType = 'backend',
|
|
|
471
463
|
}
|
|
472
464
|
async function initializeProject(targetPath, aiTool, projectType, projectName, projectDescription, flags) {
|
|
473
465
|
try {
|
|
474
|
-
|
|
475
|
-
const isInitialized = await checkIfInitialized(targetPath);
|
|
476
|
-
if (isInitialized) {
|
|
466
|
+
if (await checkIfInitialized(targetPath)) {
|
|
477
467
|
console.log(chalk.yellow('\n⚠️ Project already initialized with AI Flow'));
|
|
478
468
|
let reinitialize = false;
|
|
479
|
-
if (process.stdin.isTTY)
|
|
480
|
-
reinitialize = await confirm({
|
|
481
|
-
message: 'Do you want to reinitialize?',
|
|
482
|
-
default: false,
|
|
483
|
-
});
|
|
484
|
-
}
|
|
469
|
+
if (process.stdin.isTTY)
|
|
470
|
+
reinitialize = await confirm({ message: 'Do you want to reinitialize?', default: false });
|
|
485
471
|
if (!reinitialize) {
|
|
486
472
|
console.log(chalk.blue('Initialization cancelled'));
|
|
487
473
|
return;
|
|
488
474
|
}
|
|
489
475
|
}
|
|
490
|
-
// Select AI tools
|
|
491
476
|
const aiTools = await selectAITool(aiTool);
|
|
492
|
-
// Select project type (v1.2.0: backend or frontend)
|
|
493
477
|
const selectedProjectType = await selectProjectType(projectType);
|
|
494
|
-
// Infer project name from directory
|
|
495
478
|
const inferredName = path
|
|
496
479
|
.basename(targetPath)
|
|
497
480
|
.split('-')
|
|
498
481
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
499
482
|
.join(' ');
|
|
500
|
-
// Request minimal project data only if not provided
|
|
501
483
|
if (projectName && !isValidName(projectName)) {
|
|
502
484
|
console.error(chalk.red('Invalid project name'));
|
|
503
485
|
process.exit(EXIT.INVALID_ARGS);
|
|
504
486
|
}
|
|
505
|
-
if (projectDescription && !isValidDescription(projectDescription)) {
|
|
506
|
-
console.error(chalk.red('Invalid project description'));
|
|
507
|
-
process.exit(EXIT.INVALID_ARGS);
|
|
508
|
-
}
|
|
509
487
|
let finalProjectName = projectName;
|
|
510
488
|
if (!finalProjectName) {
|
|
511
489
|
if (process.stdin.isTTY) {
|
|
@@ -515,131 +493,23 @@ async function initializeProject(targetPath, aiTool, projectType, projectName, p
|
|
|
515
493
|
validate: (input) => isValidName(input) || 'Enter 2-100 chars: letters, numbers, space, - _ .',
|
|
516
494
|
});
|
|
517
495
|
}
|
|
518
|
-
else
|
|
496
|
+
else
|
|
519
497
|
finalProjectName = inferredName;
|
|
520
|
-
}
|
|
521
498
|
}
|
|
522
499
|
console.log(chalk.cyan('\n📦 Initializing AI Flow...\n'));
|
|
523
|
-
// Create structure
|
|
524
500
|
await createBootstrapStructure(targetPath, aiTools, selectedProjectType, flags?.dryRun, flags?.verbose);
|
|
525
501
|
await copyTemplates(targetPath, selectedProjectType, aiTools, flags?.dryRun, flags?.verbose);
|
|
526
502
|
await copyPrompts(targetPath, flags?.dryRun, flags?.verbose);
|
|
527
503
|
await setupSlashCommands(targetPath, aiTools, selectedProjectType, flags?.dryRun, flags?.verbose);
|
|
528
|
-
const modeText = flags?.dryRun ? 'DRY-RUN' : 'WRITE';
|
|
529
504
|
console.log(chalk.green('\n✅ AI Flow initialized successfully!'));
|
|
530
505
|
console.log(chalk.white('\nSummary:'));
|
|
531
|
-
console.log(chalk.gray(` Project:
|
|
532
|
-
console.log(chalk.gray(` Version:
|
|
506
|
+
console.log(chalk.gray(` Project: ${finalProjectName}`));
|
|
507
|
+
console.log(chalk.gray(` Version: ${PKG_VERSION}`));
|
|
533
508
|
console.log(chalk.gray(` Directory: ${targetPath}`));
|
|
534
|
-
console.log(chalk.gray(` Tools:
|
|
535
|
-
console.log(chalk.gray(`
|
|
536
|
-
console.log(chalk.
|
|
537
|
-
|
|
538
|
-
? aiTools[0]
|
|
539
|
-
: `${aiTools.slice(0, -1).join(', ')} and ${aiTools[aiTools.length - 1]}`;
|
|
540
|
-
if (selectedProjectType === 'fullstack') {
|
|
541
|
-
if (aiTools.includes('claude')) {
|
|
542
|
-
console.log(chalk.cyan(' 1. Open Claude Code'));
|
|
543
|
-
console.log(chalk.cyan(' 2. Run: /backend-flow-build (for backend documentation)'));
|
|
544
|
-
console.log(chalk.cyan(' 3. Run: /frontend-flow-build (for frontend documentation)'));
|
|
545
|
-
console.log(chalk.gray(' Each will guide you through up to 11 phases (0-10)\n'));
|
|
546
|
-
}
|
|
547
|
-
else if (aiTools.includes('cursor')) {
|
|
548
|
-
console.log(chalk.cyan(' 1. Open Cursor'));
|
|
549
|
-
console.log(chalk.cyan(' 2. Run: /backend-flow-build (for backend documentation)'));
|
|
550
|
-
console.log(chalk.cyan(' 3. Run: /frontend-flow-build (for frontend documentation)'));
|
|
551
|
-
console.log(chalk.gray(' Each will guide you through up to 11 phases (0-10)\n'));
|
|
552
|
-
}
|
|
553
|
-
else {
|
|
554
|
-
console.log(chalk.cyan(` 1. Open your AI tool (${toolsText})`));
|
|
555
|
-
console.log(chalk.cyan(' 2. Run: /backend-flow-build (for backend documentation)'));
|
|
556
|
-
console.log(chalk.cyan(' 3. Run: /frontend-flow-build (for frontend documentation)'));
|
|
557
|
-
console.log(chalk.gray(' Each will guide you through up to 11 phases (0-10)\n'));
|
|
558
|
-
}
|
|
559
|
-
console.log(chalk.white('Available slash commands:'));
|
|
560
|
-
console.log(chalk.gray(' Backend commands:'));
|
|
561
|
-
console.log(chalk.gray(' /backend-flow-build - Flujo completo (9 fases en orden)'));
|
|
562
|
-
console.log(chalk.gray(' /backend-flow-build fase N - Fase específica (0-9)'));
|
|
563
|
-
console.log(chalk.gray(' /backend-flow-docs-sync - Update backend documentation\n'));
|
|
564
|
-
console.log(chalk.gray(' Frontend commands:'));
|
|
565
|
-
console.log(chalk.gray(' /frontend-flow-build - Flujo completo (8 fases en orden)'));
|
|
566
|
-
console.log(chalk.gray(' /frontend-flow-build fase N - Fase específica (0-8)'));
|
|
567
|
-
console.log(chalk.gray(' /frontend-flow-docs-sync - Update frontend documentation\n'));
|
|
568
|
-
}
|
|
569
|
-
else if (selectedProjectType === 'mobile') {
|
|
570
|
-
if (aiTools.includes('claude')) {
|
|
571
|
-
console.log(chalk.cyan(' 1. Open Claude Code'));
|
|
572
|
-
console.log(chalk.cyan(' 2. Run: /flow-build'));
|
|
573
|
-
console.log(chalk.gray(' This will start the 9-phase interactive setup\n'));
|
|
574
|
-
}
|
|
575
|
-
else if (aiTools.includes('cursor')) {
|
|
576
|
-
console.log(chalk.cyan(' 1. Open Cursor'));
|
|
577
|
-
console.log(chalk.cyan(' 2. Run: /flow-build'));
|
|
578
|
-
console.log(chalk.gray(' This will start the 9-phase interactive setup\n'));
|
|
579
|
-
}
|
|
580
|
-
else {
|
|
581
|
-
console.log(chalk.cyan(` 1. Open your AI tool (${toolsText})`));
|
|
582
|
-
console.log(chalk.cyan(' 2. Run: /flow-build'));
|
|
583
|
-
console.log(chalk.gray(' This will start the 9-phase interactive setup\n'));
|
|
584
|
-
}
|
|
585
|
-
console.log(chalk.white('Available slash commands:'));
|
|
586
|
-
console.log(chalk.gray(' /flow-build - Flujo completo (8 fases en orden)'));
|
|
587
|
-
console.log(chalk.gray(' /flow-build fase N - Fase específica (0-8: Discovery, Platform, Navigation, State, Permissions, Standards, Testing, Deployment)'));
|
|
588
|
-
console.log(chalk.gray(' /flow-docs-sync - Update documentation when code changes\n'));
|
|
589
|
-
}
|
|
590
|
-
else {
|
|
591
|
-
if (aiTools.includes('claude')) {
|
|
592
|
-
console.log(chalk.cyan(' 1. Open Claude Code'));
|
|
593
|
-
console.log(chalk.cyan(' 2. Run: /flow-build'));
|
|
594
|
-
console.log(chalk.gray(' This will guide you through up to 11 phases (0-10)\n'));
|
|
595
|
-
}
|
|
596
|
-
else if (aiTools.includes('cursor')) {
|
|
597
|
-
console.log(chalk.cyan(' 1. Open Cursor'));
|
|
598
|
-
console.log(chalk.cyan(' 2. Run: /flow-build'));
|
|
599
|
-
console.log(chalk.gray(' This will guide you through up to 11 phases (0-10)\n'));
|
|
600
|
-
}
|
|
601
|
-
else if (aiTools.includes('antigravity')) {
|
|
602
|
-
console.log(chalk.cyan(' 1. Use Antigravity commands'));
|
|
603
|
-
console.log(chalk.cyan(' 2. Run: /flow-build'));
|
|
604
|
-
console.log(chalk.gray(' This will guide you through up to 11 phases (0-10)\n'));
|
|
605
|
-
}
|
|
606
|
-
else {
|
|
607
|
-
console.log(chalk.cyan(` 1. Open your AI tool (${toolsText})`));
|
|
608
|
-
console.log(chalk.cyan(' 2. Run: /flow-build'));
|
|
609
|
-
console.log(chalk.gray(' This will guide you through up to 11 phases (0-10)\n'));
|
|
610
|
-
}
|
|
611
|
-
console.log(chalk.white('Available slash commands:'));
|
|
612
|
-
console.log(chalk.gray(' /flow-build - Flujo completo (todas las fases en orden)'));
|
|
613
|
-
console.log(chalk.gray(' /flow-build fase N - Fase específica (ver lista de fases abajo)'));
|
|
614
|
-
console.log(chalk.gray(' /flow-docs-sync - Update documentation when code changes\n'));
|
|
615
|
-
if (selectedProjectType === 'backend') {
|
|
616
|
-
console.log(chalk.gray('\n Fases disponibles (Backend):'));
|
|
617
|
-
console.log(chalk.gray(' fase 0: Context Discovery (proyectos existentes)'));
|
|
618
|
-
console.log(chalk.gray(' fase 1: Discovery & Business'));
|
|
619
|
-
console.log(chalk.gray(' fase 2: Data Architecture'));
|
|
620
|
-
console.log(chalk.gray(' fase 3: System Architecture'));
|
|
621
|
-
console.log(chalk.gray(' fase 4: Security & Auth'));
|
|
622
|
-
console.log(chalk.gray(' fase 5: Code Standards'));
|
|
623
|
-
console.log(chalk.gray(' fase 6: Testing'));
|
|
624
|
-
console.log(chalk.gray(' fase 7: Operations + Tools'));
|
|
625
|
-
console.log(chalk.gray(' fase 8: Project Setup & Final Docs'));
|
|
626
|
-
console.log(chalk.gray(' fase 9: Implementation Roadmap (opcional)'));
|
|
627
|
-
console.log(chalk.gray(' fase 10: User Stories Generation (opcional)'));
|
|
628
|
-
}
|
|
629
|
-
else {
|
|
630
|
-
console.log(chalk.gray('\n Fases disponibles (Frontend):'));
|
|
631
|
-
console.log(chalk.gray(' fase 0: Context Discovery (proyectos existentes)'));
|
|
632
|
-
console.log(chalk.gray(' fase 1: Discovery & UX'));
|
|
633
|
-
console.log(chalk.gray(' fase 2: Components & Framework'));
|
|
634
|
-
console.log(chalk.gray(' fase 3: State Management'));
|
|
635
|
-
console.log(chalk.gray(' fase 4: Styling & Design'));
|
|
636
|
-
console.log(chalk.gray(' fase 5: Code Standards'));
|
|
637
|
-
console.log(chalk.gray(' fase 6: Testing'));
|
|
638
|
-
console.log(chalk.gray(' fase 7: Deployment'));
|
|
639
|
-
console.log(chalk.gray(' fase 8: Project Setup & Final Docs'));
|
|
640
|
-
}
|
|
641
|
-
console.log(chalk.gray(' /flow-docs-sync - Update documentation when code changes\n'));
|
|
642
|
-
}
|
|
509
|
+
console.log(chalk.gray(` Tools: ${aiTools.join(', ')}`));
|
|
510
|
+
console.log(chalk.gray(` Type: ${getProjectTypeLabel(selectedProjectType)} (${selectedProjectType})`));
|
|
511
|
+
console.log(chalk.gray(` Mode: ${flags?.dryRun ? 'DRY-RUN' : 'WRITE'}`));
|
|
512
|
+
printNextSteps({ aiTools, projectType: selectedProjectType });
|
|
643
513
|
if (flags?.dryRun) {
|
|
644
514
|
console.log(chalk.yellow('⚠️ Dry-run: no files were written. Run again without --dry-run to apply changes.\n'));
|
|
645
515
|
}
|
|
@@ -653,36 +523,28 @@ async function initializeProject(targetPath, aiTool, projectType, projectName, p
|
|
|
653
523
|
// CLI Commands
|
|
654
524
|
program
|
|
655
525
|
.name('ai-flow')
|
|
656
|
-
.description('AI-powered development workflow from idea to production.
|
|
526
|
+
.description('AI-powered development workflow from idea to production.')
|
|
657
527
|
.version(PKG_VERSION);
|
|
658
528
|
program
|
|
659
529
|
.command('init')
|
|
660
530
|
.description('Initialize AI Flow in current directory')
|
|
661
531
|
.argument('[path]', 'Target directory (defaults to current directory)', '.')
|
|
662
|
-
.option('--ai <tool>', 'AI tool to use
|
|
663
|
-
.option('--type <type>', 'Project type
|
|
664
|
-
.option('--name <name>', 'Project name
|
|
665
|
-
.option('--description <desc>', 'Project description
|
|
666
|
-
.option('--verbose', 'Enable verbose logging')
|
|
532
|
+
.option('--ai <tool>', 'AI tool to use')
|
|
533
|
+
.option('--type <type>', 'Project type')
|
|
534
|
+
.option('--name <name>', 'Project name')
|
|
535
|
+
.option('--description <desc>', 'Project description')
|
|
667
536
|
.option('--dry-run', 'Simulate without writing files')
|
|
668
537
|
.action(async (targetPath, options) => {
|
|
669
|
-
|
|
670
|
-
const flags = {
|
|
671
|
-
dryRun: options.dryRun === true,
|
|
672
|
-
verbose: options.verbose === true,
|
|
673
|
-
};
|
|
674
|
-
await initializeProject(absolutePath, options.ai, options.type, options.name, options.description, flags);
|
|
538
|
+
await initializeProject(path.resolve(targetPath), options.ai, options.type, options.name, options.description, { dryRun: !!options.dryRun });
|
|
675
539
|
});
|
|
676
540
|
program
|
|
677
541
|
.command('check')
|
|
678
542
|
.description('Check if current directory is initialized')
|
|
679
543
|
.action(async () => {
|
|
680
|
-
|
|
681
|
-
if (isInitialized) {
|
|
544
|
+
if (await checkIfInitialized(process.cwd())) {
|
|
682
545
|
console.log(chalk.green('✅ Project is initialized with AI Flow'));
|
|
683
546
|
const configPath = path.join(process.cwd(), '.ai-flow', 'core', 'config.json');
|
|
684
547
|
const config = await fs.readJSON(configPath);
|
|
685
|
-
// Detect project type (support both old and new config format)
|
|
686
548
|
const projectType = config.projectType ||
|
|
687
549
|
(config.backend && !config.frontend
|
|
688
550
|
? 'backend'
|
|
@@ -691,70 +553,12 @@ program
|
|
|
691
553
|
: config.mobile
|
|
692
554
|
? 'mobile'
|
|
693
555
|
: 'backend');
|
|
694
|
-
const projectTypeDisplay = projectType === 'backend'
|
|
695
|
-
? '🔧 Backend'
|
|
696
|
-
: projectType === 'frontend'
|
|
697
|
-
? '🎨 Frontend'
|
|
698
|
-
: projectType === 'fullstack'
|
|
699
|
-
? '🚀 Full Stack'
|
|
700
|
-
: projectType === 'mobile'
|
|
701
|
-
? '📱 Mobile'
|
|
702
|
-
: '🔧 Backend';
|
|
703
556
|
console.log(chalk.white('\nConfiguration:'));
|
|
704
557
|
console.log(chalk.gray(` Version: ${config.version}`));
|
|
705
|
-
console.log(chalk.gray(` Project Type: ${
|
|
558
|
+
console.log(chalk.gray(` Project Type: ${getProjectTypeLabel(projectType)} (${projectType})`));
|
|
706
559
|
console.log(chalk.gray(` AI Tools: ${config.aiTools.join(', ')}`));
|
|
707
560
|
console.log(chalk.gray(` Created: ${new Date(config.createdAt).toLocaleString()}`));
|
|
708
|
-
|
|
709
|
-
// Show correct prompts path based on project type
|
|
710
|
-
if (projectType === 'fullstack') {
|
|
711
|
-
const backendPromptsPath = path.join(process.cwd(), '.ai-flow', 'prompts', 'backend', 'flow-build.md');
|
|
712
|
-
const frontendPromptsPath = path.join(process.cwd(), '.ai-flow', 'prompts', 'frontend', 'flow-build.md');
|
|
713
|
-
console.log(chalk.gray(` Backend Prompts: ${backendPromptsPath}`));
|
|
714
|
-
console.log(chalk.gray(` Frontend Prompts: ${frontendPromptsPath}`));
|
|
715
|
-
}
|
|
716
|
-
else {
|
|
717
|
-
const promptsPath = path.join(process.cwd(), '.ai-flow', 'prompts', projectType, 'flow-build.md');
|
|
718
|
-
console.log(chalk.gray(` Prompts: ${promptsPath}`));
|
|
719
|
-
}
|
|
720
|
-
console.log(chalk.white('\nNext steps:'));
|
|
721
|
-
if (projectType === 'fullstack') {
|
|
722
|
-
if (config.aiTools.includes('claude')) {
|
|
723
|
-
console.log(chalk.cyan(' 1. Open Claude Code'));
|
|
724
|
-
console.log(chalk.cyan(' 2. Run: /backend-flow-build (for backend documentation)'));
|
|
725
|
-
console.log(chalk.cyan(' 3. Run: /frontend-flow-build (for frontend documentation)'));
|
|
726
|
-
}
|
|
727
|
-
else if (config.aiTools.includes('cursor')) {
|
|
728
|
-
console.log(chalk.cyan(' 1. Open Cursor'));
|
|
729
|
-
console.log(chalk.cyan(' 2. Run: /backend-flow-build (for backend documentation)'));
|
|
730
|
-
console.log(chalk.cyan(' 3. Run: /frontend-flow-build (for frontend documentation)'));
|
|
731
|
-
}
|
|
732
|
-
else {
|
|
733
|
-
const toolsText = config.aiTools.length === 1
|
|
734
|
-
? config.aiTools[0]
|
|
735
|
-
: `${config.aiTools.slice(0, -1).join(', ')} and ${config.aiTools[config.aiTools.length - 1]}`;
|
|
736
|
-
console.log(chalk.cyan(` 1. Open your AI tool (${toolsText})`));
|
|
737
|
-
console.log(chalk.cyan(' 2. Run: /backend-flow-build (for backend documentation)'));
|
|
738
|
-
console.log(chalk.cyan(' 3. Run: /frontend-flow-build (for frontend documentation)'));
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
else {
|
|
742
|
-
if (config.aiTools.includes('claude')) {
|
|
743
|
-
console.log(chalk.cyan(' 1. Open Claude Code'));
|
|
744
|
-
console.log(chalk.cyan(' 2. Run: /flow-build'));
|
|
745
|
-
}
|
|
746
|
-
else if (config.aiTools.includes('cursor')) {
|
|
747
|
-
console.log(chalk.cyan(' 1. Open Cursor'));
|
|
748
|
-
console.log(chalk.cyan(' 2. Run: /flow-build'));
|
|
749
|
-
}
|
|
750
|
-
else {
|
|
751
|
-
const toolsText = config.aiTools.length === 1
|
|
752
|
-
? config.aiTools[0]
|
|
753
|
-
: `${config.aiTools.slice(0, -1).join(', ')} and ${config.aiTools[config.aiTools.length - 1]}`;
|
|
754
|
-
console.log(chalk.cyan(` 1. Open your AI tool (${toolsText})`));
|
|
755
|
-
console.log(chalk.cyan(' 2. Run: /flow-build'));
|
|
756
|
-
}
|
|
757
|
-
}
|
|
561
|
+
printNextSteps({ aiTools: config.aiTools, projectType });
|
|
758
562
|
}
|
|
759
563
|
else {
|
|
760
564
|
console.log(chalk.yellow('⚠️ Project is not initialized'));
|