cmp-standards 2.8.1 → 2.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/dist/analytics/CrossProjectAnalytics.d.ts +128 -0
  2. package/dist/analytics/CrossProjectAnalytics.d.ts.map +1 -0
  3. package/dist/analytics/CrossProjectAnalytics.js +431 -0
  4. package/dist/analytics/CrossProjectAnalytics.js.map +1 -0
  5. package/dist/analytics/index.d.ts +1 -0
  6. package/dist/analytics/index.d.ts.map +1 -1
  7. package/dist/analytics/index.js +2 -0
  8. package/dist/analytics/index.js.map +1 -1
  9. package/dist/cache/EmbeddingCache.d.ts +6 -4
  10. package/dist/cache/EmbeddingCache.d.ts.map +1 -1
  11. package/dist/cache/EmbeddingCache.js +28 -17
  12. package/dist/cache/EmbeddingCache.js.map +1 -1
  13. package/dist/cli/index.js +247 -141
  14. package/dist/cli/index.js.map +1 -1
  15. package/dist/cli/ui.d.ts +134 -0
  16. package/dist/cli/ui.d.ts.map +1 -0
  17. package/dist/cli/ui.js +311 -0
  18. package/dist/cli/ui.js.map +1 -0
  19. package/dist/dashboard/tokens.d.ts +228 -0
  20. package/dist/dashboard/tokens.d.ts.map +1 -0
  21. package/dist/dashboard/tokens.js +450 -0
  22. package/dist/dashboard/tokens.js.map +1 -0
  23. package/dist/dashboard/ui.d.ts +3 -0
  24. package/dist/dashboard/ui.d.ts.map +1 -1
  25. package/dist/dashboard/ui.js +95 -61
  26. package/dist/dashboard/ui.js.map +1 -1
  27. package/dist/db/cloud.d.ts +11 -0
  28. package/dist/db/cloud.d.ts.map +1 -1
  29. package/dist/db/cloud.js +49 -1
  30. package/dist/db/cloud.js.map +1 -1
  31. package/dist/db/migrations.d.ts +1 -0
  32. package/dist/db/migrations.d.ts.map +1 -1
  33. package/dist/db/migrations.js +109 -0
  34. package/dist/db/migrations.js.map +1 -1
  35. package/dist/hooks/index.d.ts +1 -0
  36. package/dist/hooks/index.d.ts.map +1 -1
  37. package/dist/hooks/index.js +2 -0
  38. package/dist/hooks/index.js.map +1 -1
  39. package/dist/hooks/startup-verify.d.ts +31 -0
  40. package/dist/hooks/startup-verify.d.ts.map +1 -0
  41. package/dist/hooks/startup-verify.js +360 -0
  42. package/dist/hooks/startup-verify.js.map +1 -0
  43. package/dist/plugins/PluginManager.d.ts +160 -0
  44. package/dist/plugins/PluginManager.d.ts.map +1 -0
  45. package/dist/plugins/PluginManager.js +417 -0
  46. package/dist/plugins/PluginManager.js.map +1 -0
  47. package/dist/plugins/index.d.ts +7 -0
  48. package/dist/plugins/index.d.ts.map +1 -0
  49. package/dist/plugins/index.js +7 -0
  50. package/dist/plugins/index.js.map +1 -0
  51. package/dist/services/AuditLog.d.ts +205 -0
  52. package/dist/services/AuditLog.d.ts.map +1 -0
  53. package/dist/services/AuditLog.js +352 -0
  54. package/dist/services/AuditLog.js.map +1 -0
  55. package/dist/services/FeedbackCollector.d.ts +8 -0
  56. package/dist/services/FeedbackCollector.d.ts.map +1 -1
  57. package/dist/services/FeedbackCollector.js +19 -2
  58. package/dist/services/FeedbackCollector.js.map +1 -1
  59. package/dist/services/GitIntegration.d.ts +140 -0
  60. package/dist/services/GitIntegration.d.ts.map +1 -0
  61. package/dist/services/GitIntegration.js +423 -0
  62. package/dist/services/GitIntegration.js.map +1 -0
  63. package/dist/services/HookVerifier.d.ts +95 -0
  64. package/dist/services/HookVerifier.d.ts.map +1 -0
  65. package/dist/services/HookVerifier.js +493 -0
  66. package/dist/services/HookVerifier.js.map +1 -0
  67. package/dist/services/MemoryVersioning.d.ts +108 -0
  68. package/dist/services/MemoryVersioning.d.ts.map +1 -0
  69. package/dist/services/MemoryVersioning.js +281 -0
  70. package/dist/services/MemoryVersioning.js.map +1 -0
  71. package/dist/services/context-injector.d.ts +8 -0
  72. package/dist/services/context-injector.d.ts.map +1 -1
  73. package/dist/services/context-injector.js +19 -2
  74. package/dist/services/context-injector.js.map +1 -1
  75. package/dist/services/index.d.ts +4 -0
  76. package/dist/services/index.d.ts.map +1 -1
  77. package/dist/services/index.js +5 -0
  78. package/dist/services/index.js.map +1 -1
  79. package/dist/services/memory-router.d.ts +8 -0
  80. package/dist/services/memory-router.d.ts.map +1 -1
  81. package/dist/services/memory-router.js +19 -2
  82. package/dist/services/memory-router.js.map +1 -1
  83. package/dist/services/pattern-tracker.d.ts +13 -0
  84. package/dist/services/pattern-tracker.d.ts.map +1 -1
  85. package/dist/services/pattern-tracker.js +33 -3
  86. package/dist/services/pattern-tracker.js.map +1 -1
  87. package/dist/services/semantic-search.d.ts +4 -0
  88. package/dist/services/semantic-search.d.ts.map +1 -1
  89. package/dist/services/semantic-search.js +53 -13
  90. package/dist/services/semantic-search.js.map +1 -1
  91. package/dist/types/index.d.ts +1 -0
  92. package/dist/types/index.d.ts.map +1 -1
  93. package/dist/types/index.js +1 -0
  94. package/dist/types/index.js.map +1 -1
  95. package/package.json +4 -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
