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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +71 -10
- package/dist/cli/index.js +239 -57
- package/dist/cli/index.js.map +1 -1
- package/dist/features/workflow-engine/checkpoint.d.ts +1 -0
- package/dist/features/workflow-engine/checkpoint.d.ts.map +1 -1
- package/dist/features/workflow-engine/checkpoint.js +54 -0
- package/dist/features/workflow-engine/checkpoint.js.map +1 -1
- package/dist/features/workflow-engine/construction/decomposition.d.ts +7 -7
- package/dist/features/workflow-engine/construction/decomposition.d.ts.map +1 -1
- package/dist/features/workflow-engine/construction/decomposition.js +15 -14
- package/dist/features/workflow-engine/construction/decomposition.js.map +1 -1
- package/dist/features/workflow-engine/engine.d.ts +1 -0
- package/dist/features/workflow-engine/engine.d.ts.map +1 -1
- package/dist/features/workflow-engine/engine.js +25 -17
- package/dist/features/workflow-engine/engine.js.map +1 -1
- package/dist/features/workflow-engine/phase-types.d.ts +2 -0
- package/dist/features/workflow-engine/phase-types.d.ts.map +1 -1
- package/dist/features/workflow-engine/state-file.d.ts.map +1 -1
- package/dist/features/workflow-engine/state-file.js +3 -1
- package/dist/features/workflow-engine/state-file.js.map +1 -1
- package/dist/hooks/olympus-hooks.cjs +237 -229
- package/dist/hooks/registrations/user-prompt-submit.d.ts.map +1 -1
- package/dist/hooks/registrations/user-prompt-submit.js +4 -0
- package/dist/hooks/registrations/user-prompt-submit.js.map +1 -1
- package/dist/installer/index.d.ts +13 -1
- package/dist/installer/index.d.ts.map +1 -1
- package/dist/installer/index.js +236 -12
- package/dist/installer/index.js.map +1 -1
- package/dist/learning/__tests__/cli-operations.test.d.ts +2 -0
- package/dist/learning/__tests__/cli-operations.test.d.ts.map +1 -0
- package/dist/learning/__tests__/cli-operations.test.js +164 -0
- package/dist/learning/__tests__/cli-operations.test.js.map +1 -0
- package/dist/learning/__tests__/intelligence.test.d.ts +2 -0
- package/dist/learning/__tests__/intelligence.test.d.ts.map +1 -0
- package/dist/learning/__tests__/intelligence.test.js +233 -0
- package/dist/learning/__tests__/intelligence.test.js.map +1 -0
- package/dist/learning/__tests__/migration.test.d.ts +2 -0
- package/dist/learning/__tests__/migration.test.d.ts.map +1 -0
- package/dist/learning/__tests__/migration.test.js +103 -0
- package/dist/learning/__tests__/migration.test.js.map +1 -0
- package/dist/learning/__tests__/session-insights.test.d.ts +2 -0
- package/dist/learning/__tests__/session-insights.test.d.ts.map +1 -0
- package/dist/learning/__tests__/session-insights.test.js +176 -0
- package/dist/learning/__tests__/session-insights.test.js.map +1 -0
- package/dist/learning/__tests__/storage-migration.test.d.ts +2 -0
- package/dist/learning/__tests__/storage-migration.test.d.ts.map +1 -0
- package/dist/learning/__tests__/storage-migration.test.js +171 -0
- package/dist/learning/__tests__/storage-migration.test.js.map +1 -0
- package/dist/learning/aggregation.d.ts +1 -11
- package/dist/learning/aggregation.d.ts.map +1 -1
- package/dist/learning/aggregation.js +28 -11
- package/dist/learning/aggregation.js.map +1 -1
- package/dist/learning/baselines.d.ts +2 -32
- package/dist/learning/baselines.d.ts.map +1 -1
- package/dist/learning/baselines.js +3 -70
- package/dist/learning/baselines.js.map +1 -1
- package/dist/learning/cleanup.d.ts +9 -0
- package/dist/learning/cleanup.d.ts.map +1 -1
- package/dist/learning/cleanup.js +96 -3
- package/dist/learning/cleanup.js.map +1 -1
- package/dist/learning/hooks/learned-context.d.ts.map +1 -1
- package/dist/learning/hooks/learned-context.js +12 -5
- package/dist/learning/hooks/learned-context.js.map +1 -1
- package/dist/learning/migration.d.ts +11 -0
- package/dist/learning/migration.d.ts.map +1 -0
- package/dist/learning/migration.js +91 -0
- package/dist/learning/migration.js.map +1 -0
- package/dist/learning/pattern-extractor.d.ts +2 -1
- package/dist/learning/pattern-extractor.d.ts.map +1 -1
- package/dist/learning/pattern-extractor.js +7 -4
- package/dist/learning/pattern-extractor.js.map +1 -1
- package/dist/learning/preference-learner.d.ts +1 -2
- package/dist/learning/preference-learner.d.ts.map +1 -1
- package/dist/learning/preference-learner.js +12 -6
- package/dist/learning/preference-learner.js.map +1 -1
- package/dist/learning/routing.d.ts +2 -7
- package/dist/learning/routing.d.ts.map +1 -1
- package/dist/learning/routing.js +18 -17
- package/dist/learning/routing.js.map +1 -1
- package/dist/learning/session-insights.d.ts +3 -0
- package/dist/learning/session-insights.d.ts.map +1 -0
- package/dist/learning/session-insights.js +98 -0
- package/dist/learning/session-insights.js.map +1 -0
- package/dist/learning/session-state.d.ts.map +1 -1
- package/dist/learning/session-state.js +7 -3
- package/dist/learning/session-state.js.map +1 -1
- package/dist/learning/storage.d.ts +5 -8
- package/dist/learning/storage.d.ts.map +1 -1
- package/dist/learning/storage.js +60 -17
- package/dist/learning/storage.js.map +1 -1
- package/dist/learning/types.d.ts +39 -1
- package/dist/learning/types.d.ts.map +1 -1
- package/dist/learning/types.js.map +1 -1
- package/package.json +3 -2
- package/resources/agents/metis.md +6 -0
- package/resources/rules/inception/units-generation.md +218 -204
- package/resources/skills/continue/SKILL.md +2 -2
- package/resources/skills/plan/SKILL.md +20 -13
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
|
|
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
|
|
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
|
-
|
|
131
|
-
|
|
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
|
|
605
|
-
|
|
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,
|
|
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
|
-
|
|
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', '
|
|
539
|
-
.option('-e, --export', 'Export
|
|
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
|
|
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
|
-
|
|
663
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
746
|
-
|
|
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);
|
|
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
|
-
|
|
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
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
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
|
-
|
|
840
|
-
|
|
841
|
-
|
|
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
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
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
|
|
897
|
-
|
|
898
|
-
|
|
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) {
|