cmp-standards 2.8.1 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analytics/CrossProjectAnalytics.d.ts +128 -0
- package/dist/analytics/CrossProjectAnalytics.d.ts.map +1 -0
- package/dist/analytics/CrossProjectAnalytics.js +434 -0
- package/dist/analytics/CrossProjectAnalytics.js.map +1 -0
- package/dist/analytics/index.d.ts +1 -0
- package/dist/analytics/index.d.ts.map +1 -1
- package/dist/analytics/index.js +2 -0
- package/dist/analytics/index.js.map +1 -1
- package/dist/cache/EmbeddingCache.d.ts +6 -4
- package/dist/cache/EmbeddingCache.d.ts.map +1 -1
- package/dist/cache/EmbeddingCache.js +28 -17
- package/dist/cache/EmbeddingCache.js.map +1 -1
- package/dist/cli/index.js +658 -141
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/ui.d.ts +134 -0
- package/dist/cli/ui.d.ts.map +1 -0
- package/dist/cli/ui.js +311 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/dashboard/tokens.d.ts +228 -0
- package/dist/dashboard/tokens.d.ts.map +1 -0
- package/dist/dashboard/tokens.js +450 -0
- package/dist/dashboard/tokens.js.map +1 -0
- package/dist/dashboard/ui.d.ts +3 -0
- package/dist/dashboard/ui.d.ts.map +1 -1
- package/dist/dashboard/ui.js +95 -61
- package/dist/dashboard/ui.js.map +1 -1
- package/dist/db/cloud.d.ts +11 -0
- package/dist/db/cloud.d.ts.map +1 -1
- package/dist/db/cloud.js +49 -1
- package/dist/db/cloud.js.map +1 -1
- package/dist/db/migrations.d.ts +1 -0
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +109 -0
- package/dist/db/migrations.js.map +1 -1
- package/dist/db/turso-client.d.ts.map +1 -1
- package/dist/db/turso-client.js +3 -0
- package/dist/db/turso-client.js.map +1 -1
- package/dist/events/EventBus.d.ts +21 -0
- package/dist/events/EventBus.d.ts.map +1 -1
- package/dist/events/EventBus.js +81 -30
- package/dist/events/EventBus.js.map +1 -1
- package/dist/events/index.d.ts +1 -1
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +1 -1
- package/dist/events/index.js.map +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/startup-verify.d.ts +31 -0
- package/dist/hooks/startup-verify.d.ts.map +1 -0
- package/dist/hooks/startup-verify.js +360 -0
- package/dist/hooks/startup-verify.js.map +1 -0
- package/dist/plugins/PluginManager.d.ts +160 -0
- package/dist/plugins/PluginManager.d.ts.map +1 -0
- package/dist/plugins/PluginManager.js +417 -0
- package/dist/plugins/PluginManager.js.map +1 -0
- package/dist/plugins/index.d.ts +7 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +7 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/schema/expert-types.d.ts +2 -2
- package/dist/services/AuditLog.d.ts +205 -0
- package/dist/services/AuditLog.d.ts.map +1 -0
- package/dist/services/AuditLog.js +352 -0
- package/dist/services/AuditLog.js.map +1 -0
- package/dist/services/FeedbackCollector.d.ts +8 -0
- package/dist/services/FeedbackCollector.d.ts.map +1 -1
- package/dist/services/FeedbackCollector.js +19 -2
- package/dist/services/FeedbackCollector.js.map +1 -1
- package/dist/services/GitIntegration.d.ts +140 -0
- package/dist/services/GitIntegration.d.ts.map +1 -0
- package/dist/services/GitIntegration.js +423 -0
- package/dist/services/GitIntegration.js.map +1 -0
- package/dist/services/HookVerifier.d.ts +95 -0
- package/dist/services/HookVerifier.d.ts.map +1 -0
- package/dist/services/HookVerifier.js +493 -0
- package/dist/services/HookVerifier.js.map +1 -0
- package/dist/services/MemoryRelationshipService.d.ts +187 -0
- package/dist/services/MemoryRelationshipService.d.ts.map +1 -0
- package/dist/services/MemoryRelationshipService.js +375 -0
- package/dist/services/MemoryRelationshipService.js.map +1 -0
- package/dist/services/MemoryVersioning.d.ts +108 -0
- package/dist/services/MemoryVersioning.d.ts.map +1 -0
- package/dist/services/MemoryVersioning.js +281 -0
- package/dist/services/MemoryVersioning.js.map +1 -0
- package/dist/services/context-injector.d.ts +8 -0
- package/dist/services/context-injector.d.ts.map +1 -1
- package/dist/services/context-injector.js +19 -2
- package/dist/services/context-injector.js.map +1 -1
- package/dist/services/index.d.ts +5 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +7 -0
- package/dist/services/index.js.map +1 -1
- package/dist/services/memory-router.d.ts +8 -0
- package/dist/services/memory-router.d.ts.map +1 -1
- package/dist/services/memory-router.js +19 -2
- package/dist/services/memory-router.js.map +1 -1
- package/dist/services/pattern-tracker.d.ts +13 -0
- package/dist/services/pattern-tracker.d.ts.map +1 -1
- package/dist/services/pattern-tracker.js +33 -3
- package/dist/services/pattern-tracker.js.map +1 -1
- package/dist/services/semantic-search.d.ts +12 -0
- package/dist/services/semantic-search.d.ts.map +1 -1
- package/dist/services/semantic-search.js +93 -17
- package/dist/services/semantic-search.js.map +1 -1
- package/dist/testing/index.d.ts +148 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +370 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/utils/resilience.d.ts +256 -0
- package/dist/utils/resilience.d.ts.map +1 -0
- package/dist/utils/resilience.js +499 -0
- package/dist/utils/resilience.js.map +1 -0
- package/package.json +12 -1
package/dist/cli/index.js
CHANGED
|
@@ -27,6 +27,7 @@ import { getProjectRoot, getHooksDir } from '../utils/paths.js';
|
|
|
27
27
|
import { RegistryGenerator } from '../registry/generator.js';
|
|
28
28
|
import { PatternDetector } from '../auto-improve/pattern-detector.js';
|
|
29
29
|
import { ESLintGenerator } from '../auto-improve/eslint-generator.js';
|
|
30
|
+
import { ui, withSpinner, runTasks } from './ui.js';
|
|
30
31
|
const program = new Command();
|
|
31
32
|
program
|
|
32
33
|
.name('cmp-memory')
|
|
@@ -106,10 +107,8 @@ program
|
|
|
106
107
|
.command('validate')
|
|
107
108
|
.description('Validate project structure against standards')
|
|
108
109
|
.action(async () => {
|
|
109
|
-
|
|
110
|
+
ui.header('Validating Project Structure');
|
|
110
111
|
const projectRoot = await getProjectRoot();
|
|
111
|
-
const issues = [];
|
|
112
|
-
const passed = [];
|
|
113
112
|
// Check required files
|
|
114
113
|
const requiredFiles = [
|
|
115
114
|
'CLAUDE.md',
|
|
@@ -118,64 +117,79 @@ program
|
|
|
118
117
|
'.claude/project.config.json',
|
|
119
118
|
'.ai-skills/registry.json',
|
|
120
119
|
];
|
|
121
|
-
for (const file of requiredFiles) {
|
|
122
|
-
const filePath = path.join(projectRoot, file);
|
|
123
|
-
try {
|
|
124
|
-
await fs.access(filePath);
|
|
125
|
-
passed.push(file);
|
|
126
|
-
}
|
|
127
|
-
catch {
|
|
128
|
-
issues.push(`MISSING: ${file}`);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
120
|
// Check directories
|
|
132
121
|
const requiredDirs = [
|
|
133
122
|
'.claude/agents',
|
|
134
123
|
'.claude/commands',
|
|
135
124
|
'.claude/hooks',
|
|
136
125
|
];
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
126
|
+
const fileTasks = requiredFiles.map(file => ({
|
|
127
|
+
name: `Check ${file}`,
|
|
128
|
+
task: async () => {
|
|
129
|
+
const filePath = path.join(projectRoot, file);
|
|
130
|
+
await fs.access(filePath);
|
|
131
|
+
},
|
|
132
|
+
}));
|
|
133
|
+
const dirTasks = requiredDirs.map(dir => ({
|
|
134
|
+
name: `Check ${dir}/`,
|
|
135
|
+
task: async () => {
|
|
136
|
+
const dirPath = path.join(projectRoot, dir);
|
|
140
137
|
const stat = await fs.stat(dirPath);
|
|
141
|
-
if (stat.isDirectory())
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
138
|
+
if (!stat.isDirectory())
|
|
139
|
+
throw new Error('Not a directory');
|
|
140
|
+
const files = await fs.readdir(dirPath);
|
|
141
|
+
if (files.length === 0)
|
|
142
|
+
throw new Error('Directory is empty');
|
|
143
|
+
},
|
|
144
|
+
}));
|
|
145
|
+
const expertTask = {
|
|
146
|
+
name: 'Check experts command',
|
|
147
|
+
task: async () => {
|
|
148
|
+
const expertsPath = path.join(projectRoot, '.claude/commands/experts.md');
|
|
149
|
+
await fs.access(expertsPath);
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
const hookVerifyTask = {
|
|
153
|
+
name: 'Verify hook configurations',
|
|
154
|
+
task: async () => {
|
|
155
|
+
const { HookVerifierService } = await import('../services/HookVerifier.js');
|
|
156
|
+
const verifier = new HookVerifierService(projectRoot);
|
|
157
|
+
const report = await verifier.verify(false);
|
|
158
|
+
if (!report.allValid) {
|
|
159
|
+
const issues = report.results.filter(r => r.status !== 'valid');
|
|
160
|
+
throw new Error(`${issues.length} hook(s) misconfigured. Run: cmp-standards verify-hooks --fix`);
|
|
149
161
|
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const expertsPath = path.join(projectRoot, '.claude/commands/experts.md');
|
|
157
|
-
try {
|
|
158
|
-
await fs.access(expertsPath);
|
|
159
|
-
passed.push('.claude/commands/experts.md');
|
|
160
|
-
}
|
|
161
|
-
catch {
|
|
162
|
-
issues.push('MISSING: .claude/commands/experts.md (run: cmp-memory sync --commands)');
|
|
163
|
-
}
|
|
164
|
-
// Summary
|
|
165
|
-
console.log(chalk.green('✓ Passed:'));
|
|
166
|
-
for (const p of passed) {
|
|
167
|
-
console.log(chalk.green(` ${p}`));
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
const results = await runTasks([...fileTasks, ...dirTasks, expertTask, hookVerifyTask], { stopOnError: false });
|
|
165
|
+
const failed = results.filter(r => r.status === 'error');
|
|
166
|
+
if (failed.length > 0) {
|
|
167
|
+
ui.warning('Run: cmp-standards init --system YOUR_SYSTEM --force');
|
|
168
168
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
169
|
+
});
|
|
170
|
+
// =============================================================================
|
|
171
|
+
// VERIFY-HOOKS COMMAND
|
|
172
|
+
// =============================================================================
|
|
173
|
+
program
|
|
174
|
+
.command('verify-hooks')
|
|
175
|
+
.description('Verify and fix hook configurations')
|
|
176
|
+
.option('--fix', 'Auto-fix hook issues where possible')
|
|
177
|
+
.option('-p, --project <path>', 'Project path (defaults to current directory)')
|
|
178
|
+
.option('--json', 'Output as JSON')
|
|
179
|
+
.action(async (options) => {
|
|
180
|
+
const { HookVerifierService, formatVerificationReport } = await import('../services/HookVerifier.js');
|
|
181
|
+
const projectPath = options.project ?? process.cwd();
|
|
182
|
+
const verifier = new HookVerifierService(projectPath);
|
|
183
|
+
const report = await verifier.verify(options.fix);
|
|
184
|
+
if (options.json) {
|
|
185
|
+
console.log(JSON.stringify(report, null, 2));
|
|
186
|
+
return;
|
|
175
187
|
}
|
|
176
|
-
|
|
177
|
-
|
|
188
|
+
console.log(formatVerificationReport(report));
|
|
189
|
+
if (!report.allValid && !options.fix) {
|
|
190
|
+
ui.warning('Run with --fix to auto-fix issues');
|
|
178
191
|
}
|
|
192
|
+
process.exit(report.allValid ? 0 : 1);
|
|
179
193
|
});
|
|
180
194
|
// =============================================================================
|
|
181
195
|
// SESSION-CONTEXT COMMAND (for hooks)
|
|
@@ -229,17 +243,21 @@ program
|
|
|
229
243
|
.option('-i, --incremental', 'Only update changed files')
|
|
230
244
|
.option('--files <files>', 'Specific files to process (comma-separated)')
|
|
231
245
|
.action(async (options) => {
|
|
232
|
-
|
|
246
|
+
ui.header('Generating Knowledge Registry');
|
|
233
247
|
const generator = new RegistryGenerator();
|
|
234
248
|
if (options.files) {
|
|
235
249
|
const files = options.files.split(',').map((f) => f.trim());
|
|
236
|
-
|
|
237
|
-
|
|
250
|
+
await withSpinner(`Processing ${files.length} files`, async () => {
|
|
251
|
+
await generator.update(files);
|
|
252
|
+
}, { successText: `Processed ${files.length} files` });
|
|
238
253
|
}
|
|
239
254
|
else {
|
|
240
|
-
await
|
|
255
|
+
await withSpinner('Generating registry', async (spinner) => {
|
|
256
|
+
spinner.text = 'Scanning documentation...';
|
|
257
|
+
await generator.generate();
|
|
258
|
+
}, { successText: 'Registry generated' });
|
|
241
259
|
}
|
|
242
|
-
|
|
260
|
+
ui.success('Registry generation complete!');
|
|
243
261
|
});
|
|
244
262
|
// =============================================================================
|
|
245
263
|
// SCAN COMMAND
|
|
@@ -250,28 +268,36 @@ program
|
|
|
250
268
|
.option('-d, --dir <directory>', 'Directory to scan', 'src')
|
|
251
269
|
.option('-t, --threshold <number>', 'Pattern threshold', '3')
|
|
252
270
|
.action(async (options) => {
|
|
253
|
-
|
|
271
|
+
ui.header('Scanning for Patterns');
|
|
254
272
|
const projectRoot = await getProjectRoot();
|
|
255
273
|
const config = await loadConfig(projectRoot);
|
|
256
274
|
const detector = new PatternDetector(config);
|
|
257
|
-
// Find TypeScript files
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
275
|
+
// Find TypeScript files with spinner
|
|
276
|
+
const files = await withSpinner('Finding TypeScript files', async () => {
|
|
277
|
+
const { glob } = await import('glob');
|
|
278
|
+
return glob(`${options.dir}/**/*.{ts,tsx}`, {
|
|
279
|
+
cwd: projectRoot,
|
|
280
|
+
absolute: true,
|
|
281
|
+
ignore: ['**/node_modules/**', '**/*.d.ts', '**/*.test.ts'],
|
|
282
|
+
});
|
|
283
|
+
}, { successText: `Found files to scan` });
|
|
284
|
+
// Scan files with spinner
|
|
285
|
+
await withSpinner(`Scanning ${files.length} files`, async (spinner) => {
|
|
286
|
+
for (let i = 0; i < files.length; i++) {
|
|
287
|
+
const file = files[i];
|
|
288
|
+
const content = await fs.readFile(file, 'utf-8');
|
|
289
|
+
detector.scan(content, file);
|
|
290
|
+
spinner.text = `Scanning files (${i + 1}/${files.length})`;
|
|
291
|
+
}
|
|
292
|
+
}, { successText: `Scanned ${files.length} files` });
|
|
269
293
|
const results = detector.getResults();
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
294
|
+
ui.subheader('Results');
|
|
295
|
+
ui.keyValue({
|
|
296
|
+
'Total patterns': results.total,
|
|
297
|
+
[`Triggered (≥${options.threshold})`]: results.triggered.length,
|
|
298
|
+
});
|
|
273
299
|
if (results.patterns.length > 0) {
|
|
274
|
-
|
|
300
|
+
ui.subheader('Detected Patterns');
|
|
275
301
|
for (const pattern of results.patterns) {
|
|
276
302
|
const icon = pattern.needsAutoImprove ? '🔴' : '⚪';
|
|
277
303
|
const severity = chalk.gray(`[${pattern.severity}]`);
|
|
@@ -280,11 +306,11 @@ program
|
|
|
280
306
|
}
|
|
281
307
|
}
|
|
282
308
|
if (results.triggered.length > 0) {
|
|
283
|
-
|
|
309
|
+
ui.warning('Patterns ready for auto-improvement:');
|
|
284
310
|
for (const pattern of results.triggered) {
|
|
285
311
|
console.log(` - ${pattern.patternId} (${pattern.count} occurrences)`);
|
|
286
312
|
}
|
|
287
|
-
|
|
313
|
+
ui.dim('\n Run: cmp-standards improve');
|
|
288
314
|
}
|
|
289
315
|
});
|
|
290
316
|
// =============================================================================
|
|
@@ -296,21 +322,27 @@ program
|
|
|
296
322
|
.option('--dry-run', 'Show what would be done without making changes')
|
|
297
323
|
.option('-p, --pattern <pattern>', 'Specific pattern to improve')
|
|
298
324
|
.action(async (options) => {
|
|
299
|
-
|
|
325
|
+
ui.header('Running Auto-Improvement');
|
|
300
326
|
const projectRoot = await getProjectRoot();
|
|
301
327
|
const config = await loadConfig(projectRoot);
|
|
302
|
-
// First scan for patterns
|
|
328
|
+
// First scan for patterns with spinner
|
|
303
329
|
const detector = new PatternDetector(config);
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
330
|
+
const files = await withSpinner('Finding files to analyze', async () => {
|
|
331
|
+
const { glob } = await import('glob');
|
|
332
|
+
return glob('src/**/*.{ts,tsx}', {
|
|
333
|
+
cwd: projectRoot,
|
|
334
|
+
absolute: true,
|
|
335
|
+
ignore: ['**/node_modules/**', '**/*.d.ts'],
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
await withSpinner('Scanning for patterns', async (spinner) => {
|
|
339
|
+
for (let i = 0; i < files.length; i++) {
|
|
340
|
+
const file = files[i];
|
|
341
|
+
const content = await fs.readFile(file, 'utf-8');
|
|
342
|
+
detector.scan(content, file);
|
|
343
|
+
spinner.text = `Scanning patterns (${i + 1}/${files.length})`;
|
|
344
|
+
}
|
|
309
345
|
});
|
|
310
|
-
for (const file of files) {
|
|
311
|
-
const content = await fs.readFile(file, 'utf-8');
|
|
312
|
-
detector.scan(content, file);
|
|
313
|
-
}
|
|
314
346
|
const results = detector.getResults();
|
|
315
347
|
// Filter patterns to improve
|
|
316
348
|
let patternsToImprove = results.triggered;
|
|
@@ -318,31 +350,24 @@ program
|
|
|
318
350
|
patternsToImprove = patternsToImprove.filter(p => p.patternId === options.pattern);
|
|
319
351
|
}
|
|
320
352
|
if (patternsToImprove.length === 0) {
|
|
321
|
-
|
|
353
|
+
ui.success('No patterns need improvement');
|
|
322
354
|
return;
|
|
323
355
|
}
|
|
324
|
-
|
|
356
|
+
ui.info(`Found ${patternsToImprove.length} patterns to improve:`);
|
|
325
357
|
for (const pattern of patternsToImprove) {
|
|
326
|
-
|
|
358
|
+
ui.dim(` - ${pattern.patternId}`);
|
|
327
359
|
}
|
|
328
360
|
if (options.dryRun) {
|
|
329
|
-
|
|
361
|
+
ui.warning('[DRY RUN] Would generate ESLint rules for above patterns');
|
|
330
362
|
return;
|
|
331
363
|
}
|
|
332
|
-
// Generate ESLint rules
|
|
364
|
+
// Generate ESLint rules using runTasks
|
|
333
365
|
const generator = new ESLintGenerator();
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
}
|
|
340
|
-
else {
|
|
341
|
-
console.log(chalk.red(` ✗ ${result.action}: ${result.error}`));
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
const successCount = improvementResults.filter(r => r.success).length;
|
|
345
|
-
console.log(chalk.green(`\n✅ Completed: ${successCount}/${improvementResults.length} improvements`));
|
|
366
|
+
const tasks = patternsToImprove.map(pattern => ({
|
|
367
|
+
name: `Generate rule for ${pattern.patternId}`,
|
|
368
|
+
task: async () => generator.generateRule(pattern),
|
|
369
|
+
}));
|
|
370
|
+
await runTasks(tasks, { stopOnError: false });
|
|
346
371
|
});
|
|
347
372
|
// =============================================================================
|
|
348
373
|
// STATUS COMMAND
|
|
@@ -351,20 +376,22 @@ program
|
|
|
351
376
|
.command('status')
|
|
352
377
|
.description('Show memory system status')
|
|
353
378
|
.action(async () => {
|
|
354
|
-
|
|
379
|
+
ui.header('Memory System Status');
|
|
355
380
|
const projectRoot = await getProjectRoot();
|
|
356
381
|
// Check config
|
|
357
382
|
let config;
|
|
358
383
|
try {
|
|
359
384
|
config = await loadConfig(projectRoot);
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
385
|
+
ui.success('Configuration found');
|
|
386
|
+
ui.keyValue({
|
|
387
|
+
'System': config.system,
|
|
388
|
+
'Project': config.projectName,
|
|
389
|
+
'Domains': config.domains.length,
|
|
390
|
+
});
|
|
364
391
|
}
|
|
365
392
|
catch {
|
|
366
|
-
|
|
367
|
-
|
|
393
|
+
ui.error('No configuration found');
|
|
394
|
+
ui.dim('Run: cmp-standards init');
|
|
368
395
|
return;
|
|
369
396
|
}
|
|
370
397
|
// Check registry
|
|
@@ -372,23 +399,25 @@ program
|
|
|
372
399
|
try {
|
|
373
400
|
const registryContent = await fs.readFile(registryPath, 'utf-8');
|
|
374
401
|
const registry = JSON.parse(registryContent);
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
402
|
+
ui.success('Registry found');
|
|
403
|
+
ui.keyValue({
|
|
404
|
+
'Sections': registry.sections.length,
|
|
405
|
+
'Generated': registry.generatedAt || 'pending',
|
|
406
|
+
});
|
|
378
407
|
}
|
|
379
408
|
catch {
|
|
380
|
-
|
|
381
|
-
|
|
409
|
+
ui.warning('No registry found');
|
|
410
|
+
ui.dim('Run: cmp-standards generate');
|
|
382
411
|
}
|
|
383
412
|
// Check hooks
|
|
384
413
|
const hooksDir = getHooksDir(projectRoot);
|
|
385
414
|
try {
|
|
386
415
|
const hooks = await fs.readdir(hooksDir);
|
|
387
416
|
const hookFiles = hooks.filter(h => h.endsWith('.ts') || h.endsWith('.md'));
|
|
388
|
-
|
|
417
|
+
ui.success(`Hooks directory (${hookFiles.length} files)`);
|
|
389
418
|
}
|
|
390
419
|
catch {
|
|
391
|
-
|
|
420
|
+
ui.warning('No hooks directory');
|
|
392
421
|
}
|
|
393
422
|
// Check settings.json
|
|
394
423
|
const settingsPath = path.join(projectRoot, '.claude/settings.json');
|
|
@@ -397,21 +426,23 @@ program
|
|
|
397
426
|
const settings = JSON.parse(settingsContent);
|
|
398
427
|
const hasMemoryPlugin = settings.plugins?.['metanautical-memory'];
|
|
399
428
|
if (hasMemoryPlugin) {
|
|
400
|
-
|
|
429
|
+
ui.success('Memory plugin configured in settings.json');
|
|
401
430
|
}
|
|
402
431
|
else {
|
|
403
|
-
|
|
432
|
+
ui.warning('Memory plugin not in settings.json');
|
|
404
433
|
}
|
|
405
434
|
}
|
|
406
435
|
catch {
|
|
407
|
-
|
|
436
|
+
ui.warning('No settings.json found');
|
|
408
437
|
}
|
|
409
438
|
// Features status
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
439
|
+
ui.subheader('Features');
|
|
440
|
+
ui.keyValue({
|
|
441
|
+
'Guards': `${config.guards.enabled ? 'Enabled' : 'Disabled'} (${config.guards.rules.length} rules)`,
|
|
442
|
+
'Embedding': config.embedding.enabled ? 'Enabled' : 'Disabled',
|
|
443
|
+
'Checkpoint': config.checkpoint.enabled ? 'Enabled' : 'Disabled',
|
|
444
|
+
'Auto-improve': `${config.autoImprovement.enabled ? 'Enabled' : 'Disabled'} (threshold: ${config.autoImprovement.violationThreshold})`,
|
|
445
|
+
});
|
|
415
446
|
});
|
|
416
447
|
// =============================================================================
|
|
417
448
|
// DASHBOARD COMMAND
|
|
@@ -518,6 +549,81 @@ program
|
|
|
518
549
|
console.log(chalk.gray(`\nGenerated at: ${report.generatedAt}`));
|
|
519
550
|
});
|
|
520
551
|
// =============================================================================
|
|
552
|
+
// CROSS-PROJECT ANALYTICS COMMAND
|
|
553
|
+
// =============================================================================
|
|
554
|
+
program
|
|
555
|
+
.command('cross-analytics')
|
|
556
|
+
.description('Show cross-project analytics and insights')
|
|
557
|
+
.option('--json', 'Output as JSON')
|
|
558
|
+
.option('-d, --days <days>', 'Number of days to analyze', '30')
|
|
559
|
+
.action(async (options) => {
|
|
560
|
+
ui.header('Cross-Project Analytics');
|
|
561
|
+
const { getCrossProjectAnalytics } = await import('../analytics/CrossProjectAnalytics.js');
|
|
562
|
+
const analytics = getCrossProjectAnalytics();
|
|
563
|
+
const report = await withSpinner('Generating cross-project report', async () => {
|
|
564
|
+
return analytics.generateReport({
|
|
565
|
+
startDate: new Date(Date.now() - parseInt(options.days) * 24 * 60 * 60 * 1000),
|
|
566
|
+
});
|
|
567
|
+
});
|
|
568
|
+
if (options.json) {
|
|
569
|
+
console.log(JSON.stringify(report, null, 2));
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
// Total Metrics
|
|
573
|
+
ui.subheader('Ecosystem Overview');
|
|
574
|
+
ui.keyValue({
|
|
575
|
+
'Total Projects': report.totalMetrics.totalProjects,
|
|
576
|
+
'Total Memories': report.totalMetrics.totalMemories,
|
|
577
|
+
'Total Tasks': report.totalMetrics.totalTasks,
|
|
578
|
+
'Total Sessions': report.totalMetrics.totalSessions,
|
|
579
|
+
'Avg Health Score': `${report.totalMetrics.avgHealthScore.toFixed(0)}%`,
|
|
580
|
+
});
|
|
581
|
+
// Project Metrics
|
|
582
|
+
if (report.projectMetrics.length > 0) {
|
|
583
|
+
ui.subheader('Project Health');
|
|
584
|
+
for (const project of report.projectMetrics) {
|
|
585
|
+
const healthIcon = project.healthScore >= 70 ? '🟢' : project.healthScore >= 40 ? '🟡' : '🔴';
|
|
586
|
+
console.log(` ${healthIcon} ${project.system}: ${project.healthScore}% (${project.memories} memories, ${project.sessions} sessions)`);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
// Insights
|
|
590
|
+
if (report.insights.length > 0) {
|
|
591
|
+
ui.subheader('Insights');
|
|
592
|
+
for (const insight of report.insights.slice(0, 5)) {
|
|
593
|
+
const icon = insight.type === 'warning' ? '⚠️' : insight.type === 'best_practice' ? '✨' : '💡';
|
|
594
|
+
const priority = insight.priority === 'high' ? chalk.red(`[${insight.priority}]`) : chalk.gray(`[${insight.priority}]`);
|
|
595
|
+
console.log(` ${icon} ${priority} ${insight.title}`);
|
|
596
|
+
if (insight.suggestedAction) {
|
|
597
|
+
ui.dim(` → ${insight.suggestedAction}`);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
// Pattern Comparisons
|
|
602
|
+
if (report.patternComparisons.length > 0) {
|
|
603
|
+
ui.subheader('Shared Patterns');
|
|
604
|
+
for (const pattern of report.patternComparisons.slice(0, 5)) {
|
|
605
|
+
const systems = Object.keys(pattern.occurrences).length;
|
|
606
|
+
console.log(` 📊 ${pattern.patternId}: ${pattern.totalOccurrences}x across ${systems} projects`);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
// Learning Opportunities
|
|
610
|
+
if (report.learningOpportunities.length > 0) {
|
|
611
|
+
ui.subheader('Learning Transfer Opportunities');
|
|
612
|
+
for (const opp of report.learningOpportunities.slice(0, 5)) {
|
|
613
|
+
console.log(` 🔄 "${opp.memoryTitle}" (${opp.fromSystem} → ${opp.toSystem})`);
|
|
614
|
+
ui.dim(` Relevance: ${(opp.relevanceScore * 100).toFixed(0)}%`);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
// Recommendations
|
|
618
|
+
if (report.recommendations.length > 0) {
|
|
619
|
+
ui.subheader('Recommendations');
|
|
620
|
+
for (const rec of report.recommendations) {
|
|
621
|
+
console.log(` ➤ ${rec}`);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
ui.dim(`\nReport generated: ${report.generatedAt}`);
|
|
625
|
+
});
|
|
626
|
+
// =============================================================================
|
|
521
627
|
// FEEDBACK COMMAND
|
|
522
628
|
// =============================================================================
|
|
523
629
|
program
|
|
@@ -769,24 +875,24 @@ cloudCmd
|
|
|
769
875
|
.command('status')
|
|
770
876
|
.description('Check cloud services status')
|
|
771
877
|
.action(async () => {
|
|
772
|
-
|
|
773
|
-
const { initCloud
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
}
|
|
878
|
+
ui.header('Cloud Services Status');
|
|
879
|
+
const { initCloud } = await import('../db/cloud.js');
|
|
880
|
+
const status = await withSpinner('Connecting to cloud services', async () => {
|
|
881
|
+
return initCloud();
|
|
882
|
+
});
|
|
883
|
+
ui.subheader('Service Status');
|
|
884
|
+
ui.keyValue({
|
|
885
|
+
'Turso': `${status.turso.ok ? '✅' : '❌'} ${status.turso.message}`,
|
|
886
|
+
'Redis': `${status.redis.ok ? '✅' : '❌'} ${status.redis.message}`,
|
|
887
|
+
'Vector': `${status.vector.ok ? '✅' : '❌'} ${status.vector.message}`,
|
|
888
|
+
});
|
|
889
|
+
const allOk = status.turso.ok && status.redis.ok;
|
|
890
|
+
if (allOk) {
|
|
891
|
+
ui.success('All core services connected!');
|
|
787
892
|
}
|
|
788
|
-
|
|
789
|
-
|
|
893
|
+
else {
|
|
894
|
+
ui.warning('Some services unavailable');
|
|
895
|
+
ui.dim('Check your .env file for credentials');
|
|
790
896
|
}
|
|
791
897
|
});
|
|
792
898
|
cloudCmd
|
|
@@ -995,6 +1101,417 @@ cloudCmd
|
|
|
995
1101
|
}
|
|
996
1102
|
});
|
|
997
1103
|
// =============================================================================
|
|
1104
|
+
// QUICKSTART WIZARD
|
|
1105
|
+
// =============================================================================
|
|
1106
|
+
program
|
|
1107
|
+
.command('quickstart')
|
|
1108
|
+
.description('Interactive setup wizard for new projects')
|
|
1109
|
+
.action(async () => {
|
|
1110
|
+
console.log(chalk.blue(`
|
|
1111
|
+
╔═══════════════════════════════════════════════════════════════╗
|
|
1112
|
+
║ ║
|
|
1113
|
+
║ 🚀 CMP-Standards Quickstart Wizard ║
|
|
1114
|
+
║ ║
|
|
1115
|
+
║ Set up your project in minutes with guided configuration ║
|
|
1116
|
+
║ ║
|
|
1117
|
+
╚═══════════════════════════════════════════════════════════════╝
|
|
1118
|
+
`));
|
|
1119
|
+
const inquirer = (await import('inquirer')).default;
|
|
1120
|
+
const projectRoot = await getProjectRoot();
|
|
1121
|
+
// Step 1: Check existing setup
|
|
1122
|
+
ui.header('Step 1: Checking Existing Setup');
|
|
1123
|
+
const existingConfig = await loadConfig(projectRoot).catch(() => null);
|
|
1124
|
+
if (existingConfig) {
|
|
1125
|
+
console.log(chalk.green(` ✓ Existing configuration found: ${existingConfig.system}`));
|
|
1126
|
+
const { continueSetup } = await inquirer.prompt([{
|
|
1127
|
+
type: 'confirm',
|
|
1128
|
+
name: 'continueSetup',
|
|
1129
|
+
message: 'Do you want to reconfigure?',
|
|
1130
|
+
default: false
|
|
1131
|
+
}]);
|
|
1132
|
+
if (!continueSetup) {
|
|
1133
|
+
console.log(chalk.gray('\n Run `cmp-standards status` to view current setup'));
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
// Step 2: Project Configuration
|
|
1138
|
+
ui.header('Step 2: Project Configuration');
|
|
1139
|
+
const { system, projectName } = await inquirer.prompt([
|
|
1140
|
+
{
|
|
1141
|
+
type: 'input',
|
|
1142
|
+
name: 'system',
|
|
1143
|
+
message: 'System identifier (e.g., MYAPP, PANEL):',
|
|
1144
|
+
default: path.basename(projectRoot).toUpperCase().replace(/[^A-Z0-9]/g, '_'),
|
|
1145
|
+
validate: (input) => /^[A-Z][A-Z0-9_]*$/.test(input) || 'Must start with letter, use A-Z, 0-9, _'
|
|
1146
|
+
},
|
|
1147
|
+
{
|
|
1148
|
+
type: 'input',
|
|
1149
|
+
name: 'projectName',
|
|
1150
|
+
message: 'Project name:',
|
|
1151
|
+
default: path.basename(projectRoot)
|
|
1152
|
+
}
|
|
1153
|
+
]);
|
|
1154
|
+
// Step 3: Cloud Services
|
|
1155
|
+
ui.header('Step 3: Cloud Services');
|
|
1156
|
+
const { hasCloudCredentials } = await import('../utils/env-loader.js');
|
|
1157
|
+
const creds = hasCloudCredentials();
|
|
1158
|
+
console.log(' Current status:');
|
|
1159
|
+
console.log(` Turso: ${creds.turso ? chalk.green('✓ Configured') : chalk.yellow('✗ Not configured')}`);
|
|
1160
|
+
console.log(` Redis: ${creds.redis ? chalk.green('✓ Configured') : chalk.yellow('✗ Not configured')}`);
|
|
1161
|
+
console.log(` Vector: ${creds.vector ? chalk.green('✓ Configured') : chalk.gray('○ Optional')}`);
|
|
1162
|
+
if (!creds.turso || !creds.redis) {
|
|
1163
|
+
const { setupCloud } = await inquirer.prompt([{
|
|
1164
|
+
type: 'confirm',
|
|
1165
|
+
name: 'setupCloud',
|
|
1166
|
+
message: 'Would you like guidance for setting up cloud services?',
|
|
1167
|
+
default: true
|
|
1168
|
+
}]);
|
|
1169
|
+
if (setupCloud) {
|
|
1170
|
+
console.log(chalk.cyan('\n 📚 Cloud Setup Guide:'));
|
|
1171
|
+
console.log(chalk.gray(' ─'.repeat(30)));
|
|
1172
|
+
if (!creds.turso) {
|
|
1173
|
+
console.log(chalk.yellow('\n Turso (SQLite Edge Database):'));
|
|
1174
|
+
console.log(' 1. Visit: https://turso.tech');
|
|
1175
|
+
console.log(' 2. Create free account');
|
|
1176
|
+
console.log(' 3. Create database named "memory"');
|
|
1177
|
+
console.log(' 4. Get URL: turso db show memory --url');
|
|
1178
|
+
console.log(' 5. Get token: turso db tokens create memory');
|
|
1179
|
+
console.log(chalk.gray(' Add to .env: TURSO_DATABASE_URL and TURSO_AUTH_TOKEN'));
|
|
1180
|
+
}
|
|
1181
|
+
if (!creds.redis) {
|
|
1182
|
+
console.log(chalk.yellow('\n Upstash Redis:'));
|
|
1183
|
+
console.log(' 1. Visit: https://console.upstash.com');
|
|
1184
|
+
console.log(' 2. Create Redis database');
|
|
1185
|
+
console.log(' 3. Copy REST credentials');
|
|
1186
|
+
console.log(chalk.gray(' Add to .env: UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN'));
|
|
1187
|
+
}
|
|
1188
|
+
const { hasCredentials } = await inquirer.prompt([{
|
|
1189
|
+
type: 'confirm',
|
|
1190
|
+
name: 'hasCredentials',
|
|
1191
|
+
message: 'Have you added the credentials to your .env file?',
|
|
1192
|
+
default: false
|
|
1193
|
+
}]);
|
|
1194
|
+
if (!hasCredentials) {
|
|
1195
|
+
console.log(chalk.gray('\n You can continue without cloud services (local mode)'));
|
|
1196
|
+
console.log(chalk.gray(' Run `cmp-standards cloud init` later to complete setup'));
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
// Step 4: Initialize Project
|
|
1201
|
+
ui.header('Step 4: Initializing Project');
|
|
1202
|
+
const { createProjectScaffold } = await import('../services/ProjectScaffold.js');
|
|
1203
|
+
const scaffold = createProjectScaffold(projectRoot);
|
|
1204
|
+
await scaffold.scaffold({
|
|
1205
|
+
system,
|
|
1206
|
+
projectName,
|
|
1207
|
+
force: true,
|
|
1208
|
+
skipAgents: false,
|
|
1209
|
+
skipHooks: false,
|
|
1210
|
+
skipCommands: false,
|
|
1211
|
+
});
|
|
1212
|
+
// Step 5: Verify Setup
|
|
1213
|
+
ui.header('Step 5: Verifying Setup');
|
|
1214
|
+
const { HookVerifierService } = await import('../services/HookVerifier.js');
|
|
1215
|
+
const verifier = new HookVerifierService(projectRoot);
|
|
1216
|
+
const report = await verifier.verify(true); // Auto-fix
|
|
1217
|
+
console.log(` Hooks verified: ${report.results.filter(r => r.status === 'valid').length}/${report.results.length}`);
|
|
1218
|
+
// Summary
|
|
1219
|
+
console.log(chalk.green(`
|
|
1220
|
+
╔═══════════════════════════════════════════════════════════════╗
|
|
1221
|
+
║ ║
|
|
1222
|
+
║ ✅ Setup Complete! ║
|
|
1223
|
+
║ ║
|
|
1224
|
+
╚═══════════════════════════════════════════════════════════════╝
|
|
1225
|
+
`));
|
|
1226
|
+
console.log(` System: ${chalk.cyan(system)}`);
|
|
1227
|
+
console.log(` Project: ${projectName}`);
|
|
1228
|
+
console.log('');
|
|
1229
|
+
console.log(' Next steps:');
|
|
1230
|
+
console.log(` ${chalk.yellow('cmp-standards status')} - Check system status`);
|
|
1231
|
+
console.log(` ${chalk.yellow('cmp-standards validate')} - Validate project structure`);
|
|
1232
|
+
console.log(` ${chalk.yellow('/experts src/')} - Run code review`);
|
|
1233
|
+
console.log('');
|
|
1234
|
+
});
|
|
1235
|
+
// =============================================================================
|
|
1236
|
+
// DOCTOR COMMAND
|
|
1237
|
+
// =============================================================================
|
|
1238
|
+
program
|
|
1239
|
+
.command('doctor')
|
|
1240
|
+
.description('Diagnose and fix common issues')
|
|
1241
|
+
.option('--fix', 'Attempt to fix issues automatically')
|
|
1242
|
+
.option('--json', 'Output as JSON')
|
|
1243
|
+
.action(async (options) => {
|
|
1244
|
+
ui.header('CMP-Standards Doctor');
|
|
1245
|
+
const projectRoot = await getProjectRoot();
|
|
1246
|
+
const issues = [];
|
|
1247
|
+
// Check 1: Configuration
|
|
1248
|
+
console.log(chalk.gray(' Checking configuration...'));
|
|
1249
|
+
let config = null;
|
|
1250
|
+
try {
|
|
1251
|
+
config = await loadConfig(projectRoot);
|
|
1252
|
+
}
|
|
1253
|
+
catch {
|
|
1254
|
+
issues.push({
|
|
1255
|
+
category: 'config',
|
|
1256
|
+
severity: 'error',
|
|
1257
|
+
message: 'No memory-config.json found',
|
|
1258
|
+
fix: 'Run: cmp-standards init --system YOUR_SYSTEM',
|
|
1259
|
+
fixable: false
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
// Check 2: Required directories
|
|
1263
|
+
console.log(chalk.gray(' Checking directory structure...'));
|
|
1264
|
+
const requiredDirs = ['.claude', '.claude/agents', '.claude/commands', '.claude/hooks'];
|
|
1265
|
+
for (const dir of requiredDirs) {
|
|
1266
|
+
const dirPath = path.join(projectRoot, dir);
|
|
1267
|
+
try {
|
|
1268
|
+
await fs.access(dirPath);
|
|
1269
|
+
}
|
|
1270
|
+
catch {
|
|
1271
|
+
issues.push({
|
|
1272
|
+
category: 'structure',
|
|
1273
|
+
severity: 'error',
|
|
1274
|
+
message: `Missing directory: ${dir}`,
|
|
1275
|
+
fix: `mkdir -p ${dir}`,
|
|
1276
|
+
fixable: true
|
|
1277
|
+
});
|
|
1278
|
+
if (options.fix) {
|
|
1279
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
// Check 3: Required files
|
|
1284
|
+
console.log(chalk.gray(' Checking required files...'));
|
|
1285
|
+
const requiredFiles = ['CLAUDE.md', '.claude/settings.json'];
|
|
1286
|
+
for (const file of requiredFiles) {
|
|
1287
|
+
const filePath = path.join(projectRoot, file);
|
|
1288
|
+
try {
|
|
1289
|
+
await fs.access(filePath);
|
|
1290
|
+
}
|
|
1291
|
+
catch {
|
|
1292
|
+
issues.push({
|
|
1293
|
+
category: 'structure',
|
|
1294
|
+
severity: 'error',
|
|
1295
|
+
message: `Missing file: ${file}`,
|
|
1296
|
+
fix: 'Run: cmp-standards init --system YOUR_SYSTEM',
|
|
1297
|
+
fixable: false
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
// Check 4: Cloud credentials
|
|
1302
|
+
console.log(chalk.gray(' Checking cloud credentials...'));
|
|
1303
|
+
const { hasCloudCredentials } = await import('../utils/env-loader.js');
|
|
1304
|
+
const creds = hasCloudCredentials();
|
|
1305
|
+
if (!creds.turso) {
|
|
1306
|
+
issues.push({
|
|
1307
|
+
category: 'cloud',
|
|
1308
|
+
severity: 'warning',
|
|
1309
|
+
message: 'Turso credentials not configured',
|
|
1310
|
+
fix: 'Run: cmp-standards cloud init',
|
|
1311
|
+
fixable: false
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1314
|
+
if (!creds.redis) {
|
|
1315
|
+
issues.push({
|
|
1316
|
+
category: 'cloud',
|
|
1317
|
+
severity: 'warning',
|
|
1318
|
+
message: 'Upstash Redis credentials not configured',
|
|
1319
|
+
fix: 'Run: cmp-standards cloud init',
|
|
1320
|
+
fixable: false
|
|
1321
|
+
});
|
|
1322
|
+
}
|
|
1323
|
+
// Check 5: Hook configurations
|
|
1324
|
+
console.log(chalk.gray(' Checking hooks...'));
|
|
1325
|
+
try {
|
|
1326
|
+
const { HookVerifierService } = await import('../services/HookVerifier.js');
|
|
1327
|
+
const verifier = new HookVerifierService(projectRoot);
|
|
1328
|
+
const report = await verifier.verify(options.fix);
|
|
1329
|
+
for (const result of report.results) {
|
|
1330
|
+
if (result.status !== 'valid') {
|
|
1331
|
+
const isError = result.status === 'missing_file' || result.status === 'invalid_syntax';
|
|
1332
|
+
issues.push({
|
|
1333
|
+
category: 'hooks',
|
|
1334
|
+
severity: isError ? 'error' : 'warning',
|
|
1335
|
+
message: `Hook ${result.hookPath}: ${result.message}`,
|
|
1336
|
+
fix: result.autoFixable ? (options.fix && result.fixed ? 'Fixed automatically' : 'Run with --fix') : undefined,
|
|
1337
|
+
fixable: result.autoFixable
|
|
1338
|
+
});
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
catch {
|
|
1343
|
+
// No hooks to check
|
|
1344
|
+
}
|
|
1345
|
+
// Check 6: Node version
|
|
1346
|
+
console.log(chalk.gray(' Checking Node.js version...'));
|
|
1347
|
+
const nodeVersion = process.version.match(/^v(\d+)/)?.[1];
|
|
1348
|
+
if (nodeVersion && parseInt(nodeVersion) < 18) {
|
|
1349
|
+
issues.push({
|
|
1350
|
+
category: 'environment',
|
|
1351
|
+
severity: 'error',
|
|
1352
|
+
message: `Node.js ${process.version} is not supported (requires >=18)`,
|
|
1353
|
+
fixable: false
|
|
1354
|
+
});
|
|
1355
|
+
}
|
|
1356
|
+
// Output results
|
|
1357
|
+
if (options.json) {
|
|
1358
|
+
console.log(JSON.stringify({ issues, summary: {
|
|
1359
|
+
total: issues.length,
|
|
1360
|
+
errors: issues.filter(i => i.severity === 'error').length,
|
|
1361
|
+
warnings: issues.filter(i => i.severity === 'warning').length,
|
|
1362
|
+
info: issues.filter(i => i.severity === 'info').length
|
|
1363
|
+
} }, null, 2));
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1366
|
+
console.log('');
|
|
1367
|
+
if (issues.length === 0) {
|
|
1368
|
+
console.log(chalk.green(' ✅ No issues found! Your setup looks healthy.'));
|
|
1369
|
+
}
|
|
1370
|
+
else {
|
|
1371
|
+
const errors = issues.filter(i => i.severity === 'error');
|
|
1372
|
+
const warnings = issues.filter(i => i.severity === 'warning');
|
|
1373
|
+
if (errors.length > 0) {
|
|
1374
|
+
ui.subheader(`Errors (${errors.length})`);
|
|
1375
|
+
for (const issue of errors) {
|
|
1376
|
+
console.log(` ❌ [${issue.category}] ${issue.message}`);
|
|
1377
|
+
if (issue.fix) {
|
|
1378
|
+
console.log(chalk.gray(` Fix: ${issue.fix}`));
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
if (warnings.length > 0) {
|
|
1383
|
+
ui.subheader(`Warnings (${warnings.length})`);
|
|
1384
|
+
for (const issue of warnings) {
|
|
1385
|
+
console.log(` ⚠️ [${issue.category}] ${issue.message}`);
|
|
1386
|
+
if (issue.fix) {
|
|
1387
|
+
console.log(chalk.gray(` Fix: ${issue.fix}`));
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
const fixable = issues.filter(i => i.fixable && i.severity === 'error');
|
|
1392
|
+
if (fixable.length > 0 && !options.fix) {
|
|
1393
|
+
console.log(chalk.yellow(`\n 💡 ${fixable.length} issues can be fixed automatically.`));
|
|
1394
|
+
console.log(chalk.yellow(' Run: cmp-standards doctor --fix'));
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
});
|
|
1398
|
+
// =============================================================================
|
|
1399
|
+
// COMPLETION COMMAND
|
|
1400
|
+
// =============================================================================
|
|
1401
|
+
program
|
|
1402
|
+
.command('completion')
|
|
1403
|
+
.description('Generate shell completion scripts')
|
|
1404
|
+
.argument('<shell>', 'Shell type: bash, zsh, fish, powershell')
|
|
1405
|
+
.action(async (shell) => {
|
|
1406
|
+
const commands = [
|
|
1407
|
+
'init', 'sync', 'validate', 'verify-hooks', 'generate', 'scan', 'improve',
|
|
1408
|
+
'status', 'dashboard', 'mcp', 'analytics', 'cross-analytics', 'feedback',
|
|
1409
|
+
'tasks', 'ideas', 'improvements', 'plan', 'capture', 'export',
|
|
1410
|
+
'cloud', 'quickstart', 'doctor', 'completion'
|
|
1411
|
+
];
|
|
1412
|
+
const subcommands = {
|
|
1413
|
+
cloud: ['status', 'init', 'improvements', 'tasks', 'session']
|
|
1414
|
+
};
|
|
1415
|
+
switch (shell.toLowerCase()) {
|
|
1416
|
+
case 'bash':
|
|
1417
|
+
console.log(`# CMP-Standards Bash Completion
|
|
1418
|
+
# Add to ~/.bashrc or ~/.bash_profile:
|
|
1419
|
+
# source <(cmp-standards completion bash)
|
|
1420
|
+
|
|
1421
|
+
_cmp_standards_completions() {
|
|
1422
|
+
local cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
1423
|
+
local prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
1424
|
+
|
|
1425
|
+
case "\${prev}" in
|
|
1426
|
+
cmp-standards)
|
|
1427
|
+
COMPREPLY=( $(compgen -W "${commands.join(' ')}" -- "\${cur}") )
|
|
1428
|
+
return 0
|
|
1429
|
+
;;
|
|
1430
|
+
cloud)
|
|
1431
|
+
COMPREPLY=( $(compgen -W "${subcommands.cloud.join(' ')}" -- "\${cur}") )
|
|
1432
|
+
return 0
|
|
1433
|
+
;;
|
|
1434
|
+
esac
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
complete -F _cmp_standards_completions cmp-standards
|
|
1438
|
+
`);
|
|
1439
|
+
break;
|
|
1440
|
+
case 'zsh':
|
|
1441
|
+
console.log(`# CMP-Standards Zsh Completion
|
|
1442
|
+
# Add to ~/.zshrc:
|
|
1443
|
+
# source <(cmp-standards completion zsh)
|
|
1444
|
+
|
|
1445
|
+
_cmp_standards() {
|
|
1446
|
+
local -a commands
|
|
1447
|
+
commands=(
|
|
1448
|
+
${commands.map(c => `'${c}:${c} command'`).join('\n ')}
|
|
1449
|
+
)
|
|
1450
|
+
|
|
1451
|
+
local -a cloud_subcommands
|
|
1452
|
+
cloud_subcommands=(
|
|
1453
|
+
${subcommands.cloud.map(c => `'${c}:cloud ${c}'`).join('\n ')}
|
|
1454
|
+
)
|
|
1455
|
+
|
|
1456
|
+
_arguments -C \\
|
|
1457
|
+
'1: :->command' \\
|
|
1458
|
+
'*:: :->args'
|
|
1459
|
+
|
|
1460
|
+
case $state in
|
|
1461
|
+
command)
|
|
1462
|
+
_describe -t commands 'cmp-standards commands' commands
|
|
1463
|
+
;;
|
|
1464
|
+
args)
|
|
1465
|
+
case $words[1] in
|
|
1466
|
+
cloud)
|
|
1467
|
+
_describe -t commands 'cloud subcommands' cloud_subcommands
|
|
1468
|
+
;;
|
|
1469
|
+
esac
|
|
1470
|
+
;;
|
|
1471
|
+
esac
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
compdef _cmp_standards cmp-standards
|
|
1475
|
+
`);
|
|
1476
|
+
break;
|
|
1477
|
+
case 'fish':
|
|
1478
|
+
console.log(`# CMP-Standards Fish Completion
|
|
1479
|
+
# Add to ~/.config/fish/completions/cmp-standards.fish
|
|
1480
|
+
|
|
1481
|
+
${commands.map(c => `complete -c cmp-standards -f -n "__fish_use_subcommand" -a ${c} -d "${c}"`).join('\n')}
|
|
1482
|
+
${subcommands.cloud.map(c => `complete -c cmp-standards -f -n "__fish_seen_subcommand_from cloud" -a ${c} -d "cloud ${c}"`).join('\n')}
|
|
1483
|
+
`);
|
|
1484
|
+
break;
|
|
1485
|
+
case 'powershell':
|
|
1486
|
+
console.log(`# CMP-Standards PowerShell Completion
|
|
1487
|
+
# Add to $PROFILE:
|
|
1488
|
+
|
|
1489
|
+
Register-ArgumentCompleter -CommandName cmp-standards -ScriptBlock {
|
|
1490
|
+
param($wordToComplete, $commandAst, $cursorPosition)
|
|
1491
|
+
|
|
1492
|
+
$commands = @(${commands.map(c => `'${c}'`).join(', ')})
|
|
1493
|
+
|
|
1494
|
+
if ($commandAst.CommandElements.Count -eq 2) {
|
|
1495
|
+
$commands | Where-Object { $_ -like "$wordToComplete*" } |
|
|
1496
|
+
ForEach-Object {
|
|
1497
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
1498
|
+
}
|
|
1499
|
+
} elseif ($commandAst.CommandElements[1].Extent.Text -eq 'cloud') {
|
|
1500
|
+
@(${subcommands.cloud.map(c => `'${c}'`).join(', ')}) | Where-Object { $_ -like "$wordToComplete*" } |
|
|
1501
|
+
ForEach-Object {
|
|
1502
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
`);
|
|
1507
|
+
break;
|
|
1508
|
+
default:
|
|
1509
|
+
console.error(chalk.red(`Unknown shell: ${shell}`));
|
|
1510
|
+
console.log('Supported shells: bash, zsh, fish, powershell');
|
|
1511
|
+
process.exit(1);
|
|
1512
|
+
}
|
|
1513
|
+
});
|
|
1514
|
+
// =============================================================================
|
|
998
1515
|
// MAIN
|
|
999
1516
|
// =============================================================================
|
|
1000
1517
|
program.parse();
|