olympus-ai 4.1.0 → 4.2.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.
Files changed (57) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/dist/cli/index.js +196 -53
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/features/workflow-engine/construction/decomposition.d.ts +7 -7
  5. package/dist/features/workflow-engine/construction/decomposition.d.ts.map +1 -1
  6. package/dist/features/workflow-engine/construction/decomposition.js +15 -14
  7. package/dist/features/workflow-engine/construction/decomposition.js.map +1 -1
  8. package/dist/hooks/olympus-hooks.cjs +219 -219
  9. package/dist/installer/index.d.ts +1 -1
  10. package/dist/installer/index.js +1 -1
  11. package/dist/learning/__tests__/cli-operations.test.d.ts +2 -0
  12. package/dist/learning/__tests__/cli-operations.test.d.ts.map +1 -0
  13. package/dist/learning/__tests__/cli-operations.test.js +164 -0
  14. package/dist/learning/__tests__/cli-operations.test.js.map +1 -0
  15. package/dist/learning/__tests__/intelligence.test.d.ts +2 -0
  16. package/dist/learning/__tests__/intelligence.test.d.ts.map +1 -0
  17. package/dist/learning/__tests__/intelligence.test.js +233 -0
  18. package/dist/learning/__tests__/intelligence.test.js.map +1 -0
  19. package/dist/learning/__tests__/session-insights.test.d.ts +2 -0
  20. package/dist/learning/__tests__/session-insights.test.d.ts.map +1 -0
  21. package/dist/learning/__tests__/session-insights.test.js +176 -0
  22. package/dist/learning/__tests__/session-insights.test.js.map +1 -0
  23. package/dist/learning/aggregation.d.ts +1 -11
  24. package/dist/learning/aggregation.d.ts.map +1 -1
  25. package/dist/learning/aggregation.js +28 -11
  26. package/dist/learning/aggregation.js.map +1 -1
  27. package/dist/learning/baselines.d.ts +2 -32
  28. package/dist/learning/baselines.d.ts.map +1 -1
  29. package/dist/learning/baselines.js +3 -70
  30. package/dist/learning/baselines.js.map +1 -1
  31. package/dist/learning/cleanup.d.ts +9 -0
  32. package/dist/learning/cleanup.d.ts.map +1 -1
  33. package/dist/learning/cleanup.js +96 -3
  34. package/dist/learning/cleanup.js.map +1 -1
  35. package/dist/learning/hooks/learned-context.d.ts.map +1 -1
  36. package/dist/learning/hooks/learned-context.js +12 -5
  37. package/dist/learning/hooks/learned-context.js.map +1 -1
  38. package/dist/learning/pattern-extractor.d.ts +2 -1
  39. package/dist/learning/pattern-extractor.d.ts.map +1 -1
  40. package/dist/learning/pattern-extractor.js +7 -4
  41. package/dist/learning/pattern-extractor.js.map +1 -1
  42. package/dist/learning/routing.d.ts +2 -7
  43. package/dist/learning/routing.d.ts.map +1 -1
  44. package/dist/learning/routing.js +18 -17
  45. package/dist/learning/routing.js.map +1 -1
  46. package/dist/learning/session-insights.d.ts +3 -0
  47. package/dist/learning/session-insights.d.ts.map +1 -0
  48. package/dist/learning/session-insights.js +98 -0
  49. package/dist/learning/session-insights.js.map +1 -0
  50. package/dist/learning/session-state.js +3 -3
  51. package/dist/learning/session-state.js.map +1 -1
  52. package/dist/learning/types.d.ts +31 -0
  53. package/dist/learning/types.d.ts.map +1 -1
  54. package/package.json +100 -100
  55. package/resources/rules/inception/units-generation.md +218 -204
  56. package/resources/skills/continue/SKILL.md +1 -1
  57. package/resources/skills/plan/SKILL.md +18 -11
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "olympus-ai",
3
- "version": "4.1.0",
3
+ "version": "4.2.0",
4
4
  "description": "Olympus: Multi-agent orchestration for Claude Code. Summon the gods of code.",
