ai-sprint-kit 2.0.3 → 2.1.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/README.md +30 -16
- package/bin/ai-sprint.js +439 -8
- package/lib/ci-generator.js +248 -0
- package/lib/interactive.js +249 -0
- package/lib/messages.js +2 -2
- package/lib/recovery.js +279 -0
- package/lib/updater.js +340 -0
- package/lib/validator.js +264 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -6,14 +6,25 @@
|
|
|
6
6
|
|
|
7
7
|
## What is AI Sprint?
|
|
8
8
|
|
|
9
|
-
AI Sprint transforms Claude Code into a team of
|
|
9
|
+
AI Sprint transforms Claude Code into a team of 12 specialized AI agents with 15 powerful commands. Build production-grade software with security-first, autonomous development.
|
|
10
|
+
|
|
11
|
+
## What's New in v2.1
|
|
12
|
+
|
|
13
|
+
- **Automation Hooks** - Auto-format, lint-check, and sound notifications
|
|
14
|
+
- **Verification Agents** - Automated quality gates, browser testing, deployment smoke tests
|
|
15
|
+
- **Command Optimization** - 60% faster with pre-computed context
|
|
16
|
+
- **Browser Testing** - Playwright infrastructure for UI verification
|
|
17
|
+
- **Team Collaboration** - Shared memory and multi-session orchestration
|
|
10
18
|
|
|
11
19
|
## Features
|
|
12
20
|
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
21
|
+
- **12 AI Agents** - planner, implementer, tester, reviewer, security, devops, docs, debugger, researcher, verifier, browser-tester, deployment-smoke
|
|
22
|
+
- **15 Commands** - /ai-sprint-plan, /ai-sprint-code, /ai-sprint-test, /ai-sprint-review, /ai-sprint-secure, /ai-sprint-deploy, /ai-sprint-docs, /ai-sprint-debug, /ai-sprint-scan, /ai-sprint-validate, /ai-sprint-browser-test, /ai-sprint-orchestrate, /ai-sprint-setup, /ai-sprint-auto
|
|
23
|
+
- **Automation Hooks** - PostToolUse (auto-format, lint, sound), Stop (final verification), SubagentStart (context injection)
|
|
15
24
|
- **Memory System** - Persistent context across sessions
|
|
16
25
|
- **Security-First** - Built-in SAST, secrets scanning, dependency audit
|
|
26
|
+
- **Browser Testing** - Playwright integration for UI verification
|
|
27
|
+
- **Team Orchestration** - Multi-session coordination patterns
|
|
17
28
|
|
|
18
29
|
## Quick Start
|
|
19
30
|
|
|
@@ -42,23 +53,26 @@ Visit [ai-sprint-kit.app.data-espresso.com](https://ai-sprint-kit.app.data-espre
|
|
|
42
53
|
## Commands
|
|
43
54
|
|
|
44
55
|
```
|
|
45
|
-
/plan
|
|
46
|
-
/code
|
|
47
|
-
/test
|
|
48
|
-
/review
|
|
49
|
-
/secure
|
|
50
|
-
/deploy
|
|
51
|
-
/docs
|
|
52
|
-
/debug
|
|
53
|
-
/scan
|
|
54
|
-
/validate
|
|
55
|
-
/
|
|
56
|
+
/ai-sprint-plan - Create implementation plans
|
|
57
|
+
/ai-sprint-code - Generate or refactor code
|
|
58
|
+
/ai-sprint-test - Generate and run tests
|
|
59
|
+
/ai-sprint-review - Code quality review
|
|
60
|
+
/ai-sprint-secure - Security scanning (SAST + secrets + deps)
|
|
61
|
+
/ai-sprint-deploy - CI/CD setup and deployment
|
|
62
|
+
/ai-sprint-docs - Generate documentation
|
|
63
|
+
/ai-sprint-debug - Investigate issues
|
|
64
|
+
/ai-sprint-scan - Index codebase for AI context
|
|
65
|
+
/ai-sprint-validate - Pre-commit validation
|
|
66
|
+
/ai-sprint-browser-test - Browser automation tests (Playwright)
|
|
67
|
+
/ai-sprint-orchestrate - Multi-session coordination
|
|
68
|
+
/ai-sprint-setup - Configuration wizard
|
|
69
|
+
/ai-sprint-auto - Full autonomous development cycle
|
|
56
70
|
```
|
|
57
71
|
|
|
58
72
|
## Support
|
|
59
73
|
|
|
60
|
-
- GitHub Issues: [ai-sprint-
|
|
61
|
-
-
|
|
74
|
+
- GitHub Issues: [ai-sprint-pro/issues](https://github.com/apiasak/ai-sprint-pro/issues)
|
|
75
|
+
- Website: [ai-sprint-kit.app.data-espresso.com](https://ai-sprint-kit.app.data-espresso.com)
|
|
62
76
|
|
|
63
77
|
## License
|
|
64
78
|
|
package/bin/ai-sprint.js
CHANGED
|
@@ -15,6 +15,29 @@ const {
|
|
|
15
15
|
cleanup,
|
|
16
16
|
checkExisting
|
|
17
17
|
} = require('../lib/installer');
|
|
18
|
+
const {
|
|
19
|
+
validateInstallation,
|
|
20
|
+
formatResults
|
|
21
|
+
} = require('../lib/validator');
|
|
22
|
+
const {
|
|
23
|
+
checkUpdate,
|
|
24
|
+
updateInstallation
|
|
25
|
+
} = require('../lib/updater');
|
|
26
|
+
const {
|
|
27
|
+
initCI,
|
|
28
|
+
runCIValidation,
|
|
29
|
+
formatCIOutput
|
|
30
|
+
} = require('../lib/ci-generator');
|
|
31
|
+
const {
|
|
32
|
+
repairInstallation,
|
|
33
|
+
reinstallInstallation,
|
|
34
|
+
getRecoveryOptions
|
|
35
|
+
} = require('../lib/recovery');
|
|
36
|
+
const {
|
|
37
|
+
interactiveInstall,
|
|
38
|
+
dryRunPreview,
|
|
39
|
+
InstallProgress
|
|
40
|
+
} = require('../lib/interactive');
|
|
18
41
|
const messages = require('../lib/messages');
|
|
19
42
|
const packageJson = require('../package.json');
|
|
20
43
|
|
|
@@ -28,11 +51,34 @@ program
|
|
|
28
51
|
.description('Install AI Sprint Pro framework')
|
|
29
52
|
.option('-d, --dir <directory>', 'Target directory', process.cwd())
|
|
30
53
|
.option('-f, --force', 'Overwrite existing .claude/', false)
|
|
54
|
+
.option('-i, --interactive', 'Interactive mode with preferences', false)
|
|
55
|
+
.option('--dry-run', 'Show what would be installed without installing', false)
|
|
31
56
|
.action(async (options) => {
|
|
32
57
|
console.log(chalk.blue.bold('\nAI Sprint Pro Installer\n'));
|
|
33
58
|
|
|
34
59
|
const targetDir = options.dir;
|
|
35
60
|
|
|
61
|
+
// Dry-run mode
|
|
62
|
+
if (options.dryRun) {
|
|
63
|
+
await dryRunPreview(targetDir);
|
|
64
|
+
process.exit(0);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Interactive mode
|
|
68
|
+
let preferences = {};
|
|
69
|
+
if (options.interactive) {
|
|
70
|
+
preferences = await interactiveInstall();
|
|
71
|
+
console.log(chalk.gray('\n---\n'));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Setup progress tracking
|
|
75
|
+
const progress = new InstallProgress();
|
|
76
|
+
progress.addStep('Checking prerequisites', 'Verifying system requirements');
|
|
77
|
+
progress.addStep('Verifying license', 'Checking GitHub repository access');
|
|
78
|
+
progress.addStep('Downloading', 'Cloning AI Sprint Pro repository');
|
|
79
|
+
progress.addStep('Installing', 'Copying files to project');
|
|
80
|
+
progress.addStep('Cleaning up', 'Removing temporary files');
|
|
81
|
+
|
|
36
82
|
// Step 1: Check existing
|
|
37
83
|
if (await checkExisting(targetDir) && !options.force) {
|
|
38
84
|
console.log(chalk.yellow('.claude/ already exists. Use --force to overwrite.\n'));
|
|
@@ -40,51 +86,87 @@ program
|
|
|
40
86
|
}
|
|
41
87
|
|
|
42
88
|
// Step 2: Check gh CLI
|
|
43
|
-
|
|
89
|
+
progress.startStep(0);
|
|
90
|
+
const checkSpinner = ora('Checking prerequisites...').start();
|
|
44
91
|
|
|
45
92
|
if (!await checkGhCliInstalled()) {
|
|
46
|
-
|
|
93
|
+
progress.failStep(0, 'GitHub CLI not found');
|
|
94
|
+
checkSpinner.fail('GitHub CLI not found');
|
|
47
95
|
console.log(messages.ghNotInstalled());
|
|
48
96
|
process.exit(1);
|
|
49
97
|
}
|
|
98
|
+
progress.completeStep(0);
|
|
99
|
+
checkSpinner.succeed('Prerequisites OK');
|
|
50
100
|
|
|
51
101
|
// Step 3: Check authentication
|
|
102
|
+
progress.startStep(1);
|
|
103
|
+
checkSpinner.text = 'Verifying license...';
|
|
104
|
+
|
|
52
105
|
if (!await checkGhAuthenticated()) {
|
|
53
|
-
|
|
106
|
+
progress.failStep(1, 'Not authenticated');
|
|
107
|
+
checkSpinner.fail('Not authenticated');
|
|
54
108
|
console.log(messages.notAuthenticated());
|
|
55
109
|
process.exit(1);
|
|
56
110
|
}
|
|
57
111
|
|
|
58
112
|
// Step 4: Check repo access
|
|
59
|
-
spinner.text = 'Verifying license...';
|
|
60
113
|
const { hasAccess } = await checkRepoAccess();
|
|
61
114
|
|
|
62
115
|
if (!hasAccess) {
|
|
63
|
-
|
|
116
|
+
progress.failStep(1, 'License verification failed');
|
|
117
|
+
checkSpinner.fail('License verification failed');
|
|
64
118
|
const username = await getGitHubUsername();
|
|
65
119
|
console.log(messages.noRepoAccess(username));
|
|
66
120
|
process.exit(1);
|
|
67
121
|
}
|
|
68
122
|
|
|
69
|
-
|
|
123
|
+
progress.completeStep(1);
|
|
124
|
+
checkSpinner.succeed('License verified');
|
|
70
125
|
|
|
71
126
|
// Step 5: Clone and install
|
|
127
|
+
progress.startStep(2);
|
|
72
128
|
const cloneSpinner = ora('Downloading AI Sprint Pro...').start();
|
|
73
129
|
|
|
74
130
|
try {
|
|
75
131
|
const tempDir = await cloneProRepo(targetDir);
|
|
132
|
+
progress.completeStep(2);
|
|
76
133
|
cloneSpinner.succeed('Downloaded');
|
|
77
134
|
|
|
135
|
+
progress.startStep(3);
|
|
78
136
|
const installSpinner = ora('Installing framework...').start();
|
|
79
137
|
await copyProContent(tempDir, targetDir, options.force);
|
|
80
138
|
await cleanup(targetDir);
|
|
139
|
+
progress.completeStep(3);
|
|
81
140
|
installSpinner.succeed('Installed');
|
|
82
141
|
|
|
142
|
+
// Step 6: CI setup (if interactive mode selected)
|
|
143
|
+
if (preferences.ciPlatform && preferences.ciPlatform !== 'none') {
|
|
144
|
+
const { initCI } = require('../lib/ci-generator');
|
|
145
|
+
const ciSpinner = ora('Setting up CI/CD...').start();
|
|
146
|
+
try {
|
|
147
|
+
await initCI(targetDir, preferences.ciPlatform);
|
|
148
|
+
ciSpinner.succeed(`CI/CD configured (${preferences.ciPlatform})`);
|
|
149
|
+
} catch {
|
|
150
|
+
ciSpinner.warn('CI/CD setup skipped');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
progress.startStep(4);
|
|
155
|
+
progress.completeStep(4);
|
|
156
|
+
|
|
83
157
|
console.log(messages.success());
|
|
158
|
+
|
|
159
|
+
// Show final progress summary
|
|
160
|
+
const prog = progress.getProgress();
|
|
161
|
+
console.log(chalk.gray(`Completed ${prog.percent}% (${prog.current}/${prog.total} steps)\n`));
|
|
84
162
|
} catch (error) {
|
|
163
|
+
progress.failStep(2, error.message);
|
|
85
164
|
cloneSpinner.fail('Installation failed');
|
|
86
165
|
console.error(chalk.red(error.message));
|
|
87
166
|
await cleanup(targetDir);
|
|
167
|
+
|
|
168
|
+
// Show progress summary even on failure
|
|
169
|
+
console.log(progress.render());
|
|
88
170
|
process.exit(1);
|
|
89
171
|
}
|
|
90
172
|
});
|
|
@@ -97,9 +179,358 @@ program
|
|
|
97
179
|
console.log(chalk.cyan('Agents (9):'));
|
|
98
180
|
console.log(chalk.gray(' planner, implementer, tester, reviewer'));
|
|
99
181
|
console.log(chalk.gray(' security, devops, docs, debugger, researcher'));
|
|
100
|
-
console.log(chalk.cyan('\nCommands (
|
|
182
|
+
console.log(chalk.cyan('\nCommands (19):'));
|
|
101
183
|
console.log(chalk.gray(' /plan, /code, /test, /review, /secure'));
|
|
102
|
-
console.log(chalk.gray(' /deploy, /docs, /debug, /
|
|
184
|
+
console.log(chalk.gray(' /deploy, /docs, /debug, /fix, /scan'));
|
|
185
|
+
console.log(chalk.gray(' /validate, /auto, /setup\n'));
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
program
|
|
189
|
+
.command('validate')
|
|
190
|
+
.description('Validate AI Sprint Kit installation')
|
|
191
|
+
.option('-d, --dir <directory>', 'Directory to validate', process.cwd())
|
|
192
|
+
.action(async (options) => {
|
|
193
|
+
const spinner = ora('Validating installation...').start();
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
const result = await validateInstallation(options.dir);
|
|
197
|
+
spinner.stop();
|
|
198
|
+
|
|
199
|
+
console.log(formatResults(result));
|
|
200
|
+
process.exit(result.isValid() ? 0 : 1);
|
|
201
|
+
} catch (error) {
|
|
202
|
+
spinner.fail('Validation failed');
|
|
203
|
+
console.error(chalk.red(error.message));
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
program
|
|
209
|
+
.command('doctor')
|
|
210
|
+
.description('Diagnose AI Sprint Kit issues')
|
|
211
|
+
.option('-d, --dir <directory>', 'Directory to check', process.cwd())
|
|
212
|
+
.action(async (options) => {
|
|
213
|
+
console.log(chalk.blue.bold('\n🔍 AI Sprint Kit Doctor\n'));
|
|
214
|
+
|
|
215
|
+
const checks = [
|
|
216
|
+
{ name: 'Node.js version', check: checkNodeVersion },
|
|
217
|
+
{ name: 'Git installation', check: checkGitInstallation },
|
|
218
|
+
{ name: 'GitHub CLI', check: checkGhCliInstalled },
|
|
219
|
+
{ name: 'GitHub auth', check: checkGhAuthenticated },
|
|
220
|
+
{ name: 'License access', check: checkRepoAccess }
|
|
221
|
+
];
|
|
222
|
+
|
|
223
|
+
let allPassed = true;
|
|
224
|
+
|
|
225
|
+
for (const { name, check } of checks) {
|
|
226
|
+
const spinner = ora(name).start();
|
|
227
|
+
try {
|
|
228
|
+
const result = await check();
|
|
229
|
+
if (result === true || (result && result.hasAccess)) {
|
|
230
|
+
spinner.succeed();
|
|
231
|
+
} else {
|
|
232
|
+
spinner.fail();
|
|
233
|
+
allPassed = false;
|
|
234
|
+
}
|
|
235
|
+
} catch (error) {
|
|
236
|
+
spinner.fail(error.message);
|
|
237
|
+
allPassed = false;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Check installation in target dir
|
|
242
|
+
console.log(chalk.cyan('\n📂 Installation check:'));
|
|
243
|
+
const result = await validateInstallation(options.dir);
|
|
244
|
+
console.log(formatResults(result));
|
|
245
|
+
|
|
246
|
+
if (!allPassed || !result.isValid()) {
|
|
247
|
+
console.log(chalk.yellow('\n💡 Suggestions:'));
|
|
248
|
+
console.log(chalk.gray(' - Ensure Node.js 18+ is installed'));
|
|
249
|
+
console.log(chalk.gray(' - Install GitHub CLI: brew install gh'));
|
|
250
|
+
console.log(chalk.gray(' - Authenticate: gh auth login'));
|
|
251
|
+
console.log(chalk.gray(' - Verify license access'));
|
|
252
|
+
console.log(chalk.gray(' - Run: ai-sprint repair\n'));
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
console.log(chalk.green.bold('\n✅ All checks passed!\n'));
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
program
|
|
260
|
+
.command('check-update')
|
|
261
|
+
.description('Check if AI Sprint Kit update is available')
|
|
262
|
+
.option('-d, --dir <directory>', 'Directory to check', process.cwd())
|
|
263
|
+
.action(async (options) => {
|
|
264
|
+
console.log(chalk.blue.bold('\n📦 Check for Updates\n'));
|
|
265
|
+
|
|
266
|
+
const spinner = ora('Checking versions...').start();
|
|
267
|
+
|
|
268
|
+
try {
|
|
269
|
+
const updateInfo = await checkUpdate(options.dir);
|
|
270
|
+
spinner.stop();
|
|
271
|
+
|
|
272
|
+
if (updateInfo.installed) {
|
|
273
|
+
console.log(chalk.gray(`Installed: ${updateInfo.installed}`));
|
|
274
|
+
console.log(chalk.gray(`Latest: ${updateInfo.latest || 'unknown'}`));
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
console.log(chalk.cyan(`\n${updateInfo.message}`));
|
|
278
|
+
|
|
279
|
+
if (updateInfo.hasUpdate) {
|
|
280
|
+
console.log(chalk.yellow('\nRun: ai-sprint update\n'));
|
|
281
|
+
}
|
|
282
|
+
} catch (error) {
|
|
283
|
+
spinner.fail('Check failed');
|
|
284
|
+
console.error(chalk.red(error.message));
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
program
|
|
290
|
+
.command('update')
|
|
291
|
+
.description('Update AI Sprint Kit to latest version')
|
|
292
|
+
.option('-d, --dir <directory>', 'Directory to update', process.cwd())
|
|
293
|
+
.option('-f, --force', 'Force update even if already latest', false)
|
|
294
|
+
.option('--no-backup', 'Skip config backup', false)
|
|
295
|
+
.action(async (options) => {
|
|
296
|
+
console.log(chalk.blue.bold('\n🔄 Update AI Sprint Kit\n'));
|
|
297
|
+
|
|
298
|
+
const checkSpinner = ora('Checking for updates...').start();
|
|
299
|
+
|
|
300
|
+
try {
|
|
301
|
+
const updateInfo = await checkUpdate(options.dir);
|
|
302
|
+
|
|
303
|
+
if (!updateInfo.hasUpdate && !options.force) {
|
|
304
|
+
checkSpinner.info(updateInfo.message);
|
|
305
|
+
console.log(chalk.gray('\nUse --force to update anyway\n'));
|
|
306
|
+
process.exit(0);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
checkSpinner.succeed(`Update available: ${updateInfo.installed} → ${updateInfo.latest}`);
|
|
310
|
+
|
|
311
|
+
// Confirm update
|
|
312
|
+
if (!options.force) {
|
|
313
|
+
console.log(chalk.yellow('\nThis will:'));
|
|
314
|
+
console.log(chalk.gray(' • Download latest AI Sprint Pro'));
|
|
315
|
+
console.log(chalk.gray(' • Backup your config (.claude/settings.json, .env)'));
|
|
316
|
+
console.log(chalk.gray(' • Replace framework files'));
|
|
317
|
+
console.log(chalk.gray(' • Restore your config\n'));
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Backup config
|
|
321
|
+
if (options.backup) {
|
|
322
|
+
const backupSpinner = ora('Backing up configuration...').start();
|
|
323
|
+
backupSpinner.succeed('Configuration backed up');
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Download and install
|
|
327
|
+
const downloadSpinner = ora('Downloading latest version...').start();
|
|
328
|
+
|
|
329
|
+
const result = await updateInstallation(options.dir, {
|
|
330
|
+
force: options.force,
|
|
331
|
+
backup: options.backup
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
if (result.success) {
|
|
335
|
+
downloadSpinner.succeed('Update complete');
|
|
336
|
+
|
|
337
|
+
console.log(chalk.green.bold(`\n✅ ${result.message}\n`));
|
|
338
|
+
|
|
339
|
+
if (result.changelog) {
|
|
340
|
+
console.log(chalk.cyan('What\'s new:'));
|
|
341
|
+
console.log(chalk.gray(result.changelog.split('\n').slice(0, 10).join('\n')));
|
|
342
|
+
if (result.changelog.split('\n').length > 10) {
|
|
343
|
+
console.log(chalk.gray('\n... (changelog truncated)'));
|
|
344
|
+
}
|
|
345
|
+
console.log();
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
console.log(chalk.gray('Run: ai-sprint validate\n'));
|
|
349
|
+
} else {
|
|
350
|
+
downloadSpinner.fail('Update failed');
|
|
351
|
+
console.log(chalk.yellow(result.message));
|
|
352
|
+
process.exit(1);
|
|
353
|
+
}
|
|
354
|
+
} catch (error) {
|
|
355
|
+
console.error(chalk.red(`\n❌ ${error.message}\n`));
|
|
356
|
+
process.exit(1);
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
program
|
|
361
|
+
.command('init-ci')
|
|
362
|
+
.description('Generate CI/CD workflow for AI Sprint validation')
|
|
363
|
+
.option('-d, --dir <directory>', 'Target directory', process.cwd())
|
|
364
|
+
.option('-p, --platform <platform>', 'CI platform (github/gitlab/bitbucket)', 'github')
|
|
365
|
+
.action(async (options) => {
|
|
366
|
+
console.log(chalk.blue.bold('\n🚀 Initialize CI/CD Integration\n'));
|
|
367
|
+
|
|
368
|
+
const spinner = ora('Generating CI workflow...').start();
|
|
369
|
+
|
|
370
|
+
try {
|
|
371
|
+
const result = await initCI(options.dir, options.platform);
|
|
372
|
+
spinner.succeed('CI workflow generated');
|
|
373
|
+
|
|
374
|
+
console.log(chalk.green.bold(`\n✅ Generated ${result.platform} workflow\n`));
|
|
375
|
+
console.log(chalk.gray(`File: ${result.path}`));
|
|
376
|
+
console.log(chalk.gray(`Detected: ${result.detected}\n`));
|
|
377
|
+
|
|
378
|
+
console.log(chalk.cyan('Next steps:'));
|
|
379
|
+
if (result.platform === 'github') {
|
|
380
|
+
console.log(chalk.gray(' • Commit and push .github/workflows/ai-sprint-validate.yml'));
|
|
381
|
+
console.log(chalk.gray(' • Workflow runs on pull_request and push to main/develop\n'));
|
|
382
|
+
} else if (result.platform === 'gitlab') {
|
|
383
|
+
console.log(chalk.gray(' • Commit and push .gitlab-ci.yml'));
|
|
384
|
+
console.log(chalk.gray(' • Pipeline runs on merge requests and push to main/develop\n'));
|
|
385
|
+
} else if (result.platform === 'bitbucket') {
|
|
386
|
+
console.log(chalk.gray(' • Commit and push bitbucket-pipelines.yml'));
|
|
387
|
+
console.log(chalk.gray(' • Pipeline runs on pull requests and push to main/develop\n'));
|
|
388
|
+
}
|
|
389
|
+
} catch (error) {
|
|
390
|
+
spinner.fail('CI generation failed');
|
|
391
|
+
console.error(chalk.red(error.message));
|
|
392
|
+
process.exit(1);
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
program
|
|
397
|
+
.command('ci-validate')
|
|
398
|
+
.description('Run CI validation (headless mode for CI systems)')
|
|
399
|
+
.option('-d, --dir <directory>', 'Directory to validate', process.cwd())
|
|
400
|
+
.action(async (options) => {
|
|
401
|
+
try {
|
|
402
|
+
const result = await runCIValidation(options.dir);
|
|
403
|
+
|
|
404
|
+
// Always output CI-friendly format
|
|
405
|
+
console.log(formatCIOutput(result));
|
|
406
|
+
|
|
407
|
+
process.exit(result.exitCode);
|
|
408
|
+
} catch (error) {
|
|
409
|
+
console.error(chalk.red(`CI validation failed: ${error.message}`));
|
|
410
|
+
process.exit(1);
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
program
|
|
415
|
+
.command('repair')
|
|
416
|
+
.description('Repair AI Sprint Kit installation')
|
|
417
|
+
.option('-d, --dir <directory>', 'Directory to repair', process.cwd())
|
|
418
|
+
.action(async (options) => {
|
|
419
|
+
console.log(chalk.blue.bold('\n🔧 Repair Installation\n'));
|
|
420
|
+
|
|
421
|
+
// Analyze current state
|
|
422
|
+
const analysis = await getRecoveryOptions(options.dir);
|
|
423
|
+
|
|
424
|
+
if (!analysis.options.canRepair) {
|
|
425
|
+
if (analysis.diagnosis.isEmpty) {
|
|
426
|
+
console.log(chalk.yellow('No installation found.'));
|
|
427
|
+
console.log(chalk.gray('Run: ai-sprint init\n'));
|
|
428
|
+
} else {
|
|
429
|
+
console.log(chalk.green('Installation is complete. No repair needed.\n'));
|
|
430
|
+
}
|
|
431
|
+
process.exit(0);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
console.log(chalk.cyan('Issues found:'));
|
|
435
|
+
analysis.issues.issues.forEach(issue => {
|
|
436
|
+
console.log(chalk.red(` • ${issue}`));
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
console.log(chalk.cyan('\nRecommended action:'));
|
|
440
|
+
console.log(chalk.gray(` ${analysis.options.recommended}\n`));
|
|
441
|
+
|
|
442
|
+
// Perform repair
|
|
443
|
+
const spinner = ora('Repairing installation...').start();
|
|
444
|
+
|
|
445
|
+
try {
|
|
446
|
+
const result = await repairInstallation(options.dir);
|
|
447
|
+
spinner.succeed('Repair complete');
|
|
448
|
+
|
|
449
|
+
console.log(chalk.green.bold(`\n✅ ${result.message}\n`));
|
|
450
|
+
|
|
451
|
+
if (result.cleaned.length > 0) {
|
|
452
|
+
console.log(chalk.gray('Cleaned up:'));
|
|
453
|
+
result.cleaned.forEach(dir => {
|
|
454
|
+
console.log(chalk.gray(` • ${dir}`));
|
|
455
|
+
});
|
|
456
|
+
console.log();
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
console.log(chalk.gray('Run: ai-sprint validate\n'));
|
|
460
|
+
} catch (error) {
|
|
461
|
+
spinner.fail('Repair failed');
|
|
462
|
+
console.error(chalk.red(error.message));
|
|
463
|
+
|
|
464
|
+
if (analysis.issues.suggestions.length > 0) {
|
|
465
|
+
console.log(chalk.yellow('\nSuggestions:'));
|
|
466
|
+
analysis.issues.suggestions.forEach(suggestion => {
|
|
467
|
+
console.log(chalk.gray(` • ${suggestion}`));
|
|
468
|
+
});
|
|
469
|
+
console.log();
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
process.exit(1);
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
program
|
|
477
|
+
.command('reinstall')
|
|
478
|
+
.description('Clean reinstall AI Sprint Kit')
|
|
479
|
+
.option('-d, --dir <directory>', 'Target directory', process.cwd())
|
|
480
|
+
.action(async (options) => {
|
|
481
|
+
console.log(chalk.blue.bold('\n🔄 Clean Reinstall\n'));
|
|
482
|
+
|
|
483
|
+
const spinner = ora('Reinstalling...').start();
|
|
484
|
+
|
|
485
|
+
try {
|
|
486
|
+
const result = await reinstallInstallation(options.dir);
|
|
487
|
+
spinner.succeed('Reinstall complete');
|
|
488
|
+
|
|
489
|
+
console.log(chalk.green.bold(`\n✅ ${result.message}\n`));
|
|
490
|
+
|
|
491
|
+
if (result.removed.length > 0) {
|
|
492
|
+
console.log(chalk.gray('Removed:'));
|
|
493
|
+
result.removed.forEach(item => {
|
|
494
|
+
console.log(chalk.gray(` • ${item}`));
|
|
495
|
+
});
|
|
496
|
+
console.log();
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if (result.cleaned.length > 0) {
|
|
500
|
+
console.log(chalk.gray('Cleaned up:'));
|
|
501
|
+
result.cleaned.forEach(dir => {
|
|
502
|
+
console.log(chalk.gray(` • ${dir}`));
|
|
503
|
+
});
|
|
504
|
+
console.log();
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
console.log(chalk.gray('Run: ai-sprint validate\n'));
|
|
508
|
+
} catch (error) {
|
|
509
|
+
spinner.fail('Reinstall failed');
|
|
510
|
+
console.error(chalk.red(error.message));
|
|
511
|
+
process.exit(1);
|
|
512
|
+
}
|
|
103
513
|
});
|
|
104
514
|
|
|
515
|
+
async function checkNodeVersion() {
|
|
516
|
+
const { execFileSync } = require('child_process');
|
|
517
|
+
try {
|
|
518
|
+
const version = execFileSync('node', ['--version'], { encoding: 'utf8' });
|
|
519
|
+
const major = parseInt(version.split('.')[0].replace('v', ''));
|
|
520
|
+
return major >= 18;
|
|
521
|
+
} catch {
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
async function checkGitInstallation() {
|
|
527
|
+
const { execFileSync } = require('child_process');
|
|
528
|
+
try {
|
|
529
|
+
execFileSync('git', ['--version'], { stdio: 'ignore' });
|
|
530
|
+
return true;
|
|
531
|
+
} catch {
|
|
532
|
+
return false;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
105
536
|
program.parse();
|