@ekkos/cli 1.3.8 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -38,15 +38,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
38
38
  };
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
40
  const commander_1 = require("commander");
41
- // The `run` command is the default (bare `ekkos`), so it stays eager.
42
- // `dashboardCommand` is eager because `run --dashboard` spawns `ekkos dashboard` in tmux.
43
- // All other command modules are loaded lazily inside .action() callbacks.
44
- const run_1 = require("./commands/run");
45
- const dashboard_1 = require("./commands/dashboard");
41
+ // Command modules loaded lazily inside .action() callbacks to avoid
42
+ // importing most heavy modules (node-pty, swarm, etc.) on every launch.
43
+ // Commands that must work in subprocess mode (usage/dashboard) are registered eagerly.
46
44
  const chalk_1 = __importDefault(require("chalk"));
47
45
  const fs = __importStar(require("fs"));
48
46
  const path = __importStar(require("path"));
49
47
  const child_process_1 = require("child_process");
48
+ const index_1 = require("./commands/usage/index");
49
+ const dashboard_1 = require("./commands/dashboard");
50
50
  // Get version from package.json (CommonJS compatible)
51
51
  const pkgPath = path.resolve(__dirname, '../package.json');
52
52
  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
@@ -139,7 +139,7 @@ commander_1.program
139
139
  commands: [
140
140
  { name: 'init', desc: 'First-time setup — authenticate and configure IDEs' },
141
141
  { name: 'scan', desc: 'Scan repo structure and seed ekkOS system registry' },
142
- { name: 'docs', desc: 'Watch and regenerate local ekkOS_CONTEXT.md files' },
142
+ { name: 'docs', desc: 'Cortex: Watch and regenerate local ekkOS_CONTEXT.md files' },
143
143
  { name: 'status', desc: 'Show overall system status (Memory, Sync Daemon, Auth)' },
144
144
  { name: 'doctor', desc: 'Check system prerequisites and fix runaway processes' },
145
145
  ],
@@ -237,13 +237,28 @@ commander_1.program
237
237
  path: options.path,
238
238
  });
239
239
  });
240
- // Local living docs command
240
+ // ekkOS Cortex command group
241
241
  const docsCmd = commander_1.program
242
242
  .command('docs')
243
- .description('Manage local ekkOS_CONTEXT.md living docs');
243
+ .alias('cortex')
244
+ .description('ekkOS Cortex: Manage local living docs (ekkOS_CONTEXT.md)');
245
+ docsCmd
246
+ .command('init')
247
+ .description('Initialize Cortex: Discover systems and perform a one-shot generation of all ekkOS_CONTEXT.md files')
248
+ .option('-p, --path <path>', 'Path to repository root (default: current directory)')
249
+ .option('--timezone <iana-tz>', 'IANA timezone for timestamps (default: local machine timezone)')
250
+ .option('--no-seed', 'Do not seed the platform registry')
251
+ .action(async (options) => {
252
+ const { initLivingDocs } = await Promise.resolve().then(() => __importStar(require('./commands/init-living-docs')));
253
+ await initLivingDocs({
254
+ path: options.path,
255
+ timeZone: options.timezone,
256
+ noSeed: options.seed === false,
257
+ });
258
+ });
244
259
  docsCmd
245
260
  .command('watch')
246
- .description('Watch the local workspace and keep ekkOS_CONTEXT.md files current on disk')
261
+ .description('Watch the local workspace and keep Cortex docs current on disk')
247
262
  .option('-p, --path <path>', 'Path to watch (default: current directory)')
248
263
  .option('--timezone <iana-tz>', 'IANA timezone for timestamps (default: local machine timezone)')
249
264
  .option('--poll-interval-ms <ms>', 'Polling interval fallback in milliseconds', (value) => parseInt(value, 10))
@@ -259,6 +274,30 @@ docsCmd
259
274
  noSeed: options.seed === false,
260
275
  });
261
276
  });
