grimoire-framework 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/.grimoire/install-manifest.yaml +2 -2
  2. package/.grimoire/memory/README.md +13 -0
  3. package/.grimoire/memory/entities/context.md +19 -0
  4. package/.grimoire/memory/entities/decisions.md +17 -0
  5. package/.grimoire/memory/entities/patterns.md +20 -0
  6. package/.grimoire/memory/sessions/2026-02-22.json +9 -0
  7. package/.grimoire/memory/sessions/archived/2026-02-22.jsonl +3 -0
  8. package/bin/commands/memory.js +215 -0
  9. package/bin/commands/metrics.js +107 -0
  10. package/bin/grimoire-cli.js +103 -0
  11. package/bin/grimoire-ids.js +2 -2
  12. package/bin/grimoire-minimal.js +1 -1
  13. package/bin/grimoire.js +1118 -1118
  14. package/bin/modules/env-config.js +2 -2
  15. package/bin/modules/mcp-installer.js +1 -1
  16. package/bin/utils/install-errors.js +1 -1
  17. package/bin/utils/install-transaction.js +2 -2
  18. package/bin/utils/pro-detector.js +110 -110
  19. package/package.json +158 -158
  20. package/packages/gemini-grimoire-extension/commands/grimoire-agents.js +2 -2
  21. package/packages/gemini-grimoire-extension/commands/grimoire-master.js +1 -1
  22. package/packages/gemini-grimoire-extension/commands/grimoire-status.js +2 -2
  23. package/packages/gemini-grimoire-extension/commands/grimoire-validate.js +2 -2
  24. package/packages/gemini-grimoire-extension/commands/lib/agent-launcher.js +147 -147
  25. package/packages/gemini-grimoire-extension/extension.json +2 -2
  26. package/packages/gemini-grimoire-extension/gemini-extension.json +2 -2
  27. package/packages/gemini-grimoire-extension/hooks/hooks.json +2 -2
  28. package/packages/grimoire-install/.releaserc.json +1 -1
  29. package/packages/grimoire-install/CHANGELOG.md +1 -1
  30. package/packages/grimoire-install/README.md +2 -2
  31. package/packages/grimoire-install/bin/edmcp.js +1 -1
  32. package/packages/grimoire-install/bin/grimoire-install.js +1 -1
  33. package/packages/grimoire-install/jest.config.js +1 -1
  34. package/packages/grimoire-install/package.json +2 -2
  35. package/packages/grimoire-install/src/edmcp/index.js +1 -1
  36. package/packages/grimoire-install/src/installer.js +2 -2
  37. package/packages/grimoire-pro-cli/bin/grimoire-pro.js +2 -2
  38. package/packages/grimoire-pro-cli/package.json +2 -2
  39. package/packages/grimoire-pro-cli/src/recover.js +1 -1
  40. package/packages/installer/package.json +1 -1
  41. package/packages/installer/src/__tests__/performance-benchmark.js +2 -2
  42. package/packages/installer/src/config/configure-environment.js +2 -2
  43. package/packages/installer/src/config/ide-configs.js +2 -2
  44. package/packages/installer/src/config/templates/core-config-template.js +2 -2
  45. package/packages/installer/src/config/templates/env-template.js +2 -2
  46. package/packages/installer/src/config/validation/config-validator.js +1 -1
  47. package/packages/installer/src/detection/detect-project-type.js +2 -2
  48. package/packages/installer/src/installer/brownfield-upgrader.js +2 -2
  49. package/packages/installer/src/installer/grimoire-core-installer.js +2 -2
  50. package/packages/installer/src/installer/manifest-signature.js +2 -2
  51. package/packages/installer/src/installer/post-install-validator.js +2 -2
  52. package/packages/installer/src/installer/squad-installer.js +109 -0
  53. package/packages/installer/src/merger/index.js +1 -1
  54. package/packages/installer/src/merger/parsers/markdown-section-parser.js +1 -1
  55. package/packages/installer/src/merger/strategies/env-merger.js +1 -1
  56. package/packages/installer/src/merger/strategies/markdown-merger.js +1 -1
  57. package/packages/installer/src/merger/types.js +1 -1
  58. package/packages/installer/src/pro/pro-scaffolder.js +2 -2
  59. package/packages/installer/src/updater/index.js +2 -2
  60. package/packages/installer/src/utils/grimoire-colors.js +1 -1
  61. package/packages/installer/src/wizard/i18n.js +2 -2
  62. package/packages/installer/src/wizard/ide-config-generator.js +2 -2
  63. package/packages/installer/src/wizard/ide-selector.js +1 -1
  64. package/packages/installer/src/wizard/index.js +28 -36
  65. package/packages/installer/src/wizard/pro-setup.js +2 -2
  66. package/packages/installer/src/wizard/questions.js +20 -15
  67. package/packages/installer/src/wizard/validation/index.js +1 -1
  68. package/packages/installer/src/wizard/validation/report-generator.js +1 -1
  69. package/packages/installer/src/wizard/validation/troubleshooting-system.js +2 -2
  70. package/packages/installer/src/wizard/validation/validators/config-validator.js +2 -2
  71. package/packages/installer/src/wizard/validation/validators/file-structure-validator.js +1 -1
  72. package/packages/installer/src/wizard/wizard.js +2 -2
  73. package/packages/installer/tests/integration/environment-configuration.test.js +2 -2
  74. package/packages/installer/tests/integration/wizard-detection.test.js +2 -2
  75. package/packages/installer/tests/unit/config-validator.test.js +1 -1
  76. package/packages/installer/tests/unit/detection/detect-project-type.test.js +2 -2
  77. package/packages/installer/tests/unit/env-template.test.js +2 -2
  78. package/packages/installer/tests/unit/merger/env-merger.test.js +1 -1
  79. package/packages/installer/tests/unit/merger/markdown-merger.test.js +1 -1
  80. package/scripts/check-markdown-links.py +352 -352
  81. package/scripts/code-intel-health-check.js +1 -1
  82. package/scripts/dashboard-parallel-dev.sh +1 -1
  83. package/scripts/dashboard-parallel-phase3.sh +1 -1
  84. package/scripts/dashboard-parallel-phase4.sh +1 -1
  85. package/scripts/ensure-manifest.js +1 -1
  86. package/scripts/generate-install-manifest.js +2 -2
  87. package/scripts/install-monitor-hooks.sh +2 -2
  88. package/scripts/package-synapse.js +2 -2
  89. package/scripts/semantic-lint.js +1 -1
  90. package/scripts/sign-manifest.sh +2 -2
  91. package/scripts/validate-manifest.js +2 -2
  92. package/scripts/validate-package-completeness.js +2 -2
