olympus-ai 4.0.4 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +71 -10
  3. package/dist/cli/index.js +239 -57
  4. package/dist/cli/index.js.map +1 -1
  5. package/dist/features/workflow-engine/checkpoint.d.ts +1 -0
  6. package/dist/features/workflow-engine/checkpoint.d.ts.map +1 -1
  7. package/dist/features/workflow-engine/checkpoint.js +54 -0
  8. package/dist/features/workflow-engine/checkpoint.js.map +1 -1
  9. package/dist/features/workflow-engine/construction/decomposition.d.ts +7 -7
  10. package/dist/features/workflow-engine/construction/decomposition.d.ts.map +1 -1
  11. package/dist/features/workflow-engine/construction/decomposition.js +15 -14
  12. package/dist/features/workflow-engine/construction/decomposition.js.map +1 -1
  13. package/dist/features/workflow-engine/engine.d.ts +1 -0
  14. package/dist/features/workflow-engine/engine.d.ts.map +1 -1
  15. package/dist/features/workflow-engine/engine.js +25 -17
  16. package/dist/features/workflow-engine/engine.js.map +1 -1
  17. package/dist/features/workflow-engine/phase-types.d.ts +2 -0
  18. package/dist/features/workflow-engine/phase-types.d.ts.map +1 -1
  19. package/dist/features/workflow-engine/state-file.d.ts.map +1 -1
  20. package/dist/features/workflow-engine/state-file.js +3 -1
  21. package/dist/features/workflow-engine/state-file.js.map +1 -1
  22. package/dist/hooks/olympus-hooks.cjs +237 -229
  23. package/dist/hooks/registrations/user-prompt-submit.d.ts.map +1 -1
  24. package/dist/hooks/registrations/user-prompt-submit.js +4 -0
  25. package/dist/hooks/registrations/user-prompt-submit.js.map +1 -1
  26. package/dist/installer/index.d.ts +13 -1
  27. package/dist/installer/index.d.ts.map +1 -1
  28. package/dist/installer/index.js +236 -12
  29. package/dist/installer/index.js.map +1 -1
  30. package/dist/learning/__tests__/cli-operations.test.d.ts +2 -0
  31. package/dist/learning/__tests__/cli-operations.test.d.ts.map +1 -0
  32. package/dist/learning/__tests__/cli-operations.test.js +164 -0
  33. package/dist/learning/__tests__/cli-operations.test.js.map +1 -0
  34. package/dist/learning/__tests__/intelligence.test.d.ts +2 -0
  35. package/dist/learning/__tests__/intelligence.test.d.ts.map +1 -0
  36. package/dist/learning/__tests__/intelligence.test.js +233 -0
  37. package/dist/learning/__tests__/intelligence.test.js.map +1 -0
  38. package/dist/learning/__tests__/migration.test.d.ts +2 -0
  39. package/dist/learning/__tests__/migration.test.d.ts.map +1 -0
  40. package/dist/learning/__tests__/migration.test.js +103 -0
  41. package/dist/learning/__tests__/migration.test.js.map +1 -0
  42. package/dist/learning/__tests__/session-insights.test.d.ts +2 -0
  43. package/dist/learning/__tests__/session-insights.test.d.ts.map +1 -0
  44. package/dist/learning/__tests__/session-insights.test.js +176 -0
  45. package/dist/learning/__tests__/session-insights.test.js.map +1 -0
  46. package/dist/learning/__tests__/storage-migration.test.d.ts +2 -0
  47. package/dist/learning/__tests__/storage-migration.test.d.ts.map +1 -0
  48. package/dist/learning/__tests__/storage-migration.test.js +171 -0
  49. package/dist/learning/__tests__/storage-migration.test.js.map +1 -0
  50. package/dist/learning/aggregation.d.ts +1 -11
  51. package/dist/learning/aggregation.d.ts.map +1 -1
  52. package/dist/learning/aggregation.js +28 -11
  53. package/dist/learning/aggregation.js.map +1 -1
  54. package/dist/learning/baselines.d.ts +2 -32
  55. package/dist/learning/baselines.d.ts.map +1 -1
  56. package/dist/learning/baselines.js +3 -70
  57. package/dist/learning/baselines.js.map +1 -1
  58. package/dist/learning/cleanup.d.ts +9 -0
  59. package/dist/learning/cleanup.d.ts.map +1 -1
  60. package/dist/learning/cleanup.js +96 -3
  61. package/dist/learning/cleanup.js.map +1 -1
  62. package/dist/learning/hooks/learned-context.d.ts.map +1 -1
  63. package/dist/learning/hooks/learned-context.js +12 -5
  64. package/dist/learning/hooks/learned-context.js.map +1 -1
  65. package/dist/learning/migration.d.ts +11 -0
  66. package/dist/learning/migration.d.ts.map +1 -0
  67. package/dist/learning/migration.js +91 -0
  68. package/dist/learning/migration.js.map +1 -0
  69. package/dist/learning/pattern-extractor.d.ts +2 -1
  70. package/dist/learning/pattern-extractor.d.ts.map +1 -1
  71. package/dist/learning/pattern-extractor.js +7 -4
  72. package/dist/learning/pattern-extractor.js.map +1 -1
  73. package/dist/learning/preference-learner.d.ts +1 -2
  74. package/dist/learning/preference-learner.d.ts.map +1 -1
  75. package/dist/learning/preference-learner.js +12 -6
  76. package/dist/learning/preference-learner.js.map +1 -1
  77. package/dist/learning/routing.d.ts +2 -7
  78. package/dist/learning/routing.d.ts.map +1 -1
  79. package/dist/learning/routing.js +18 -17
  80. package/dist/learning/routing.js.map +1 -1
  81. package/dist/learning/session-insights.d.ts +3 -0
  82. package/dist/learning/session-insights.d.ts.map +1 -0
  83. package/dist/learning/session-insights.js +98 -0
  84. package/dist/learning/session-insights.js.map +1 -0
  85. package/dist/learning/session-state.d.ts.map +1 -1
  86. package/dist/learning/session-state.js +7 -3
  87. package/dist/learning/session-state.js.map +1 -1
  88. package/dist/learning/storage.d.ts +5 -8
  89. package/dist/learning/storage.d.ts.map +1 -1
  90. package/dist/learning/storage.js +60 -17
  91. package/dist/learning/storage.js.map +1 -1
  92. package/dist/learning/types.d.ts +39 -1
  93. package/dist/learning/types.d.ts.map +1 -1
  94. package/dist/learning/types.js.map +1 -1
  95. package/package.json +3 -2
  96. package/resources/agents/metis.md +6 -0
  97. package/resources/rules/inception/units-generation.md +218 -204
  98. package/resources/skills/continue/SKILL.md +2 -2
  99. package/resources/skills/plan/SKILL.md +20 -13
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "olympus-ai",
3
- "version": "4.0.4",
3
+ "version": "4.1.1",
4
4
  "description": "Olympus: Multi-agent orchestration for Claude Code. Summon the gods of code.",
