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.
- package/.grimoire/install-manifest.yaml +2 -2
- package/.grimoire/memory/README.md +13 -0
- package/.grimoire/memory/entities/context.md +19 -0
- package/.grimoire/memory/entities/decisions.md +17 -0
- package/.grimoire/memory/entities/patterns.md +20 -0
- package/.grimoire/memory/sessions/2026-02-22.json +9 -0
- package/.grimoire/memory/sessions/archived/2026-02-22.jsonl +3 -0
- package/bin/commands/memory.js +215 -0
- package/bin/commands/metrics.js +107 -0
- package/bin/grimoire-cli.js +103 -0
- package/bin/grimoire-ids.js +2 -2
- package/bin/grimoire-minimal.js +1 -1
- package/bin/grimoire.js +1118 -1118
- package/bin/modules/env-config.js +2 -2
- package/bin/modules/mcp-installer.js +1 -1
- package/bin/utils/install-errors.js +1 -1
- package/bin/utils/install-transaction.js +2 -2
- package/bin/utils/pro-detector.js +110 -110
- package/package.json +158 -158
- package/packages/gemini-grimoire-extension/commands/grimoire-agents.js +2 -2
- package/packages/gemini-grimoire-extension/commands/grimoire-master.js +1 -1
- package/packages/gemini-grimoire-extension/commands/grimoire-status.js +2 -2
- package/packages/gemini-grimoire-extension/commands/grimoire-validate.js +2 -2
- package/packages/gemini-grimoire-extension/commands/lib/agent-launcher.js +147 -147
- package/packages/gemini-grimoire-extension/extension.json +2 -2
- package/packages/gemini-grimoire-extension/gemini-extension.json +2 -2
- package/packages/gemini-grimoire-extension/hooks/hooks.json +2 -2
- package/packages/grimoire-install/.releaserc.json +1 -1
- package/packages/grimoire-install/CHANGELOG.md +1 -1
- package/packages/grimoire-install/README.md +2 -2
- package/packages/grimoire-install/bin/edmcp.js +1 -1
- package/packages/grimoire-install/bin/grimoire-install.js +1 -1
- package/packages/grimoire-install/jest.config.js +1 -1
- package/packages/grimoire-install/package.json +2 -2
- package/packages/grimoire-install/src/edmcp/index.js +1 -1
- package/packages/grimoire-install/src/installer.js +2 -2
- package/packages/grimoire-pro-cli/bin/grimoire-pro.js +2 -2
- package/packages/grimoire-pro-cli/package.json +2 -2
- package/packages/grimoire-pro-cli/src/recover.js +1 -1
- package/packages/installer/package.json +1 -1
- package/packages/installer/src/__tests__/performance-benchmark.js +2 -2
- package/packages/installer/src/config/configure-environment.js +2 -2
- package/packages/installer/src/config/ide-configs.js +2 -2
- package/packages/installer/src/config/templates/core-config-template.js +2 -2
- package/packages/installer/src/config/templates/env-template.js +2 -2
- package/packages/installer/src/config/validation/config-validator.js +1 -1
- package/packages/installer/src/detection/detect-project-type.js +2 -2
- package/packages/installer/src/installer/brownfield-upgrader.js +2 -2
- package/packages/installer/src/installer/grimoire-core-installer.js +2 -2
- package/packages/installer/src/installer/manifest-signature.js +2 -2
- package/packages/installer/src/installer/post-install-validator.js +2 -2
- package/packages/installer/src/installer/squad-installer.js +109 -0
- package/packages/installer/src/merger/index.js +1 -1
- package/packages/installer/src/merger/parsers/markdown-section-parser.js +1 -1
- package/packages/installer/src/merger/strategies/env-merger.js +1 -1
- package/packages/installer/src/merger/strategies/markdown-merger.js +1 -1
- package/packages/installer/src/merger/types.js +1 -1
- package/packages/installer/src/pro/pro-scaffolder.js +2 -2
- package/packages/installer/src/updater/index.js +2 -2
- package/packages/installer/src/utils/grimoire-colors.js +1 -1
- package/packages/installer/src/wizard/i18n.js +2 -2
- package/packages/installer/src/wizard/ide-config-generator.js +2 -2
- package/packages/installer/src/wizard/ide-selector.js +1 -1
- package/packages/installer/src/wizard/index.js +28 -36
- package/packages/installer/src/wizard/pro-setup.js +2 -2
- package/packages/installer/src/wizard/questions.js +20 -15
- package/packages/installer/src/wizard/validation/index.js +1 -1
- package/packages/installer/src/wizard/validation/report-generator.js +1 -1
- package/packages/installer/src/wizard/validation/troubleshooting-system.js +2 -2
- package/packages/installer/src/wizard/validation/validators/config-validator.js +2 -2
- package/packages/installer/src/wizard/validation/validators/file-structure-validator.js +1 -1
- package/packages/installer/src/wizard/wizard.js +2 -2
- package/packages/installer/tests/integration/environment-configuration.test.js +2 -2
- package/packages/installer/tests/integration/wizard-detection.test.js +2 -2
- package/packages/installer/tests/unit/config-validator.test.js +1 -1
- package/packages/installer/tests/unit/detection/detect-project-type.test.js +2 -2
- package/packages/installer/tests/unit/env-template.test.js +2 -2
- package/packages/installer/tests/unit/merger/env-merger.test.js +1 -1
- package/packages/installer/tests/unit/merger/markdown-merger.test.js +1 -1
- package/scripts/check-markdown-links.py +352 -352
- package/scripts/code-intel-health-check.js +1 -1
- package/scripts/dashboard-parallel-dev.sh +1 -1
- package/scripts/dashboard-parallel-phase3.sh +1 -1
- package/scripts/dashboard-parallel-phase4.sh +1 -1
- package/scripts/ensure-manifest.js +1 -1
- package/scripts/generate-install-manifest.js +2 -2
- package/scripts/install-monitor-hooks.sh +2 -2
- package/scripts/package-synapse.js +2 -2
- package/scripts/semantic-lint.js +1 -1
- package/scripts/sign-manifest.sh +2 -2
- package/scripts/validate-manifest.js +2 -2
- 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
|
+
|