package/bin/grimoire.js CHANGED
@@ -1,1118 +1,1118 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * grimoire-FullStack CLI
5
- * Main entry point - Standalone (no external dependencies for npx compatibility)
6
- * Version: 4.0.0
7
- */
8
-
9
- const path = require('path');
10
- const fs = require('fs');
11
- const { execSync } = require('child_process');
12
-
13
- // Read package.json for version
14
- const packageJsonPath = path.join(__dirname, '..', 'package.json');
15
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
16
-
17
- // Parse arguments
18
- const args = process.argv.slice(2);
19
- const command = args[0];
20
-
21
- // Helper: Run initialization wizard
22
- async function runWizard(options = {}) {
23
- // Use the v4 wizard from packages/installer/src/wizard/index.js
24
- const wizardPath = path.join(__dirname, '..', 'packages', 'installer', 'src', 'wizard', 'index.js');
25
-
26
- if (!fs.existsSync(wizardPath)) {
27
- // Fallback to legacy wizard if new wizard not found
28
- const legacyScript = path.join(__dirname, 'grimoire-init.js');
29
- if (fs.existsSync(legacyScript)) {
30
- if (!options.quiet) {
31
- console.log('⚠️ Using legacy wizard (src/wizard not found)');
32
- }
33
- // Legacy wizard doesn't support options, pass via env vars
34
- process.env.grimoire_INSTALL_FORCE = options.force ? '1' : '';
35
- process.env.grimoire_INSTALL_QUIET = options.quiet ? '1' : '';
36
- process.env.grimoire_INSTALL_DRY_RUN = options.dryRun ? '1' : '';
37
- require(legacyScript);
38
- return;
39
- }
40
- console.error('❌ Initialization wizard not found');
41
- console.error('Please ensure grimoire-FullStack is installed correctly.');
42
- process.exit(1);
43
- }
44
-
45
- try {
46
- // Run the v4 wizard with options
47
- const { runWizard: executeWizard } = require(wizardPath);
48
- await executeWizard(options);
49
- } catch (error) {
50
- console.error('❌ Wizard error:', error.message);
51
- process.exit(1);
52
- }
53
- }
54
-
55
- // Helper: Show help
56
- function showHelp() {
57
- console.log(`
58
- grimoire-FullStack v${packageJson.version}
59
- AI-Orchestrated System for Full Stack Development
60
-
61
- USAGE:
62
- npx grimoire-framework # Run installation wizard
63
- npx grimoire-framework install # Install in current project
64
- npx grimoire-framework init <name> # Create new project
65
- npx grimoire-framework update # Update to latest version
66
- npx grimoire-framework validate # Validate installation integrity
67
- npx grimoire-framework info # Show system info
68
- npx grimoire-framework doctor # Run diagnostics
69
- npx grimoire-framework --version # Show version
70
- npx grimoire-framework --version -d # Show detailed version info
71
- npx grimoire-framework --help # Show this help
72
-
73
- UPDATE:
74
- grimoire-framework update # Update to latest version
75
- grimoire-framework update --check # Check for updates without applying
76
- grimoire-framework update --dry-run # Preview what would be updated
77
- grimoire-framework update --force # Force update even if up-to-date
78
- grimoire-framework update --verbose # Show detailed output
79
-
80
- VALIDATION:
81
- grimoire-framework validate # Validate installation integrity
82
- grimoire-framework validate --repair # Repair missing/corrupted files
83
- grimoire-framework validate --repair --dry-run # Preview repairs
84
- grimoire-framework validate --detailed # Show detailed file list
85
-
86
- CONFIGURATION:
87
- grimoire-framework config show # Show resolved configuration
88
- grimoire-framework config show --debug # Show with source annotations
89
- grimoire-framework config diff --levels L1,L2 # Compare config levels
90
- grimoire-framework config migrate # Migrate monolithic to layered
91
- grimoire-framework config validate # Validate config files
92
- grimoire-framework config init-local # Create local-config.yaml
93
-
94
- SERVICE DISCOVERY:
95
- grimoire-framework workers search <query> # Search for workers
96
- grimoire-framework workers search "json" --category=data
97
- grimoire-framework workers search "transform" --tags=etl,data
98
- grimoire-framework workers search "api" --format=json
99
-
100
- EXAMPLES:
101
- # Install in current directory
102
- npx grimoire-framework
103
-
104
- # Install with minimal mode (only expansion-creator)
105
- npx grimoire-minimal
106
-
107
- # Create new project
108
- npx grimoire-framework init my-project
109
-
110
- # Search for workers
111
- grimoire-framework workers search "json csv"
112
-
113
- For more information, visit: https://github.com/grimoireAI/grimoire
114
- `);
115
- }
116
-
117
- // Helper: Show version
118
- async function showVersion() {
119
- const isDetailed = args.includes('--detailed') || args.includes('-d');
120
-
121
- if (!isDetailed) {
122
- // Simple version output (backwards compatible)
123
- console.log(packageJson.version);
124
- return;
125
- }
126
-
127
- // Detailed version output (Story 7.2: Version Tracking)
128
- console.log(`grimoire-FullStack v${packageJson.version}`);
129
- console.log('Package: grimoire');
130
-
131
- // Check for local installation
132
- const localVersionPath = path.join(process.cwd(), '.grimoire', 'version.json');
133
-
134
- if (fs.existsSync(localVersionPath)) {
135
- try {
136
- const versionInfo = JSON.parse(fs.readFileSync(localVersionPath, 'utf8'));
137
- console.log('\n📦 Local Installation:');
138
- console.log(` Version: ${versionInfo.version}`);
139
- console.log(` Mode: ${versionInfo.mode || 'unknown'}`);
140
-
141
- if (versionInfo.installedAt) {
142
- const installedDate = new Date(versionInfo.installedAt);
143
- console.log(` Installed: ${installedDate.toLocaleDateString()}`);
144
- }
145
-
146
- if (versionInfo.updatedAt) {
147
- const updatedDate = new Date(versionInfo.updatedAt);
148
- console.log(` Updated: ${updatedDate.toLocaleDateString()}`);
149
- }
150
-
151
- if (versionInfo.fileHashes) {
152
- const fileCount = Object.keys(versionInfo.fileHashes).length;
153
- console.log(` Files: ${fileCount} tracked`);
154
- }
155
-
156
- if (versionInfo.customized && versionInfo.customized.length > 0) {
157
- console.log(` Customized: ${versionInfo.customized.length} files`);
158
- }
159
-
160
- // Version comparison
161
- if (versionInfo.version !== packageJson.version) {
162
- console.log('\n⚠️ Version mismatch!');
163
- console.log(` Local: ${versionInfo.version}`);
164
- console.log(` Latest: ${packageJson.version}`);
165
- console.log(' Run \'npx grimoire-framework update\' to update.');
166
- } else {
167
- console.log('\n✅ Up to date');
168
- }
169
- } catch (error) {
170
- console.log(`\n⚠️ Could not read version.json: ${error.message}`);
171
- }
172
- } else {
173
- console.log('\n📭 No local installation found');
174
- console.log(' Run \'npx grimoire-framework install\' to install grimoire in this project.');
175
- }
176
- }
177
-
178
- // Helper: Show system info
179
- function showInfo() {
180
- console.log('📊 grimoire-FullStack System Information\n');
181
- console.log(`Version: ${packageJson.version}`);
182
- console.log(`Platform: ${process.platform}`);
183
- console.log(`Node.js: ${process.version}`);
184
- console.log(`Architecture: ${process.arch}`);
185
- console.log(`Working Directory: ${process.cwd()}`);
186
- console.log(`Install Location: ${path.join(__dirname, '..')}`);
187
-
188
- // Check if .grimoire exists
189
- const grimoireCoreDir = path.join(__dirname, '..', '.grimoire');
190
- if (fs.existsSync(grimoireCoreDir)) {
191
- console.log('\n✓ grimoire Core installed');
192
-
193
- // Count components
194
- const countFiles = (dir) => {
195
- try {
196
- return fs.readdirSync(dir).length;
197
- } catch {
198
- return 0;
199
- }
200
- };
201
-
202
- console.log(` - Agents: ${countFiles(path.join(grimoireCoreDir, 'development', 'agents'))}`);
203
- console.log(` - Tasks: ${countFiles(path.join(grimoireCoreDir, 'development', 'tasks'))}`);
204
- console.log(` - Templates: ${countFiles(path.join(grimoireCoreDir, 'development', 'templates'))}`);
205
- console.log(` - Workflows: ${countFiles(path.join(grimoireCoreDir, 'development', 'workflows'))}`);
206
- } else {
207
- console.log('\n⚠️ grimoire Core not found');
208
- }
209
-
210
- // Check grimoire Pro status (Task 5.1)
211
- const proDir = path.join(__dirname, '..', 'pro');
212
- if (fs.existsSync(proDir)) {
213
- console.log('\n✓ grimoire Pro installed');
214
-
215
- try {
216
- const { featureGate } = require(path.join(proDir, 'license', 'feature-gate'));
217
- const state = featureGate.getLicenseState();
218
- const info = featureGate.getLicenseInfo();
219
-
220
- const stateEmoji = {
221
- 'Active': '✅',
222
- 'Grace': '⚠️',
223
- 'Expired': '❌',
224
- 'Not Activated': '➖',
225
- };
226
-
227
- console.log(` - License: ${stateEmoji[state] || ''} ${state}`);
228
-
229
- if (info && info.features) {
230
- const availableCount = featureGate.listAvailable().length;
231
- console.log(` - Features: ${availableCount} available`);
232
- }
233
- } catch {
234
- console.log(' - License: Unable to check status');
235
- }
236
- }
237
- }
238
-
239
- // Helper: Run installation validation
240
- async function runValidate() {
241
- const validateArgs = args.slice(1); // Remove 'validate' from args
242
-
243
- try {
244
- // Load the validate command module
245
- const { createValidateCommand } = require('../.grimoire/cli/commands/validate/index.js');
246
- const validateCmd = createValidateCommand();
247
-
248
- // Parse and execute (Note: don't include 'validate' as it's the command name, not an argument)
249
- await validateCmd.parseAsync(['node', 'grimoire', ...validateArgs]);
250
- } catch (_error) {
251
- // Fallback: Run quick validation inline
252
- console.log('Running installation validation...\n');
253
-
254
- try {
255
- const validatorPath = path.join(
256
- __dirname,
257
- '..',
258
- 'packages',
259
- 'installer',
260
- 'src',
261
- 'installer',
262
- 'post-install-validator.js',
263
- );
264
- const { PostInstallValidator, formatReport } = require(validatorPath);
265
-
266
- const projectRoot = process.cwd();
267
- const validator = new PostInstallValidator(projectRoot, path.join(__dirname, '..'));
268
- const report = await validator.validate();
269
-
270
- console.log(formatReport(report, { colors: true }));
271
-
272
- if (
273
- report.status === 'failed' ||
274
- report.stats.missingFiles > 0 ||
275
- report.stats.corruptedFiles > 0
276
- ) {
277
- process.exit(1);
278
- }
279
- } catch (validatorError) {
280
- console.error(`❌ Validation error: ${validatorError.message}`);
281
- if (args.includes('--verbose') || args.includes('-v')) {
282
- console.error(validatorError.stack);
283
- }
284
- process.exit(2);
285
- }
286
- }
287
- }
288
-
289
- // Helper: Run update command
290
- async function runUpdate() {
291
- const updateArgs = args.slice(1);
292
- const isCheck = updateArgs.includes('--check');
293
- const isDryRun = updateArgs.includes('--dry-run');
294
- const isForce = updateArgs.includes('--force');
295
- const isVerbose = updateArgs.includes('--verbose') || updateArgs.includes('-v');
296
-
297
- try {
298
- const updaterPath = path.join(__dirname, '..', 'packages', 'installer', 'src', 'updater', 'index.js');
299
-
300
- if (!fs.existsSync(updaterPath)) {
301
- console.error('❌ Updater module not found');
302
- console.error('Please ensure grimoire-FullStack is installed correctly.');
303
- process.exit(1);
304
- }
305
-
306
- const { grimoireUpdater, formatCheckResult, formatUpdateResult } = require(updaterPath);
307
-
308
- const updater = new grimoireUpdater(process.cwd(), {
309
- verbose: isVerbose,
310
- force: isForce,
311
- });
312
-
313
- if (isCheck) {
314
- // Check only mode
315
- console.log('🔍 Checking for updates...\n');
316
- const result = await updater.checkForUpdates();
317
- console.log(formatCheckResult(result, { colors: true }));
318
-
319
- if (result.status === 'check_failed') {
320
- process.exit(1);
321
- }
322
- } else {
323
- // Update mode
324
- console.log('🔄 grimoire Update\n');
325
-
326
- const result = await updater.update({
327
- dryRun: isDryRun,
328
- onProgress: (phase, message) => {
329
- if (isVerbose) {
330
- console.log(`[${phase}] ${message}`);
331
- }
332
- },
333
- });
334
-
335
- console.log(formatUpdateResult(result, { colors: true }));
336
-
337
- if (!result.success && result.error !== 'Already up to date') {
338
- process.exit(1);
339
- }
340
- }
341
- } catch (error) {
342
- console.error(`❌ Update error: ${error.message}`);
343
- if (args.includes('--verbose') || args.includes('-v')) {
344
- console.error(error.stack);
345
- }
346
- process.exit(1);
347
- }
348
- }
349
-
350
- // Helper: Run doctor diagnostics
351
- async function runDoctor() {
352
- const doctorArgs = args.slice(1);
353
- const shouldFix = doctorArgs.includes('--fix');
354
- const isDryRun = doctorArgs.includes('--dry-run');
355
- const showHelp = doctorArgs.includes('--help') || doctorArgs.includes('-h');
356
-
357
- if (showHelp) {
358
- console.log(`
359
- Usage: grimoire doctor [options]
360
-
361
- Run system diagnostics and optionally fix issues.
362
-
363
- Options:
364
- --fix Attempt to automatically fix detected issues
365
- --dry-run Show what would be fixed without making changes
366
- -h, --help Show this help message
367
-
368
- Examples:
369
- $ npx grimoire-framework doctor # Run diagnostics
370
- $ npx grimoire-framework doctor --fix # Fix detected issues
371
- $ npx grimoire-framework doctor --dry-run # Preview fixes
372
- `);
373
- return;
374
- }
375
-
376
- console.log('🏥 grimoire System Diagnostics\n');
377
-
378
- const issues = [];
379
- let hasErrors = false;
380
-
381
- // Helper: Compare semver versions
382
- const compareVersions = (a, b) => {
383
- const pa = a.split('.').map((n) => parseInt(n, 10));
384
- const pb = b.split('.').map((n) => parseInt(n, 10));
385
- for (let i = 0; i < 3; i++) {
386
- const na = pa[i] || 0;
387
- const nb = pb[i] || 0;
388
- if (na > nb) return 1;
389
- if (na < nb) return -1;
390
- }
391
- return 0;
392
- };
393
-
394
- // Check 1: Node.js version
395
- const nodeVersion = process.version.replace('v', '');
396
- const requiredNodeVersion = '18.0.0';
397
- const nodeOk = compareVersions(nodeVersion, requiredNodeVersion) >= 0;
398
-
399
- if (!nodeOk) {
400
- issues.push({
401
- type: 'node_version',
402
- autoFix: false,
403
- message: `Node.js version: ${process.version} (requires >=18.0.0)`,
404
- suggestion: 'nvm install 20 && nvm use 20',
405
- });
406
- hasErrors = true;
407
- }
408
- console.log(
409
- `${nodeOk ? '✔' : '✗'} Node.js version: ${process.version} ${nodeOk ? '(meets requirement: >=18.0.0)' : '(requires >=18.0.0)'}`,
410
- );
411
-
412
- // Check 2: npm
413
- try {
414
- const npmVersion = execSync('npm --version', { encoding: 'utf8' }).trim();
415
- console.log(`✔ npm version: ${npmVersion}`);
416
- } catch {
417
- issues.push({
418
- type: 'npm',
419
- autoFix: false,
420
- message: 'npm not found',
421
- suggestion: 'Install Node.js from https://nodejs.org (includes npm)',
422
- });
423
- console.log('✗ npm not found');
424
- hasErrors = true;
425
- }
426
-
427
- // Check 3: Git
428
- try {
429
- const gitVersion = execSync('git --version', { encoding: 'utf8' }).trim();
430
- console.log(`✔ Git installed: ${gitVersion}`);
431
- } catch {
432
- issues.push({
433
- type: 'git',
434
- autoFix: false,
435
- message: 'Git not found (optional but recommended)',
436
- suggestion: 'Install Git from https://git-scm.com',
437
- });
438
- console.log('⚠️ Git not found (optional but recommended)');
439
- }
440
-
441
- // Check 4: grimoire installation
442
- const grimoireCoreDir = path.join(__dirname, '..', '.grimoire');
443
- if (fs.existsSync(grimoireCoreDir)) {
444
- console.log(`✔ Grimoire: v${packageJson.version}`);
445
-
446
- // Check for corruption using validate (if available)
447
- try {
448
- const validatorPath = path.join(__dirname, '..', 'packages', 'installer', 'src', 'installer', 'post-install-validator');
449
- const { PostInstallValidator } = require(validatorPath);
450
- const validator = new PostInstallValidator(process.cwd(), path.join(__dirname, '..'));
451
- const report = await validator.validate();
452
-
453
- if (report.stats && (report.stats.missingFiles > 0 || report.stats.corruptedFiles > 0)) {
454
- issues.push({
455
- type: 'grimoire_corrupted',
456
- autoFix: true,
457
- message: `grimoire Core: ${report.stats.missingFiles} missing, ${report.stats.corruptedFiles} corrupted files`,
458
- fixAction: async () => {
459
- console.log(' 🔧 Repairing grimoire installation...');
460
- await validator.repair();
461
- console.log(' ✓ Repair complete');
462
- },
463
- });
464
- hasErrors = true;
465
- console.log(`⚠️ grimoire Core: ${report.stats.missingFiles} missing, ${report.stats.corruptedFiles} corrupted files`);
466
- }
467
- } catch {
468
- // Validation not available, skip corruption check
469
- }
470
- } else {
471
- issues.push({
472
- type: 'grimoire_missing',
473
- autoFix: true,
474
- message: 'grimoire Core not installed',
475
- fixAction: async () => {
476
- console.log(' 🔧 Installing grimoire...');
477
- try {
478
- execSync('npx grimoire-framework install --force --quiet', { stdio: 'inherit', timeout: 60000 });
479
- console.log(' ✓ Installation complete');
480
- } catch (installError) {
481
- console.error(` ✗ Installation failed: ${installError.message}`);
482
- throw installError;
483
- }
484
- },
485
- });
486
- hasErrors = true;
487
- console.log('✗ grimoire Core not installed');
488
- console.log(' Run: npx grimoire-framework');
489
- }
490
-
491
- // Check 5: grimoire Pro status
492
- const proDetector = require('./utils/pro-detector');
493
- if (proDetector.isProAvailable()) {
494
- console.log('✔ grimoire Pro: Active (Full Version)');
495
- }
496
-
497
- // Apply fixes if --fix
498
- if (shouldFix && issues.length > 0) {
499
- console.log('\n🔧 Attempting fixes...\n');
500
-
501
- let fixed = 0;
502
- let manual = 0;
503
-
504
- for (const issue of issues) {
505
- if (issue.autoFix && issue.fixAction) {
506
- if (isDryRun) {
507
- console.log(` [DRY RUN] Would fix: ${issue.type}`);
508
- fixed++;
509
- } else {
510
- try {
511
- await issue.fixAction();
512
- fixed++;
513
- } catch (fixError) {
514
- console.error(` ✗ Failed to fix ${issue.type}: ${fixError.message}`);
515
- manual++;
516
- }
517
- }
518
- } else {
519
- manual++;
520
- console.log(` ⚠️ ${issue.message}`);
521
- console.log(` 💡 Fix: ${issue.suggestion}`);
522
- }
523
- }
524
-
525
- console.log('');
526
- if (isDryRun) {
527
- console.log(`✅ Dry run completed - ${fixed} issues would be fixed`);
528
- if (manual > 0) {
529
- console.log(`⚠️ ${manual} issues require manual action`);
530
- }
531
- } else {
532
- if (fixed > 0) {
533
- console.log(`✅ Fixed ${fixed} issue${fixed > 1 ? 's' : ''}`);
534
- }
535
- if (manual > 0) {
536
- console.log(`⚠️ ${manual} issue${manual > 1 ? 's' : ''} require manual action`);
537
- process.exit(1);
538
- }
539
- }
540
- } else {
541
- // Summary (no --fix)
542
- console.log('');
543
- if (hasErrors) {
544
- console.log('⚠️ Some issues were detected. Run with --fix to auto-repair.');
545
- process.exit(1);
546
- } else {
547
- console.log('✅ All checks passed! Your installation is healthy.');
548
- }
549
- }
550
- }
551
-
552
- // Helper: Format bytes to human readable
553
- function formatBytes(bytes) {
554
- if (bytes === 0) return '0 Bytes';
555
- const k = 1024;
556
- const sizes = ['Bytes', 'KB', 'MB', 'GB'];
557
- const i = Math.floor(Math.log(bytes) / Math.log(k));
558
- return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
559
- }
560
-
561
- // Helper: Remove grimoire sections from .gitignore
562
- function cleanGitignore(gitignorePath) {
563
- if (!fs.existsSync(gitignorePath)) return { removed: false };
564
-
565
- const content = fs.readFileSync(gitignorePath, 'utf8');
566
- const lines = content.split('\n');
567
- const newLines = [];
568
- let ingrimoireSection = false;
569
- let removedLines = 0;
570
-
571
- for (const line of lines) {
572
- if (line.includes('# grimoire') || line.includes('# Added by grimoire')) {
573
- ingrimoireSection = true;
574
- removedLines++;
575
- continue;
576
- }
577
- if (ingrimoireSection && line.trim() === '') {
578
- ingrimoireSection = false;
579
- continue;
580
- }
581
- if (ingrimoireSection) {
582
- removedLines++;
583
- continue;
584
- }
585
- newLines.push(line);
586
- }
587
-
588
- if (removedLines > 0) {
589
- fs.writeFileSync(gitignorePath, newLines.join('\n'));
590
- return { removed: true, lines: removedLines };
591
- }
592
- return { removed: false };
593
- }
594
-
595
- // Helper: Show uninstall help
596
- function showUninstallHelp() {
597
- console.log(`
598
- Usage: npx grimoire-framework uninstall [options]
599
-
600
- Remove grimoire from the current project.
601
-
602
- Options:
603
- --force Skip confirmation prompt
604
- --keep-data Keep .grimoire/ directory (settings and history)
605
- --dry-run Show what would be removed without removing
606
- -h, --help Show this help message
607
-
608
- What gets removed:
609
- - .grimoire/ Framework core files
610
- - docs/stories/ Story files (if created by grimoire)
611
- - squads/ Squad definitions
612
- - .gitignore grimoire-added entries only
613
-
614
- What is preserved (with --keep-data):
615
- - .grimoire/ Project settings and agent history
616
-
617
- Exit Codes:
618
- 0 Uninstall successful
619
- 1 Uninstall failed or cancelled
620
-
621
- Examples:
622
- # Interactive uninstall (with confirmation)
623
- npx grimoire-framework uninstall
624
-
625
- # Force uninstall without prompts
626
- npx grimoire-framework uninstall --force
627
-
628
- # See what would be removed
629
- npx grimoire-framework uninstall --dry-run
630
-
631
- # Uninstall but keep project data
632
- npx grimoire-framework uninstall --keep-data
633
- `);
634
- }
635
-
636
- // Helper: Show doctor help
637
- function showDoctorHelp() {
638
- console.log(`
639
- Usage: npx grimoire-framework doctor [options]
640
-
641
- Run health checks on your grimoire installation.
642
-
643
- Options:
644
- --fix Automatically fix detected issues
645
- --dry-run Show what --fix would do without making changes
646
- --quiet Minimal output (exit code only)
647
- -h, --help Show this help message
648
-
649
- Checks performed:
650
- • Required directories exist (.grimoire/, .grimoire/)
651
- • Configuration files are valid JSON/YAML
652
- • Agent definitions are complete
653
- • Task files have required fields
654
- • Dependencies are installed
655
-
656
- Exit Codes:
657
- 0 All checks passed (or issues fixed with --fix)
658
- 1 Issues detected (run with --fix to repair)
659
-
660
- Examples:
661
- # Run health check
662
- npx grimoire-framework doctor
663
-
664
- # Auto-fix detected issues
665
- npx grimoire-framework doctor --fix
666
-
667
- # Preview what would be fixed
668
- npx grimoire-framework doctor --fix --dry-run
669
- `);
670
- }
671
-
672
- // Uninstall grimoire from project
673
- async function runUninstall(options = {}) {
674
- const { force = false, keepData = false, dryRun = false, quiet = false } = options;
675
- const cwd = process.cwd();
676
-
677
- // Items to remove
678
- const itemsToRemove = [
679
- { path: '.grimoire', description: 'Framework core' },
680
- { path: 'squads', description: 'Squad definitions' },
681
- ];
682
-
683
- // Optionally remove .grimoire
684
- if (!keepData) {
685
- itemsToRemove.push({ path: '.grimoire', description: 'Project data and settings' });
686
- }
687
-
688
- // Check what exists
689
- const existingItems = itemsToRemove.filter(item =>
690
- fs.existsSync(path.join(cwd, item.path)),
691
- );
692
-
693
- if (existingItems.length === 0) {
694
- console.log('ℹ️ No grimoire installation found in this directory.');
695
- return;
696
- }
697
-
698
- // Calculate total size
699
- let totalSize = 0;
700
- const itemSizes = [];
701
-
702
- for (const item of existingItems) {
703
- const itemPath = path.join(cwd, item.path);
704
- try {
705
- const stats = fs.statSync(itemPath);
706
- if (stats.isDirectory()) {
707
- // Simple recursive size calculation
708
- const getSize = (dir) => {
709
- let size = 0;
710
- try {
711
- const files = fs.readdirSync(dir);
712
- for (const file of files) {
713
- const filePath = path.join(dir, file);
714
- const stat = fs.statSync(filePath);
715
- if (stat.isDirectory()) {
716
- size += getSize(filePath);
717
- } else {
718
- size += stat.size;
719
- }
720
- }
721
- } catch { /* ignore errors */ }
722
- return size;
723
- };
724
- const size = getSize(itemPath);
725
- totalSize += size;
726
- itemSizes.push({ ...item, size });
727
- } else {
728
- totalSize += stats.size;
729
- itemSizes.push({ ...item, size: stats.size });
730
- }
731
- } catch {
732
- itemSizes.push({ ...item, size: 0 });
733
- }
734
- }
735
-
736
- // Show what will be removed
737
- if (!quiet) {
738
- console.log('\n📋 Items to be removed:\n');
739
- for (const item of itemSizes) {
740
- const sizeStr = item.size > 0 ? ` (${formatBytes(item.size)})` : '';
741
- console.log(` • ${item.path}/${sizeStr} - ${item.description}`);
742
- }
743
- console.log(`\n Total: ${formatBytes(totalSize)}`);
744
-
745
- // Check for .gitignore cleanup
746
- const gitignorePath = path.join(cwd, '.gitignore');
747
- if (fs.existsSync(gitignorePath)) {
748
- const content = fs.readFileSync(gitignorePath, 'utf8');
749
- if (content.includes('# grimoire') || content.includes('# Added by grimoire')) {
750
- console.log(' • .gitignore grimoire entries will be cleaned');
751
- }
752
- }
753
- console.log('');
754
- }
755
-
756
- // Dry run - stop here
757
- if (dryRun) {
758
- console.log('🔍 Dry run - no changes made.');
759
- return;
760
- }
761
-
762
- // Confirmation
763
- if (!force) {
764
- const readline = require('readline');
765
- const rl = readline.createInterface({
766
- input: process.stdin,
767
- output: process.stdout,
768
- });
769
-
770
- const answer = await new Promise(resolve => {
771
- rl.question('⚠️ Are you sure you want to uninstall grimoire? (y/N): ', resolve);
772
- });
773
- rl.close();
774
-
775
- if (answer.toLowerCase() !== 'y' && answer.toLowerCase() !== 'yes') {
776
- console.log('❌ Uninstall cancelled.');
777
- process.exit(1);
778
- }
779
- }
780
-
781
- // Perform removal
782
- if (!quiet) console.log('\n🗑️ Removing grimoire components...\n');
783
-
784
- for (const item of existingItems) {
785
- const itemPath = path.join(cwd, item.path);
786
- try {
787
- fs.rmSync(itemPath, { recursive: true, force: true });
788
- if (!quiet) console.log(` ✓ Removed ${item.path}/`);
789
- } catch (error) {
790
- console.error(` ✗ Failed to remove ${item.path}: ${error.message}`);
791
- }
792
- }
793
-
794
- // Clean .gitignore
795
- const gitignorePath = path.join(cwd, '.gitignore');
796
- const gitignoreResult = cleanGitignore(gitignorePath);
797
- if (gitignoreResult.removed && !quiet) {
798
- console.log(` ✓ Cleaned ${gitignoreResult.lines} grimoire entries from .gitignore`);
799
- }
800
-
801
- // Summary
802
- if (!quiet) {
803
- console.log('\n✅ grimoire has been uninstalled.');
804
- if (keepData) {
805
- console.log(' Your project data in .grimoire/ has been preserved.');
806
- }
807
- console.log('\n To reinstall: npx grimoire-framework install');
808
- }
809
- }
810
-
811
- // Helper: Show install help
812
- function showInstallHelp() {
813
- console.log(`
814
- Usage: npx grimoire-framework install [options]
815
-
816
- Install grimoire in the current directory.
817
-
818
- Options:
819
- --force Overwrite existing grimoire installation
820
- --quiet Minimal output (no banner, no prompts) - ideal for CI/CD
821
- --dry-run Simulate installation without modifying files
822
- --merge Auto-merge existing config files (brownfield mode)
823
- --no-merge Disable merge option, use legacy overwrite behavior
824
- -h, --help Show this help message
825
-
826
- Smart Merge (Brownfield):
827
- When installing in a project with existing config files (.env, CLAUDE.md),
828
- grimoire can merge new settings while preserving your customizations.
829
-
830
- - .env files: Adds new variables, preserves existing values
831
- - CLAUDE.md: Updates grimoire sections, keeps your custom rules
832
-
833
- Exit Codes:
834
- 0 Installation successful
835
- 1 Installation failed
836
-
837
- Examples:
838
- # Interactive installation
839
- npx grimoire-framework install
840
-
841
- # Force reinstall without prompts
842
- npx grimoire-framework install --force
843
-
844
- # Brownfield: merge configs automatically
845
- npx grimoire-framework install --merge
846
-
847
- # Silent install for CI/CD
848
- npx grimoire-framework install --quiet --force
849
-
850
- # Preview what would be installed
851
- npx grimoire-framework install --dry-run
852
- `);
853
- }
854
-
855
- // Helper: Create new project
856
- // Helper: Show init help
857
- function showInitHelp() {
858
- console.log(`
859
- Usage: npx grimoire-framework init <project-name> [options]
860
-
861
- Create a new grimoire project with the specified name.
862
-
863
- Options:
864
- --force Force creation in non-empty directory
865
- --skip-install Skip npm dependency installation
866
- --template <name> Use specific template (default: default)
867
- -t <name> Shorthand for --template
868
- -h, --help Show this help message
869
-
870
- Available Templates:
871
- default Full installation with all agents, tasks, and workflows
872
- minimal Essential files only (dev agent + basic tasks)
873
- enterprise Everything + dashboards + team integrations
874
-
875
- Examples:
876
- npx grimoire-framework init my-project
877
- npx grimoire-framework init my-project --template minimal
878
- npx grimoire-framework init my-project --force --skip-install
879
- npx grimoire-framework init . --template enterprise
880
- `);
881
- }
882
-
883
- async function initProject() {
884
- // 1. Parse ALL args after 'init'
885
- const initArgs = args.slice(1);
886
-
887
- // 2. Handle --help FIRST (before creating any directories)
888
- if (initArgs.includes('--help') || initArgs.includes('-h')) {
889
- showInitHelp();
890
- return;
891
- }
892
-
893
- // 3. Parse flags
894
- const isForce = initArgs.includes('--force');
895
- const skipInstall = initArgs.includes('--skip-install');
896
-
897
- // Template with argument
898
- const templateIndex = initArgs.findIndex((a) => a === '--template' || a === '-t');
899
- let template = 'default';
900
- if (templateIndex !== -1) {
901
- template = initArgs[templateIndex + 1];
902
- if (!template || template.startsWith('-')) {
903
- console.error('❌ --template requires a template name');
904
- console.error('Available templates: default, minimal, enterprise');
905
- process.exit(1);
906
- }
907
- }
908
-
909
- // Validate template
910
- const validTemplates = ['default', 'minimal', 'enterprise'];
911
- if (!validTemplates.includes(template)) {
912
- console.error(`❌ Unknown template: ${template}`);
913
- console.error(`Available templates: ${validTemplates.join(', ')}`);
914
- process.exit(1);
915
- }
916
-
917
- // 4. Extract project name (anything that doesn't start with - and isn't a template value)
918
- const projectName = initArgs.find((arg, i) => {
919
- if (arg.startsWith('-')) return false;
920
- // Skip if it's the value after --template
921
- const prevArg = initArgs[i - 1];
922
- if (prevArg === '--template' || prevArg === '-t') return false;
923
- return true;
924
- });
925
-
926
- if (!projectName) {
927
- console.error('❌ Project name is required');
928
- console.log('\nUsage: npx grimoire-framework init <project-name> [options]');
929
- console.log('Run with --help for more information.');
930
- process.exit(1);
931
- }
932
-
933
- // 5. Handle "." to install in current directory
934
- const isCurrentDir = projectName === '.';
935
- const targetPath = isCurrentDir ? process.cwd() : path.join(process.cwd(), projectName);
936
- const displayName = isCurrentDir ? path.basename(process.cwd()) : projectName;
937
-
938
- // 6. Check if directory exists
939
- if (fs.existsSync(targetPath) && !isCurrentDir) {
940
- const contents = fs.readdirSync(targetPath).filter((f) => !f.startsWith('.'));
941
- if (contents.length > 0 && !isForce) {
942
- console.error(`❌ Directory already exists and is not empty: ${projectName}`);
943
- console.error('Use --force to overwrite.');
944
- process.exit(1);
945
- }
946
- if (contents.length > 0 && isForce) {
947
- console.log(`⚠️ Using --force: overwriting existing directory: ${projectName}`);
948
- } else {
949
- console.log(`✓ Using existing empty directory: ${projectName}`);
950
- }
951
- } else if (!fs.existsSync(targetPath)) {
952
- fs.mkdirSync(targetPath, { recursive: true });
953
- console.log(`✓ Created directory: ${projectName}`);
954
- }
955
-
956
- console.log(`Creating new grimoire project: ${displayName}`);
957
- if (template !== 'default') {
958
- console.log(`Template: ${template}`);
959
- }
960
- if (skipInstall) {
961
- console.log('Skip install: enabled');
962
- }
963
- console.log('');
964
-
965
- // 7. Change to project directory (if not already there)
966
- if (!isCurrentDir) {
967
- process.chdir(targetPath);
968
- }
969
-
970
- // 8. Run the initialization wizard with options
971
- await runWizard({
972
- template,
973
- skipInstall,
974
- force: isForce,
975
- });
976
- }
977
-
978
- // Command routing (async main function)
979
- async function main() {
980
- switch (command) {
981
- case 'workers':
982
- // Service Discovery CLI - Story 2.7
983
- try {
984
- const { run } = require('../.grimoire/cli/index.js');
985
- await run(process.argv);
986
- } catch (error) {
987
- console.error(`❌ Workers command error: ${error.message}`);
988
- process.exit(1);
989
- }
990
- break;
991
-
992
- case 'config':
993
- // Layered Configuration CLI - Story PRO-4
994
- try {
995
- const { run } = require('../.grimoire/cli/index.js');
996
- await run(process.argv);
997
- } catch (error) {
998
- console.error(`❌ Config command error: ${error.message}`);
999
- process.exit(1);
1000
- }
1001
- break;
1002
-
1003
- case 'pro':
1004
- // grimoire Pro License Management - Story PRO-6
1005
- try {
1006
- const { run } = require('../.grimoire/cli/index.js');
1007
- await run(process.argv);
1008
- } catch (error) {
1009
- console.error(`❌ Pro command error: ${error.message}`);
1010
- process.exit(1);
1011
- }
1012
- break;
1013
-
1014
- case 'install': {
1015
- // Install in current project with flag support
1016
- const installArgs = args.slice(1);
1017
- if (installArgs.includes('--help') || installArgs.includes('-h')) {
1018
- showInstallHelp();
1019
- break;
1020
- }
1021
- const installOptions = {
1022
- force: installArgs.includes('--force'),
1023
- quiet: installArgs.includes('--quiet'),
1024
- dryRun: installArgs.includes('--dry-run'),
1025
- forceMerge: installArgs.includes('--merge'),
1026
- noMerge: installArgs.includes('--no-merge'),
1027
- };
1028
- if (!installOptions.quiet) {
1029
- console.log('grimoire-FullStack Installation\n');
1030
- }
1031
- await runWizard(installOptions);
1032
- break;
1033
- }
1034
-
1035
- case 'uninstall': {
1036
- // Uninstall grimoire from project
1037
- const uninstallArgs = args.slice(1);
1038
- if (uninstallArgs.includes('--help') || uninstallArgs.includes('-h')) {
1039
- showUninstallHelp();
1040
- break;
1041
- }
1042
- const uninstallOptions = {
1043
- force: uninstallArgs.includes('--force'),
1044
- keepData: uninstallArgs.includes('--keep-data'),
1045
- dryRun: uninstallArgs.includes('--dry-run'),
1046
- quiet: uninstallArgs.includes('--quiet'),
1047
- };
1048
- await runUninstall(uninstallOptions);
1049
- break;
1050
- }
1051
-
1052
- case 'init': {
1053
- // Create new project (flags parsed inside initProject)
1054
- await initProject();
1055
- break;
1056
- }
1057
-
1058
- case 'info':
1059
- showInfo();
1060
- break;
1061
-
1062
- case 'doctor': {
1063
- // Run health check with flag support
1064
- const doctorArgs = args.slice(1);
1065
- if (doctorArgs.includes('--help') || doctorArgs.includes('-h')) {
1066
- showDoctorHelp();
1067
- break;
1068
- }
1069
- const doctorOptions = {
1070
- fix: doctorArgs.includes('--fix'),
1071
- dryRun: doctorArgs.includes('--dry-run'),
1072
- quiet: doctorArgs.includes('--quiet'),
1073
- };
1074
- await runDoctor(doctorOptions);
1075
- break;
1076
- }
1077
-
1078
- case 'validate':
1079
- // Post-installation validation - Story 6.19
1080
- await runValidate();
1081
- break;
1082
-
1083
- case 'update':
1084
- // Update to latest version - Epic 7
1085
- await runUpdate();
1086
- break;
1087
-
1088
- case '--version':
1089
- case '-v':
1090
- case '-V':
1091
- await showVersion();
1092
- break;
1093
-
1094
- case '--help':
1095
- case '-h':
1096
- showHelp();
1097
- break;
1098
-
1099
- case undefined:
1100
- // No arguments - run wizard directly (npx default behavior)
1101
- console.log('grimoire-FullStack Installation\n');
1102
- await runWizard();
1103
- break;
1104
-
1105
- default:
1106
- console.error(`❌ Unknown command: ${command}`);
1107
- console.log('\nRun with --help to see available commands');
1108
- process.exit(1);
1109
- }
1110
- }
1111
-
1112
- // Execute main function
1113
- main().catch((error) => {
1114
- console.error('❌ Fatal error:', error.message);
1115
- process.exit(1);
1116
- });
1117
-
1118
-
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * grimoire-FullStack CLI
5
+ * Main entry point - Standalone (no external dependencies for npx compatibility)
6
+ * Version: 4.0.0
7
+ */
8
+
9
+ const path = require('path');
10
+ const fs = require('fs');
11
+ const { execSync } = require('child_process');
12
+
13
+ // Read package.json for version
14
+ const packageJsonPath = path.join(__dirname, '..', 'package.json');
15
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
16
+
17
+ // Parse arguments
18
+ const args = process.argv.slice(2);
19
+ const command = args[0];
20
+
21
+ // Helper: Run initialization wizard
22
+ async function runWizard(options = {}) {
23
+ // Use the v4 wizard from packages/installer/src/wizard/index.js
24
+ const wizardPath = path.join(__dirname, '..', 'packages', 'installer', 'src', 'wizard', 'index.js');
25
+
26
+ if (!fs.existsSync(wizardPath)) {
27
+ // Fallback to legacy wizard if new wizard not found
28
+ const legacyScript = path.join(__dirname, 'grimoire-init.js');
29
+ if (fs.existsSync(legacyScript)) {
30
+ if (!options.quiet) {
31
+ console.log('⚠️ Using legacy wizard (src/wizard not found)');
32
+ }
33
+ // Legacy wizard doesn't support options, pass via env vars
34
+ process.env.grimoire_INSTALL_FORCE = options.force ? '1' : '';
35
+ process.env.grimoire_INSTALL_QUIET = options.quiet ? '1' : '';
36
+ process.env.grimoire_INSTALL_DRY_RUN = options.dryRun ? '1' : '';
37
+ require(legacyScript);
38
+ return;
39
+ }
40
+ console.error('❌ Initialization wizard not found');
41
+ console.error('Please ensure grimoire-FullStack is installed correctly.');
42
+ process.exit(1);
43
+ }
44
+
45
+ try {
46
+ // Run the v4 wizard with options
47
+ const { runWizard: executeWizard } = require(wizardPath);
48
+ await executeWizard(options);
49
+ } catch (error) {
50
+ console.error('❌ Wizard error:', error.message);
51
+ process.exit(1);
52
+ }
53
+ }
54
+
55
+ // Helper: Show help
56
+ function showHelp() {
57
+ console.log(`
58
+ grimoire-FullStack v${packageJson.version}
59
+ AI-Orchestrated System for Full Stack Development
60
+
61
+ USAGE:
62
+ npx grimoire-framework # Run installation wizard
63
+ npx grimoire-framework install # Install in current project
64
+ npx grimoire-framework init <name> # Create new project
65
+ npx grimoire-framework update # Update to latest version
66
+ npx grimoire-framework validate # Validate installation integrity
67
+ npx grimoire-framework info # Show system info
68
+ npx grimoire-framework doctor # Run diagnostics
69
+ npx grimoire-framework --version # Show version
70
+ npx grimoire-framework --version -d # Show detailed version info
71
+ npx grimoire-framework --help # Show this help
72
+
73
+ UPDATE:
74
+ grimoire-framework update # Update to latest version
75
+ grimoire-framework update --check # Check for updates without applying
76
+ grimoire-framework update --dry-run # Preview what would be updated
77
+ grimoire-framework update --force # Force update even if up-to-date
78
+ grimoire-framework update --verbose # Show detailed output
79
+
80
+ VALIDATION:
81
+ grimoire-framework validate # Validate installation integrity
82
+ grimoire-framework validate --repair # Repair missing/corrupted files
83
+ grimoire-framework validate --repair --dry-run # Preview repairs
84
+ grimoire-framework validate --detailed # Show detailed file list
85
+
86
+ CONFIGURATION:
87
+ grimoire-framework config show # Show resolved configuration
88
+ grimoire-framework config show --debug # Show with source annotations
89
+ grimoire-framework config diff --levels L1,L2 # Compare config levels
90
+ grimoire-framework config migrate # Migrate monolithic to layered
91
+ grimoire-framework config validate # Validate config files
92
+ grimoire-framework config init-local # Create local-config.yaml
93
+
94
+ SERVICE DISCOVERY:
95
+ grimoire-framework workers search <query> # Search for workers
96
+ grimoire-framework workers search "json" --category=data
97
+ grimoire-framework workers search "transform" --tags=etl,data
98
+ grimoire-framework workers search "api" --format=json
99
+
100
+ EXAMPLES:
101
+ # Install in current directory
102
+ npx grimoire-framework
103
+
104
+ # Install with minimal mode (only expansion-creator)
105
+ npx grimoire-minimal
106
+
107
+ # Create new project
108
+ npx grimoire-framework init my-project
109
+
110
+ # Search for workers
111
+ grimoire-framework workers search "json csv"
112
+
113
+ For more information, visit: https://github.com/grimoireAI/grimoire
114
+ `);
115
+ }
116
+
117
+ // Helper: Show version
118
+ async function showVersion() {
119
+ const isDetailed = args.includes('--detailed') || args.includes('-d');
120
+
121
+ if (!isDetailed) {
122
+ // Simple version output (backwards compatible)
123
+ console.log(packageJson.version);
124
+ return;
125
+ }
126
+
127
+ // Detailed version output (Story 7.2: Version Tracking)
128
+ console.log(`grimoire-FullStack v${packageJson.version}`);
129
+ console.log('Package: grimoire');
130
+
131
+ // Check for local installation
132
+ const localVersionPath = path.join(process.cwd(), '.grimoire', 'version.json');
133
+
134
+ if (fs.existsSync(localVersionPath)) {
135
+ try {
136
+ const versionInfo = JSON.parse(fs.readFileSync(localVersionPath, 'utf8'));
137
+ console.log('\n📦 Local Installation:');
138
+ console.log(` Version: ${versionInfo.version}`);
139
+ console.log(` Mode: ${versionInfo.mode || 'unknown'}`);
140
+
141
+ if (versionInfo.installedAt) {
142
+ const installedDate = new Date(versionInfo.installedAt);
143
+ console.log(` Installed: ${installedDate.toLocaleDateString()}`);
144
+ }
145
+
146
+ if (versionInfo.updatedAt) {
147
+ const updatedDate = new Date(versionInfo.updatedAt);
148
+ console.log(` Updated: ${updatedDate.toLocaleDateString()}`);
149
+ }
150
+
151
+ if (versionInfo.fileHashes) {
152
+ const fileCount = Object.keys(versionInfo.fileHashes).length;
153
+ console.log(` Files: ${fileCount} tracked`);
154
+ }
155
+
156
+ if (versionInfo.customized && versionInfo.customized.length > 0) {
157
+ console.log(` Customized: ${versionInfo.customized.length} files`);
158
+ }
159
+
160
+ // Version comparison
161
+ if (versionInfo.version !== packageJson.version) {
162
+ console.log('\n⚠️ Version mismatch!');
163
+ console.log(` Local: ${versionInfo.version}`);
164
+ console.log(` Latest: ${packageJson.version}`);
165
+ console.log(' Run \'npx grimoire-framework update\' to update.');
166
+ } else {
167
+ console.log('\n✅ Up to date');
168
+ }
169
+ } catch (error) {
170
+ console.log(`\n⚠️ Could not read version.json: ${error.message}`);
171
+ }
172
+ } else {
173
+ console.log('\n📭 No local installation found');
174
+ console.log(' Run \'npx grimoire-framework install\' to install grimoire in this project.');
175
+ }
176
+ }
177
+
178
+ // Helper: Show system info
179
+ function showInfo() {
180
+ console.log('📊 grimoire-FullStack System Information\n');
181
+ console.log(`Version: ${packageJson.version}`);
182
+ console.log(`Platform: ${process.platform}`);
183
+ console.log(`Node.js: ${process.version}`);
184
+ console.log(`Architecture: ${process.arch}`);
185
+ console.log(`Working Directory: ${process.cwd()}`);
186
+ console.log(`Install Location: ${path.join(__dirname, '..')}`);
187
+
188
+ // Check if .grimoire exists
189
+ const grimoireCoreDir = path.join(__dirname, '..', '.grimoire');
190
+ if (fs.existsSync(grimoireCoreDir)) {
191
+ console.log('\n✓ grimoire Core installed');
192
+
193
+ // Count components
194
+ const countFiles = (dir) => {
195
+ try {
196
+ return fs.readdirSync(dir).length;
197
+ } catch {
198
+ return 0;
199
+ }
200
+ };
201
+
202
+ console.log(` - Agents: ${countFiles(path.join(grimoireCoreDir, 'development', 'agents'))}`);
203
+ console.log(` - Tasks: ${countFiles(path.join(grimoireCoreDir, 'development', 'tasks'))}`);
204
+ console.log(` - Templates: ${countFiles(path.join(grimoireCoreDir, 'development', 'templates'))}`);
205
+ console.log(` - Workflows: ${countFiles(path.join(grimoireCoreDir, 'development', 'workflows'))}`);
206
+ } else {
207
+ console.log('\n⚠️ grimoire Core not found');
208
+ }
209
+
210
+ // Check grimoire Pro status (Task 5.1)
211
+ const proDir = path.join(__dirname, '..', 'pro');
212
+ if (fs.existsSync(proDir)) {
213
+ console.log('\n✓ grimoire Pro installed');
214
+
215
+ try {
216
+ const { featureGate } = require(path.join(proDir, 'license', 'feature-gate'));
217
+ const state = featureGate.getLicenseState();
218
+ const info = featureGate.getLicenseInfo();
219
+
220
+ const stateEmoji = {
221
+ 'Active': '✅',
222
+ 'Grace': '⚠️',
223
+ 'Expired': '❌',
224
+ 'Not Activated': '➖',
225
+ };
226
+
227
+ console.log(` - License: ${stateEmoji[state] || ''} ${state}`);
228
+
229
+ if (info && info.features) {
230
+ const availableCount = featureGate.listAvailable().length;
231
+ console.log(` - Features: ${availableCount} available`);
232
+ }
233
+ } catch {
234
+ console.log(' - License: Unable to check status');
235
+ }
236
+ }
237
+ }
238
+
239
+ // Helper: Run installation validation
240
+ async function runValidate() {
241
+ const validateArgs = args.slice(1); // Remove 'validate' from args
242
+
243
+ try {
244
+ // Load the validate command module
245
+ const { createValidateCommand } = require('../.grimoire/cli/commands/validate/index.js');
246
+ const validateCmd = createValidateCommand();
247
+
248
+ // Parse and execute (Note: don't include 'validate' as it's the command name, not an argument)
249
+ await validateCmd.parseAsync(['node', 'grimoire', ...validateArgs]);
250
+ } catch (_error) {
251
+ // Fallback: Run quick validation inline
252
+ console.log('Running installation validation...\n');
253
+
254
+ try {
255
+ const validatorPath = path.join(
256
+ __dirname,
257
+ '..',
258
+ 'packages',
259
+ 'installer',
260
+ 'src',
261
+ 'installer',
262
+ 'post-install-validator.js',
263
+ );
264
+ const { PostInstallValidator, formatReport } = require(validatorPath);
265
+
266
+ const projectRoot = process.cwd();
267
+ const validator = new PostInstallValidator(projectRoot, path.join(__dirname, '..'));
268
+ const report = await validator.validate();
269
+
270
+ console.log(formatReport(report, { colors: true }));
271
+
272
+ if (
273
+ report.status === 'failed' ||
274
+ report.stats.missingFiles > 0 ||
275
+ report.stats.corruptedFiles > 0
276
+ ) {
277
+ process.exit(1);
278
+ }
279
+ } catch (validatorError) {
280
+ console.error(`❌ Validation error: ${validatorError.message}`);
281
+ if (args.includes('--verbose') || args.includes('-v')) {
282
+ console.error(validatorError.stack);
283
+ }
284
+ process.exit(2);
285
+ }
286
+ }
287
+ }
288
+
289
+ // Helper: Run update command
290
+ async function runUpdate() {
291
+ const updateArgs = args.slice(1);
292
+ const isCheck = updateArgs.includes('--check');
293
+ const isDryRun = updateArgs.includes('--dry-run');
294
+ const isForce = updateArgs.includes('--force');
295
+ const isVerbose = updateArgs.includes('--verbose') || updateArgs.includes('-v');
296
+
297
+ try {
298
+ const updaterPath = path.join(__dirname, '..', 'packages', 'installer', 'src', 'updater', 'index.js');
299
+
300
+ if (!fs.existsSync(updaterPath)) {
301
+ console.error('❌ Updater module not found');
302
+ console.error('Please ensure grimoire-FullStack is installed correctly.');
303
+ process.exit(1);
304
+ }
305
+
306
+ const { grimoireUpdater, formatCheckResult, formatUpdateResult } = require(updaterPath);
307
+
308
+ const updater = new grimoireUpdater(process.cwd(), {
309
+ verbose: isVerbose,
310
+ force: isForce,
311
+ });
312
+
313
+ if (isCheck) {
314
+ // Check only mode
315
+ console.log('🔍 Checking for updates...\n');
316
+ const result = await updater.checkForUpdates();
317
+ console.log(formatCheckResult(result, { colors: true }));
318
+
319
+ if (result.status === 'check_failed') {
320
+ process.exit(1);
321
+ }
322
+ } else {
323
+ // Update mode
324
+ console.log('🔄 grimoire Update\n');
325
+
326
+ const result = await updater.update({
327
+ dryRun: isDryRun,
328
+ onProgress: (phase, message) => {
329
+ if (isVerbose) {
330
+ console.log(`[${phase}] ${message}`);
331
+ }
332
+ },
333
+ });
334
+
335
+ console.log(formatUpdateResult(result, { colors: true }));
336
+
337
+ if (!result.success && result.error !== 'Already up to date') {
338
+ process.exit(1);
339
+ }
340
+ }
341
+ } catch (error) {
342
+ console.error(`❌ Update error: ${error.message}`);
343
+ if (args.includes('--verbose') || args.includes('-v')) {
344
+ console.error(error.stack);
345
+ }
346
+ process.exit(1);
347
+ }
348
+ }
349
+
350
+ // Helper: Run doctor diagnostics
351
+ async function runDoctor() {
352
+ const doctorArgs = args.slice(1);
353
+ const shouldFix = doctorArgs.includes('--fix');
354
+ const isDryRun = doctorArgs.includes('--dry-run');
355
+ const showHelp = doctorArgs.includes('--help') || doctorArgs.includes('-h');
356
+
357
+ if (showHelp) {
358
+ console.log(`
359
+ Usage: grimoire doctor [options]
360
+
361
+ Run system diagnostics and optionally fix issues.
362
+
363
+ Options:
364
+ --fix Attempt to automatically fix detected issues
365
+ --dry-run Show what would be fixed without making changes
366
+ -h, --help Show this help message
367
+
368
+ Examples:
369
+ $ npx grimoire-framework doctor # Run diagnostics
370
+ $ npx grimoire-framework doctor --fix # Fix detected issues
371
+ $ npx grimoire-framework doctor --dry-run # Preview fixes
372
+ `);
373
+ return;
374
+ }
375
+
376
+ console.log('🏥 grimoire System Diagnostics\n');
377
+
378
+ const issues = [];
379
+ let hasErrors = false;
380
+
381
+ // Helper: Compare semver versions
382
+ const compareVersions = (a, b) => {
383
+ const pa = a.split('.').map((n) => parseInt(n, 10));
384
+ const pb = b.split('.').map((n) => parseInt(n, 10));
385
+ for (let i = 0; i < 3; i++) {
386
+ const na = pa[i] || 0;
387
+ const nb = pb[i] || 0;
388
+ if (na > nb) return 1;
389
+ if (na < nb) return -1;
390
+ }
391
+ return 0;
392
+ };
393
+
394
+ // Check 1: Node.js version
395
+ const nodeVersion = process.version.replace('v', '');
396
+ const requiredNodeVersion = '18.0.0';
397
+ const nodeOk = compareVersions(nodeVersion, requiredNodeVersion) >= 0;
398
+
399
+ if (!nodeOk) {
400
+ issues.push({
401
+ type: 'node_version',
402
+ autoFix: false,
403
+ message: `Node.js version: ${process.version} (requires >=18.0.0)`,
404
+ suggestion: 'nvm install 20 && nvm use 20',
405
+ });
406
+ hasErrors = true;
407
+ }
408
+ console.log(
409
+ `${nodeOk ? '✔' : '✗'} Node.js version: ${process.version} ${nodeOk ? '(meets requirement: >=18.0.0)' : '(requires >=18.0.0)'}`,
410
+ );
411
+
412
+ // Check 2: npm
413
+ try {
414
+ const npmVersion = execSync('npm --version', { encoding: 'utf8' }).trim();
415
+ console.log(`✔ npm version: ${npmVersion}`);
416
+ } catch {
417
+ issues.push({
418
+ type: 'npm',
419
+ autoFix: false,
420
+ message: 'npm not found',
421
+ suggestion: 'Install Node.js from https://nodejs.org (includes npm)',
422
+ });
423
+ console.log('✗ npm not found');
424
+ hasErrors = true;
425
+ }
426
+
427
+ // Check 3: Git
428
+ try {
429
+ const gitVersion = execSync('git --version', { encoding: 'utf8' }).trim();
430
+ console.log(`✔ Git installed: ${gitVersion}`);
431
+ } catch {
432
+ issues.push({
433
+ type: 'git',
434
+ autoFix: false,
435
+ message: 'Git not found (optional but recommended)',
436
+ suggestion: 'Install Git from https://git-scm.com',
437
+ });
438
+ console.log('⚠️ Git not found (optional but recommended)');
439
+ }
440
+
441
+ // Check 4: grimoire installation
442
+ const grimoireCoreDir = path.join(__dirname, '..', '.grimoire');
443
+ if (fs.existsSync(grimoireCoreDir)) {
444
+ console.log(`✔ Grimoire: v${packageJson.version}`);
445
+
446
+ // Check for corruption using validate (if available)
447
+ try {
448
+ const validatorPath = path.join(__dirname, '..', 'packages', 'installer', 'src', 'installer', 'post-install-validator');
449
+ const { PostInstallValidator } = require(validatorPath);
450
+ const validator = new PostInstallValidator(process.cwd(), path.join(__dirname, '..'));
451
+ const report = await validator.validate();
452
+
453
+ if (report.stats && (report.stats.missingFiles > 0 || report.stats.corruptedFiles > 0)) {
454
+ issues.push({
455
+ type: 'grimoire_corrupted',
456
+ autoFix: true,
457
+ message: `grimoire Core: ${report.stats.missingFiles} missing, ${report.stats.corruptedFiles} corrupted files`,
458
+ fixAction: async () => {
459
+ console.log(' 🔧 Repairing grimoire installation...');
460
+ await validator.repair();
461
+ console.log(' ✓ Repair complete');
462
+ },
463
+ });
464
+ hasErrors = true;
465
+ console.log(`⚠️ grimoire Core: ${report.stats.missingFiles} missing, ${report.stats.corruptedFiles} corrupted files`);
466
+ }
467
+ } catch {
468
+ // Validation not available, skip corruption check
469
+ }
470
+ } else {
471
+ issues.push({
472
+ type: 'grimoire_missing',
473
+ autoFix: true,
474
+ message: 'grimoire Core not installed',
475
+ fixAction: async () => {
476
+ console.log(' 🔧 Installing grimoire...');
477
+ try {
478
+ execSync('npx grimoire-framework install --force --quiet', { stdio: 'inherit', timeout: 60000 });
479
+ console.log(' ✓ Installation complete');
480
+ } catch (installError) {
481
+ console.error(` ✗ Installation failed: ${installError.message}`);
482
+ throw installError;
483
+ }
484
+ },
485
+ });
486
+ hasErrors = true;
487
+ console.log('✗ grimoire Core not installed');
488
+ console.log(' Run: npx grimoire-framework');
489
+ }
490
+
491
+ // Check 5: grimoire Pro status
492
+ const proDetector = require('./utils/pro-detector');
493
+ if (proDetector.isProAvailable()) {
494
+ console.log('✔ grimoire Pro: Active (Full Version)');
495
+ }
496
+
497
+ // Apply fixes if --fix
498
+ if (shouldFix && issues.length > 0) {
499
+ console.log('\n🔧 Attempting fixes...\n');
500
+
501
+ let fixed = 0;
502
+ let manual = 0;
503
+
504
+ for (const issue of issues) {
505
+ if (issue.autoFix && issue.fixAction) {
506
+ if (isDryRun) {
507
+ console.log(` [DRY RUN] Would fix: ${issue.type}`);
508
+ fixed++;
509
+ } else {
510
+ try {
511
+ await issue.fixAction();
512
+ fixed++;
513
+ } catch (fixError) {
514
+ console.error(` ✗ Failed to fix ${issue.type}: ${fixError.message}`);
515
+ manual++;
516
+ }
517
+ }
518
+ } else {
519
+ manual++;
520
+ console.log(` ⚠️ ${issue.message}`);
521
+ console.log(` 💡 Fix: ${issue.suggestion}`);
522
+ }
523
+ }
524
+
525
+ console.log('');
526
+ if (isDryRun) {
527
+ console.log(`✅ Dry run completed - ${fixed} issues would be fixed`);
528
+ if (manual > 0) {
529
+ console.log(`⚠️ ${manual} issues require manual action`);
530
+ }
531
+ } else {
532
+ if (fixed > 0) {
533
+ console.log(`✅ Fixed ${fixed} issue${fixed > 1 ? 's' : ''}`);
534
+ }
535
+ if (manual > 0) {
536
+ console.log(`⚠️ ${manual} issue${manual > 1 ? 's' : ''} require manual action`);
537
+ process.exit(1);
538
+ }
539
+ }
540
+ } else {
541
+ // Summary (no --fix)
542
+ console.log('');
543
+ if (hasErrors) {
544
+ console.log('⚠️ Some issues were detected. Run with --fix to auto-repair.');
545
+ process.exit(1);
546
+ } else {
547
+ console.log('✅ All checks passed! Your installation is healthy.');
548
+ }
549
+ }
550
+ }
551
+
552
+ // Helper: Format bytes to human readable
553
+ function formatBytes(bytes) {
554
+ if (bytes === 0) return '0 Bytes';
555
+ const k = 1024;
556
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
557
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
558
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
559
+ }
560
+
561
+ // Helper: Remove grimoire sections from .gitignore
562
+ function cleanGitignore(gitignorePath) {
563
+ if (!fs.existsSync(gitignorePath)) return { removed: false };
564
+
565
+ const content = fs.readFileSync(gitignorePath, 'utf8');
566
+ const lines = content.split('\n');
567
+ const newLines = [];
568
+ let ingrimoireSection = false;
569
+ let removedLines = 0;
570
+
571
+ for (const line of lines) {
572
+ if (line.includes('# grimoire') || line.includes('# Added by grimoire')) {
573
+ ingrimoireSection = true;
574
+ removedLines++;
575
+ continue;
576
+ }
577
+ if (ingrimoireSection && line.trim() === '') {
578
+ ingrimoireSection = false;
579
+ continue;
580
+ }
581
+ if (ingrimoireSection) {
582
+ removedLines++;
583
+ continue;
584
+ }
585
+ newLines.push(line);
586
+ }
587
+
588
+ if (removedLines > 0) {
589
+ fs.writeFileSync(gitignorePath, newLines.join('\n'));
590
+ return { removed: true, lines: removedLines };
591
+ }
592
+ return { removed: false };
593
+ }
594
+
595
+ // Helper: Show uninstall help
596
+ function showUninstallHelp() {
597
+ console.log(`
598
+ Usage: npx grimoire-framework uninstall [options]
599
+
600
+ Remove grimoire from the current project.
601
+
602
+ Options:
603
+ --force Skip confirmation prompt
604
+ --keep-data Keep .grimoire/ directory (settings and history)
605
+ --dry-run Show what would be removed without removing
606
+ -h, --help Show this help message
607
+
608
+ What gets removed:
609
+ - .grimoire/ Framework core files
610
+ - docs/stories/ Story files (if created by grimoire)
611
+ - squads/ Squad definitions
612
+ - .gitignore grimoire-added entries only
613
+
614
+ What is preserved (with --keep-data):
615
+ - .grimoire/ Project settings and agent history
616
+
617
+ Exit Codes:
618
+ 0 Uninstall successful
619
+ 1 Uninstall failed or cancelled
620
+
621
+ Examples:
622
+ # Interactive uninstall (with confirmation)
623
+ npx grimoire-framework uninstall
624
+
625
+ # Force uninstall without prompts
626
+ npx grimoire-framework uninstall --force
627
+
628
+ # See what would be removed
629
+ npx grimoire-framework uninstall --dry-run
630
+
631
+ # Uninstall but keep project data
632
+ npx grimoire-framework uninstall --keep-data
633
+ `);
634
+ }
635
+
636
+ // Helper: Show doctor help
637
+ function showDoctorHelp() {
638
+ console.log(`
639
+ Usage: npx grimoire-framework doctor [options]
640
+
641
+ Run health checks on your grimoire installation.
642
+
643
+ Options:
644
+ --fix Automatically fix detected issues
645
+ --dry-run Show what --fix would do without making changes
646
+ --quiet Minimal output (exit code only)
647
+ -h, --help Show this help message
648
+
649
+ Checks performed:
650
+ • Required directories exist (.grimoire/, .grimoire/)
651
+ • Configuration files are valid JSON/YAML
652
+ • Agent definitions are complete
653
+ • Task files have required fields
654
+ • Dependencies are installed
655
+
656
+ Exit Codes:
657
+ 0 All checks passed (or issues fixed with --fix)
658
+ 1 Issues detected (run with --fix to repair)
659
+
660
+ Examples:
661
+ # Run health check
662
+ npx grimoire-framework doctor
663
+
664
+ # Auto-fix detected issues
665
+ npx grimoire-framework doctor --fix
666
+
667
+ # Preview what would be fixed
668
+ npx grimoire-framework doctor --fix --dry-run
669
+ `);
670
+ }
671
+
672
+ // Uninstall grimoire from project
673
+ async function runUninstall(options = {}) {
674
+ const { force = false, keepData = false, dryRun = false, quiet = false } = options;
675
+ const cwd = process.cwd();
676
+
677
+ // Items to remove
678
+ const itemsToRemove = [
679
+ { path: '.grimoire', description: 'Framework core' },
680
+ { path: 'squads', description: 'Squad definitions' },
681
+ ];
682
+
683
+ // Optionally remove .grimoire
684
+ if (!keepData) {
685
+ itemsToRemove.push({ path: '.grimoire', description: 'Project data and settings' });
686
+ }
687
+
688
+ // Check what exists
689
+ const existingItems = itemsToRemove.filter(item =>
690
+ fs.existsSync(path.join(cwd, item.path)),
691
+ );
692
+
693
+ if (existingItems.length === 0) {
694
+ console.log('ℹ️ No grimoire installation found in this directory.');
695
+ return;
696
+ }
697
+
698
+ // Calculate total size
699
+ let totalSize = 0;
700
+ const itemSizes = [];
701
+
702
+ for (const item of existingItems) {
703
+ const itemPath = path.join(cwd, item.path);
704
+ try {
705
+ const stats = fs.statSync(itemPath);
706
+ if (stats.isDirectory()) {
707
+ // Simple recursive size calculation
708
+ const getSize = (dir) => {
709
+ let size = 0;
710
+ try {
711
+ const files = fs.readdirSync(dir);
712
+ for (const file of files) {
713
+ const filePath = path.join(dir, file);
714
+ const stat = fs.statSync(filePath);
715
+ if (stat.isDirectory()) {
716
+ size += getSize(filePath);
717
+ } else {
718
+ size += stat.size;
719
+ }
720
+ }
721
+ } catch { /* ignore errors */ }
722
+ return size;
723
+ };
724
+ const size = getSize(itemPath);
725
+ totalSize += size;
726
+ itemSizes.push({ ...item, size });
727
+ } else {
728
+ totalSize += stats.size;
729
+ itemSizes.push({ ...item, size: stats.size });
730
+ }
731
+ } catch {
732
+ itemSizes.push({ ...item, size: 0 });
733
+ }
734
+ }
735
+
736
+ // Show what will be removed
737
+ if (!quiet) {
738
+ console.log('\n📋 Items to be removed:\n');
739
+ for (const item of itemSizes) {
740
+ const sizeStr = item.size > 0 ? ` (${formatBytes(item.size)})` : '';
741
+ console.log(` • ${item.path}/${sizeStr} - ${item.description}`);
742
+ }
743
+ console.log(`\n Total: ${formatBytes(totalSize)}`);
744
+
745
+ // Check for .gitignore cleanup
746
+ const gitignorePath = path.join(cwd, '.gitignore');
747
+ if (fs.existsSync(gitignorePath)) {
748
+ const content = fs.readFileSync(gitignorePath, 'utf8');
749
+ if (content.includes('# grimoire') || content.includes('# Added by grimoire')) {
750
+ console.log(' • .gitignore grimoire entries will be cleaned');
751
+ }
752
+ }
753
+ console.log('');
754
+ }
755
+
756
+ // Dry run - stop here
757
+ if (dryRun) {
758
+ console.log('🔍 Dry run - no changes made.');
759
+ return;
760
+ }
761
+
762
+ // Confirmation
763
+ if (!force) {
764
+ const readline = require('readline');
765
+ const rl = readline.createInterface({
766
+ input: process.stdin,
767
+ output: process.stdout,
768
+ });
769
+
770
+ const answer = await new Promise(resolve => {
771
+ rl.question('⚠️ Are you sure you want to uninstall grimoire? (y/N): ', resolve);
772
+ });
773
+ rl.close();
774
+
775
+ if (answer.toLowerCase() !== 'y' && answer.toLowerCase() !== 'yes') {
776
+ console.log('❌ Uninstall cancelled.');
777
+ process.exit(1);
778
+ }
779
+ }
780
+
781
+ // Perform removal
782
+ if (!quiet) console.log('\n🗑️ Removing grimoire components...\n');
783
+
784
+ for (const item of existingItems) {
785
+ const itemPath = path.join(cwd, item.path);
786
+ try {
787
+ fs.rmSync(itemPath, { recursive: true, force: true });
788
+ if (!quiet) console.log(` ✓ Removed ${item.path}/`);
789
+ } catch (error) {
790
+ console.error(` ✗ Failed to remove ${item.path}: ${error.message}`);
791
+ }
792
+ }
793
+
794
+ // Clean .gitignore
795
+ const gitignorePath = path.join(cwd, '.gitignore');
796
+ const gitignoreResult = cleanGitignore(gitignorePath);
797
+ if (gitignoreResult.removed && !quiet) {
798
+ console.log(` ✓ Cleaned ${gitignoreResult.lines} grimoire entries from .gitignore`);
799
+ }
800
+
801
+ // Summary
802
+ if (!quiet) {
803
+ console.log('\n✅ grimoire has been uninstalled.');
804
+ if (keepData) {
805
+ console.log(' Your project data in .grimoire/ has been preserved.');
806
+ }
807
+ console.log('\n To reinstall: npx grimoire-framework install');
808
+ }
809
+ }
810
+
811
+ // Helper: Show install help
812
+ function showInstallHelp() {
813
+ console.log(`
814
+ Usage: npx grimoire-framework install [options]
815
+
816
+ Install grimoire in the current directory.
817
+
818
+ Options:
819
+ --force Overwrite existing grimoire installation
820
+ --quiet Minimal output (no banner, no prompts) - ideal for CI/CD
821
+ --dry-run Simulate installation without modifying files
822
+ --merge Auto-merge existing config files (brownfield mode)
823
+ --no-merge Disable merge option, use legacy overwrite behavior
824
+ -h, --help Show this help message
825
+
826
+ Smart Merge (Brownfield):
827
+ When installing in a project with existing config files (.env, CLAUDE.md),
828
+ grimoire can merge new settings while preserving your customizations.
829
+
830
+ - .env files: Adds new variables, preserves existing values
831
+ - CLAUDE.md: Updates grimoire sections, keeps your custom rules
832
+
833
+ Exit Codes:
834
+ 0 Installation successful
835
+ 1 Installation failed
836
+
837
+ Examples:
838
+ # Interactive installation
839
+ npx grimoire-framework install
840
+
841
+ # Force reinstall without prompts
842
+ npx grimoire-framework install --force
843
+
844
+ # Brownfield: merge configs automatically
845
+ npx grimoire-framework install --merge
846
+
847
+ # Silent install for CI/CD
848
+ npx grimoire-framework install --quiet --force
849
+
850
+ # Preview what would be installed
851
+ npx grimoire-framework install --dry-run
852
+ `);
853
+ }
854
+
855
+ // Helper: Create new project
856
+ // Helper: Show init help
857
+ function showInitHelp() {
858
+ console.log(`
859
+ Usage: npx grimoire-framework init <project-name> [options]
860
+
861
+ Create a new grimoire project with the specified name.
862
+
863
+ Options:
864
+ --force Force creation in non-empty directory
865
+ --skip-install Skip npm dependency installation
866
+ --template <name> Use specific template (default: default)
867
+ -t <name> Shorthand for --template
868
+ -h, --help Show this help message
869
+
870
+ Available Templates:
871
+ default Full installation with all agents, tasks, and workflows
872
+ minimal Essential files only (dev agent + basic tasks)
873
+ enterprise Everything + dashboards + team integrations
874
+
875
+ Examples:
876
+ npx grimoire-framework init my-project
877
+ npx grimoire-framework init my-project --template minimal
878
+ npx grimoire-framework init my-project --force --skip-install
879
+ npx grimoire-framework init . --template enterprise
880
+ `);
881
+ }
882
+
883
+ async function initProject() {
884
+ // 1. Parse ALL args after 'init'
885
+ const initArgs = args.slice(1);
886
+
887
+ // 2. Handle --help FIRST (before creating any directories)
888
+ if (initArgs.includes('--help') || initArgs.includes('-h')) {
889
+ showInitHelp();
890
+ return;
891
+ }
892
+
893
+ // 3. Parse flags
894
+ const isForce = initArgs.includes('--force');
895
+ const skipInstall = initArgs.includes('--skip-install');
896
+
897
+ // Template with argument
898
+ const templateIndex = initArgs.findIndex((a) => a === '--template' || a === '-t');
899
+ let template = 'default';
900
+ if (templateIndex !== -1) {
901
+ template = initArgs[templateIndex + 1];
902
+ if (!template || template.startsWith('-')) {
903
+ console.error('❌ --template requires a template name');
904
+ console.error('Available templates: default, minimal, enterprise');
905
+ process.exit(1);
906
+ }
907
+ }
908
+
909
+ // Validate template
910
+ const validTemplates = ['default', 'minimal', 'enterprise'];
911
+ if (!validTemplates.includes(template)) {
912
+ console.error(`❌ Unknown template: ${template}`);
913
+ console.error(`Available templates: ${validTemplates.join(', ')}`);
914
+ process.exit(1);
915
+ }
916
+
917
+ // 4. Extract project name (anything that doesn't start with - and isn't a template value)
918
+ const projectName = initArgs.find((arg, i) => {
919
+ if (arg.startsWith('-')) return false;
920
+ // Skip if it's the value after --template
921
+ const prevArg = initArgs[i - 1];
922
+ if (prevArg === '--template' || prevArg === '-t') return false;
923
+ return true;
924
+ });
925
+
926
+ if (!projectName) {
927
+ console.error('❌ Project name is required');
928
+ console.log('\nUsage: npx grimoire-framework init <project-name> [options]');
929
+ console.log('Run with --help for more information.');
930
+ process.exit(1);
931
+ }
932
+
933
+ // 5. Handle "." to install in current directory
934
+ const isCurrentDir = projectName === '.';
935
+ const targetPath = isCurrentDir ? process.cwd() : path.join(process.cwd(), projectName);
936
+ const displayName = isCurrentDir ? path.basename(process.cwd()) : projectName;
937
+
938
+ // 6. Check if directory exists
939
+ if (fs.existsSync(targetPath) && !isCurrentDir) {
940
+ const contents = fs.readdirSync(targetPath).filter((f) => !f.startsWith('.'));
941
+ if (contents.length > 0 && !isForce) {
942
+ console.error(`❌ Directory already exists and is not empty: ${projectName}`);
943
+ console.error('Use --force to overwrite.');
944
+ process.exit(1);
945
+ }
946
+ if (contents.length > 0 && isForce) {
947
+ console.log(`⚠️ Using --force: overwriting existing directory: ${projectName}`);
948
+ } else {
949
+ console.log(`✓ Using existing empty directory: ${projectName}`);
950
+ }
951
+ } else if (!fs.existsSync(targetPath)) {
952
+ fs.mkdirSync(targetPath, { recursive: true });
953
+ console.log(`✓ Created directory: ${projectName}`);
954
+ }
955
+
956
+ console.log(`Creating new grimoire project: ${displayName}`);
957
+ if (template !== 'default') {
958
+ console.log(`Template: ${template}`);
959
+ }
960
+ if (skipInstall) {
961
+ console.log('Skip install: enabled');
962
+ }
963
+ console.log('');
964
+
965
+ // 7. Change to project directory (if not already there)
966
+ if (!isCurrentDir) {
967
+ process.chdir(targetPath);
968
+ }
969
+
970
+ // 8. Run the initialization wizard with options
971
+ await runWizard({
972
+ template,
973
+ skipInstall,
974
+ force: isForce,
975
+ });
976
+ }
977
+
978
+ // Command routing (async main function)
979
+ async function main() {
980
+ switch (command) {
981
+ case 'workers':
982
+ // Service Discovery CLI - Story 2.7
983
+ try {
984
+ const { run } = require('../.grimoire/cli/index.js');
985
+ await run(process.argv);
986
+ } catch (error) {
987
+ console.error(`❌ Workers command error: ${error.message}`);
988
+ process.exit(1);
989
+ }
990
+ break;
991
+
992
+ case 'config':
993
+ // Layered Configuration CLI - Story PRO-4
994
+ try {
995
+ const { run } = require('../.grimoire/cli/index.js');
996
+ await run(process.argv);
997
+ } catch (error) {
998
+ console.error(`❌ Config command error: ${error.message}`);
999
+ process.exit(1);
1000
+ }
1001
+ break;
1002
+
1003
+ case 'pro':
1004
+ // grimoire Pro License Management - Story PRO-6
1005
+ try {
1006
+ const { run } = require('../.grimoire/cli/index.js');
1007
+ await run(process.argv);
1008
+ } catch (error) {
1009
+ console.error(`❌ Pro command error: ${error.message}`);
1010
+ process.exit(1);
1011
+ }
1012
+ break;
1013
+
1014
+ case 'install': {
1015
+ // Install in current project with flag support
1016
+ const installArgs = args.slice(1);
1017
+ if (installArgs.includes('--help') || installArgs.includes('-h')) {
1018
+ showInstallHelp();
1019
+ break;
1020
+ }
1021
+ const installOptions = {
1022
+ force: installArgs.includes('--force'),
1023
+ quiet: installArgs.includes('--quiet'),
1024
+ dryRun: installArgs.includes('--dry-run'),
1025
+ forceMerge: installArgs.includes('--merge'),
1026
+ noMerge: installArgs.includes('--no-merge'),
1027
+ };
1028
+ if (!installOptions.quiet) {
1029
+ console.log('grimoire-FullStack Installation\n');
1030
+ }
1031
+ await runWizard(installOptions);
1032
+ break;
1033
+ }
1034
+
1035
+ case 'uninstall': {
1036
+ // Uninstall grimoire from project
1037
+ const uninstallArgs = args.slice(1);
1038
+ if (uninstallArgs.includes('--help') || uninstallArgs.includes('-h')) {
1039
+ showUninstallHelp();
1040
+ break;
1041
+ }
1042
+ const uninstallOptions = {
1043
+ force: uninstallArgs.includes('--force'),
1044
+ keepData: uninstallArgs.includes('--keep-data'),
1045
+ dryRun: uninstallArgs.includes('--dry-run'),
1046
+ quiet: uninstallArgs.includes('--quiet'),
1047
+ };
1048
+ await runUninstall(uninstallOptions);
1049
+ break;
1050
+ }
1051
+
1052
+ case 'init': {
1053
+ // Create new project (flags parsed inside initProject)
1054
+ await initProject();
1055
+ break;
1056
+ }
1057
+
1058
+ case 'info':
1059
+ showInfo();
1060
+ break;
1061
+
1062
+ case 'doctor': {
1063
+ // Run health check with flag support
1064
+ const doctorArgs = args.slice(1);
1065
+ if (doctorArgs.includes('--help') || doctorArgs.includes('-h')) {
1066
+ showDoctorHelp();
1067
+ break;
1068
+ }
1069
+ const doctorOptions = {
1070
+ fix: doctorArgs.includes('--fix'),
1071
+ dryRun: doctorArgs.includes('--dry-run'),
1072
+ quiet: doctorArgs.includes('--quiet'),
1073
+ };
1074
+ await runDoctor(doctorOptions);
1075
+ break;
1076
+ }
1077
+
1078
+ case 'validate':
1079
+ // Post-installation validation - Story 6.19
1080
+ await runValidate();
1081
+ break;
1082
+
1083
+ case 'update':
1084
+ // Update to latest version - Epic 7
1085
+ await runUpdate();
1086
+ break;
1087
+
1088
+ case '--version':
1089
+ case '-v':
1090
+ case '-V':
1091
+ await showVersion();
1092
+ break;
1093
+
1094
+ case '--help':
1095
+ case '-h':
1096
+ showHelp();
1097
+ break;
1098
+
1099
+ case undefined:
1100
+ // No arguments - run wizard directly (npx default behavior)
1101
+ console.log('grimoire-FullStack Installation\n');
1102
+ await runWizard();
1103
+ break;
1104
+
1105
+ default:
1106
+ console.error(`❌ Unknown command: ${command}`);
1107
+ console.log('\nRun with --help to see available commands');
1108
+ process.exit(1);
1109
+ }
1110
+ }
1111
+
1112
+ // Execute main function
1113
+ main().catch((error) => {
1114
+ console.error('❌ Fatal error:', error.message);
1115
+ process.exit(1);
1116
+ });
1117
+
1118
+