@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/LICENSE +21 -0
- package/dist/commands/dashboard.js +520 -42
- package/dist/commands/gemini.d.ts +1 -0
- package/dist/commands/gemini.js +180 -19
- package/dist/commands/init-living-docs.d.ts +6 -0
- package/dist/commands/init-living-docs.js +57 -0
- package/dist/commands/living-docs.js +3 -3
- package/dist/commands/run.d.ts +5 -0
- package/dist/commands/run.js +508 -35
- package/dist/commands/setup-ci.d.ts +3 -0
- package/dist/commands/setup-ci.js +107 -0
- package/dist/commands/validate-living-docs.d.ts +27 -0
- package/dist/commands/validate-living-docs.js +489 -0
- package/dist/index.js +97 -25
- package/dist/utils/proxy-url.d.ts +6 -1
- package/dist/utils/proxy-url.js +16 -2
- package/dist/utils/state.d.ts +16 -3
- package/dist/utils/state.js +10 -3
- package/package.json +27 -19
- package/templates/agents/prune.md +83 -0
- package/templates/agents/rewind.md +84 -0
- package/templates/agents/scout.md +102 -0
- package/templates/agents/trace.md +99 -0
- package/templates/commands/continue.md +47 -0
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
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
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
|
-
//
|
|
240
|
+
// ekkOS Cortex command group
|
|
241
241
|
const docsCmd = commander_1.program
|
|
242
242
|
.command('docs')
|
|
243
|
-
.
|
|
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
|
|
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
|
-
(
|
|
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
|
|
424
|
-
|
|
425
|
-
|
|
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 () => {
|
|
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}
|
package/dist/utils/proxy-url.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
package/dist/utils/state.d.ts
CHANGED
|
@@ -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
|
|
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;
|
package/dist/utils/state.js
CHANGED
|
@@ -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.
|
|
4
|
-
"description": "
|
|
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
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
|
|
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":
|
|
29
|
-
|
|
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
|