5
5
  "author": {
6
6
  "name": "mikev10",
package/README.md CHANGED
@@ -111,27 +111,37 @@ claude
111
111
 
112
112
  ## Installation
113
113
 
114
+ ### Prerequisites
115
+
116
+ - [Claude Code](https://docs.anthropic.com/claude-code) installed
117
+ - Node.js 20+ (`node -v` to check)
118
+
114
119
  ### Global Installation (Recommended)
115
120
 
116
- Install Olympus globally to use across all projects:
121
+ Install Olympus globally to enable it across all your projects:
117
122
 
118
123
  ```bash
119
124
  npm install -g olympus-ai
120
125
  olympus-ai install
121
126
  ```
122
127
 
123
- This installs agents, skills, rules, and hooks to `~/.claude/`.
128
+ This installs agents, skills, rules, and hooks to `~/.claude/` so every Claude Code session has access to Olympus.
124
129
 
125
130
  ### Local Project Installation
126
131
 
127
- Install Olympus for a specific project only:
132
+ Install Olympus as a dev dependency for a specific project. This is useful for teams that want Olympus pinned to a specific version in their repo:
128
133
 
129
134
  ```bash
130
- npm install -g olympus-ai
131
- olympus-ai install --local
135
+ # Install as a dev dependency
136
+ npm install --save-dev olympus-ai
137
+
138
+ # Run the installer for this project only
139
+ npx olympus-ai install --local
132
140
  ```
133
141
 
134
- This installs to `./.claude/` in your current project directory.
142
+ This installs agents, skills, rules, hooks, and settings to `./.claude/` in your current project directory.
143
+
144
+ > **Note:** You can use both. A global install provides Olympus across all projects, while a local install scopes everything to the current project. Local files take precedence over global ones.
135
145
 
136
146
  ---
137
147
 
@@ -600,17 +610,68 @@ This is a TypeScript monorepo using:
600
610
 
601
611
  ## Uninstall
602
612
 
613
+ Olympus provides a safe uninstall that **only removes Olympus-owned files** — your own Claude Code agents, skills, hooks, and CLAUDE.md content are preserved.
614
+
615
+ ### Quick Uninstall (One Command)
616
+
617
+ For global installations, a single npm command handles everything — the `preuninstall` hook automatically cleans up Olympus config files before removing the package:
618
+
619
+ ```bash
620
+ npm uninstall -g olympus-ai
621
+ ```
622
+
623
+ For local project installations:
624
+
625
+ ```bash
626
+ npm uninstall olympus-ai
627
+ ```
628
+
629
+ > **Note:** Local `npm uninstall` removes the package dependency but does not clean up `.claude/` config files. Run `npx olympus-ai uninstall --local` first if you need to remove those, or see the manual steps below.
630
+
631
+ ### Manual Uninstall
632
+
633
+ If you prefer more control, or need to remove config files without uninstalling the package:
634
+
603
635
  ```bash
604
- # Remove agents and skills
605
- rm -rf ~/.claude/agents ~/.claude/skills ~/.claude/hooks ~/.claude/olympus ~/.claude/CLAUDE.md
636
+ # Remove Olympus config files from ~/.claude/ (global)
637
+ olympus-ai uninstall
638
+
639
+ # Remove Olympus config files from ./.claude/ (current project)
640
+ olympus-ai uninstall --local
641
+
642
+ # Preview what would be removed without deleting anything
643
+ olympus-ai uninstall --dry-run
606
644
  ```
607
645
 
646
+ Then remove the npm package:
647
+
648
+ ```bash
649
+ # Global
650
+ npm uninstall -g olympus-ai
651
+
652
+ # Local
653
+ npm uninstall olympus-ai
654
+ ```
655
+
656
+ ### What Gets Removed
657
+
658
+ The uninstall command removes only files that Olympus installed:
659
+ - Olympus agent definitions from `agents/` (e.g., `oracle.md`, `prometheus.md`)
660
+ - Olympus skill directories from `skills/` (e.g., `ultrawork/`, `plan/`)
661
+ - The `olympus/` directory (rules, learning data)
662
+ - Olympus hook scripts from `hooks/`
663
+ - Olympus hook entries from `settings.json`
664
+ - The Olympus section from `CLAUDE.md` (user content is preserved)
665
+ - The `.olympus-version.json` metadata file
666
+ - The Olympus plugin registration
667
+
668
+ Any custom agents, skills, hooks, or CLAUDE.md content you added yourself will **not** be touched.
669
+
608
670
  ---
609
671
 
610
672
  ## Requirements
611
673
 
612
- - [Claude Code](https://docs.anthropic.com/claude-code) installed
613
- - Anthropic API key (`ANTHROPIC_API_KEY` environment variable)
674
+ - [Claude Code](https://docs.anthropic.com/claude-code) installed and configured
614
675
  - Node.js 20+ (for npm installation)
615
676
 
616
677
  ---
package/dist/cli/index.js CHANGED
@@ -12,13 +12,13 @@
12
12
  import { Command } from 'commander';
13
13
  import chalk from 'chalk';
14
14
  import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
15
- import { join, dirname } from 'path';
15
+ import { join, dirname, resolve } from 'path';
16
16
  import { fileURLToPath } from 'url';
17
17
  import { loadConfig, getConfigPaths, generateConfigSchema } from '../config/loader.js';
18
18
  import { createOlympusSession } from '../index.js';
19
19
  import { checkForUpdates, performUpdate, formatUpdateNotification, getInstalledVersion } from '../features/auto-update.js';
20
- import { install as installOlympus, isInstalled, getInstallInfo } from '../installer/index.js';
21
- import { readFeedbackLog, readJsonFile, getLearningDir, writeJsonFile, appendFeedback, updateAgentPerformance, loadSessionSummaries, getLastSessionSummary, } from '../learning/storage.js';
20
+ import { install as installOlympus, uninstall as uninstallOlympus, isInstalled, getInstallInfo } from '../installer/index.js';
21
+ import { readFeedbackLog, readJsonFile, getLearningDir, writeJsonFile, appendFeedback, updateAgentPerformance, loadSessionSummaries, readAgentPerformance, } from '../learning/storage.js';
22
22
  import { extractPatterns } from '../learning/pattern-extractor.js';
23
23
  import { extractPatterns as extractTaskPatterns, computePatternConfidence } from '../learning/pattern-matcher.js';
24
24
  import { updatePreferences, createDefaultPreferences } from '../learning/preference-learner.js';
@@ -27,13 +27,14 @@ import { generatePromptPatches, previewPatches, applyPromptPatches } from '../le
27
27
  import { readDiscoveries, recordDiscovery } from '../learning/discovery.js';
28
28
  import { migrateNotepads } from '../learning/migrate-notepads.js';
29
29
  import { generateLearningStats, formatLearningStats } from '../learning/stats.js';
30
- import { cleanupLearning, formatCleanupResult } from '../learning/cleanup.js';
30
+ import { cleanupLearning, formatCleanupResult, collectProjectDirStats } from '../learning/cleanup.js';
31
+ import { resolveProjectRoot, deriveProjectSlug, getProjectScopedDir } from '../learning/project-resolver.js';
31
32
  import { getWarningThreshold } from '../learning/baselines.js';
32
33
  import { calculateCost, DEFAULT_PRICING } from '../learning/pricing.js';
33
34
  import { getTokenUsage, hasEfficiencyMetrics, safeTokenTotal } from '../learning/utils.js';
34
35
  import { getSessionStatePath } from '../learning/session-state.js';
35
36
  import { randomUUID } from 'crypto';
36
- import { rmSync, appendFileSync } from 'fs';
37
+ import { rmSync, appendFileSync, readdirSync, statSync } from 'fs';
37
38
  import { showMetrics, exportMetrics, analyzeMetrics, cleanMetrics } from './commands/metrics.js';
38
39
  const __dirname = dirname(fileURLToPath(import.meta.url));
39
40
  // Try to load package.json for version
@@ -496,9 +497,48 @@ program
496
497
  process.exit(1);
497
498
  }
498
499
  });
499
- /**
500
- * Postinstall command - Silent install for npm postinstall hook
501
- */
500
+ program
501
+ .command('uninstall')
502
+ .description('Uninstall Olympus files (only removes Olympus-owned files)')
503
+ .option('--local', 'Uninstall from current project (./.claude/) instead of global (~/.claude/)')
504
+ .option('--dry-run', 'Show what would be removed without actually removing')
505
+ .option('-v, --verbose', 'Show detailed output')
506
+ .action((opts) => {
507
+ const prefix = opts.dryRun ? chalk.yellow('[DRY RUN] ') : '';
508
+ console.log(chalk.blue(`${prefix}Uninstalling Olympus...`));
509
+ console.log('');
510
+ const result = uninstallOlympus({
511
+ local: opts.local,
512
+ dryRun: opts.dryRun,
513
+ verbose: opts.verbose || opts.dryRun
514
+ });
515
+ if (result.errors.length > 0) {
516
+ result.errors.forEach(err => console.error(chalk.red(` Error: ${err}`)));
517
+ console.log('');
518
+ }
519
+ if (opts.dryRun) {
520
+ console.log(chalk.yellow(`Would remove ${result.removedFiles.length} file(s)/directories.`));
521
+ console.log(chalk.gray('Run without --dry-run to apply.'));
522
+ }
523
+ else {
524
+ if (result.success) {
525
+ console.log(chalk.green(`Removed ${result.removedFiles.length} file(s)/directories. Olympus has been uninstalled.`));
526
+ }
527
+ else {
528
+ console.log(chalk.yellow(`Removed ${result.removedFiles.length} file(s)/directories (with ${result.errors.length} error(s)).`));
529
+ }
530
+ console.log('');
531
+ if (opts.local) {
532
+ console.log(chalk.gray('To also remove the npm package: npm uninstall olympus-ai'));
533
+ }
534
+ else {
535
+ console.log(chalk.gray('To also remove the npm package: npm uninstall -g olympus-ai'));
536
+ }
537
+ }
538
+ if (!result.success) {
539
+ process.exit(1);
540
+ }
541
+ });
502
542
  program
503
543
  .command('postinstall', { hidden: true })
504
544
  .description('Run post-install setup (called automatically by npm)')
@@ -535,18 +575,72 @@ program
535
575
  .option('--suggest', 'Show suggested prompt improvements')
536
576
  .option('--apply', 'Apply suggested improvements')
537
577
  .option('-f, --forget', 'Forget all learnings')
538
- .option('-p, --project', 'Scope to current project (with --forget)')
539
- .option('-e, --export', 'Export learnings to JSON')
578
+ .option('-p, --project [slug]', 'Project slug for --forget; omit value to use current project')
579
+ .option('-e, --export [file]', 'Export current project data to file or stdout')
540
580
  .option('-i, --import <file>', 'Import learnings from JSON')
541
581
  .option('--efficiency', 'Show agent efficiency rankings and token metrics')
542
582
  .option('--show-costs', 'Show cost breakdown by model and agent')
543
583
  .option('--budget-status', 'Show current session token budget status')
544
584
  .option('--last-session', 'Show last session summary')
545
585
  .option('--sessions [n]', 'Show last N sessions (default: 10)')
586
+ .option('--global', 'Show global learning data, bypassing project scoping')
587
+ .option('--all-projects', 'List all project directories with stats')
588
+ .option('--confirm', 'Confirm destructive operations (required for --forget)')
546
589
  .action(async (options) => {
547
590
  const learningDir = getLearningDir();
591
+ function resolveProjectContext() {
592
+ if (options.global) {
593
+ return { projectPath: null, isInProject: false };
594
+ }
595
+ try {
596
+ const resolved = resolveProjectRoot(process.cwd());
597
+ const cwd = resolve(process.cwd());
598
+ if (resolved === cwd) {
599
+ return { projectPath: null, isInProject: false };
600
+ }
601
+ return { projectPath: resolved, isInProject: true };
602
+ }
603
+ catch {
604
+ return { projectPath: null, isInProject: false };
605
+ }
606
+ }
607
+ if (options.allProjects) {
608
+ try {
609
+ const projectsDir = join(getLearningDir(), 'projects');
610
+ const stats = collectProjectDirStats(projectsDir);
611
+ if (stats.length === 0) {
612
+ console.log('No project directories found.');
613
+ return;
614
+ }
615
+ const slugW = 40, dateW = 22, sizeW = 10, feedW = 10, sessW = 8;
616
+ const header = 'Project'.padEnd(slugW) + 'Last Modified'.padEnd(dateW) + 'Size'.padStart(sizeW) + 'Feedback'.padStart(feedW) + 'Sessions'.padStart(sessW);
617
+ const divider = '-'.repeat(slugW + dateW + sizeW + feedW + sessW);
618
+ console.log(header);
619
+ console.log(divider);
620
+ for (const s of stats) {
621
+ const sizeKb = (s.sizeBytes / 1024).toFixed(0) + ' KB';
622
+ const dateStr = s.lastModified.toISOString().replace('T', ' ').substring(0, 16);
623
+ const line = s.slug.substring(0, slugW).padEnd(slugW)
624
+ + dateStr.padEnd(dateW)
625
+ + sizeKb.padStart(sizeW)
626
+ + String(s.feedbackCount).padStart(feedW)
627
+ + String(s.sessionCount).padStart(sessW);
628
+ console.log(line);
629
+ }
630
+ }
631
+ catch (err) {
632
+ const msg = err instanceof Error ? err.message : String(err);
633
+ console.error(`[Olympus] Failed to list project directories: ${msg}`);
634
+ }
635
+ return;
636
+ }
548
637
  if (options.lastSession) {
549
- const summary = getLastSessionSummary();
638
+ const { projectPath: lsProjectPath } = resolveProjectContext();
639
+ if (!lsProjectPath && !options.global) {
640
+ console.log('Not inside a recognized project directory. Showing global data.');
641
+ }
642
+ const lsSummaries = loadSessionSummaries(lsProjectPath ?? undefined);
643
+ const summary = lsSummaries.length > 0 ? lsSummaries[lsSummaries.length - 1] : null;
550
644
  if (!summary) {
551
645
  console.log(chalk.yellow('No session summaries recorded yet. Complete a session first.'));
552
646
  return;
@@ -576,9 +670,13 @@ program
576
670
  return;
577
671
  }
578
672
  if (options.sessions !== undefined) {
673
+ const { projectPath: sessProjectPath } = resolveProjectContext();
674
+ if (!sessProjectPath && !options.global) {
675
+ console.log('Not inside a recognized project directory. Showing global data.');
676
+ }
579
677
  const n = typeof options.sessions === 'string' ? parseInt(options.sessions) : 10;
580
678
  const count = isNaN(n) ? 10 : n;
581
- const allSummaries = loadSessionSummaries();
679
+ const allSummaries = loadSessionSummaries(sessProjectPath ?? undefined);
582
680
  if (allSummaries.length === 0) {
583
681
  console.log(chalk.yellow('No session summaries recorded yet. Complete a session first.'));
584
682
  return;
@@ -630,6 +728,24 @@ program
630
728
  if (options.stats) {
631
729
  const stats = generateLearningStats(process.cwd());
632
730
  console.log(formatLearningStats(stats));
731
+ try {
732
+ const projectsDir = join(getLearningDir(), 'projects');
733
+ const projStats = collectProjectDirStats(projectsDir);
734
+ if (projStats.length > 0) {
735
+ console.log('\nPer-Project Storage:');
736
+ let totalBytes = 0;
737
+ for (const ps of projStats) {
738
+ const sizeKb = (ps.sizeBytes / 1024).toFixed(0) + ' KB';
739
+ console.log(` ${ps.slug.padEnd(40)} ${sizeKb.padStart(8)}`);
740
+ totalBytes += ps.sizeBytes;
741
+ }
742
+ const totalKb = (totalBytes / 1024).toFixed(0) + ' KB';
743
+ console.log(` ${'-'.repeat(49)}`);
744
+ console.log(` Total (${projStats.length} project${projStats.length === 1 ? '' : 's'}):${' '.repeat(Math.max(0, 33 - String(projStats.length).length - 10))} ${totalKb.padStart(8)}`);
745
+ console.log(`\nGlobal learning directory: ${getLearningDir()} (${totalKb})`);
746
+ }
747
+ }
748
+ catch { }
633
749
  return;
634
750
  }
635
751
  if (options.cleanup) {
@@ -643,10 +759,14 @@ program
643
759
  return;
644
760
  }
645
761
  if (options.show) {
762
+ const { projectPath: showProjectPath, isInProject: showIsInProject } = resolveProjectContext();
763
+ if (!showIsInProject && !options.global) {
764
+ console.log('Not inside a recognized project directory. Showing global data.');
765
+ }
646
766
  console.log(chalk.blue.bold('\n╭─────────────────────────────────────────────────────────────╮'));
647
767
  console.log(chalk.blue.bold('│ OLYMPUS LEARNING STATUS │'));
648
768
  console.log(chalk.blue.bold('╰─────────────────────────────────────────────────────────────╯\n'));
649
- const feedback = readFeedbackLog();
769
+ const feedback = readFeedbackLog(showProjectPath ?? undefined);
650
770
  const revisions = feedback.filter(f => f.event_type === 'revision').length;
651
771
  const cancellations = feedback.filter(f => f.event_type === 'cancellation').length;
652
772
  const successes = feedback.filter(f => f.event_type === 'success').length;
@@ -659,8 +779,18 @@ program
659
779
  console.log(` • Verbosity: ${prefs.verbosity}`);
660
780
  if (prefs.autonomy !== 'unknown')
661
781
  console.log(` • Autonomy: ${prefs.autonomy}`);
662
- for (const rule of prefs.explicit_rules.slice(0, 3)) {
663
- console.log(` • ${rule}`);
782
+ const filteredRules = prefs.explicit_rules.filter((r) => {
783
+ if (typeof r === 'object' && r !== null && 'rule' in r) {
784
+ const rObj = r;
785
+ return !showProjectPath || !rObj.project_path || rObj.project_path === showProjectPath;
786
+ }
787
+ return true;
788
+ });
789
+ for (const rule of filteredRules.slice(0, 3)) {
790
+ const ruleText = typeof rule === 'object' && rule !== null && 'rule' in rule
791
+ ? rule.rule
792
+ : String(rule);
793
+ console.log(` * ${ruleText}`);
664
794
  }
665
795
  console.log('');
666
796
  }
@@ -671,7 +801,7 @@ program
671
801
  }
672
802
  console.log('');
673
803
  }
674
- const agentPerf = readJsonFile(join(learningDir, 'agent-performance.json'), {});
804
+ const agentPerf = readAgentPerformance(showProjectPath ?? undefined);
675
805
  if (Object.keys(agentPerf).length > 0) {
676
806
  console.log(chalk.white('🤖 Agent Performance:'));
677
807
  for (const [name, perf] of Object.entries(agentPerf)) {
@@ -680,7 +810,16 @@ program
680
810
  }
681
811
  console.log('');
682
812
  }
683
- // Show discoveries
813
+ const showSummaries = loadSessionSummaries(showProjectPath ?? undefined);
814
+ if (showSummaries.length > 0) {
815
+ console.log(chalk.white('📅 Sessions:'));
816
+ console.log(` • Total recorded: ${showSummaries.length}`);
817
+ const lastS = showSummaries[showSummaries.length - 1];
818
+ if (lastS) {
819
+ console.log(` • Last: ${new Date(lastS.started_at).toLocaleString()}`);
820
+ }
821
+ console.log('');
822
+ }
684
823
  const discoveries = readDiscoveries(process.cwd());
685
824
  if (discoveries.total_discoveries > 0) {
686
825
  console.log(chalk.white('💡 Discoveries:'));
@@ -695,7 +834,6 @@ program
695
834
  }
696
835
  console.log('');
697
836
  }
698
- // Show auto-discovery configuration status
699
837
  try {
700
838
  const { loadDiscoveryConfig } = await import('../learning/config.js');
701
839
  const discoveryConfig = loadDiscoveryConfig(process.cwd());
@@ -704,7 +842,6 @@ program
704
842
  console.log(` • Min Confidence: ${discoveryConfig.minConfidence}`);
705
843
  console.log(` • Limits: ${discoveryConfig.maxPerSession}/session, ${discoveryConfig.maxPerDay}/day`);
706
844
  console.log(` • Dedup Window: ${discoveryConfig.deduplicationWindowDays} days`);
707
- // Show recent auto-discoveries with confidence scores
708
845
  if (discoveries.total_discoveries > 0) {
709
846
  const recentAuto = [...discoveries.project_discoveries]
710
847
  .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())
@@ -721,29 +858,31 @@ program
721
858
  console.log('');
722
859
  }
723
860
  catch {
724
- // Silent failure - auto-discovery status is non-critical
725
861
  }
726
862
  return;
727
863
  }
728
864
  if (options.analyze) {
865
+ const { projectPath: analyzeProjectPath } = resolveProjectContext();
866
+ if (!analyzeProjectPath && !options.global) {
867
+ console.log('Not inside a recognized project directory. Showing global data.');
868
+ }
729
869
  console.log(chalk.blue('Analyzing feedback...\n'));
730
- const feedback = readFeedbackLog();
731
- const patterns = extractPatterns(feedback);
870
+ const feedback = readFeedbackLog(analyzeProjectPath ?? undefined);
871
+ const patterns = extractPatterns(feedback, undefined, undefined, analyzeProjectPath ?? undefined);
732
872
  console.log(chalk.white(`Found ${patterns.length} patterns:\n`));
733
873
  for (const p of patterns) {
734
874
  console.log(` [${(p.confidence * 100).toFixed(0)}%] ${p.pattern.substring(0, 60)}...`);
735
875
  console.log(chalk.gray(` Seen ${p.evidence_count}x, scope: ${p.scope}, category: ${p.category}`));
736
876
  }
737
- // Update preferences
738
877
  const currentPrefs = readJsonFile(join(learningDir, 'user-preferences.json'), createDefaultPreferences());
739
- const updatedPrefs = updatePreferences(currentPrefs, feedback, patterns);
878
+ const updatedPrefs = updatePreferences(currentPrefs, feedback, patterns, analyzeProjectPath ?? undefined);
740
879
  writeJsonFile(join(learningDir, 'user-preferences.json'), updatedPrefs);
741
- // Update agent performance
742
880
  const agentPerf = evaluateAgentPerformance(feedback);
743
- // Extract task patterns for routing recommendations
744
881
  const agentPerfObj = Object.fromEntries(agentPerf);
745
- // Load existing performance to preserve existing task_patterns
746
- const existingPerf = readJsonFile(join(learningDir, 'agent-performance.json'), {});
882
+ const agentPerfWritePath = analyzeProjectPath
883
+ ? join(getProjectScopedDir(analyzeProjectPath), 'agent-performance.json')
884
+ : join(learningDir, 'agent-performance.json');
885
+ const existingPerf = readJsonFile(agentPerfWritePath, {});
747
886
  // Analyze task patterns per agent
748
887
  for (const [agentName, perf] of Object.entries(agentPerfObj)) {
749
888
  const agentFeedback = feedback.filter(f => f.agent_used === agentName);
@@ -776,15 +915,13 @@ program
776
915
  });
777
916
  }
778
917
  if (taskPatterns.length > 0) {
779
- perf.task_patterns = taskPatterns.slice(0, 10); // Limit to top 10 patterns
918
+ perf.task_patterns = taskPatterns.slice(0, 10);
780
919
  }
781
920
  else if (existingPerf[agentName]?.task_patterns) {
782
- // Preserve existing patterns if no new ones found
783
921
  perf.task_patterns = existingPerf[agentName].task_patterns;
784
922
  }
785
923
  }
786
- // Write with patterns included
787
- writeJsonFile(join(learningDir, 'agent-performance.json'), agentPerfObj);
924
+ writeJsonFile(agentPerfWritePath, agentPerfObj);
788
925
  const totalPatterns = Object.values(agentPerfObj).reduce((sum, p) => sum + (p.task_patterns?.length || 0), 0);
789
926
  if (totalPatterns > 0) {
790
927
  console.log(chalk.green(`✓ ${totalPatterns} task patterns extracted for routing optimization.`));
@@ -828,19 +965,42 @@ program
828
965
  }
829
966
  if (options.forget) {
830
967
  if (options.project) {
831
- const projectDir = join(process.cwd(), '.olympus', 'learning');
832
- if (existsSync(projectDir)) {
833
- rmSync(projectDir, { recursive: true });
834
- console.log(chalk.green('✓ Project learnings forgotten.'));
835
- }
836
- else {
837
- console.log(chalk.yellow('No project learnings found.'));
968
+ try {
969
+ const targetSlug = typeof options.project === 'string'
970
+ ? options.project
971
+ : deriveProjectSlug(resolveProjectRoot(process.cwd()));
972
+ const targetPath = join(getLearningDir(), 'projects', targetSlug);
973
+ if (!options.confirm) {
974
+ if (!existsSync(targetPath)) {
975
+ console.log(`Project directory not found: ${targetSlug}`);
976
+ return;
977
+ }
978
+ const files = readdirSync(targetPath);
979
+ console.log(`Would delete: ${targetPath}`);
980
+ for (const f of files) {
981
+ const fp = join(targetPath, f);
982
+ let sz = 0;
983
+ try {
984
+ sz = statSync(fp).size;
985
+ }
986
+ catch { }
987
+ console.log(` ${f} (${sz} bytes)`);
988
+ }
989
+ console.log('Run with --confirm to delete.');
990
+ }
991
+ else {
992
+ if (!existsSync(targetPath)) {
993
+ console.log(`Project directory not found: ${targetSlug}`);
994
+ return;
995
+ }
996
+ const fileCount = readdirSync(targetPath).length;
997
+ rmSync(targetPath, { recursive: true, force: true });
998
+ console.log(chalk.green(`Deleted project data for ${targetSlug} (${fileCount} files removed).`));
999
+ }
838
1000
  }
839
- // DELETE SESSION STATE (privacy fix)
840
- const sessionStatePath = getSessionStatePath(process.cwd());
841
- if (existsSync(sessionStatePath)) {
842
- rmSync(sessionStatePath);
843
- console.log(chalk.green('✓ Session state deleted.'));
1001
+ catch (err) {
1002
+ const msg = err instanceof Error ? err.message : String(err);
1003
+ console.error(`[Olympus] Failed to forget project: ${msg}`);
844
1004
  }
845
1005
  }
846
1006
  else {
@@ -855,15 +1015,35 @@ program
855
1015
  }
856
1016
  return;
857
1017
  }
858
- if (options.export) {
859
- const data = {
860
- feedback: readFeedbackLog(),
861
- preferences: readJsonFile(join(learningDir, 'user-preferences.json'), null),
862
- agentPerformance: readJsonFile(join(learningDir, 'agent-performance.json'), {}),
863
- discoveries: readDiscoveries(process.cwd()),
864
- exportedAt: new Date().toISOString(),
865
- };
866
- console.log(JSON.stringify(data, null, 2));
1018
+ if (options.export !== undefined) {
1019
+ try {
1020
+ const { projectPath: exportProjectPath, isInProject: exportIsInProject } = resolveProjectContext();
1021
+ if (!exportIsInProject) {
1022
+ console.log('Not inside a recognized project directory. Showing global data.');
1023
+ return;
1024
+ }
1025
+ const exportProjDir = getProjectScopedDir(exportProjectPath);
1026
+ const readRaw = (file) => {
1027
+ const p = join(exportProjDir, file);
1028
+ return existsSync(p) ? readFileSync(p, 'utf-8') : null;
1029
+ };
1030
+ const output = JSON.stringify({
1031
+ feedback_log: readRaw('feedback-log.jsonl'),
1032
+ agent_performance: readRaw('agent-performance.json'),
1033
+ session_insights: readRaw('session-insights.json'),
1034
+ }, null, 2);
1035
+ if (typeof options.export === 'string' && options.export.length > 0) {
1036
+ writeFileSync(options.export, output, 'utf-8');
1037
+ console.log(chalk.green(`✓ Exported to ${options.export}`));
1038
+ }
1039
+ else {
1040
+ console.log(output);
1041
+ }
1042
+ }
1043
+ catch (err) {
1044
+ const msg = err instanceof Error ? err.message : String(err);
1045
+ console.error(`[Olympus] Failed to export: ${msg}`);
1046
+ }
867
1047
  return;
868
1048
  }
869
1049
  if (options.import) {
@@ -893,9 +1073,12 @@ program
893
1073
  return;
894
1074
  }
895
1075
  if (options.efficiency) {
896
- const feedback = readFeedbackLog();
897
- const agentPerfRaw = readJsonFile(join(learningDir, 'agent-performance.json'), {});
898
- // Compute agent performance from feedback if not already computed
1076
+ const { projectPath: effProjectPath } = resolveProjectContext();
1077
+ if (!effProjectPath && !options.global) {
1078
+ console.log('Not inside a recognized project directory. Showing global data.');
1079
+ }
1080
+ const feedback = readFeedbackLog(effProjectPath ?? undefined);
1081
+ const agentPerfRaw = readAgentPerformance(effProjectPath ?? undefined);
899
1082
  const agentPerf = {};
900
1083
  const agentNames = new Set(feedback.filter(f => f.agent_used).map(f => f.agent_used));
901
1084
  for (const agentName of agentNames) {
@@ -904,7 +1087,6 @@ program
904
1087
  agentPerf[agentName] = perf;
905
1088
  }
906
1089
  }
907
- // Merge with existing performance data
908
1090
  Object.assign(agentPerf, agentPerfRaw);
909
1091
  const agents = Object.values(agentPerf).filter(p => hasEfficiencyMetrics(p));
910
1092
  if (agents.length === 0) {