5
5
  "author": {
6
6
  "name": "mikev10",
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
20
  import { install as installOlympus, uninstall as uninstallOlympus, isInstalled, getInstallInfo } from '../installer/index.js';
21
- import { readFeedbackLog, readJsonFile, getLearningDir, writeJsonFile, appendFeedback, updateAgentPerformance, loadSessionSummaries, getLastSessionSummary, } from '../learning/storage.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
@@ -574,18 +575,72 @@ program
574
575
  .option('--suggest', 'Show suggested prompt improvements')
575
576
  .option('--apply', 'Apply suggested improvements')
576
577
  .option('-f, --forget', 'Forget all learnings')
577
- .option('-p, --project', 'Scope to current project (with --forget)')
578
- .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')
579
580
  .option('-i, --import <file>', 'Import learnings from JSON')
580
581
  .option('--efficiency', 'Show agent efficiency rankings and token metrics')
581
582
  .option('--show-costs', 'Show cost breakdown by model and agent')
582
583
  .option('--budget-status', 'Show current session token budget status')
583
584
  .option('--last-session', 'Show last session summary')
584
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)')
585
589
  .action(async (options) => {
586
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
+ }
587
637
  if (options.lastSession) {
588
- 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;
589
644
  if (!summary) {
590
645
  console.log(chalk.yellow('No session summaries recorded yet. Complete a session first.'));
591
646
  return;
@@ -615,9 +670,13 @@ program
615
670
  return;
616
671
  }
617
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
+ }
618
677
  const n = typeof options.sessions === 'string' ? parseInt(options.sessions) : 10;
619
678
  const count = isNaN(n) ? 10 : n;
620
- const allSummaries = loadSessionSummaries();
679
+ const allSummaries = loadSessionSummaries(sessProjectPath ?? undefined);
621
680
  if (allSummaries.length === 0) {
622
681
  console.log(chalk.yellow('No session summaries recorded yet. Complete a session first.'));
623
682
  return;
@@ -669,6 +728,24 @@ program
669
728
  if (options.stats) {
670
729
  const stats = generateLearningStats(process.cwd());
671
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 { }
672
749
  return;
673
750
  }
674
751
  if (options.cleanup) {
@@ -682,10 +759,14 @@ program
682
759
  return;
683
760
  }
684
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
+ }
685
766
  console.log(chalk.blue.bold('\n╭─────────────────────────────────────────────────────────────╮'));
686
767
  console.log(chalk.blue.bold('│ OLYMPUS LEARNING STATUS │'));
687
768
  console.log(chalk.blue.bold('╰─────────────────────────────────────────────────────────────╯\n'));
688
- const feedback = readFeedbackLog();
769
+ const feedback = readFeedbackLog(showProjectPath ?? undefined);
689
770
  const revisions = feedback.filter(f => f.event_type === 'revision').length;
690
771
  const cancellations = feedback.filter(f => f.event_type === 'cancellation').length;
691
772
  const successes = feedback.filter(f => f.event_type === 'success').length;
@@ -698,8 +779,18 @@ program
698
779
  console.log(` • Verbosity: ${prefs.verbosity}`);
699
780
  if (prefs.autonomy !== 'unknown')
700
781
  console.log(` • Autonomy: ${prefs.autonomy}`);
701
- for (const rule of prefs.explicit_rules.slice(0, 3)) {
702
- 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}`);
703
794
  }
704
795
  console.log('');
705
796
  }
@@ -710,7 +801,7 @@ program
710
801
  }
711
802
  console.log('');
712
803
  }
713
- const agentPerf = readJsonFile(join(learningDir, 'agent-performance.json'), {});
804
+ const agentPerf = readAgentPerformance(showProjectPath ?? undefined);
714
805
  if (Object.keys(agentPerf).length > 0) {
715
806
  console.log(chalk.white('🤖 Agent Performance:'));
716
807
  for (const [name, perf] of Object.entries(agentPerf)) {
@@ -719,7 +810,16 @@ program
719
810
  }
720
811
  console.log('');
721
812
  }
722
- // 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
+ }
723
823
  const discoveries = readDiscoveries(process.cwd());
724
824
  if (discoveries.total_discoveries > 0) {
725
825
  console.log(chalk.white('💡 Discoveries:'));
@@ -734,7 +834,6 @@ program
734
834
  }
735
835
  console.log('');
736
836
  }
737
- // Show auto-discovery configuration status
738
837
  try {
739
838
  const { loadDiscoveryConfig } = await import('../learning/config.js');
740
839
  const discoveryConfig = loadDiscoveryConfig(process.cwd());
@@ -743,7 +842,6 @@ program
743
842
  console.log(` • Min Confidence: ${discoveryConfig.minConfidence}`);
744
843
  console.log(` • Limits: ${discoveryConfig.maxPerSession}/session, ${discoveryConfig.maxPerDay}/day`);
745
844
  console.log(` • Dedup Window: ${discoveryConfig.deduplicationWindowDays} days`);
746
- // Show recent auto-discoveries with confidence scores
747
845
  if (discoveries.total_discoveries > 0) {
748
846
  const recentAuto = [...discoveries.project_discoveries]
749
847
  .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())
@@ -760,29 +858,31 @@ program
760
858
  console.log('');
761
859
  }
762
860
  catch {
763
- // Silent failure - auto-discovery status is non-critical
764
861
  }
765
862
  return;
766
863
  }
767
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
+ }
768
869
  console.log(chalk.blue('Analyzing feedback...\n'));
769
- const feedback = readFeedbackLog();
770
- const patterns = extractPatterns(feedback);
870
+ const feedback = readFeedbackLog(analyzeProjectPath ?? undefined);
871
+ const patterns = extractPatterns(feedback, undefined, undefined, analyzeProjectPath ?? undefined);
771
872
  console.log(chalk.white(`Found ${patterns.length} patterns:\n`));
772
873
  for (const p of patterns) {
773
874
  console.log(` [${(p.confidence * 100).toFixed(0)}%] ${p.pattern.substring(0, 60)}...`);
774
875
  console.log(chalk.gray(` Seen ${p.evidence_count}x, scope: ${p.scope}, category: ${p.category}`));
775
876
  }
776
- // Update preferences
777
877
  const currentPrefs = readJsonFile(join(learningDir, 'user-preferences.json'), createDefaultPreferences());
778
- const updatedPrefs = updatePreferences(currentPrefs, feedback, patterns);
878
+ const updatedPrefs = updatePreferences(currentPrefs, feedback, patterns, analyzeProjectPath ?? undefined);
779
879
  writeJsonFile(join(learningDir, 'user-preferences.json'), updatedPrefs);
780
- // Update agent performance
781
880
  const agentPerf = evaluateAgentPerformance(feedback);
782
- // Extract task patterns for routing recommendations
783
881
  const agentPerfObj = Object.fromEntries(agentPerf);
784
- // Load existing performance to preserve existing task_patterns
785
- 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, {});
786
886
  // Analyze task patterns per agent
787
887
  for (const [agentName, perf] of Object.entries(agentPerfObj)) {
788
888
  const agentFeedback = feedback.filter(f => f.agent_used === agentName);
@@ -815,15 +915,13 @@ program
815
915
  });
816
916
  }
817
917
  if (taskPatterns.length > 0) {
818
- perf.task_patterns = taskPatterns.slice(0, 10); // Limit to top 10 patterns
918
+ perf.task_patterns = taskPatterns.slice(0, 10);
819
919
  }
820
920
  else if (existingPerf[agentName]?.task_patterns) {
821
- // Preserve existing patterns if no new ones found
822
921
  perf.task_patterns = existingPerf[agentName].task_patterns;
823
922
  }
824
923
  }
825
- // Write with patterns included
826
- writeJsonFile(join(learningDir, 'agent-performance.json'), agentPerfObj);
924
+ writeJsonFile(agentPerfWritePath, agentPerfObj);
827
925
  const totalPatterns = Object.values(agentPerfObj).reduce((sum, p) => sum + (p.task_patterns?.length || 0), 0);
828
926
  if (totalPatterns > 0) {
829
927
  console.log(chalk.green(`✓ ${totalPatterns} task patterns extracted for routing optimization.`));
@@ -867,19 +965,42 @@ program
867
965
  }
868
966
  if (options.forget) {
869
967
  if (options.project) {
870
- const projectDir = join(process.cwd(), '.olympus', 'learning');
871
- if (existsSync(projectDir)) {
872
- rmSync(projectDir, { recursive: true });
873
- console.log(chalk.green('✓ Project learnings forgotten.'));
874
- }
875
- else {
876
- 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
+ }
877
1000
  }
878
- // DELETE SESSION STATE (privacy fix)
879
- const sessionStatePath = getSessionStatePath(process.cwd());
880
- if (existsSync(sessionStatePath)) {
881
- rmSync(sessionStatePath);
882
- 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}`);
883
1004
  }