277
+ docsCmd
278
+ .command('setup-ci')
279
+ .description('Generate a GitHub Actions workflow to automatically validate Cortex docs on PRs')
280
+ .option('-p, --path <path>', 'Path to repository root (default: current directory)')
281
+ .action(async (options) => {
282
+ const { setupCiCommand } = await Promise.resolve().then(() => __importStar(require('./commands/setup-ci')));
283
+ const exitCode = await setupCiCommand({ repoRoot: options.path || process.cwd() });
284
+ process.exit(exitCode);
285
+ });
286
+ docsCmd
287
+ .command('validate')
288
+ .description('Validate Cortex docs (ekkOS_CONTEXT.md) in a repository (CI/CD friendly)')
289
+ .option('-p, --path <path>', 'Path to repository root (default: current directory)')
290
+ .option('--fix', 'Attempt to automatically fix content hash mismatches')
291
+ .option('--system <system_id>', 'Filter validation to a specific system_id')
292
+ .action(async (options) => {
293
+ const { validateLivingDocsCommand } = await Promise.resolve().then(() => __importStar(require('./commands/validate-living-docs')));
294
+ const exitCode = await validateLivingDocsCommand({
295
+ repoRoot: options.path || process.cwd(),
296
+ fix: options.fix,
297
+ systemFilter: options.system,
298
+ });
299
+ process.exit(exitCode);
300
+ });
262
301
  // Status command
263
302
  commander_1.program
264
303
  .command('status')
@@ -320,6 +359,10 @@ commander_1.program
320
359
  .description('Launch Claude Code with ekkOS memory proxy (IPC compression + pattern injection)')
321
360
  .option('-s, --session <name>', 'Session name to restore on clear')
322
361
  .option('-b, --bypass', 'Enable bypass permissions mode (dangerously skip all permission checks)')
362
+ .option('--model [model]', 'Launch model: pass a model ID, or omit value to open the interactive selector')
363
+ .option('--context-window <window>', 'Context lane override: auto, 200k, or 1m')
364
+ .option('--continue-last', 'Continue most recent Claude conversation')
365
+ .option('--resume-session <id>', 'Resume Claude conversation by ID')
323
366
  .option('-v, --verbose', 'Show debug output')
324
367
  .option('-d, --doctor', 'Run diagnostics before starting')
325
368
  .option('-r, --research', 'Auto-run research agent on startup (scans arXiv for new AI papers)')
@@ -327,8 +370,9 @@ commander_1.program
327
370
  .option('--skip-proxy', 'Skip API proxy (use direct Anthropic API, disables seamless context eviction)')
328
371
  .option('--dashboard', 'Launch with live usage dashboard in an isolated 60/40 tmux split (requires tmux)')
329
372
  .option('--add-dir <dirs...>', 'Additional directories Claude Code can access (outside working directory)')
