ai-flow-dev 1.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/LICENSE +21 -0
- package/README.md +408 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +791 -0
- package/dist/cli.js.map +1 -0
- package/dist/fs-utils.d.ts +2 -0
- package/dist/fs-utils.d.ts.map +1 -0
- package/dist/fs-utils.js +46 -0
- package/dist/fs-utils.js.map +1 -0
- package/package.json +71 -0
- package/prompts/backend/flow-dev-feature.md +1318 -0
- package/prompts/backend/flow-dev-fix.md +903 -0
- package/prompts/backend/flow-dev-refactor.md +715 -0
- package/prompts/backend/flow-dev-review.md +401 -0
- package/prompts/backend/flow-dev-work.md +1129 -0
- package/prompts/backend/flow-docs-gen-phase-0.md +1840 -0
- package/prompts/backend/flow-docs-gen-phase-1.md +435 -0
- package/prompts/backend/flow-docs-gen-phase-2.md +460 -0
- package/prompts/backend/flow-docs-gen-phase-3.md +684 -0
- package/prompts/backend/flow-docs-gen-phase-4.md +516 -0
- package/prompts/backend/flow-docs-gen-phase-5.md +637 -0
- package/prompts/backend/flow-docs-gen-phase-6.md +465 -0
- package/prompts/backend/flow-docs-gen-phase-7.md +1207 -0
- package/prompts/backend/flow-docs-gen.md +820 -0
- package/prompts/backend/flow-docs-sync.md +526 -0
- package/prompts/backend/flow-project-init.md +248 -0
- package/prompts/backend/flow-project-roadmap.md +1159 -0
- package/prompts/frontend/flow-docs-gen-phase-0.md +494 -0
- package/prompts/frontend/flow-docs-gen-phase-1.md +449 -0
- package/prompts/frontend/flow-docs-gen-phase-2.md +983 -0
- package/prompts/frontend/flow-docs-gen-phase-3.md +685 -0
- package/prompts/frontend/flow-docs-gen-phase-4.md +480 -0
- package/prompts/frontend/flow-docs-gen-phase-5.md +483 -0
- package/prompts/frontend/flow-docs-gen-phase-6.md +570 -0
- package/prompts/frontend/flow-docs-gen-phase-7.md +582 -0
- package/prompts/frontend/flow-docs-gen.md +413 -0
- package/prompts/frontend/flow-docs-sync.md +561 -0
- package/prompts/mobile/flow-docs-gen-phase-0.md +387 -0
- package/prompts/mobile/flow-docs-gen-phase-1.md +530 -0
- package/prompts/mobile/flow-docs-gen-phase-2.md +584 -0
- package/prompts/mobile/flow-docs-gen-phase-3.md +659 -0
- package/prompts/mobile/flow-docs-gen-phase-4.md +363 -0
- package/prompts/mobile/flow-docs-gen-phase-5.md +369 -0
- package/prompts/mobile/flow-docs-gen-phase-6.md +490 -0
- package/prompts/mobile/flow-docs-gen-phase-7.md +407 -0
- package/prompts/mobile/flow-docs-gen.md +430 -0
- package/prompts/mobile/flow-docs-sync.md +634 -0
- package/templates/backend/.clauderules.template +111 -0
- package/templates/backend/.cursorrules.template +102 -0
- package/templates/backend/.env.example.template +122 -0
- package/templates/backend/README.template.md +200 -0
- package/templates/backend/ai-instructions.template.md +354 -0
- package/templates/backend/copilot-instructions.template.md +160 -0
- package/templates/backend/docs/api.template.md +251 -0
- package/templates/backend/docs/architecture.template.md +612 -0
- package/templates/backend/docs/business-flows.template.md +109 -0
- package/templates/backend/docs/code-standards.template.md +828 -0
- package/templates/backend/docs/contributing.template.md +163 -0
- package/templates/backend/docs/data-model.template.md +416 -0
- package/templates/backend/docs/operations.template.md +591 -0
- package/templates/backend/docs/testing.template.md +762 -0
- package/templates/backend/project-brief.template.md +176 -0
- package/templates/backend/specs/configuration.template.md +133 -0
- package/templates/backend/specs/security.template.md +422 -0
- package/templates/frontend/README.template.md +121 -0
- package/templates/frontend/ai-instructions.template.md +368 -0
- package/templates/frontend/docs/api-integration.template.md +390 -0
- package/templates/frontend/docs/components.template.md +567 -0
- package/templates/frontend/docs/error-handling.template.md +385 -0
- package/templates/frontend/docs/operations.template.md +123 -0
- package/templates/frontend/docs/performance.template.md +140 -0
- package/templates/frontend/docs/pwa.template.md +135 -0
- package/templates/frontend/docs/state-management.template.md +394 -0
- package/templates/frontend/docs/styling.template.md +779 -0
- package/templates/frontend/docs/testing.template.md +736 -0
- package/templates/frontend/project-brief.template.md +55 -0
- package/templates/frontend/specs/accessibility.template.md +111 -0
- package/templates/frontend/specs/configuration.template.md +520 -0
- package/templates/frontend/specs/security.template.md +197 -0
- package/templates/fullstack/README.template.md +282 -0
- package/templates/fullstack/ai-instructions.template.md +487 -0
- package/templates/fullstack/project-brief.template.md +197 -0
- package/templates/fullstack/specs/configuration.template.md +380 -0
- package/templates/mobile/AGENT.template.md +251 -0
- package/templates/mobile/README.template.md +195 -0
- package/templates/mobile/ai-instructions.template.md +221 -0
- package/templates/mobile/docs/app-store.template.md +163 -0
- package/templates/mobile/docs/architecture.template.md +100 -0
- package/templates/mobile/docs/native-features.template.md +137 -0
- package/templates/mobile/docs/navigation.template.md +81 -0
- package/templates/mobile/docs/offline-strategy.template.md +90 -0
- package/templates/mobile/docs/permissions.template.md +70 -0
- package/templates/mobile/docs/state-management.template.md +116 -0
- package/templates/mobile/docs/testing.template.md +146 -0
- package/templates/mobile/project-brief.template.md +97 -0
- package/templates/mobile/specs/build-configuration.template.md +116 -0
- package/templates/mobile/specs/deployment.template.md +114 -0
- package/templates/shared/AGENT.template.md +252 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,791 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
const commander_1 = require("commander");
|
|
41
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
42
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
43
|
+
const ora_1 = __importDefault(require("ora"));
|
|
44
|
+
const fs = __importStar(require("fs-extra"));
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
const ejs_1 = __importDefault(require("ejs"));
|
|
47
|
+
const fs_utils_1 = require("./fs-utils");
|
|
48
|
+
const ROOT_DIR = path.resolve(__dirname, '..');
|
|
49
|
+
const program = new commander_1.Command();
|
|
50
|
+
const AI_TOOLS = [
|
|
51
|
+
{
|
|
52
|
+
name: 'GitHub Copilot',
|
|
53
|
+
value: 'copilot',
|
|
54
|
+
description: '',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: 'Claude Code',
|
|
58
|
+
value: 'claude',
|
|
59
|
+
description: '',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'Cursor',
|
|
63
|
+
value: 'cursor',
|
|
64
|
+
description: '',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'Gemini',
|
|
68
|
+
value: 'gemini',
|
|
69
|
+
description: '',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'All AI Tools',
|
|
73
|
+
value: 'all',
|
|
74
|
+
description: '',
|
|
75
|
+
},
|
|
76
|
+
];
|
|
77
|
+
const PKG_VERSION = fs.readJSONSync(path.join(__dirname, '..', 'package.json')).version;
|
|
78
|
+
const EXIT = {
|
|
79
|
+
OK: 0,
|
|
80
|
+
INVALID_ARGS: 2,
|
|
81
|
+
FS_ERROR: 3,
|
|
82
|
+
};
|
|
83
|
+
function logVerbose(message, verbose) {
|
|
84
|
+
if (verbose)
|
|
85
|
+
console.log(chalk_1.default.gray(message));
|
|
86
|
+
}
|
|
87
|
+
function isValidName(value) {
|
|
88
|
+
const v = value.trim();
|
|
89
|
+
if (v.length < 2 || v.length > 100)
|
|
90
|
+
return false;
|
|
91
|
+
return /^[A-Za-z0-9 _\-\.]+$/.test(v);
|
|
92
|
+
}
|
|
93
|
+
function isValidDescription(value) {
|
|
94
|
+
const v = value.trim();
|
|
95
|
+
if (v.length < 2 || v.length > 500)
|
|
96
|
+
return false;
|
|
97
|
+
return /^[\p{L}\p{N} \-_,.!?:()]+$/u.test(v);
|
|
98
|
+
}
|
|
99
|
+
function fsErrorMessage(e) {
|
|
100
|
+
const anyErr = e;
|
|
101
|
+
const code = anyErr && anyErr.code ? String(anyErr.code) : 'UNKNOWN';
|
|
102
|
+
const msg = anyErr && anyErr.message ? String(anyErr.message) : String(e);
|
|
103
|
+
return `${code}: ${msg}`;
|
|
104
|
+
}
|
|
105
|
+
async function selectAITool(providedTool) {
|
|
106
|
+
if (providedTool) {
|
|
107
|
+
const tool = AI_TOOLS.find((t) => t.value === providedTool);
|
|
108
|
+
if (!tool) {
|
|
109
|
+
console.error(chalk_1.default.red(`ā Invalid AI tool: ${providedTool}`));
|
|
110
|
+
console.log(chalk_1.default.yellow('Available options: claude, cursor, copilot, gemini, all'));
|
|
111
|
+
process.exit(EXIT.INVALID_ARGS);
|
|
112
|
+
}
|
|
113
|
+
return providedTool === 'all'
|
|
114
|
+
? ['claude', 'cursor', 'copilot', 'gemini']
|
|
115
|
+
: [providedTool];
|
|
116
|
+
}
|
|
117
|
+
// Display banner
|
|
118
|
+
console.log('\n');
|
|
119
|
+
console.log(chalk_1.default.cyan(' āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
120
|
+
console.log(chalk_1.default.cyan(' ā') +
|
|
121
|
+
' ' +
|
|
122
|
+
chalk_1.default.cyan('ā'));
|
|
123
|
+
console.log(chalk_1.default.cyan(' ā') +
|
|
124
|
+
chalk_1.default.bold.cyan(' āāāāāā āāā āāāāāāāāāāā āāāāāāā āāā āāā') +
|
|
125
|
+
' ' +
|
|
126
|
+
chalk_1.default.cyan('ā'));
|
|
127
|
+
console.log(chalk_1.default.cyan(' ā') +
|
|
128
|
+
chalk_1.default.bold.cyan(' āāāāāāāāāāā āāāāāāāāāāā āāāāāāāāāāāā āāā') +
|
|
129
|
+
' ' +
|
|
130
|
+
chalk_1.default.cyan('ā'));
|
|
131
|
+
console.log(chalk_1.default.cyan(' ā') +
|
|
132
|
+
chalk_1.default.bold.cyan(' āāāāāāāāāāā āāāāāā āāā āāā āāāāāā āā āāā') +
|
|
133
|
+
' ' +
|
|
134
|
+
chalk_1.default.cyan('ā'));
|
|
135
|
+
console.log(chalk_1.default.cyan(' ā') +
|
|
136
|
+
chalk_1.default.bold.cyan(' āāāāāāāāāāā āāāāāā āāā āāā āāāāāāāāāāāāā') +
|
|
137
|
+
' ' +
|
|
138
|
+
chalk_1.default.cyan('ā'));
|
|
139
|
+
console.log(chalk_1.default.cyan(' ā') +
|
|
140
|
+
chalk_1.default.bold.cyan(' āāā āāāāāā āāā āāāāāāāāāāāāāāāāāāāāāāāāāāā') +
|
|
141
|
+
' ' +
|
|
142
|
+
chalk_1.default.cyan('ā'));
|
|
143
|
+
console.log(chalk_1.default.cyan(' ā') +
|
|
144
|
+
chalk_1.default.cyan(' āāā āāāāāā āāā āāāāāāāā āāāāāāā āāāāāāāā') +
|
|
145
|
+
' ' +
|
|
146
|
+
chalk_1.default.cyan('ā'));
|
|
147
|
+
console.log(chalk_1.default.cyan(' ā') +
|
|
148
|
+
' ' +
|
|
149
|
+
chalk_1.default.cyan('ā'));
|
|
150
|
+
console.log(chalk_1.default.cyan(' ā') +
|
|
151
|
+
' ' +
|
|
152
|
+
chalk_1.default.white('⨠From Idea to Production with AI Guidance') +
|
|
153
|
+
' ' +
|
|
154
|
+
chalk_1.default.cyan('ā'));
|
|
155
|
+
console.log(chalk_1.default.cyan(' ā') +
|
|
156
|
+
' ' +
|
|
157
|
+
chalk_1.default.cyan('ā'));
|
|
158
|
+
console.log(chalk_1.default.cyan(' āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
159
|
+
console.log('\n');
|
|
160
|
+
console.log(chalk_1.default.white(' š Project Setup'));
|
|
161
|
+
console.log(chalk_1.default.gray(' āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
162
|
+
console.log(chalk_1.default.gray(` Working Directory: ${process.cwd()}`));
|
|
163
|
+
console.log(chalk_1.default.gray(` Version: ${PKG_VERSION}`));
|
|
164
|
+
console.log('\n');
|
|
165
|
+
console.log(chalk_1.default.white(' š¤ Select your AI development tool:'));
|
|
166
|
+
console.log(chalk_1.default.gray(' āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
167
|
+
const { selectedTool } = await inquirer_1.default.prompt([
|
|
168
|
+
{
|
|
169
|
+
type: 'list',
|
|
170
|
+
name: 'selectedTool',
|
|
171
|
+
message: '\u200B', // invisible char para ocultar el ?
|
|
172
|
+
choices: AI_TOOLS.map((tool) => ({
|
|
173
|
+
name: ' ' + tool.name, // 4 espacios para alinear
|
|
174
|
+
value: tool.value,
|
|
175
|
+
})),
|
|
176
|
+
pageSize: 10,
|
|
177
|
+
},
|
|
178
|
+
]);
|
|
179
|
+
return selectedTool === 'all'
|
|
180
|
+
? ['claude', 'cursor', 'copilot', 'gemini']
|
|
181
|
+
: [selectedTool];
|
|
182
|
+
}
|
|
183
|
+
async function selectProjectType(providedType) {
|
|
184
|
+
// v1.4.0: Backend, Frontend, Fullstack, and Mobile supported
|
|
185
|
+
if (providedType) {
|
|
186
|
+
const valid = ['backend', 'frontend', 'fullstack', 'mobile'];
|
|
187
|
+
if (!valid.includes(providedType)) {
|
|
188
|
+
console.error(chalk_1.default.red(`ā Invalid project type: ${providedType}`));
|
|
189
|
+
console.log(chalk_1.default.yellow('Available options: backend, frontend, fullstack, mobile'));
|
|
190
|
+
process.exit(EXIT.INVALID_ARGS);
|
|
191
|
+
}
|
|
192
|
+
return providedType;
|
|
193
|
+
}
|
|
194
|
+
// If no TTY available (non-interactive mode, e.g., in tests), default to backend
|
|
195
|
+
// This maintains backward compatibility with existing tests and scripts
|
|
196
|
+
if (!process.stdin.isTTY) {
|
|
197
|
+
return 'backend';
|
|
198
|
+
}
|
|
199
|
+
// v1.4.0: Interactive selection for backend/frontend/fullstack/mobile
|
|
200
|
+
const answer = await inquirer_1.default.prompt([
|
|
201
|
+
{
|
|
202
|
+
type: 'list',
|
|
203
|
+
name: 'projectType',
|
|
204
|
+
message: 'What type of project are you bootstrapping?',
|
|
205
|
+
choices: [
|
|
206
|
+
{ name: 'š§ Backend API/Service', value: 'backend' },
|
|
207
|
+
{ name: 'šØ Frontend Application', value: 'frontend' },
|
|
208
|
+
{ name: 'š Full Stack Application', value: 'fullstack' },
|
|
209
|
+
{ name: 'š± Mobile Application', value: 'mobile' },
|
|
210
|
+
],
|
|
211
|
+
},
|
|
212
|
+
]);
|
|
213
|
+
return answer.projectType;
|
|
214
|
+
}
|
|
215
|
+
async function checkIfInitialized(targetPath) {
|
|
216
|
+
const bootstrapPath = path.join(targetPath, '.ai-flow');
|
|
217
|
+
return await fs.pathExists(bootstrapPath);
|
|
218
|
+
}
|
|
219
|
+
async function createBootstrapStructure(targetPath, aiTools, projectType = 'backend', dryRun, verbose) {
|
|
220
|
+
const spinner = (0, ora_1.default)('Creating .ai-flow structure...').start();
|
|
221
|
+
try {
|
|
222
|
+
const bootstrapPath = path.join(targetPath, '.ai-flow');
|
|
223
|
+
// Create core directories
|
|
224
|
+
if (dryRun) {
|
|
225
|
+
spinner.succeed('Created .ai-flow structure (dry-run)');
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
await (0, fs_utils_1.assertDirWritable)(targetPath);
|
|
229
|
+
await fs.ensureDir(path.join(bootstrapPath, 'core'));
|
|
230
|
+
await fs.ensureDir(path.join(bootstrapPath, 'prompts'));
|
|
231
|
+
await fs.ensureDir(path.join(bootstrapPath, 'templates', 'docs'));
|
|
232
|
+
await fs.ensureDir(path.join(bootstrapPath, 'templates', 'specs'));
|
|
233
|
+
await fs.ensureDir(path.join(bootstrapPath, 'scripts'));
|
|
234
|
+
// Create config file with new projectType field
|
|
235
|
+
const config = {
|
|
236
|
+
version: PKG_VERSION,
|
|
237
|
+
aiTools: aiTools,
|
|
238
|
+
createdAt: new Date().toISOString(),
|
|
239
|
+
projectType: projectType,
|
|
240
|
+
// Deprecated fields for backward compatibility
|
|
241
|
+
backend: projectType === 'backend' || projectType === 'fullstack',
|
|
242
|
+
frontend: projectType === 'frontend' || projectType === 'fullstack',
|
|
243
|
+
mobile: projectType === 'mobile',
|
|
244
|
+
};
|
|
245
|
+
await fs.writeJSON(path.join(bootstrapPath, 'core', 'config.json'), config, { spaces: 2 });
|
|
246
|
+
logVerbose(`Wrote ${path.join(bootstrapPath, 'core', 'config.json')}`, verbose);
|
|
247
|
+
spinner.succeed('Created .ai-flow structure');
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
spinner.fail(fsErrorMessage(error));
|
|
251
|
+
throw error;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
async function renderTemplates(targetPath, projectData, projectType = 'backend', aiTools = [], dryRun, verbose) {
|
|
255
|
+
const spinner = (0, ora_1.default)('Generating documentation from templates...').start();
|
|
256
|
+
try {
|
|
257
|
+
const templatesTarget = path.join(targetPath, '.ai-flow', 'templates');
|
|
258
|
+
if (dryRun) {
|
|
259
|
+
spinner.succeed('Documentation generated from templates (dry-run)');
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
await (0, fs_utils_1.assertDirWritable)(templatesTarget);
|
|
263
|
+
await fs.ensureDir(templatesTarget);
|
|
264
|
+
// Find all .template.md and .template files in a directory and subfolders
|
|
265
|
+
const walk = async (dir) => {
|
|
266
|
+
let files = [];
|
|
267
|
+
for (const entry of await fs.readdir(dir)) {
|
|
268
|
+
const fullPath = path.join(dir, entry);
|
|
269
|
+
const stat = await fs.stat(fullPath);
|
|
270
|
+
if (stat.isDirectory()) {
|
|
271
|
+
files = files.concat(await walk(fullPath));
|
|
272
|
+
}
|
|
273
|
+
else if (entry.endsWith('.template.md') ||
|
|
274
|
+
entry.endsWith('.template')) {
|
|
275
|
+
files.push(fullPath);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return files;
|
|
279
|
+
};
|
|
280
|
+
// Collect template files from shared and project-type-specific directories
|
|
281
|
+
const templateSources = [];
|
|
282
|
+
// Always include shared templates (e.g., AGENT.md)
|
|
283
|
+
const sharedSource = path.join(ROOT_DIR, 'templates', 'shared');
|
|
284
|
+
templateSources.push({ source: sharedSource, base: sharedSource });
|
|
285
|
+
// Include project-type-specific templates
|
|
286
|
+
if (projectType === 'backend') {
|
|
287
|
+
const backendSource = path.join(ROOT_DIR, 'templates', 'backend');
|
|
288
|
+
templateSources.push({ source: backendSource, base: backendSource });
|
|
289
|
+
}
|
|
290
|
+
else if (projectType === 'frontend') {
|
|
291
|
+
const frontendSource = path.join(ROOT_DIR, 'templates', 'frontend');
|
|
292
|
+
templateSources.push({ source: frontendSource, base: frontendSource });
|
|
293
|
+
}
|
|
294
|
+
else if (projectType === 'fullstack') {
|
|
295
|
+
// v1.3.0: Copy both backend and frontend templates
|
|
296
|
+
// Priority: fullstack-specific templates > backend templates > frontend templates
|
|
297
|
+
const fullstackSource = path.join(ROOT_DIR, 'templates', 'fullstack');
|
|
298
|
+
const backendSource = path.join(ROOT_DIR, 'templates', 'backend');
|
|
299
|
+
const frontendSource = path.join(ROOT_DIR, 'templates', 'frontend');
|
|
300
|
+
// Check if fullstack templates directory exists
|
|
301
|
+
const fullstackExists = await fs.pathExists(fullstackSource);
|
|
302
|
+
if (fullstackExists) {
|
|
303
|
+
templateSources.push({
|
|
304
|
+
source: fullstackSource,
|
|
305
|
+
base: fullstackSource,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
// Backend templates (used as base for conflicts)
|
|
309
|
+
templateSources.push({ source: backendSource, base: backendSource });
|
|
310
|
+
// Frontend templates (will overwrite only if not in fullstack and not conflicting with backend)
|
|
311
|
+
templateSources.push({ source: frontendSource, base: frontendSource });
|
|
312
|
+
}
|
|
313
|
+
else if (projectType === 'mobile') {
|
|
314
|
+
// v1.4.0: Copy mobile templates
|
|
315
|
+
const mobileSource = path.join(ROOT_DIR, 'templates', 'mobile');
|
|
316
|
+
templateSources.push({ source: mobileSource, base: mobileSource });
|
|
317
|
+
}
|
|
318
|
+
// Walk all source directories and collect template files
|
|
319
|
+
// For fullstack, use a Map to track processed files (priority: fullstack > backend > frontend)
|
|
320
|
+
const processedFiles = new Map();
|
|
321
|
+
for (const { source, base } of templateSources) {
|
|
322
|
+
const files = await walk(source);
|
|
323
|
+
for (const file of files) {
|
|
324
|
+
const relPath = path
|
|
325
|
+
.relative(base, file)
|
|
326
|
+
.replace('.template.md', '.md')
|
|
327
|
+
.replace('.template', '');
|
|
328
|
+
// Only add if not already processed (first occurrence wins)
|
|
329
|
+
if (!processedFiles.has(relPath)) {
|
|
330
|
+
processedFiles.set(relPath, { file, base });
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
// Render each template
|
|
335
|
+
for (const [relPath, { file: templateFile }] of processedFiles) {
|
|
336
|
+
// Skip AI tool-specific config files if the tool is not selected
|
|
337
|
+
const fileName = path.basename(relPath);
|
|
338
|
+
if (fileName === '.clauderules' &&
|
|
339
|
+
!aiTools.includes('claude') &&
|
|
340
|
+
!aiTools.includes('all')) {
|
|
341
|
+
logVerbose(`Skipping ${relPath} (Claude not selected)`, verbose);
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
if (fileName === '.cursorrules' &&
|
|
345
|
+
!aiTools.includes('cursor') &&
|
|
346
|
+
!aiTools.includes('all')) {
|
|
347
|
+
logVerbose(`Skipping ${relPath} (Cursor not selected)`, verbose);
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
const destPath = path.join(templatesTarget, relPath);
|
|
351
|
+
await fs.ensureDir(path.dirname(destPath));
|
|
352
|
+
const templateContent = await fs.readFile(templateFile, 'utf8');
|
|
353
|
+
// Render with EJS, leaving {{PLACEHOLDER}} for everything except name/description
|
|
354
|
+
const rendered = ejs_1.default.render(templateContent, {
|
|
355
|
+
PROJECT_NAME: projectData.name,
|
|
356
|
+
PROJECT_DESCRIPTION: projectData.description,
|
|
357
|
+
PROJECT_TYPE: projectType,
|
|
358
|
+
GENERATION_DATE: new Date().toISOString().split('T')[0],
|
|
359
|
+
PLACEHOLDER: '{{PLACEHOLDER}}',
|
|
360
|
+
}, { delimiter: '?' });
|
|
361
|
+
await fs.writeFile(destPath, rendered, 'utf8');
|
|
362
|
+
logVerbose(`Rendered ${destPath}`, verbose);
|
|
363
|
+
}
|
|
364
|
+
spinner.succeed('Documentation generated from templates');
|
|
365
|
+
}
|
|
366
|
+
catch (error) {
|
|
367
|
+
spinner.fail(fsErrorMessage(error));
|
|
368
|
+
throw error;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
async function copyPrompts(targetPath, dryRun, verbose) {
|
|
372
|
+
const spinner = (0, ora_1.default)('Copying master prompts...').start();
|
|
373
|
+
try {
|
|
374
|
+
const promptsSource = path.join(ROOT_DIR, 'prompts');
|
|
375
|
+
const promptsTarget = path.join(targetPath, '.ai-flow', 'prompts');
|
|
376
|
+
if (dryRun) {
|
|
377
|
+
spinner.succeed('Master prompts copied (dry-run)');
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
await (0, fs_utils_1.assertDirWritable)(promptsTarget);
|
|
381
|
+
await fs.copy(promptsSource, promptsTarget);
|
|
382
|
+
logVerbose(`Copied prompts to ${promptsTarget}`, verbose);
|
|
383
|
+
spinner.succeed('Master prompts copied');
|
|
384
|
+
}
|
|
385
|
+
catch (error) {
|
|
386
|
+
spinner.fail(fsErrorMessage(error));
|
|
387
|
+
throw error;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
async function setupSlashCommands(targetPath, aiTools, projectType = 'backend', dryRun, verbose) {
|
|
391
|
+
const spinner = (0, ora_1.default)('Setting up slash commands...').start();
|
|
392
|
+
try {
|
|
393
|
+
// Determine which prompt directories to copy from
|
|
394
|
+
const promptSources = [];
|
|
395
|
+
if (projectType === 'backend') {
|
|
396
|
+
promptSources.push({ dir: 'backend' });
|
|
397
|
+
}
|
|
398
|
+
else if (projectType === 'frontend') {
|
|
399
|
+
promptSources.push({ dir: 'frontend' });
|
|
400
|
+
}
|
|
401
|
+
else if (projectType === 'fullstack') {
|
|
402
|
+
// For fullstack, copy both with prefixes
|
|
403
|
+
promptSources.push({ dir: 'backend', prefix: 'backend-' });
|
|
404
|
+
promptSources.push({ dir: 'frontend', prefix: 'frontend-' });
|
|
405
|
+
}
|
|
406
|
+
else if (projectType === 'mobile') {
|
|
407
|
+
promptSources.push({ dir: 'mobile' });
|
|
408
|
+
}
|
|
409
|
+
for (const { dir, prefix } of promptSources) {
|
|
410
|
+
const promptsSource = path.join(ROOT_DIR, 'prompts', dir);
|
|
411
|
+
const allFiles = await fs.readdir(promptsSource);
|
|
412
|
+
// Filter all markdown files (all prompts are valid slash commands)
|
|
413
|
+
const files = allFiles.filter((file) => file.endsWith('.md'));
|
|
414
|
+
for (const tool of aiTools) {
|
|
415
|
+
if (tool === 'copilot') {
|
|
416
|
+
// Copilot: prompts in .github/prompts with .prompt.md suffix
|
|
417
|
+
const promptsTarget = path.join(targetPath, '.github', 'prompts');
|
|
418
|
+
if (!dryRun) {
|
|
419
|
+
await (0, fs_utils_1.assertDirWritable)(promptsTarget);
|
|
420
|
+
await fs.ensureDir(promptsTarget);
|
|
421
|
+
}
|
|
422
|
+
for (const file of files) {
|
|
423
|
+
const srcFile = path.join(promptsSource, file);
|
|
424
|
+
const base = file.replace(/\.md$/, '');
|
|
425
|
+
const destName = prefix
|
|
426
|
+
? `${prefix}${base}.prompt.md`
|
|
427
|
+
: `${base}.prompt.md`;
|
|
428
|
+
const destFile = path.join(promptsTarget, destName);
|
|
429
|
+
if (!dryRun)
|
|
430
|
+
await fs.copyFile(srcFile, destFile);
|
|
431
|
+
logVerbose(`Installed ${destFile}`, verbose);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
else if (tool === 'claude') {
|
|
435
|
+
const commandsTarget = path.join(targetPath, '.claude', 'commands');
|
|
436
|
+
if (!dryRun) {
|
|
437
|
+
await (0, fs_utils_1.assertDirWritable)(commandsTarget);
|
|
438
|
+
await fs.ensureDir(commandsTarget);
|
|
439
|
+
}
|
|
440
|
+
for (const file of files) {
|
|
441
|
+
const srcFile = path.join(promptsSource, file);
|
|
442
|
+
const destName = prefix ? `${prefix}${file}` : file;
|
|
443
|
+
const destFile = path.join(commandsTarget, destName);
|
|
444
|
+
if (!dryRun)
|
|
445
|
+
await fs.copyFile(srcFile, destFile);
|
|
446
|
+
logVerbose(`Installed ${destFile}`, verbose);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
else if (tool === 'cursor') {
|
|
450
|
+
const commandsTarget = path.join(targetPath, '.cursor', 'commands');
|
|
451
|
+
if (!dryRun) {
|
|
452
|
+
await (0, fs_utils_1.assertDirWritable)(commandsTarget);
|
|
453
|
+
await fs.ensureDir(commandsTarget);
|
|
454
|
+
}
|
|
455
|
+
for (const file of files) {
|
|
456
|
+
const srcFile = path.join(promptsSource, file);
|
|
457
|
+
const destName = prefix ? `${prefix}${file}` : file;
|
|
458
|
+
const destFile = path.join(commandsTarget, destName);
|
|
459
|
+
if (!dryRun)
|
|
460
|
+
await fs.copyFile(srcFile, destFile);
|
|
461
|
+
logVerbose(`Installed ${destFile}`, verbose);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
else if (tool === 'gemini') {
|
|
465
|
+
const commandsTarget = path.join(targetPath, '.gemini', 'commands');
|
|
466
|
+
if (!dryRun) {
|
|
467
|
+
await (0, fs_utils_1.assertDirWritable)(commandsTarget);
|
|
468
|
+
await fs.ensureDir(commandsTarget);
|
|
469
|
+
}
|
|
470
|
+
for (const file of files) {
|
|
471
|
+
const srcFile = path.join(promptsSource, file);
|
|
472
|
+
const destName = prefix ? `${prefix}${file}` : file;
|
|
473
|
+
const destFile = path.join(commandsTarget, destName);
|
|
474
|
+
if (!dryRun)
|
|
475
|
+
await fs.copyFile(srcFile, destFile);
|
|
476
|
+
logVerbose(`Installed ${destFile}`, verbose);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
spinner.succeed(`Slash commands set up for: ${aiTools.join(', ')}`);
|
|
482
|
+
}
|
|
483
|
+
catch (error) {
|
|
484
|
+
spinner.fail(fsErrorMessage(error));
|
|
485
|
+
throw error;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
async function initializeProject(targetPath, aiTool, projectType, projectName, projectDescription, flags) {
|
|
489
|
+
try {
|
|
490
|
+
// Check if already initialized
|
|
491
|
+
const isInitialized = await checkIfInitialized(targetPath);
|
|
492
|
+
if (isInitialized) {
|
|
493
|
+
console.log(chalk_1.default.yellow('\nā ļø Project already initialized with AI Flow'));
|
|
494
|
+
const { reinitialize } = await inquirer_1.default.prompt([
|
|
495
|
+
{
|
|
496
|
+
type: 'confirm',
|
|
497
|
+
name: 'reinitialize',
|
|
498
|
+
message: 'Do you want to reinitialize?',
|
|
499
|
+
default: false,
|
|
500
|
+
},
|
|
501
|
+
]);
|
|
502
|
+
if (!reinitialize) {
|
|
503
|
+
console.log(chalk_1.default.blue('Initialization cancelled'));
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
// Select AI tools
|
|
508
|
+
const aiTools = await selectAITool(aiTool);
|
|
509
|
+
// Select project type (v1.2.0: backend or frontend)
|
|
510
|
+
const selectedProjectType = await selectProjectType(projectType);
|
|
511
|
+
// Infer project name from directory
|
|
512
|
+
const inferredName = path
|
|
513
|
+
.basename(targetPath)
|
|
514
|
+
.split('-')
|
|
515
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
516
|
+
.join(' ');
|
|
517
|
+
// Request minimal project data only if not provided
|
|
518
|
+
if (projectName && !isValidName(projectName)) {
|
|
519
|
+
console.error(chalk_1.default.red('Invalid project name'));
|
|
520
|
+
process.exit(EXIT.INVALID_ARGS);
|
|
521
|
+
}
|
|
522
|
+
if (projectDescription && !isValidDescription(projectDescription)) {
|
|
523
|
+
console.error(chalk_1.default.red('Invalid project description'));
|
|
524
|
+
process.exit(EXIT.INVALID_ARGS);
|
|
525
|
+
}
|
|
526
|
+
let finalProjectName = projectName;
|
|
527
|
+
let finalProjectDescription = projectDescription || 'TBD - Run /flow-docs-gen to define';
|
|
528
|
+
if (!finalProjectName) {
|
|
529
|
+
const answers = await inquirer_1.default.prompt([
|
|
530
|
+
{
|
|
531
|
+
type: 'input',
|
|
532
|
+
name: 'projectName',
|
|
533
|
+
message: 'Project name (you can refine it in /bootstrap):',
|
|
534
|
+
default: inferredName,
|
|
535
|
+
validate: (input) => isValidName(input) ||
|
|
536
|
+
'Enter 2-100 chars: letters, numbers, space, - _ .',
|
|
537
|
+
},
|
|
538
|
+
]);
|
|
539
|
+
finalProjectName = answers.projectName;
|
|
540
|
+
}
|
|
541
|
+
console.log(chalk_1.default.cyan('\nš¦ Initializing AI Flow...\n'));
|
|
542
|
+
// Create structure
|
|
543
|
+
await createBootstrapStructure(targetPath, aiTools, selectedProjectType, flags?.dryRun, flags?.verbose);
|
|
544
|
+
await renderTemplates(targetPath, { name: finalProjectName, description: finalProjectDescription }, selectedProjectType, aiTools, flags?.dryRun, flags?.verbose);
|
|
545
|
+
await copyPrompts(targetPath, flags?.dryRun, flags?.verbose);
|
|
546
|
+
await setupSlashCommands(targetPath, aiTools, selectedProjectType, flags?.dryRun, flags?.verbose);
|
|
547
|
+
const modeText = flags?.dryRun ? 'DRY-RUN' : 'WRITE';
|
|
548
|
+
console.log(chalk_1.default.green('\nā
AI Flow initialized successfully!'));
|
|
549
|
+
console.log(chalk_1.default.white('\nSummary:'));
|
|
550
|
+
console.log(chalk_1.default.gray(` Project: ${finalProjectName}`));
|
|
551
|
+
console.log(chalk_1.default.gray(` Version: ${PKG_VERSION}`));
|
|
552
|
+
console.log(chalk_1.default.gray(` Directory: ${targetPath}`));
|
|
553
|
+
console.log(chalk_1.default.gray(` Tools: ${aiTools.join(', ')}`));
|
|
554
|
+
console.log(chalk_1.default.gray(` Mode: ${modeText}`));
|
|
555
|
+
console.log(chalk_1.default.white('\nNext steps:\n'));
|
|
556
|
+
const toolsText = aiTools.length === 1
|
|
557
|
+
? aiTools[0]
|
|
558
|
+
: `${aiTools.slice(0, -1).join(', ')} and ${aiTools[aiTools.length - 1]}`;
|
|
559
|
+
if (selectedProjectType === 'fullstack') {
|
|
560
|
+
if (aiTools.includes('claude')) {
|
|
561
|
+
console.log(chalk_1.default.cyan(' 1. Open Claude Code'));
|
|
562
|
+
console.log(chalk_1.default.cyan(' 2. Run: /backend-flow-docs-gen (for backend documentation)'));
|
|
563
|
+
console.log(chalk_1.default.cyan(' 3. Run: /frontend-flow-docs-gen (for frontend documentation)'));
|
|
564
|
+
console.log(chalk_1.default.gray(' Each will guide you through 7 phases\n'));
|
|
565
|
+
}
|
|
566
|
+
else if (aiTools.includes('cursor')) {
|
|
567
|
+
console.log(chalk_1.default.cyan(' 1. Open Cursor'));
|
|
568
|
+
console.log(chalk_1.default.cyan(' 2. Run: /backend-flow-docs-gen (for backend documentation)'));
|
|
569
|
+
console.log(chalk_1.default.cyan(' 3. Run: /frontend-flow-docs-gen (for frontend documentation)'));
|
|
570
|
+
console.log(chalk_1.default.gray(' Each will guide you through 7 phases\n'));
|
|
571
|
+
}
|
|
572
|
+
else {
|
|
573
|
+
console.log(chalk_1.default.cyan(` 1. Open your AI tool (${toolsText})`));
|
|
574
|
+
console.log(chalk_1.default.cyan(' 2. Run: /backend-flow-docs-gen (for backend documentation)'));
|
|
575
|
+
console.log(chalk_1.default.cyan(' 3. Run: /frontend-flow-docs-gen (for frontend documentation)'));
|
|
576
|
+
console.log(chalk_1.default.gray(' Each will guide you through 7 phases\n'));
|
|
577
|
+
}
|
|
578
|
+
console.log(chalk_1.default.white('Available slash commands:'));
|
|
579
|
+
console.log(chalk_1.default.gray(' Backend commands:'));
|
|
580
|
+
console.log(chalk_1.default.gray(' /backend-flow-docs-gen - Backend 7-phase documentation generation'));
|
|
581
|
+
console.log(chalk_1.default.gray(' /backend-flow-docs-gen-phase-0-context - Backend context discovery'));
|
|
582
|
+
console.log(chalk_1.default.gray(' /backend-flow-docs-gen-phase-1-business - Backend discovery & business'));
|
|
583
|
+
console.log(chalk_1.default.gray(' /backend-flow-docs-gen-phase-2-data - Backend data architecture'));
|
|
584
|
+
console.log(chalk_1.default.gray(' /backend-flow-docs-gen-phase-3-architecture - Backend system architecture'));
|
|
585
|
+
console.log(chalk_1.default.gray(' /backend-flow-docs-gen-phase-4-security - Backend security & auth'));
|
|
586
|
+
console.log(chalk_1.default.gray(' /backend-flow-docs-gen-phase-5-standards - Backend code standards'));
|
|
587
|
+
console.log(chalk_1.default.gray(' /backend-flow-docs-gen-phase-6-testing - Backend testing'));
|
|
588
|
+
console.log(chalk_1.default.gray(' /backend-flow-docs-gen-phase-7-operations - Backend operations + tools'));
|
|
589
|
+
console.log(chalk_1.default.gray(' /backend-flow-docs-sync - Update backend documentation\n'));
|
|
590
|
+
console.log(chalk_1.default.gray(' Frontend commands:'));
|
|
591
|
+
console.log(chalk_1.default.gray(' /frontend-flow-docs-gen - Frontend 7-phase documentation generation'));
|
|
592
|
+
console.log(chalk_1.default.gray(' /frontend-flow-docs-gen-phase-0-context - Frontend context discovery'));
|
|
593
|
+
console.log(chalk_1.default.gray(' /frontend-flow-docs-gen-phase-1-discovery - Frontend discovery & UX'));
|
|
594
|
+
console.log(chalk_1.default.gray(' /frontend-flow-docs-gen-phase-2-components - Frontend components & framework'));
|
|
595
|
+
console.log(chalk_1.default.gray(' /frontend-flow-docs-gen-phase-3-state - Frontend state management'));
|
|
596
|
+
console.log(chalk_1.default.gray(' /frontend-flow-docs-gen-phase-4-styling - Frontend styling & design'));
|
|
597
|
+
console.log(chalk_1.default.gray(' /frontend-flow-docs-gen-phase-5-standards - Frontend code standards'));
|
|
598
|
+
console.log(chalk_1.default.gray(' /frontend-flow-docs-gen-phase-6-testing - Frontend testing'));
|
|
599
|
+
console.log(chalk_1.default.gray(' /frontend-flow-docs-gen-phase-7-deployment - Frontend deployment'));
|
|
600
|
+
console.log(chalk_1.default.gray(' /frontend-flow-docs-sync - Update frontend documentation\n'));
|
|
601
|
+
}
|
|
602
|
+
else if (selectedProjectType === 'mobile') {
|
|
603
|
+
if (aiTools.includes('claude')) {
|
|
604
|
+
console.log(chalk_1.default.cyan(' 1. Open Claude Code'));
|
|
605
|
+
console.log(chalk_1.default.cyan(' 2. Run: /bootstrap'));
|
|
606
|
+
console.log(chalk_1.default.gray(' This will start the 7-phase interactive setup\n'));
|
|
607
|
+
}
|
|
608
|
+
else if (aiTools.includes('cursor')) {
|
|
609
|
+
console.log(chalk_1.default.cyan(' 1. Open Cursor'));
|
|
610
|
+
console.log(chalk_1.default.cyan(' 2. Run: /bootstrap'));
|
|
611
|
+
console.log(chalk_1.default.gray(' This will start the 7-phase interactive setup\n'));
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
console.log(chalk_1.default.cyan(` 1. Open your AI tool (${toolsText})`));
|
|
615
|
+
console.log(chalk_1.default.cyan(' 2. Run: /bootstrap'));
|
|
616
|
+
console.log(chalk_1.default.gray(' This will start the 7-phase interactive setup\n'));
|
|
617
|
+
}
|
|
618
|
+
console.log(chalk_1.default.white('Available slash commands:'));
|
|
619
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen - Full 7-phase documentation generation'));
|
|
620
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen-phase-0-context - Context Discovery (existing projects)'));
|
|
621
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen-phase-1-platform - Platform & Framework Selection'));
|
|
622
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen-phase-2-navigation - Navigation & Architecture'));
|
|
623
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen-phase-3-state - State & Data Management'));
|
|
624
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen-phase-4-permissions - Permissions & Native Features'));
|
|
625
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen-phase-5-standards - Code Standards'));
|
|
626
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen-phase-6-testing - Testing Strategy'));
|
|
627
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen-phase-7-deployment - Store Deployment'));
|
|
628
|
+
console.log(chalk_1.default.gray(' /flow-docs-sync - Update documentation when code changes\n'));
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
if (aiTools.includes('claude')) {
|
|
632
|
+
console.log(chalk_1.default.cyan(' 1. Open Claude Code'));
|
|
633
|
+
console.log(chalk_1.default.cyan(' 2. Run: /bootstrap'));
|
|
634
|
+
console.log(chalk_1.default.gray(' This will start the 7-phase interactive setup\n'));
|
|
635
|
+
}
|
|
636
|
+
else if (aiTools.includes('cursor')) {
|
|
637
|
+
console.log(chalk_1.default.cyan(' 1. Open Cursor'));
|
|
638
|
+
console.log(chalk_1.default.cyan(' 2. Run: /bootstrap'));
|
|
639
|
+
console.log(chalk_1.default.gray(' This will start the 7-phase interactive setup\n'));
|
|
640
|
+
}
|
|
641
|
+
else {
|
|
642
|
+
console.log(chalk_1.default.cyan(` 1. Open your AI tool (${toolsText})`));
|
|
643
|
+
console.log(chalk_1.default.cyan(' 2. Run: /bootstrap'));
|
|
644
|
+
console.log(chalk_1.default.gray(' This will start the 7-phase interactive setup\n'));
|
|
645
|
+
}
|
|
646
|
+
console.log(chalk_1.default.white('Available slash commands:'));
|
|
647
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen - Full 7-phase documentation generation'));
|
|
648
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen-phase-0-context - Context Discovery (existing projects)'));
|
|
649
|
+
if (selectedProjectType === 'backend') {
|
|
650
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen-phase-1-business - Discovery & Business'));
|
|
651
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen-phase-2-data - Data Architecture'));
|
|
652
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen-phase-3-architecture - System Architecture'));
|
|
653
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen-phase-4-security - Security & Auth'));
|
|
654
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen-phase-5-standards - Code Standards'));
|
|
655
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen-phase-6-testing - Testing'));
|
|
656
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen-phase-7-operations - Operations + Tools'));
|
|
657
|
+
}
|
|
658
|
+
else {
|
|
659
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen-phase-1-discovery - Discovery & UX'));
|
|
660
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen-phase-2-components - Components & Framework'));
|
|
661
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen-phase-3-state - State Management'));
|
|
662
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen-phase-4-styling - Styling & Design'));
|
|
663
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen-phase-5-standards - Code Standards'));
|
|
664
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen-phase-6-testing - Testing'));
|
|
665
|
+
console.log(chalk_1.default.gray(' /flow-docs-gen-phase-7-deployment - Deployment'));
|
|
666
|
+
}
|
|
667
|
+
console.log(chalk_1.default.gray(' /flow-docs-sync - Update documentation when code changes\n'));
|
|
668
|
+
}
|
|
669
|
+
if (flags?.dryRun) {
|
|
670
|
+
console.log(chalk_1.default.yellow('ā ļø Dry-run: no files were written. Run again without --dry-run to apply changes.\n'));
|
|
671
|
+
}
|
|
672
|
+
console.log(chalk_1.default.yellow('š” Tip: You can run individual phases if you want to work step-by-step\n'));
|
|
673
|
+
}
|
|
674
|
+
catch (error) {
|
|
675
|
+
console.error(chalk_1.default.red('\nā Initialization failed:'), fsErrorMessage(error));
|
|
676
|
+
process.exit(EXIT.FS_ERROR);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
// CLI Commands
|
|
680
|
+
program
|
|
681
|
+
.name('ai-flow')
|
|
682
|
+
.description('AI-powered development workflow from idea to production. Generate specs, plan features, and build with AI assistance.')
|
|
683
|
+
.version('1.0.1');
|
|
684
|
+
program
|
|
685
|
+
.command('init')
|
|
686
|
+
.description('Initialize AI Flow in current directory')
|
|
687
|
+
.argument('[path]', 'Target directory (defaults to current directory)', '.')
|
|
688
|
+
.option('--ai <tool>', 'AI tool to use (claude, cursor, copilot, gemini, all)')
|
|
689
|
+
.option('--type <type>', 'Project type (backend, frontend, fullstack, mobile)')
|
|
690
|
+
.option('--name <name>', 'Project name (skip interactive prompt)')
|
|
691
|
+
.option('--description <desc>', 'Project description (skip interactive prompt)')
|
|
692
|
+
.option('--verbose', 'Enable verbose logging')
|
|
693
|
+
.option('--dry-run', 'Simulate without writing files')
|
|
694
|
+
.action(async (targetPath, options) => {
|
|
695
|
+
const absolutePath = path.resolve(targetPath);
|
|
696
|
+
const flags = {
|
|
697
|
+
dryRun: options.dryRun === true,
|
|
698
|
+
verbose: options.verbose === true,
|
|
699
|
+
};
|
|
700
|
+
await initializeProject(absolutePath, options.ai, options.type, options.name, options.description, flags);
|
|
701
|
+
});
|
|
702
|
+
program
|
|
703
|
+
.command('check')
|
|
704
|
+
.description('Check if current directory is initialized')
|
|
705
|
+
.action(async () => {
|
|
706
|
+
const isInitialized = await checkIfInitialized(process.cwd());
|
|
707
|
+
if (isInitialized) {
|
|
708
|
+
console.log(chalk_1.default.green('ā
Project is initialized with AI Flow'));
|
|
709
|
+
const configPath = path.join(process.cwd(), '.ai-flow', 'core', 'config.json');
|
|
710
|
+
const config = await fs.readJSON(configPath);
|
|
711
|
+
// Detect project type (support both old and new config format)
|
|
712
|
+
const projectType = config.projectType ||
|
|
713
|
+
(config.backend && !config.frontend
|
|
714
|
+
? 'backend'
|
|
715
|
+
: config.frontend && !config.backend
|
|
716
|
+
? 'frontend'
|
|
717
|
+
: config.mobile
|
|
718
|
+
? 'mobile'
|
|
719
|
+
: 'backend');
|
|
720
|
+
const projectTypeDisplay = projectType === 'backend'
|
|
721
|
+
? 'š§ Backend'
|
|
722
|
+
: projectType === 'frontend'
|
|
723
|
+
? 'šØ Frontend'
|
|
724
|
+
: projectType === 'fullstack'
|
|
725
|
+
? 'š Full Stack'
|
|
726
|
+
: projectType === 'mobile'
|
|
727
|
+
? 'š± Mobile'
|
|
728
|
+
: 'š§ Backend';
|
|
729
|
+
console.log(chalk_1.default.white('\nConfiguration:'));
|
|
730
|
+
console.log(chalk_1.default.gray(` Version: ${config.version}`));
|
|
731
|
+
console.log(chalk_1.default.gray(` Project Type: ${projectTypeDisplay}`));
|
|
732
|
+
console.log(chalk_1.default.gray(` AI Tools: ${config.aiTools.join(', ')}`));
|
|
733
|
+
console.log(chalk_1.default.gray(` Created: ${new Date(config.createdAt).toLocaleString()}`));
|
|
734
|
+
console.log(chalk_1.default.gray(` Working Dir: ${process.cwd()}`));
|
|
735
|
+
// Show correct prompts path based on project type
|
|
736
|
+
if (projectType === 'fullstack') {
|
|
737
|
+
const backendPromptsPath = path.join(process.cwd(), '.ai-flow', 'prompts', 'backend', 'bootstrap.md');
|
|
738
|
+
const frontendPromptsPath = path.join(process.cwd(), '.ai-flow', 'prompts', 'frontend', 'bootstrap.md');
|
|
739
|
+
console.log(chalk_1.default.gray(` Backend Prompts: ${backendPromptsPath}`));
|
|
740
|
+
console.log(chalk_1.default.gray(` Frontend Prompts: ${frontendPromptsPath}`));
|
|
741
|
+
}
|
|
742
|
+
else {
|
|
743
|
+
const promptsPath = path.join(process.cwd(), '.ai-flow', 'prompts', projectType, 'bootstrap.md');
|
|
744
|
+
console.log(chalk_1.default.gray(` Prompts: ${promptsPath}`));
|
|
745
|
+
}
|
|
746
|
+
console.log(chalk_1.default.white('\nNext steps:'));
|
|
747
|
+
if (projectType === 'fullstack') {
|
|
748
|
+
if (config.aiTools.includes('claude')) {
|
|
749
|
+
console.log(chalk_1.default.cyan(' 1. Open Claude Code'));
|
|
750
|
+
console.log(chalk_1.default.cyan(' 2. Run: /backend-flow-docs-gen (for backend documentation)'));
|
|
751
|
+
console.log(chalk_1.default.cyan(' 3. Run: /frontend-flow-docs-gen (for frontend documentation)'));
|
|
752
|
+
}
|
|
753
|
+
else if (config.aiTools.includes('cursor')) {
|
|
754
|
+
console.log(chalk_1.default.cyan(' 1. Open Cursor'));
|
|
755
|
+
console.log(chalk_1.default.cyan(' 2. Run: /backend-flow-docs-gen (for backend documentation)'));
|
|
756
|
+
console.log(chalk_1.default.cyan(' 3. Run: /frontend-flow-docs-gen (for frontend documentation)'));
|
|
757
|
+
}
|
|
758
|
+
else {
|
|
759
|
+
const toolsText = config.aiTools.length === 1
|
|
760
|
+
? config.aiTools[0]
|
|
761
|
+
: `${config.aiTools.slice(0, -1).join(', ')} and ${config.aiTools[config.aiTools.length - 1]}`;
|
|
762
|
+
console.log(chalk_1.default.cyan(` 1. Open your AI tool (${toolsText})`));
|
|
763
|
+
console.log(chalk_1.default.cyan(' 2. Run: /backend-flow-docs-gen (for backend documentation)'));
|
|
764
|
+
console.log(chalk_1.default.cyan(' 3. Run: /frontend-flow-docs-gen (for frontend documentation)'));
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
else {
|
|
768
|
+
if (config.aiTools.includes('claude')) {
|
|
769
|
+
console.log(chalk_1.default.cyan(' 1. Open Claude Code'));
|
|
770
|
+
console.log(chalk_1.default.cyan(' 2. Run: /bootstrap'));
|
|
771
|
+
}
|
|
772
|
+
else if (config.aiTools.includes('cursor')) {
|
|
773
|
+
console.log(chalk_1.default.cyan(' 1. Open Cursor'));
|
|
774
|
+
console.log(chalk_1.default.cyan(' 2. Run: /bootstrap'));
|
|
775
|
+
}
|
|
776
|
+
else {
|
|
777
|
+
const toolsText = config.aiTools.length === 1
|
|
778
|
+
? config.aiTools[0]
|
|
779
|
+
: `${config.aiTools.slice(0, -1).join(', ')} and ${config.aiTools[config.aiTools.length - 1]}`;
|
|
780
|
+
console.log(chalk_1.default.cyan(` 1. Open your AI tool (${toolsText})`));
|
|
781
|
+
console.log(chalk_1.default.cyan(' 2. Run: /bootstrap'));
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
else {
|
|
786
|
+
console.log(chalk_1.default.yellow('ā ļø Project is not initialized'));
|
|
787
|
+
console.log(chalk_1.default.white('Run: ai-flow init .'));
|
|
788
|
+
}
|
|
789
|
+
});
|
|
790
|
+
program.parse();
|
|
791
|
+
//# sourceMappingURL=cli.js.map
|