884
1005
  }
885
1006
  else {
@@ -894,15 +1015,35 @@ program
894
1015
  }
895
1016
  return;
896
1017
  }
897
- if (options.export) {
898
- const data = {
899
- feedback: readFeedbackLog(),
900
- preferences: readJsonFile(join(learningDir, 'user-preferences.json'), null),
901
- agentPerformance: readJsonFile(join(learningDir, 'agent-performance.json'), {}),
902
- discoveries: readDiscoveries(process.cwd()),
903
- exportedAt: new Date().toISOString(),
904
- };
905
- 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
+ }
906
1047
  return;
907
1048
  }
908
1049
  if (options.import) {
@@ -932,9 +1073,12 @@ program
932
1073
  return;
933
1074
  }
934
1075
  if (options.efficiency) {
935
- const feedback = readFeedbackLog();
936
- const agentPerfRaw = readJsonFile(join(learningDir, 'agent-performance.json'), {});
937
- // 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);
938
1082
  const agentPerf = {};
939
1083
  const agentNames = new Set(feedback.filter(f => f.agent_used).map(f => f.agent_used));
940
1084
  for (const agentName of agentNames) {
@@ -943,7 +1087,6 @@ program
943
1087
  agentPerf[agentName] = perf;
944
1088
  }
945
1089
  }
946
- // Merge with existing performance data
947
1090
  Object.assign(agentPerf, agentPerfRaw);
948
1091
  const agents = Object.values(agentPerf).filter(p => hasEfficiencyMetrics(p));
949
1092
  if (agents.length === 0) {