ai-sprint-kit 2.0.4 → 2.1.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/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 specialized AI agents with 11 powerful commands. Build production-grade software with security-first, autonomous development.
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
- - **9 AI Agents** - planner, implementer, tester, reviewer, security, devops, docs, debugger, researcher
14
- - **11 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-auto
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
 
@@ -21,6 +32,20 @@ AI Sprint transforms Claude Code into a team of 9 specialized AI agents with 11
21
32
  npx ai-sprint-kit init
22
33
  ```
23
34
 
35
+ ## CLI Commands
36
+
37
+ ```bash
38
+ ai-sprint init # Install AI Sprint framework
39
+ ai-sprint update # Update to latest version
40
+ ai-sprint check-update # Check if update available
41
+ ai-sprint validate # Validate installation
42
+ ai-sprint doctor # Diagnose issues
43
+ ai-sprint repair # Repair installation
44
+ ai-sprint reinstall # Clean reinstall
45
+ ai-sprint list # Show features
46
+ ai-sprint init-ci # Generate CI/CD workflow
47
+ ```
48
+
24
49
  ## Requirements
25
50
 
26
51
  1. **Claude Code** - [Install Claude Code](https://claude.ai/code)
@@ -42,17 +67,20 @@ Visit [ai-sprint-kit.app.data-espresso.com](https://ai-sprint-kit.app.data-espre
42
67
  ## Commands
43
68
 
44
69
  ```
45
- /ai-sprint-plan - Create implementation plans
46
- /ai-sprint-code - Generate or refactor code
47
- /ai-sprint-test - Generate and run tests
48
- /ai-sprint-review - Code quality review
49
- /ai-sprint-secure - Security scanning (SAST + secrets + deps)
50
- /ai-sprint-deploy - CI/CD setup and deployment
51
- /ai-sprint-docs - Generate documentation
52
- /ai-sprint-debug - Investigate issues
53
- /ai-sprint-scan - Index codebase for AI context
54
- /ai-sprint-validate - Pre-commit validation
55
- /ai-sprint-auto - Full autonomous development cycle
70
+ /ai-sprint-plan - Create implementation plans
71
+ /ai-sprint-code - Generate or refactor code
72
+ /ai-sprint-test - Generate and run tests
73
+ /ai-sprint-review - Code quality review
74
+ /ai-sprint-secure - Security scanning (SAST + secrets + deps)
75
+ /ai-sprint-deploy - CI/CD setup and deployment
76
+ /ai-sprint-docs - Generate documentation
77
+ /ai-sprint-debug - Investigate issues
78
+ /ai-sprint-scan - Index codebase for AI context
79
+ /ai-sprint-validate - Pre-commit validation
80
+ /ai-sprint-browser-test - Browser automation tests (Playwright)
81
+ /ai-sprint-orchestrate - Multi-session coordination
82
+ /ai-sprint-setup - Configuration wizard
83
+ /ai-sprint-auto - Full autonomous development cycle
56
84
  ```
57
85
 
58
86
  ## Support
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
- const spinner = ora('Checking prerequisites...').start();
89
+ progress.startStep(0);
90
+ const checkSpinner = ora('Checking prerequisites...').start();
44
91
 
45
92
  if (!await checkGhCliInstalled()) {
46
- spinner.fail('GitHub CLI not found');
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
- spinner.fail('Not authenticated');
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
- spinner.fail('License verification failed');
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
- spinner.succeed('License verified');
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 (11):'));
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, /scan, /validate, /auto\n'));
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();