ai-dev-starter 0.0.0-stage → 1.0.0
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 +35 -0
- package/README.md +383 -2
- package/adapters/antigravity/AGENTS.md +59 -0
- package/adapters/antigravity/skills/architecture-review/SKILL.md +33 -0
- package/adapters/antigravity/skills/bug-fix/SKILL.md +36 -0
- package/adapters/antigravity/skills/code-review/SKILL.md +34 -0
- package/adapters/antigravity/skills/feature-development/SKILL.md +37 -0
- package/adapters/antigravity/skills/performance-audit/SKILL.md +34 -0
- package/adapters/antigravity/skills/refactor/SKILL.md +35 -0
- package/adapters/antigravity/skills/security-audit/SKILL.md +35 -0
- package/adapters/antigravity/skills/test-generation/SKILL.md +34 -0
- package/adapters/cursor/bug-fix.mdc +38 -0
- package/adapters/cursor/code-review.mdc +56 -0
- package/adapters/cursor/core-rules.mdc +49 -0
- package/adapters/cursor/feature-development.mdc +31 -0
- package/adapters/cursor/performance-arch-tests.mdc +57 -0
- package/adapters/cursor/refactor-and-security.mdc +50 -0
- package/bin/ai-dev-starter.js +2 -0
- package/core/agent-principles.md +96 -0
- package/core/git-analysis.md +150 -0
- package/core/review-methodology.md +154 -0
- package/core/safety-policy.md +70 -0
- package/core/severity-levels.md +104 -0
- package/package.json +40 -4
- package/scripts/ai-dev-starter.js +709 -0
- package/scripts/detector.js +405 -0
- package/standards/backend.md +84 -0
- package/standards/database.md +100 -0
- package/standards/frontend.md +81 -0
- package/standards/security.md +84 -0
- package/standards/testing.md +132 -0
- package/templates/architecture-decision.md +92 -0
- package/templates/project-context.md +144 -0
- package/templates/review-report.md +102 -0
- package/workflows/architecture-review.md +186 -0
- package/workflows/bug-fix.md +149 -0
- package/workflows/code-review.md +189 -0
- package/workflows/feature-development.md +154 -0
- package/workflows/performance-audit.md +169 -0
- package/workflows/refactor.md +128 -0
- package/workflows/security-audit.md +164 -0
- package/workflows/test-generation.md +168 -0
|
@@ -0,0 +1,709 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ai-dev-starter CLI
|
|
5
|
+
*
|
|
6
|
+
* Interactive graphical installation and management tool for the Universal AI Development Starter Kit.
|
|
7
|
+
* Compatible with Google Antigravity, Cursor, and modern AI coding IDEs.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* npx ai-dev-starter [init] [--ide antigravity|cursor|both] [--skills all|review|dev|<comma-separated>] [--lang fa|en] [--merge] [-y] [--target <path>]
|
|
11
|
+
* npx ai-dev-starter update [--target <path>]
|
|
12
|
+
* npx ai-dev-starter doctor [--target <path>]
|
|
13
|
+
* npx ai-dev-starter detect [--target <path>]
|
|
14
|
+
* npx ai-dev-starter uninstall [--target <path>]
|
|
15
|
+
*
|
|
16
|
+
* Zero external runtime dependencies. Runs instantly with standard Node.js.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
const readline = require('readline');
|
|
22
|
+
const { execSync } = require('child_process');
|
|
23
|
+
const { detectProjectContext, renderProjectContextMarkdown } = require('./detector');
|
|
24
|
+
|
|
25
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
const STARTER_KIT_DIR = path.resolve(__dirname, '..');
|
|
28
|
+
const MANIFEST_FILE = '.ai-dev-starter-manifest.json';
|
|
29
|
+
const MANIFEST_VERSION = '1.0.0';
|
|
30
|
+
|
|
31
|
+
const ALL_SKILLS = [
|
|
32
|
+
'code-review',
|
|
33
|
+
'bug-fix',
|
|
34
|
+
'feature-development',
|
|
35
|
+
'refactor',
|
|
36
|
+
'security-audit',
|
|
37
|
+
'performance-audit',
|
|
38
|
+
'architecture-review',
|
|
39
|
+
'test-generation',
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
// ─── Graphical UI & Styling ───────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
const C = {
|
|
45
|
+
reset: '\x1b[0m',
|
|
46
|
+
bold: '\x1b[1m',
|
|
47
|
+
dim: '\x1b[2m',
|
|
48
|
+
italic: '\x1b[3m',
|
|
49
|
+
underline: '\x1b[4m',
|
|
50
|
+
|
|
51
|
+
black: '\x1b[30m',
|
|
52
|
+
red: '\x1b[31m',
|
|
53
|
+
green: '\x1b[32m',
|
|
54
|
+
yellow: '\x1b[33m',
|
|
55
|
+
blue: '\x1b[34m',
|
|
56
|
+
magenta: '\x1b[35m',
|
|
57
|
+
cyan: '\x1b[36m',
|
|
58
|
+
white: '\x1b[37m',
|
|
59
|
+
gray: '\x1b[90m',
|
|
60
|
+
|
|
61
|
+
bgBlue: '\x1b[44m',
|
|
62
|
+
bgCyan: '\x1b[46m',
|
|
63
|
+
bgMagenta: '\x1b[45m',
|
|
64
|
+
bgGray: '\x1b[100m',
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
function color(name, text) {
|
|
68
|
+
return (C[name] || '') + text + C.reset;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function log(msg = '') { console.log(msg); }
|
|
72
|
+
function ok(msg) { console.log(` ${color('green', '✔')} ${msg}`); }
|
|
73
|
+
function warn(msg) { console.log(` ${color('yellow', '▲')} ${color('yellow', msg)}`); }
|
|
74
|
+
function err(msg) { console.log(` ${color('red', '✖')} ${color('red', msg)}`); }
|
|
75
|
+
function info(msg) { console.log(` ${color('cyan', '◆')} ${msg}`); }
|
|
76
|
+
function sub(msg) { console.log(` ${color('gray', '↳')} ${color('dim', msg)}`); }
|
|
77
|
+
|
|
78
|
+
function badge(text, bgCol = 'bgCyan', fgCol = 'black') {
|
|
79
|
+
return `${C[bgCol]}${C[fgCol]}${C.bold} ${text} ${C.reset}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function printBanner() {
|
|
83
|
+
log('');
|
|
84
|
+
log(color('cyan', ' ╭──────────────────────────────────────────────────────────────╮'));
|
|
85
|
+
log(color('cyan', ' │') + ` ${color('bold', color('white', '⚡ AI DEV STARTER'))} ${color('dim', 'v' + MANIFEST_VERSION)} ` + color('cyan', '│'));
|
|
86
|
+
log(color('cyan', ' │') + ` ${color('gray', 'Universal AI Development Kit for Antigravity & Cursor')} ` + color('cyan', '│'));
|
|
87
|
+
log(color('cyan', ' ╰──────────────────────────────────────────────────────────────╯'));
|
|
88
|
+
log('');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function printCard(title, items) {
|
|
92
|
+
const line = '─'.repeat(58);
|
|
93
|
+
log(color('gray', ` ┌─ ${color('bold', color('white', title))} ${line.slice(title.length + 4)}`));
|
|
94
|
+
for (const [k, v] of Object.entries(items)) {
|
|
95
|
+
const pad = ' '.repeat(Math.max(1, 14 - k.length));
|
|
96
|
+
log(color('gray', ' │ ') + color('cyan', k) + pad + color('white', v));
|
|
97
|
+
}
|
|
98
|
+
log(color('gray', ` └${line}`));
|
|
99
|
+
log('');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function printStepHeader(current, total, title) {
|
|
103
|
+
log('');
|
|
104
|
+
log(` ${color('magenta', '◆')} ${color('bold', `Step ${current} of ${total}:`)} ${color('bold', color('white', title))}`);
|
|
105
|
+
log(color('gray', ' ──────────────────────────────────────────────────────────'));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function ask(question, defaultValue = '') {
|
|
109
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
110
|
+
return new Promise(resolve => {
|
|
111
|
+
const promptText = ` ${color('cyan', '?')} ${color('bold', question)}${defaultValue ? color('dim', ` (${defaultValue})`) : ''}: `;
|
|
112
|
+
rl.question(promptText, answer => {
|
|
113
|
+
rl.close();
|
|
114
|
+
const val = answer.trim();
|
|
115
|
+
resolve(val ? val.toLowerCase() : defaultValue.toLowerCase());
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ─── File Installation Manifests ──────────────────────────────────────────────
|
|
121
|
+
|
|
122
|
+
const ANTIGRAVITY_FILES = [
|
|
123
|
+
// Core files (always required)
|
|
124
|
+
{ source: 'core/agent-principles.md', target: '.agents/core/agent-principles.md', category: 'Core' },
|
|
125
|
+
{ source: 'core/safety-policy.md', target: '.agents/core/safety-policy.md', category: 'Core' },
|
|
126
|
+
{ source: 'core/git-analysis.md', target: '.agents/core/git-analysis.md', category: 'Core' },
|
|
127
|
+
{ source: 'core/review-methodology.md', target: '.agents/core/review-methodology.md', category: 'Core' },
|
|
128
|
+
{ source: 'core/severity-levels.md', target: '.agents/core/severity-levels.md', category: 'Core' },
|
|
129
|
+
|
|
130
|
+
// Workflow files
|
|
131
|
+
{ source: 'workflows/code-review.md', target: '.agents/workflows/code-review.md', skill: 'code-review', category: 'Workflow' },
|
|
132
|
+
{ source: 'workflows/bug-fix.md', target: '.agents/workflows/bug-fix.md', skill: 'bug-fix', category: 'Workflow' },
|
|
133
|
+
{ source: 'workflows/feature-development.md', target: '.agents/workflows/feature-development.md', skill: 'feature-development', category: 'Workflow' },
|
|
134
|
+
{ source: 'workflows/refactor.md', target: '.agents/workflows/refactor.md', skill: 'refactor', category: 'Workflow' },
|
|
135
|
+
{ source: 'workflows/security-audit.md', target: '.agents/workflows/security-audit.md', skill: 'security-audit', category: 'Workflow' },
|
|
136
|
+
{ source: 'workflows/performance-audit.md', target: '.agents/workflows/performance-audit.md', skill: 'performance-audit', category: 'Workflow' },
|
|
137
|
+
{ source: 'workflows/architecture-review.md', target: '.agents/workflows/architecture-review.md', skill: 'architecture-review', category: 'Workflow' },
|
|
138
|
+
{ source: 'workflows/test-generation.md', target: '.agents/workflows/test-generation.md', skill: 'test-generation', category: 'Workflow' },
|
|
139
|
+
|
|
140
|
+
// Standards files
|
|
141
|
+
{ source: 'standards/backend.md', target: '.agents/standards/backend.md', category: 'Standard' },
|
|
142
|
+
{ source: 'standards/frontend.md', target: '.agents/standards/frontend.md', category: 'Standard' },
|
|
143
|
+
{ source: 'standards/database.md', target: '.agents/standards/database.md', category: 'Standard' },
|
|
144
|
+
{ source: 'standards/security.md', target: '.agents/standards/security.md', category: 'Standard' },
|
|
145
|
+
{ source: 'standards/testing.md', target: '.agents/standards/testing.md', category: 'Standard' },
|
|
146
|
+
|
|
147
|
+
// Templates
|
|
148
|
+
{ source: 'templates/review-report.md', target: '.agents/templates/review-report.md', category: 'Template' },
|
|
149
|
+
{ source: 'templates/architecture-decision.md', target: '.agents/templates/architecture-decision.md', category: 'Template' },
|
|
150
|
+
|
|
151
|
+
// Antigravity adapter
|
|
152
|
+
{ source: 'adapters/antigravity/AGENTS.md', target: 'AGENTS.md', userModifiable: true, category: 'Adapter' },
|
|
153
|
+
|
|
154
|
+
// Skills
|
|
155
|
+
{ source: 'adapters/antigravity/skills/code-review/SKILL.md', target: '.agents/skills/code-review/SKILL.md', skill: 'code-review', category: 'Skill' },
|
|
156
|
+
{ source: 'adapters/antigravity/skills/bug-fix/SKILL.md', target: '.agents/skills/bug-fix/SKILL.md', skill: 'bug-fix', category: 'Skill' },
|
|
157
|
+
{ source: 'adapters/antigravity/skills/feature-development/SKILL.md', target: '.agents/skills/feature-development/SKILL.md', skill: 'feature-development', category: 'Skill' },
|
|
158
|
+
{ source: 'adapters/antigravity/skills/refactor/SKILL.md', target: '.agents/skills/refactor/SKILL.md', skill: 'refactor', category: 'Skill' },
|
|
159
|
+
{ source: 'adapters/antigravity/skills/security-audit/SKILL.md', target: '.agents/skills/security-audit/SKILL.md', skill: 'security-audit', category: 'Skill' },
|
|
160
|
+
{ source: 'adapters/antigravity/skills/performance-audit/SKILL.md', target: '.agents/skills/performance-audit/SKILL.md', skill: 'performance-audit', category: 'Skill' },
|
|
161
|
+
{ source: 'adapters/antigravity/skills/architecture-review/SKILL.md', target: '.agents/skills/architecture-review/SKILL.md', skill: 'architecture-review', category: 'Skill' },
|
|
162
|
+
{ source: 'adapters/antigravity/skills/test-generation/SKILL.md', target: '.agents/skills/test-generation/SKILL.md', skill: 'test-generation', category: 'Skill' },
|
|
163
|
+
];
|
|
164
|
+
|
|
165
|
+
const CURSOR_FILES = [
|
|
166
|
+
// Core files
|
|
167
|
+
{ source: 'core/agent-principles.md', target: '.agents/core/agent-principles.md', category: 'Core' },
|
|
168
|
+
{ source: 'core/safety-policy.md', target: '.agents/core/safety-policy.md', category: 'Core' },
|
|
169
|
+
{ source: 'core/git-analysis.md', target: '.agents/core/git-analysis.md', category: 'Core' },
|
|
170
|
+
{ source: 'core/review-methodology.md', target: '.agents/core/review-methodology.md', category: 'Core' },
|
|
171
|
+
{ source: 'core/severity-levels.md', target: '.agents/core/severity-levels.md', category: 'Core' },
|
|
172
|
+
|
|
173
|
+
// Workflows
|
|
174
|
+
{ source: 'workflows/code-review.md', target: '.agents/workflows/code-review.md', skill: 'code-review', category: 'Workflow' },
|
|
175
|
+
{ source: 'workflows/bug-fix.md', target: '.agents/workflows/bug-fix.md', skill: 'bug-fix', category: 'Workflow' },
|
|
176
|
+
{ source: 'workflows/feature-development.md', target: '.agents/workflows/feature-development.md', skill: 'feature-development', category: 'Workflow' },
|
|
177
|
+
{ source: 'workflows/refactor.md', target: '.agents/workflows/refactor.md', skill: 'refactor', category: 'Workflow' },
|
|
178
|
+
{ source: 'workflows/security-audit.md', target: '.agents/workflows/security-audit.md', skill: 'security-audit', category: 'Workflow' },
|
|
179
|
+
{ source: 'workflows/performance-audit.md', target: '.agents/workflows/performance-audit.md', skill: 'performance-audit', category: 'Workflow' },
|
|
180
|
+
{ source: 'workflows/architecture-review.md', target: '.agents/workflows/architecture-review.md', skill: 'architecture-review', category: 'Workflow' },
|
|
181
|
+
{ source: 'workflows/test-generation.md', target: '.agents/workflows/test-generation.md', skill: 'test-generation', category: 'Workflow' },
|
|
182
|
+
|
|
183
|
+
// Standards
|
|
184
|
+
{ source: 'standards/backend.md', target: '.agents/standards/backend.md', category: 'Standard' },
|
|
185
|
+
{ source: 'standards/frontend.md', target: '.agents/standards/frontend.md', category: 'Standard' },
|
|
186
|
+
{ source: 'standards/database.md', target: '.agents/standards/database.md', category: 'Standard' },
|
|
187
|
+
{ source: 'standards/security.md', target: '.agents/standards/security.md', category: 'Standard' },
|
|
188
|
+
{ source: 'standards/testing.md', target: '.agents/standards/testing.md', category: 'Standard' },
|
|
189
|
+
|
|
190
|
+
// Templates
|
|
191
|
+
{ source: 'templates/review-report.md', target: '.agents/templates/review-report.md', category: 'Template' },
|
|
192
|
+
{ source: 'templates/architecture-decision.md', target: '.agents/templates/architecture-decision.md', category: 'Template' },
|
|
193
|
+
|
|
194
|
+
// Cursor rules
|
|
195
|
+
{ source: 'adapters/cursor/core-rules.mdc', target: '.cursor/rules/ai-core-rules.mdc', category: 'Rule' },
|
|
196
|
+
{ source: 'adapters/cursor/code-review.mdc', target: '.cursor/rules/code-review.mdc', skill: 'code-review', category: 'Rule' },
|
|
197
|
+
{ source: 'adapters/cursor/bug-fix.mdc', target: '.cursor/rules/bug-fix.mdc', skill: 'bug-fix', category: 'Rule' },
|
|
198
|
+
{ source: 'adapters/cursor/feature-development.mdc', target: '.cursor/rules/feature-development.mdc', skill: 'feature-development', category: 'Rule' },
|
|
199
|
+
{ source: 'adapters/cursor/refactor-and-security.mdc', target: '.cursor/rules/refactor-and-security.mdc', skills: ['refactor', 'security-audit'], category: 'Rule' },
|
|
200
|
+
{ source: 'adapters/cursor/performance-arch-tests.mdc', target: '.cursor/rules/performance-arch-tests.mdc', skills: ['performance-audit', 'architecture-review', 'test-generation'], category: 'Rule' },
|
|
201
|
+
];
|
|
202
|
+
|
|
203
|
+
// ─── Utilities ────────────────────────────────────────────────────────────────
|
|
204
|
+
|
|
205
|
+
function readManifest(projectRoot) {
|
|
206
|
+
const manifestPath = path.join(projectRoot, MANIFEST_FILE);
|
|
207
|
+
if (!fs.existsSync(manifestPath)) return null;
|
|
208
|
+
try {
|
|
209
|
+
return JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
210
|
+
} catch {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function writeManifest(projectRoot, manifest) {
|
|
216
|
+
const manifestPath = path.join(projectRoot, MANIFEST_FILE);
|
|
217
|
+
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function isGitRepo(dir) {
|
|
221
|
+
try {
|
|
222
|
+
execSync('git rev-parse --git-dir', { cwd: dir, stdio: 'pipe' });
|
|
223
|
+
return true;
|
|
224
|
+
} catch { return false; }
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function fileWasUserModified(filePath, manifest) {
|
|
228
|
+
if (!manifest || !manifest.files) return false;
|
|
229
|
+
const record = manifest.files.find(f => f.target === filePath);
|
|
230
|
+
if (!record) return false;
|
|
231
|
+
if (!fs.existsSync(filePath)) return false;
|
|
232
|
+
const currentContent = fs.readFileSync(filePath, 'utf8');
|
|
233
|
+
return currentContent !== record.installedContent;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function filterFilesBySkills(files, selectedSkills) {
|
|
237
|
+
return files.filter(f => {
|
|
238
|
+
if (!f.skill && !f.skills) return true;
|
|
239
|
+
if (f.skill && selectedSkills.includes(f.skill)) return true;
|
|
240
|
+
if (f.skills && f.skills.some(s => selectedSkills.includes(s))) return true;
|
|
241
|
+
return false;
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// ─── Interactive Graphical Questionnaire ──────────────────────────────────────
|
|
246
|
+
|
|
247
|
+
async function promptInstallationChoices(options) {
|
|
248
|
+
const isInteractive = Boolean(process.stdin.isTTY && !options.yes);
|
|
249
|
+
|
|
250
|
+
// 1. Choose IDE
|
|
251
|
+
let ide = options.ide;
|
|
252
|
+
if (!ide && isInteractive) {
|
|
253
|
+
printStepHeader(1, 3, 'Select Target AI IDE / Environment');
|
|
254
|
+
log(` ${color('cyan', '1)')} ${color('bold', 'Google Antigravity')} ${color('dim', '(native Agent Skills + AGENTS.md)')} ${color('green', '★ Recommended')}`);
|
|
255
|
+
log(` ${color('cyan', '2)')} ${color('bold', 'Cursor')} ${color('dim', '(.cursor/rules/*.mdc)')}`);
|
|
256
|
+
log(` ${color('cyan', '3)')} ${color('bold', 'Both')} ${color('dim', '(Antigravity & Cursor dual support)')}`);
|
|
257
|
+
log('');
|
|
258
|
+
const answer = await ask('Select IDE [1-3]', '1');
|
|
259
|
+
if (answer === '2' || answer === 'cursor') ide = 'cursor';
|
|
260
|
+
else if (answer === '3' || answer === 'both') ide = 'both';
|
|
261
|
+
else ide = 'antigravity';
|
|
262
|
+
} else if (!ide) {
|
|
263
|
+
ide = 'both';
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// 2. Choose Skills
|
|
267
|
+
let selectedSkills = ALL_SKILLS;
|
|
268
|
+
if (options.skills) {
|
|
269
|
+
if (options.skills === 'review') {
|
|
270
|
+
selectedSkills = ['code-review', 'security-audit', 'performance-audit'];
|
|
271
|
+
} else if (options.skills === 'dev') {
|
|
272
|
+
selectedSkills = ['bug-fix', 'feature-development', 'refactor', 'test-generation'];
|
|
273
|
+
} else if (options.skills !== 'all') {
|
|
274
|
+
selectedSkills = options.skills.split(',').map(s => s.trim()).filter(s => ALL_SKILLS.includes(s));
|
|
275
|
+
}
|
|
276
|
+
} else if (isInteractive) {
|
|
277
|
+
printStepHeader(2, 3, 'Select Capabilities & Skills to Install');
|
|
278
|
+
log(` ${color('cyan', '1)')} ${color('bold', 'All 8 Skills')} ${color('dim', '(Review, Bug Fix, Feature, Refactor, Security, Perf, Arch, Tests)')} ${color('green', '★ Default')}`);
|
|
279
|
+
log(` ${color('cyan', '2)')} ${color('bold', 'Code Review & Quality only')} ${color('dim', '(code-review, security-audit, performance-audit)')}`);
|
|
280
|
+
log(` ${color('cyan', '3)')} ${color('bold', 'Development & Testing only')} ${color('dim', '(bug-fix, feature-development, refactor, test-generation)')}`);
|
|
281
|
+
log(` ${color('cyan', '4)')} ${color('bold', 'Custom Selection')} ${color('dim', '(choose exact skills)')}`);
|
|
282
|
+
log('');
|
|
283
|
+
const answer = await ask('Select skills [1-4]', '1');
|
|
284
|
+
if (answer === '2') {
|
|
285
|
+
selectedSkills = ['code-review', 'security-audit', 'performance-audit'];
|
|
286
|
+
} else if (answer === '3') {
|
|
287
|
+
selectedSkills = ['bug-fix', 'feature-development', 'refactor', 'test-generation'];
|
|
288
|
+
} else if (answer === '4') {
|
|
289
|
+
log('\n Available skills:');
|
|
290
|
+
ALL_SKILLS.forEach((s, idx) => log(` ${color('cyan', String(idx + 1) + ')')} ${s}`));
|
|
291
|
+
log('');
|
|
292
|
+
const chosen = await ask('Enter comma-separated numbers (e.g. 1,2,5)', '1,2,3,4,5,6,7,8');
|
|
293
|
+
const indices = chosen.split(',').map(n => parseInt(n.trim(), 10) - 1).filter(i => i >= 0 && i < ALL_SKILLS.length);
|
|
294
|
+
selectedSkills = indices.map(i => ALL_SKILLS[i]);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// 3. Choose Report Language
|
|
299
|
+
let lang = options.lang;
|
|
300
|
+
if (!lang && isInteractive) {
|
|
301
|
+
printStepHeader(3, 3, 'Select Review Report Output Language');
|
|
302
|
+
log(` ${color('cyan', '1)')} ${color('bold', 'Persian / فارسی (fa)')} ${color('green', '★ پیشفرض')} ${color('dim', '(شواهد انگلیسی، توضیحات فارسی)')}`);
|
|
303
|
+
log(` ${color('cyan', '2)')} ${color('bold', 'English (en)')} ${color('dim', '(full English report)')}`);
|
|
304
|
+
log('');
|
|
305
|
+
const answer = await ask('Select language [1-2]', '1');
|
|
306
|
+
lang = (answer === '2' || answer === 'en' || answer === 'english') ? 'en' : 'fa';
|
|
307
|
+
} else if (!lang) {
|
|
308
|
+
lang = 'fa';
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return { ide, selectedSkills, lang };
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// ─── File Installation Core ───────────────────────────────────────────────────
|
|
315
|
+
|
|
316
|
+
async function installFile(projectRoot, fileSpec, manifest, options = {}) {
|
|
317
|
+
const force = options.force || options.overwrite;
|
|
318
|
+
const autoMerge = options.merge;
|
|
319
|
+
const sourcePath = path.join(STARTER_KIT_DIR, fileSpec.source);
|
|
320
|
+
const targetPath = path.join(projectRoot, fileSpec.target);
|
|
321
|
+
const targetDir = path.dirname(targetPath);
|
|
322
|
+
|
|
323
|
+
if (!fs.existsSync(sourcePath)) {
|
|
324
|
+
err(`Source file not found: ${fileSpec.source}`);
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const newContent = fs.readFileSync(sourcePath, 'utf8');
|
|
329
|
+
|
|
330
|
+
// Check existing file
|
|
331
|
+
if (fs.existsSync(targetPath) && !force) {
|
|
332
|
+
const existingContent = fs.readFileSync(targetPath, 'utf8');
|
|
333
|
+
|
|
334
|
+
if (existingContent === newContent) {
|
|
335
|
+
log(` ${color('gray', '•')} ${color('dim', fileSpec.target)} ${color('dim', '(up-to-date)')}`);
|
|
336
|
+
return true;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Special handling for user-modifiable files like AGENTS.md
|
|
340
|
+
if (fileSpec.userModifiable && fileSpec.target === 'AGENTS.md') {
|
|
341
|
+
if (existingContent.includes('AI Development Rules')) {
|
|
342
|
+
ok(`${color('cyan', '[Merged]')} ${fileSpec.target} (rules already merged)`);
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// If --merge flag given, or -y/non-interactive, merge automatically
|
|
347
|
+
if (autoMerge || options.yes || !process.stdin.isTTY) {
|
|
348
|
+
fs.appendFileSync(targetPath, '\n\n---\n\n' + newContent);
|
|
349
|
+
ok(`${color('cyan', '[Merged]')} ${fileSpec.target} ${color('dim', '(automatically appended kit rules)')}`);
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Interactive prompt defaulting to merge
|
|
354
|
+
log('');
|
|
355
|
+
warn(`Existing ${fileSpec.target} detected with your local project notes.`);
|
|
356
|
+
const choice = await ask(`Handle ${fileSpec.target}? [m = merge (keep notes + append rules), y = overwrite, n = skip]`, 'm');
|
|
357
|
+
if (choice === 'm' || choice === 'merge') {
|
|
358
|
+
fs.appendFileSync(targetPath, '\n\n---\n\n' + newContent);
|
|
359
|
+
ok(`${color('cyan', '[Merged]')} ${fileSpec.target} (preserved your notes & appended rules)`);
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
if (choice !== 'y' && choice !== 'yes') {
|
|
363
|
+
warn(`Skipped: ${fileSpec.target}`);
|
|
364
|
+
return true;
|
|
365
|
+
}
|
|
366
|
+
} else if (fileWasUserModified(targetPath, manifest)) {
|
|
367
|
+
warn(`File has local modifications: ${fileSpec.target}`);
|
|
368
|
+
if (process.stdin.isTTY) {
|
|
369
|
+
const choice = await ask(`Overwrite ${fileSpec.target}? [y = yes, n = skip]`, 'n');
|
|
370
|
+
if (choice !== 'y' && choice !== 'yes') {
|
|
371
|
+
warn(`Skipped: ${fileSpec.target}`);
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (!fs.existsSync(targetDir)) {
|
|
379
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
fs.writeFileSync(targetPath, newContent);
|
|
383
|
+
const cat = fileSpec.category ? `[${fileSpec.category}]` : '';
|
|
384
|
+
ok(`${color('dim', cat.padEnd(10))} ${fileSpec.target}`);
|
|
385
|
+
|
|
386
|
+
if (manifest && manifest.files) {
|
|
387
|
+
const existing = manifest.files.find(f => f.target === fileSpec.target);
|
|
388
|
+
if (existing) {
|
|
389
|
+
existing.installedContent = newContent;
|
|
390
|
+
existing.installedAt = new Date().toISOString();
|
|
391
|
+
} else {
|
|
392
|
+
manifest.files.push({
|
|
393
|
+
target: fileSpec.target,
|
|
394
|
+
source: fileSpec.source,
|
|
395
|
+
installedAt: new Date().toISOString(),
|
|
396
|
+
installedContent: newContent,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return true;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function installProjectContext(projectRoot, lang) {
|
|
405
|
+
const contextPath = path.join(projectRoot, '.agents', 'project-context.md');
|
|
406
|
+
if (fs.existsSync(contextPath)) {
|
|
407
|
+
log(` ${color('gray', '•')} ${color('dim', '.agents/project-context.md')} ${color('dim', '(preserved user config)')}`);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const dir = path.join(projectRoot, '.agents');
|
|
412
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
413
|
+
|
|
414
|
+
const ctx = detectProjectContext(projectRoot);
|
|
415
|
+
if (lang) ctx.reportLanguage = lang;
|
|
416
|
+
|
|
417
|
+
const content = renderProjectContextMarkdown(ctx);
|
|
418
|
+
fs.writeFileSync(contextPath, content, 'utf8');
|
|
419
|
+
ok(`${color('dim', '[Context]'.padEnd(10))} .agents/project-context.md`);
|
|
420
|
+
sub(`Detected: ${ctx.backendFramework || 'none'} • ${ctx.frontendFramework || 'none'} • lang: ${ctx.reportLanguage}`);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// ─── Commands ─────────────────────────────────────────────────────────────────
|
|
424
|
+
|
|
425
|
+
async function cmdInit(projectRoot, options) {
|
|
426
|
+
printBanner();
|
|
427
|
+
|
|
428
|
+
if (!fs.existsSync(projectRoot)) {
|
|
429
|
+
err(`Target directory does not exist: ${projectRoot}`);
|
|
430
|
+
process.exit(1);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Pre-scan project
|
|
434
|
+
const ctx = detectProjectContext(projectRoot);
|
|
435
|
+
printCard('Workspace Context', {
|
|
436
|
+
'Project': ctx.projectName,
|
|
437
|
+
'Directory': projectRoot,
|
|
438
|
+
'Backend': `${ctx.backendLanguage || 'none'} (${ctx.backendFramework || 'none'})`,
|
|
439
|
+
'Frontend': `${ctx.frontendLanguage || 'none'} (${ctx.frontendFramework || 'none'})`,
|
|
440
|
+
'Styling': ctx.styling || 'none',
|
|
441
|
+
'Test Runner': ctx.testFramework || 'none',
|
|
442
|
+
'Git Repo': isGitRepo(projectRoot) ? 'Yes (ready for code reviews)' : 'No (init git for diff reviews)',
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
// Questionnaire / options resolution
|
|
446
|
+
const { ide, selectedSkills, lang } = await promptInstallationChoices(options);
|
|
447
|
+
|
|
448
|
+
log('');
|
|
449
|
+
log(color('gray', ' ──────────────────────────────────────────────────────────'));
|
|
450
|
+
info(`Installing ${color('bold', selectedSkills.length)} skills for ${color('bold', color('cyan', ide.toUpperCase()))}...`);
|
|
451
|
+
log('');
|
|
452
|
+
|
|
453
|
+
const manifest = readManifest(projectRoot) || {
|
|
454
|
+
version: MANIFEST_VERSION,
|
|
455
|
+
ide,
|
|
456
|
+
installedSkills: selectedSkills,
|
|
457
|
+
installedAt: new Date().toISOString(),
|
|
458
|
+
projectRoot,
|
|
459
|
+
files: [],
|
|
460
|
+
};
|
|
461
|
+
manifest.ide = ide;
|
|
462
|
+
manifest.installedSkills = selectedSkills;
|
|
463
|
+
|
|
464
|
+
let filesToInstall = [];
|
|
465
|
+
if (ide === 'antigravity') {
|
|
466
|
+
filesToInstall = filterFilesBySkills(ANTIGRAVITY_FILES, selectedSkills);
|
|
467
|
+
} else if (ide === 'cursor') {
|
|
468
|
+
filesToInstall = filterFilesBySkills(CURSOR_FILES, selectedSkills);
|
|
469
|
+
} else {
|
|
470
|
+
const combined = [...new Map([...ANTIGRAVITY_FILES, ...CURSOR_FILES].map(f => [f.target, f])).values()];
|
|
471
|
+
filesToInstall = filterFilesBySkills(combined, selectedSkills);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
for (const fileSpec of filesToInstall) {
|
|
475
|
+
await installFile(projectRoot, fileSpec, manifest, options);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
installProjectContext(projectRoot, lang);
|
|
479
|
+
writeManifest(projectRoot, manifest);
|
|
480
|
+
|
|
481
|
+
log('');
|
|
482
|
+
log(color('green', ' ╭──────────────────────────────────────────────────────────────╮'));
|
|
483
|
+
log(color('green', ' │') + ` ${color('bold', color('white', '🎉 Installation Completed Successfully!'))} ` + color('green', '│'));
|
|
484
|
+
log(color('green', ' ├──────────────────────────────────────────────────────────────┤'));
|
|
485
|
+
if (ide === 'antigravity' || ide === 'both') {
|
|
486
|
+
log(color('green', ' │') + ` ${color('cyan', '•')} In Antigravity: Open chat & say: ${color('bold', '"review my code"')} ` + color('green', '│'));
|
|
487
|
+
}
|
|
488
|
+
if (ide === 'cursor' || ide === 'both') {
|
|
489
|
+
log(color('green', ' │') + ` ${color('cyan', '•')} In Cursor: Rules loaded! Mention: ${color('bold', '@code-review.mdc')} ` + color('green', '│'));
|
|
490
|
+
}
|
|
491
|
+
log(color('green', ' │') + ` ${color('gray', '•')} Run health check: ${color('bold', 'ai-dev-starter doctor')} ` + color('green', '│'));
|
|
492
|
+
log(color('green', ' ╰──────────────────────────────────────────────────────────────╯'));
|
|
493
|
+
log('');
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
async function cmdUpdate(projectRoot, options) {
|
|
497
|
+
printBanner();
|
|
498
|
+
info(`Updating AI Dev Starter in ${color('bold', projectRoot)}...`);
|
|
499
|
+
|
|
500
|
+
const manifest = readManifest(projectRoot);
|
|
501
|
+
if (!manifest) {
|
|
502
|
+
err(`No manifest found in ${projectRoot}. Run 'ai-dev-starter init' first.`);
|
|
503
|
+
process.exit(1);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const ide = options.ide || manifest.ide || 'both';
|
|
507
|
+
const selectedSkills = manifest.installedSkills || ALL_SKILLS;
|
|
508
|
+
|
|
509
|
+
let filesToInstall = [];
|
|
510
|
+
if (ide === 'antigravity') {
|
|
511
|
+
filesToInstall = filterFilesBySkills(ANTIGRAVITY_FILES, selectedSkills);
|
|
512
|
+
} else if (ide === 'cursor') {
|
|
513
|
+
filesToInstall = filterFilesBySkills(CURSOR_FILES, selectedSkills);
|
|
514
|
+
} else {
|
|
515
|
+
const combined = [...new Map([...ANTIGRAVITY_FILES, ...CURSOR_FILES].map(f => [f.target, f])).values()];
|
|
516
|
+
filesToInstall = filterFilesBySkills(combined, selectedSkills);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
log('');
|
|
520
|
+
for (const fileSpec of filesToInstall) {
|
|
521
|
+
await installFile(projectRoot, fileSpec, manifest, options);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
manifest.updatedAt = new Date().toISOString();
|
|
525
|
+
writeManifest(projectRoot, manifest);
|
|
526
|
+
log('');
|
|
527
|
+
ok('Update complete.');
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
function cmdDoctor(projectRoot) {
|
|
531
|
+
printBanner();
|
|
532
|
+
info(`Running diagnostics for ${color('bold', projectRoot)}...`);
|
|
533
|
+
log('');
|
|
534
|
+
|
|
535
|
+
const manifest = readManifest(projectRoot);
|
|
536
|
+
if (!manifest) {
|
|
537
|
+
warn('No manifest found. Run: npx ai-dev-starter init');
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
printCard('System Manifest', {
|
|
542
|
+
'Version': manifest.version,
|
|
543
|
+
'Target IDE': manifest.ide,
|
|
544
|
+
'Skills': (manifest.installedSkills || ALL_SKILLS).join(', '),
|
|
545
|
+
'Installed': manifest.installedAt ? manifest.installedAt.split('T')[0] : 'unknown',
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
const ide = manifest.ide || 'both';
|
|
549
|
+
const selectedSkills = manifest.installedSkills || ALL_SKILLS;
|
|
550
|
+
|
|
551
|
+
let expectedFiles = [];
|
|
552
|
+
if (ide === 'antigravity') {
|
|
553
|
+
expectedFiles = filterFilesBySkills(ANTIGRAVITY_FILES, selectedSkills);
|
|
554
|
+
} else if (ide === 'cursor') {
|
|
555
|
+
expectedFiles = filterFilesBySkills(CURSOR_FILES, selectedSkills);
|
|
556
|
+
} else {
|
|
557
|
+
const combined = [...new Map([...ANTIGRAVITY_FILES, ...CURSOR_FILES].map(f => [f.target, f])).values()];
|
|
558
|
+
expectedFiles = filterFilesBySkills(combined, selectedSkills);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
let issues = 0;
|
|
562
|
+
|
|
563
|
+
for (const fileSpec of expectedFiles) {
|
|
564
|
+
const targetPath = path.join(projectRoot, fileSpec.target);
|
|
565
|
+
if (!fs.existsSync(targetPath)) {
|
|
566
|
+
err(`Missing: ${fileSpec.target}`);
|
|
567
|
+
issues++;
|
|
568
|
+
} else if (fileWasUserModified(targetPath, manifest)) {
|
|
569
|
+
warn(`User-modified: ${fileSpec.target}`);
|
|
570
|
+
} else {
|
|
571
|
+
ok(`${color('dim', '[OK]'.padEnd(6))} ${fileSpec.target}`);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
const contextPath = path.join(projectRoot, '.agents', 'project-context.md');
|
|
576
|
+
if (!fs.existsSync(contextPath)) {
|
|
577
|
+
warn('.agents/project-context.md missing');
|
|
578
|
+
issues++;
|
|
579
|
+
} else {
|
|
580
|
+
ok(`${color('dim', '[OK]'.padEnd(6))} .agents/project-context.md`);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if (isGitRepo(projectRoot)) {
|
|
584
|
+
ok(`${color('dim', '[OK]'.padEnd(6))} Git repository detected`);
|
|
585
|
+
} else {
|
|
586
|
+
warn('Not a git repository — git analysis requires git');
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
log('');
|
|
590
|
+
if (issues === 0) {
|
|
591
|
+
log(color('green', ' ✔ All checks passed. Everything is healthy and ready!'));
|
|
592
|
+
} else {
|
|
593
|
+
warn(`${issues} issue(s) found. Run: npx ai-dev-starter update`);
|
|
594
|
+
}
|
|
595
|
+
log('');
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
function cmdDetect(projectRoot) {
|
|
599
|
+
printBanner();
|
|
600
|
+
const ctx = detectProjectContext(projectRoot);
|
|
601
|
+
printCard('Detected Tech Stack', {
|
|
602
|
+
'Project': ctx.projectName,
|
|
603
|
+
'Backend': `${ctx.backendLanguage || 'none'} (${ctx.backendFramework || 'none'})`,
|
|
604
|
+
'Frontend': `${ctx.frontendLanguage || 'none'} (${ctx.frontendFramework || 'none'})`,
|
|
605
|
+
'Styling': ctx.styling || 'none',
|
|
606
|
+
'Databases': ctx.databases.map(d => `${d.type}/${d.orm}`).join(', ') || 'none',
|
|
607
|
+
'Tests': `${ctx.testFramework || 'none'} (${ctx.commands.test})`,
|
|
608
|
+
'Dev Server': ctx.commands.dev_server || 'none',
|
|
609
|
+
'Pattern': ctx.architecturePattern || 'Modular',
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
async function cmdUninstall(projectRoot) {
|
|
614
|
+
printBanner();
|
|
615
|
+
warn('This will remove files installed by ai-dev-starter.');
|
|
616
|
+
warn('User-modified files will be preserved.');
|
|
617
|
+
log('');
|
|
618
|
+
|
|
619
|
+
const manifest = readManifest(projectRoot);
|
|
620
|
+
if (!manifest) {
|
|
621
|
+
err('No manifest found. Nothing to uninstall.');
|
|
622
|
+
process.exit(1);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
if (process.stdin.isTTY) {
|
|
626
|
+
const confirm = await ask('Type "yes" to proceed with uninstallation', 'no');
|
|
627
|
+
if (confirm !== 'yes') {
|
|
628
|
+
info('Uninstallation cancelled.');
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
for (const record of manifest.files) {
|
|
634
|
+
const targetPath = path.join(projectRoot, record.target);
|
|
635
|
+
if (!fs.existsSync(targetPath)) continue;
|
|
636
|
+
|
|
637
|
+
if (fileWasUserModified(targetPath, manifest)) {
|
|
638
|
+
warn(`User-modified — keeping: ${record.target}`);
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
fs.unlinkSync(targetPath);
|
|
643
|
+
ok(`Removed: ${record.target}`);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
const manifestPath = path.join(projectRoot, MANIFEST_FILE);
|
|
647
|
+
if (fs.existsSync(manifestPath)) {
|
|
648
|
+
fs.unlinkSync(manifestPath);
|
|
649
|
+
ok(`Removed: ${MANIFEST_FILE}`);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
log('');
|
|
653
|
+
ok('Uninstallation complete.');
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// ─── Argument Parsing ─────────────────────────────────────────────────────────
|
|
657
|
+
|
|
658
|
+
function parseArgs(argv) {
|
|
659
|
+
const args = argv.slice(2);
|
|
660
|
+
let command = args[0] && !args[0].startsWith('-') ? args[0] : null;
|
|
661
|
+
|
|
662
|
+
const options = {};
|
|
663
|
+
for (let i = 0; i < args.length; i++) {
|
|
664
|
+
const arg = args[i];
|
|
665
|
+
if (arg === '--target' && args[i + 1]) options.target = args[++i];
|
|
666
|
+
else if (arg === '--ide' && args[i + 1]) options.ide = args[++i];
|
|
667
|
+
else if (arg === '--skills' && args[i + 1]) options.skills = args[++i];
|
|
668
|
+
else if (arg === '--lang' && args[i + 1]) options.lang = args[++i];
|
|
669
|
+
else if (arg === '--merge') options.merge = true;
|
|
670
|
+
else if (arg === '-y' || arg === '--yes') options.yes = true;
|
|
671
|
+
else if (arg === '--help' || arg === '-h') options.help = true;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
if (!command) {
|
|
675
|
+
command = options.help ? 'help' : 'init';
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
return { command, options };
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// ─── Main ─────────────────────────────────────────────────────────────────────
|
|
682
|
+
|
|
683
|
+
async function main() {
|
|
684
|
+
const { command, options } = parseArgs(process.argv);
|
|
685
|
+
const projectRoot = path.resolve(options.target || process.cwd());
|
|
686
|
+
|
|
687
|
+
switch (command) {
|
|
688
|
+
case 'init': await cmdInit(projectRoot, options); break;
|
|
689
|
+
case 'update': await cmdUpdate(projectRoot, options); break;
|
|
690
|
+
case 'doctor': cmdDoctor(projectRoot); break;
|
|
691
|
+
case 'detect': cmdDetect(projectRoot); break;
|
|
692
|
+
case 'uninstall': await cmdUninstall(projectRoot); break;
|
|
693
|
+
default:
|
|
694
|
+
printBanner();
|
|
695
|
+
log(' Usage:');
|
|
696
|
+
log(` ${color('cyan', 'npx ai-dev-starter')} ${color('bold', '[init]')} ${color('dim', '[--ide antigravity|cursor|both] [--skills all|review|dev|<list>] [--lang fa|en] [--merge] [-y]')}`);
|
|
697
|
+
log(` ${color('cyan', 'npx ai-dev-starter')} ${color('bold', 'update')} ${color('dim', '[--target <path>]')}`);
|
|
698
|
+
log(` ${color('cyan', 'npx ai-dev-starter')} ${color('bold', 'doctor')} ${color('dim', '[--target <path>]')}`);
|
|
699
|
+
log(` ${color('cyan', 'npx ai-dev-starter')} ${color('bold', 'detect')} ${color('dim', '[--target <path>]')}`);
|
|
700
|
+
log(` ${color('cyan', 'npx ai-dev-starter')} ${color('bold', 'uninstall')} ${color('dim', '[--target <path>]')}`);
|
|
701
|
+
log('');
|
|
702
|
+
process.exit(0);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
main().catch(e => {
|
|
707
|
+
err(e.message);
|
|
708
|
+
process.exit(1);
|
|
709
|
+
});
|