- console.log(chalk.blue('šŸ” Validating Project Structure\n'));
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
- for (const dir of requiredDirs) {
138
- const dirPath = path.join(projectRoot, dir);
139
- try {
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
- const files = await fs.readdir(dirPath);
143
- if (files.length > 0) {
144
- passed.push(`${dir}/ (${files.length} files)`);
145
- }
146
- else {
147
- issues.push(`EMPTY: ${dir}/`);
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
- catch {
152
- issues.push(`MISSING: ${dir}/`);
153
- }
154
- }
155
- // Check experts command exists
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
- if (issues.length > 0) {
170
- console.log(chalk.red('\nāœ— Issues:'));
171
- for (const issue of issues) {
172
- console.log(chalk.red(` ${issue}`));
173
- }
174
- console.log(chalk.yellow('\nRun: cmp-memory init --system YOUR_SYSTEM --force'));
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
- else {
177
- console.log(chalk.green('\nāœ… All validations passed!'));
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
- console.log(chalk.blue('šŸ“š Generating Knowledge Registry\n'));
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
- console.log(`Processing ${files.length} specific files...`);
237
- await generator.update(files);
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 generator.generate();
255
+ await withSpinner('Generating registry', async (spinner) => {
256
+ spinner.text = 'Scanning documentation...';
257
+ await generator.generate();
258
+ }, { successText: 'Registry generated' });
241
259
  }
242
- console.log(chalk.green('\nāœ… Registry generation complete!'));
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
- console.log(chalk.blue('šŸ” Scanning for Patterns\n'));
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 { glob } = await import('glob');
259
- const files = await glob(`${options.dir}/**/*.{ts,tsx}`, {
260
- cwd: projectRoot,
261
- absolute: true,
262
- ignore: ['**/node_modules/**', '**/*.d.ts', '**/*.test.ts'],
263
- });
264
- console.log(`Scanning ${files.length} files...`);
265
- for (const file of files) {
266
- const content = await fs.readFile(file, 'utf-8');
267
- detector.scan(content, file);
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
- console.log(chalk.blue('\nšŸ“Š Results:'));
271
- console.log(` Total patterns: ${results.total}`);
272
- console.log(` Triggered (≄${options.threshold}): ${results.triggered.length}`);
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
- console.log(chalk.blue('\nšŸ“‹ Detected Patterns:'));
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
- console.log(chalk.yellow('\nāš ļø Patterns ready for auto-improvement:'));
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
- console.log(chalk.gray('\n Run: claude-memory improve'));
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
- console.log(chalk.blue('šŸ”§ Running Auto-Improvement\n'));
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 { glob } = await import('glob');
305
- const files = await glob('src/**/*.{ts,tsx}', {
306
- cwd: projectRoot,
307
- absolute: true,
308
- ignore: ['**/node_modules/**', '**/*.d.ts'],
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
- console.log(chalk.green('āœ… No patterns need improvement'));
353
+ ui.success('No patterns need improvement');
322
354
  return;
323
355
  }
324
- console.log(`Found ${patternsToImprove.length} patterns to improve:`);
356
+ ui.info(`Found ${patternsToImprove.length} patterns to improve:`);
325
357
  for (const pattern of patternsToImprove) {
326
- console.log(` - ${pattern.patternId}`);
358
+ ui.dim(` - ${pattern.patternId}`);
327
359
  }
328
360
  if (options.dryRun) {
329
- console.log(chalk.yellow('\n[DRY RUN] Would generate ESLint rules for above patterns'));
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 improvementResults = await generator.generateAllRules(patternsToImprove);
335
- console.log(chalk.blue('\nšŸ“‹ Improvement Results:'));
336
- for (const result of improvementResults) {
337
- if (result.success) {
338
- console.log(chalk.green(` āœ“ ${result.action}: ${result.filePath}`));
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
- console.log(chalk.blue('šŸ“Š Memory System Status\n'));
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
- console.log(chalk.green('āœ“ Configuration found'));
361
- console.log(` System: ${config.system}`);
362
- console.log(` Project: ${config.projectName}`);
363
- console.log(` Domains: ${config.domains.length}`);
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
- console.log(chalk.red('āœ— No configuration found'));
367
- console.log(chalk.gray(' Run: claude-memory init'));
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
- console.log(chalk.green('āœ“ Registry found'));
376
- console.log(` Sections: ${registry.sections.length}`);
377
- console.log(` Generated: ${registry.generatedAt || 'pending'}`);
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
- console.log(chalk.yellow('⚠ No registry found'));
381
- console.log(chalk.gray(' Run: claude-memory generate'));
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
- console.log(chalk.green(`āœ“ Hooks directory (${hookFiles.length} files)`));
417
+ ui.success(`Hooks directory (${hookFiles.length} files)`);
389
418
  }
390
419
  catch {
391
- console.log(chalk.yellow('⚠ No hooks directory'));
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
- console.log(chalk.green('āœ“ Memory plugin configured in settings.json'));
429
+ ui.success('Memory plugin configured in settings.json');
401
430
  }
402
431
  else {
403
- console.log(chalk.yellow('⚠ Memory plugin not in settings.json'));
432
+ ui.warning('Memory plugin not in settings.json');
404
433
  }
405
434
  }
406
435
  catch {
407
- console.log(chalk.yellow('⚠ No settings.json found'));
436
+ ui.warning('No settings.json found');
408
437
  }
409
438
  // Features status
410
- console.log(chalk.blue('\nšŸ”§ Features:'));
411
- console.log(` Guards: ${config.guards.enabled ? 'Enabled' : 'Disabled'} (${config.guards.rules.length} rules)`);
412
- console.log(` Embedding: ${config.embedding.enabled ? 'Enabled' : 'Disabled'}`);
413
- console.log(` Checkpoint: ${config.checkpoint.enabled ? 'Enabled' : 'Disabled'}`);
414
- console.log(` Auto-improve: ${config.autoImprovement.enabled ? 'Enabled' : 'Disabled'} (threshold: ${config.autoImprovement.violationThreshold})`);
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
- console.log(chalk.blue('ā˜ļø Cloud Services Status\n'));
773
- const { initCloud, healthCheck } = await import('../db/cloud.js');
774
- try {
775
- const status = await initCloud();
776
- console.log(`Turso: ${status.turso.ok ? chalk.green('āœ…') : chalk.red('āŒ')} ${status.turso.message}`);
777
- console.log(`Redis: ${status.redis.ok ? chalk.green('āœ…') : chalk.red('āŒ')} ${status.redis.message}`);
778
- console.log(`Vector: ${status.vector.ok ? chalk.green('āœ…') : chalk.red('āŒ')} ${status.vector.message}`);
779
- const allOk = status.turso.ok && status.redis.ok;
780
- if (allOk) {
781
- console.log(chalk.green('\nāœ… All core services connected!'));
782
- }
783
- else {
784
- console.log(chalk.yellow('\nāš ļø Some services unavailable'));
785
- console.log(chalk.gray('Check your .env file for credentials'));
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
- catch (error) {
789
- console.log(chalk.red(`āŒ Error: ${error}`));
893
+ else {
894
+ ui.warning('Some services unavailable');
895
+ ui.dim('Check your .env file for credentials');
790
896
  }
791
897
  });
792
898
  cloudCmd