330
- .action((options) => {
331
- (0, run_1.run)({
373
+ .action(async (options) => {
374
+ const { run } = await Promise.resolve().then(() => __importStar(require('./commands/run')));
375
+ run({
332
376
  session: options.session,
333
377
  bypass: options.bypass,
334
378
  verbose: options.verbose,
@@ -338,6 +382,38 @@ commander_1.program
338
382
  noProxy: options.skipProxy,
339
383
  dashboard: options.dashboard,
340
384
  addDirs: options.addDir,
385
+ model: options.model,
386
+ contextWindow: options.contextWindow,
387
+ continueLast: options.continueLast,
388
+ resumeSession: options.resumeSession,
389
+ });
390
+ });
391
+ commander_1.program
392
+ .command('pulse')
393
+ .description('Launch PULSE mode (equivalent to `ekkos -b --model --dashboard`)')
394
+ .option('-s, --session <name>', 'Session name to restore on clear')
395
+ .option('--context-window <window>', 'Context lane override: auto, 200k, or 1m')
396
+ .option('--continue-last', 'Continue most recent Claude conversation')
397
+ .option('--resume-session <id>', 'Resume Claude conversation by ID')
398
+ .option('-v, --verbose', 'Show debug output')
399
+ .option('--skip-inject', 'Monitor-only mode (detect context wall but print instructions instead of auto-inject)')
400
+ .option('--skip-proxy', 'Skip API proxy (use direct Anthropic API, disables seamless context eviction)')
401
+ .option('--add-dir <dirs...>', 'Additional directories Claude Code can access (outside working directory)')
402
+ .action(async (options) => {
403
+ const { run } = await Promise.resolve().then(() => __importStar(require('./commands/run')));
404
+ run({
405
+ session: options.session,
406
+ bypass: true,
407
+ pulse: true,
408
+ verbose: options.verbose,
409
+ noInject: options.skipInject,
410
+ noProxy: options.skipProxy,
411
+ dashboard: true,
412
+ addDirs: options.addDir,
413
+ model: true,
414
+ contextWindow: options.contextWindow,
415
+ continueLast: options.continueLast,
416
+ resumeSession: options.resumeSession,
341
417
  });
342
418
  });
343
419
  // Gemini CLI — launch Gemini with ekkOS proxy
@@ -347,12 +423,14 @@ commander_1.program
347
423
  .option('-s, --session <name>', 'Session name')
348
424
  .option('-v, --verbose', 'Show debug output')
349
425
  .option('--skip-proxy', 'Skip API proxy (use direct Gemini API)')
426
+ .option('--dashboard', 'Launch Gemini with a dedicated live dashboard split (requires tmux)')
350
427
  .action(async (options) => {
351
428
  const { gemini } = await Promise.resolve().then(() => __importStar(require('./commands/gemini')));
352
429
  gemini({
353
430
  session: options.session,
354
431
  verbose: options.verbose,
355
432
  noProxy: options.skipProxy,
433
+ dashboard: options.dashboard,
356
434
  });
357
435
  });
358
436
  // Test Claude — bare proxy test (no CLI wrapper)
@@ -420,19 +498,9 @@ commander_1.program
420
498
  console.log(chalk_1.default.gray(' the ekkOS proxy — no shell hooks required.'));
421
499
  console.log('');
422
500
  });
423
- // Usage command lightweight stub, heavy handler loaded lazily
424
- commander_1.program
425
- .command('usage')
426
- .description('Token usage and cost tracking (daily, weekly, monthly, session)')
427
- .allowUnknownOption()
428
- .action(async () => {
429
- const { registerUsageCommand } = await Promise.resolve().then(() => __importStar(require('./commands/usage/index')));
430
- const { Command } = await Promise.resolve().then(() => __importStar(require('commander')));
431
- const usageProgram = new Command('ekkos');
432
- registerUsageCommand(usageProgram);
433
- usageProgram.parse(['node', 'ekkos', 'usage', ...process.argv.slice(3)]);
434
- });
435
- // Dashboard command — eager because `run --dashboard` spawns `ekkos dashboard` in tmux
501
+ // Usage and dashboard are registered synchronously so command parsing is reliable
502
+ // when launched as subprocesses (for example tmux split-pane dashboard mode).
503
+ (0, index_1.registerUsageCommand)(commander_1.program);
436
504
  commander_1.program.addCommand(dashboard_1.dashboardCommand);
437
505
  // Sessions command - list active Claude Code sessions (swarm support)
438
506
  commander_1.program
@@ -518,6 +586,7 @@ swarmCmd
518
586
  .option('--queen-strategy <strategy>', 'Queen strategy (adaptive-default, hierarchical-cascade, mesh-consensus)')
519
587
  .option('-v, --verbose', 'Show debug output')
520
588
  .action(async (options) => {
589
+ // Auto-open wizard when --task is missing
521
590
  if (!options.task) {
522
591
  const { swarmSetup } = await Promise.resolve().then(() => __importStar(require('./commands/swarm-setup')));
523
592
  swarmSetup();
@@ -537,7 +606,10 @@ swarmCmd
537
606
  swarmCmd
538
607
  .command('setup')
539
608
  .description('Interactive TUI wizard for configuring and launching a swarm')
540
- .action(async () => { const { swarmSetup } = await Promise.resolve().then(() => __importStar(require('./commands/swarm-setup'))); swarmSetup(); });
609
+ .action(async () => {
610
+ const { swarmSetup } = await Promise.resolve().then(() => __importStar(require('./commands/swarm-setup')));
611
+ swarmSetup();
612
+ });
541
613
  swarmCmd
542
614
  .command('swarm-dashboard')
543
615
  .description('Live swarm dashboard')
@@ -3,13 +3,18 @@
3
3
  * Used by: commands/run.ts, commands/test-claude.ts, commands/gemini.ts
4
4
  */
5
5
  export declare const EKKOS_PROXY_URL: string;
6
+ export interface ClaudeProxyLaunchOptions {
7
+ claudeCodeVersion?: string;
8
+ claudeProfile?: string;
9
+ claudeContextWindow?: 'auto' | '200k' | '1m';
10
+ }
6
11
  /**
7
12
  * Build a fully-qualified proxy URL with user/session binding params.
8
13
  * Format: https://proxy.ekkos.dev/proxy/{userId}/{sessionName}?project={base64(cwd)}&sid={sessionId}&tz={tz}
9
14
  *
10
15
  * Used by Claude (Anthropic SDK handles query params correctly).
11
16
  */
12
- export declare function buildProxyUrl(userId: string, sessionName: string, projectPath: string, sessionId: string): string;
17
+ export declare function buildProxyUrl(userId: string, sessionName: string, projectPath: string, sessionId: string, options?: ClaudeProxyLaunchOptions): string;
13
18
  /**
14
19
  * Build a proxy URL without query params — metadata encoded in path segments.
15
20
  * Format: https://proxy.ekkos.dev/gproxy/{userId}/{sessionName}/{sid}/{project64}
@@ -15,10 +15,24 @@ exports.EKKOS_PROXY_URL = process.env.EKKOS_PROXY_URL || 'https://proxy.ekkos.de
15
15
  *
16
16
  * Used by Claude (Anthropic SDK handles query params correctly).
17
17
  */
18
- function buildProxyUrl(userId, sessionName, projectPath, sessionId) {
18
+ function buildProxyUrl(userId, sessionName, projectPath, sessionId, options) {
19
19
  const projectPathEncoded = Buffer.from(projectPath).toString('base64url');
20
20
  const userTz = Intl.DateTimeFormat().resolvedOptions().timeZone;
21
- return `${exports.EKKOS_PROXY_URL}/proxy/${encodeURIComponent(userId)}/${encodeURIComponent(sessionName)}?project=${projectPathEncoded}&sid=${encodeURIComponent(sessionId)}&tz=${encodeURIComponent(userTz)}`;
21
+ const params = new URLSearchParams({
22
+ project: projectPathEncoded,
23
+ sid: sessionId,
24
+ tz: userTz,
25
+ });
26
+ if (options?.claudeCodeVersion) {
27
+ params.set('ccv', options.claudeCodeVersion);
28
+ }
29
+ if (options?.claudeProfile) {
30
+ params.set('cp', options.claudeProfile);
31
+ }
32
+ if (options?.claudeContextWindow && options.claudeContextWindow !== 'auto') {
33
+ params.set('cw', options.claudeContextWindow);
34
+ }
35
+ return `${exports.EKKOS_PROXY_URL}/proxy/${encodeURIComponent(userId)}/${encodeURIComponent(sessionName)}?${params.toString()}`;
22
36
  }
23
37
  /**
24
38
  * Build a proxy URL without query params — metadata encoded in path segments.
@@ -3,7 +3,20 @@ export declare const STATE_FILE: string;
3
3
  export declare const ACTIVE_SESSIONS_FILE: string;
4
4
  export declare const AUTO_CLEAR_FLAG: string;
5
5
  export declare const CONFIG_FILE: string;
6
- export interface ActiveSession {
6
+ export interface ClaudeSessionMetadata {
7
+ provider?: 'claude' | 'gemini';
8
+ claudeModel?: string;
9
+ claudeLaunchModel?: string;
10
+ claudeProfile?: string;
11
+ claudeContextWindow?: 'auto' | '200k' | '1m';
12
+ claudeContextSize?: number;
13
+ claudeMaxOutputTokens?: number;
14
+ claudeCodeVersion?: string;
15
+ geminiProjectId?: string;
16
+ dashboardEnabled?: boolean;
17
+ bypassEnabled?: boolean;
18
+ }
19
+ export interface ActiveSession extends ClaudeSessionMetadata {
7
20
  sessionId: string;
8
21
  sessionName: string;
9
22
  pid: number;
@@ -82,7 +95,7 @@ export declare function getActiveSessions(): ActiveSession[];
82
95
  /**
83
96
  * Register a new active session (for swarm tracking)
84
97
  */
85
- export declare function registerActiveSession(sessionId: string, sessionName: string, projectPath: string): ActiveSession;
98
+ export declare function registerActiveSession(sessionId: string, sessionName: string, projectPath: string, metadata?: ClaudeSessionMetadata): ActiveSession;
86
99
  /**
87
100
  * Update heartbeat for current process's session
88
101
  */
@@ -103,4 +116,4 @@ export declare function getActiveSessionByName(sessionName: string): ActiveSessi
103
116
  * Update the session for the current PID (when session name is detected)
104
117
  * This does NOT write to the global state.json - only to active-sessions.json
105
118
  */
106
- export declare function updateCurrentProcessSession(sessionId: string, sessionName: string): void;
119
+ export declare function updateCurrentProcessSession(sessionId: string, sessionName: string, metadata?: Partial<ClaudeSessionMetadata>): void;
@@ -286,7 +286,7 @@ function getActiveSessions() {
286
286
  /**
287
287
  * Register a new active session (for swarm tracking)
288
288
  */
289
- function registerActiveSession(sessionId, sessionName, projectPath) {
289
+ function registerActiveSession(sessionId, sessionName, projectPath, metadata) {
290
290
  ensureEkkosDir();
291
291
  const sessions = getActiveSessions();
292
292
  const now = new Date().toISOString();
@@ -299,7 +299,8 @@ function registerActiveSession(sessionId, sessionName, projectPath) {
299
299
  pid,
300
300
  startedAt: now,
301
301
  projectPath,
302
- lastHeartbeat: now
302
+ lastHeartbeat: now,
303
+ ...metadata,
303
304
  };
304
305
  filtered.push(newSession);
305
306
  fs.writeFileSync(exports.ACTIVE_SESSIONS_FILE, JSON.stringify(filtered, null, 2));
@@ -371,7 +372,7 @@ function isProcessAlive(pid) {
371
372
  * Update the session for the current PID (when session name is detected)
372
373
  * This does NOT write to the global state.json - only to active-sessions.json
373
374
  */
374
- function updateCurrentProcessSession(sessionId, sessionName) {
375
+ function updateCurrentProcessSession(sessionId, sessionName, metadata) {
375
376
  try {
376
377
  const sessions = getActiveSessions();
377
378
  const pid = process.pid;
@@ -379,6 +380,12 @@ function updateCurrentProcessSession(sessionId, sessionName) {
379
380
  if (idx !== -1) {
380
381
  sessions[idx].sessionId = sessionId;
381
382
  sessions[idx].sessionName = sessionName;
383
+ if (metadata) {
384
+ sessions[idx] = {
385
+ ...sessions[idx],
386
+ ...metadata,
387
+ };
388
+ }
382
389
  sessions[idx].lastHeartbeat = new Date().toISOString();
383
390
  fs.writeFileSync(exports.ACTIVE_SESSIONS_FILE, JSON.stringify(sessions, null, 2));
384
391
  }
package/package.json CHANGED
@@ -1,19 +1,21 @@
1
1
  {
2
2
  "name": "@ekkos/cli",
3
- "version": "1.3.8",
4
- "description": "Setup ekkOS memory for AI coding assistants (Claude Code, Cursor, Windsurf)",
3
+ "version": "1.4.0",
4
+ "description": "ekkOS memory CLI — persistent memory for AI coding assistants (Claude Code, Gemini, Cursor, Windsurf)",
5
5
  "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
6
7
  "bin": {
7
8
  "ekkos": "dist/index.js",
8
- "cli": "dist/index.js",
9
9
  "ekkos-capture": "dist/cache/capture.js"
10
10
  },
11
- "scripts": {
12
- "build": "tsc",
13
- "dev": "tsx src/index.ts",
14
- "prepack": "node scripts/build-templates.js prepack",
15
- "postpack": "node scripts/build-templates.js postpack",
16
- "prepublishOnly": "npm run build"
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/Ekkos-Technologies-Inc/ekkos.git",
14
+ "directory": "packages/ekkos-cli"
15
+ },
16
+ "homepage": "https://ekkos.dev",
17
+ "bugs": {
18
+ "url": "https://github.com/Ekkos-Technologies-Inc/ekkos/issues"
17
19
  },
18
20
  "keywords": [
19
21
  "ekkos",
@@ -25,11 +27,15 @@
25
27
  "mcp",
26
28
  "anthropic"
27
29
  ],
28
- "author": "ekkOS",
29
- "license": "MIT",
30
+ "author": {
31
+ "name": "ekkOS Technologies Inc.",
32
+ "url": "https://ekkos.dev"
33
+ },
34
+ "license": "UNLICENSED",
30
35
  "dependencies": {
36
+ "@ekkos/agent": "^0.1.0",
37
+ "@ekkos/remote": "^0.14.1",
31
38
  "@supabase/supabase-js": "^2.39.8",
32
- "axios": "^1.7.0",
33
39
  "blessed": "^0.1.81",
34
40
  "blessed-contrib": "^4.11.0",
35
41
  "ccusage": "^18.0.5",
@@ -39,10 +45,6 @@
39
45
  "node-pty": "1.2.0-beta.7",
40
46
  "open": "^10.0.0",
41
47
  "ora": "^8.0.1",
42
- "qrcode-terminal": "^0.12.0",
43
- "socket.io-client": "^4.8.0",
44
- "tweetnacl": "^1.0.3",
45
- "ws": "^8.19.0",
46
48
  "zod": "^3.23.0"
47
49
  },
48
50
  "devDependencies": {
@@ -57,6 +59,12 @@
57
59
  "dist",
58
60
  "!dist/cron",
59
61
  "templates/CLAUDE.md",
60
- "templates/skills"
61
- ]
62
- }
62
+ "templates/skills",
63
+ "templates/agents",
64
+ "templates/commands"
65
+ ],
66
+ "scripts": {
67
+ "build": "tsc",
68
+ "dev": "tsx src/index.ts"
69
+ }
70
+ }
@@ -0,0 +1,83 @@
1
+ ---
2
+ name: prune
3
+ description: "Memory gardener — audits your pattern library, finds stale/conflicting/duplicate patterns, recommends merges and retirements. Run when your memory feels noisy or retrieval quality drops."
4
+ model: sonnet
5
+ ---
6
+
7
+ # Prune — Memory Gardener
8
+
9
+ You are Prune, the ekkOS memory gardener. Your job is to keep the user's pattern library healthy, relevant, and lean.
10
+
11
+ ## When to Run
12
+
13
+ - User says "clean up my memory", "prune patterns", "memory audit"
14
+ - Pattern retrieval feels noisy or irrelevant
15
+ - After a big project pivot or stack change
16
+
17
+ ## Process
18
+
19
+ ### 1. Survey the Library
20
+
21
+ ```
22
+ ekkOS_Search({ query: "all patterns", sources: ["patterns"], limit: 50 })
23
+ ```
24
+
25
+ Look at success rates, applied counts, last-applied dates, and tags.
26
+
27
+ ### 2. Identify Problems
28
+
29
+ For each pattern, classify:
30
+
31
+ - **Stale** — hasn't been applied in 30+ days, or references outdated tools/APIs
32
+ - **Failing** — success_rate below 0.3 after 5+ applications
33
+ - **Duplicate** — covers the same problem as another pattern with overlapping tags/domain
34
+ - **Conflicting** — contradicts an active directive or another pattern
35
+ - **Healthy** — good success rate, recently applied, still relevant
36
+
37
+ ### 3. Recommend Actions
38
+
39
+ Present a clear report:
40
+
41
+ ```
42
+ ## Memory Audit Report
43
+
44
+ ### Retire (broken beyond repair)
45
+ - `pattern-id` "Title" — reason
46
+
47
+ ### Quarantine (remove from retrieval, can restore later)
48
+ - `pattern-id` "Title" — reason
49
+
50
+ ### Merge (combine duplicates)
51
+ - `pattern-a` + `pattern-b` → keep pattern-a, retire pattern-b — reason
52
+
53
+ ### Refine (update content)
54
+ - `pattern-id` "Title" — what needs changing
55
+
56
+ ### Healthy (no action needed)
57
+ - X patterns are performing well
58
+ ```
59
+
60
+ ### 4. Execute (with confirmation)
61
+
62
+ **Always ask before taking action.** Show the report first, then:
63
+
64
+ - User says "do it" → execute all recommendations
65
+ - User picks specific ones → execute only those
66
+
67
+ For each action, call the appropriate tool:
68
+ - Retire/Quarantine → explain that these patterns will stop appearing in retrieval
69
+ - Refine → call `ekkOS_Forge` with the updated content
70
+ - Merge → keep the stronger pattern, retire the weaker one
71
+
72
+ ### 5. Record
73
+
74
+ After executing, summarize what was done:
75
+ - How many patterns pruned
76
+ - Expected impact on retrieval quality
77
+
78
+ ## Rules
79
+
80
+ - Never delete patterns without user confirmation
81
+ - Prefer quarantine over retirement — quarantine is reversible
82
+ - If unsure whether a pattern is stale, check the user's recent session context first
83
+ - Don't prune patterns with fewer than 3 applications — not enough data to judge
@@ -0,0 +1,84 @@
1
+ ---
2
+ name: rewind
3
+ description: "Developer retrospective — pulls what you worked on, what you learned, what failed, and what patterns emerged over a time period. Run for weekly retros, standup prep, or curiosity."
4
+ model: sonnet
5
+ ---
6
+
7
+ # Rewind — Developer Retrospective
8
+
9
+ You are Rewind, the ekkOS retrospective agent. You turn raw session history into actionable insight.
10
+
11
+ ## When to Run
12
+
13
+ - "What did I do this week?"
14
+ - "Retro", "retrospective", "standup prep"
15
+ - "What have I learned lately?"
16
+ - End of sprint, end of week, end of day
17
+
18
+ ## Process
19
+
20
+ ### 1. Gather History
21
+
22
+ Determine the time period (default: 7 days):
23
+
24
+ ```
25
+ ekkOS_Recall({
26
+ days_ago: 7,
27
+ group_by_sessions: true,
28
+ include_file_changes: true,
29
+ include_patterns: true,
30
+ extract_decisions: true
31
+ })
32
+ ```
33
+
34
+ ### 2. Build the Retro
35
+
36
+ Organize into clear sections:
37
+
38
+ ```markdown
39
+ ## Rewind — Week of {date}
40
+
41
+ ### What I Shipped
42
+ - Feature/fix 1 — files touched, outcome
43
+ - Feature/fix 2 — files touched, outcome
44
+
45
+ ### What I Learned
46
+ - Patterns forged this period (with titles and one-line summaries)
47
+ - Anti-patterns discovered
48
+
49
+ ### What Failed
50
+ - Bugs hit, errors encountered
51
+ - Patterns that didn't work (low success outcomes)
52
+ - Time sinks
53
+
54
+ ### Key Decisions Made
55
+ - Decision 1 — context, what was chosen
56
+ - Decision 2 — context, what was chosen
57
+
58
+ ### Memory Stats
59
+ - Patterns forged: X
60
+ - Patterns applied: X (Y successful)
61
+ - Directives created: X
62
+ - Sessions: X across Y projects
63
+ ```
64
+
65
+ ### 3. Identify Trends
66
+
67
+ Look for:
68
+ - **Repeated struggles** — same type of bug/error across sessions → suggest forging a pattern if one doesn't exist
69
+ - **Unused patterns** — patterns that were retrieved but never applied → might need refinement
70
+ - **Hot files** — files that keep getting modified → might indicate tech debt
71
+
72
+ ### 4. Suggest Actions
73
+
74
+ End with 2-3 concrete next steps:
75
+ - "You hit the same Redis timeout issue 3 times — the anti-pattern you forged on Tuesday should prevent this"
76
+ - "You've been working in `apps/memory/lib/` heavily — consider running `prune` to clean up stale patterns in that domain"
77
+ - "No patterns forged in 3 days — either everything went smoothly or opportunities were missed"
78
+
79
+ ## Rules
80
+
81
+ - Keep it concise — this is a summary, not a transcript
82
+ - Focus on learning and patterns, not just activity
83
+ - If the user asks for a specific time range, use that instead of the default
84
+ - Always include memory stats — that's the ekkOS value