@ekkos/cli 1.3.9 → 1.4.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/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'));
@@ -63,16 +63,13 @@ commander_1.program
63
63
  .addHelpText('after', [
64
64
  '',
65
65
  chalk_1.default.cyan.bold('Examples:'),
66
- ` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos')} ${chalk_1.default.gray('Launch Claude Code with ekkOS memory (default)')}`,
67
- ` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos pulse')} ${chalk_1.default.gray('Launch ekkOS Pulse preset: bypass + selector + live dashboard')}`,
68
- ` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos --model')} ${chalk_1.default.gray('Open Claude launch selector (model + context + resume)')}`,
69
- ` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos gemini')} ${chalk_1.default.gray('Launch Gemini CLI with ekkOS memory')}`,
70
- ` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos run --dashboard')} ${chalk_1.default.gray('Claude Code with live usage dashboard')}`,
71
- ` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos init')} ${chalk_1.default.gray('First-time setup authenticate + configure IDEs')}`,
72
- ` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos docs watch')} ${chalk_1.default.gray('Keep ekkOS_CONTEXT.md files updated on disk')}`,
73
- ` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos connect gemini')} ${chalk_1.default.gray('Store your Gemini API key in the cloud')}`,
74
- ` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos swarm launch -t "task"')} ${chalk_1.default.gray('Parallel workers on a decomposed task')}`,
75
- ` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos agent create --path ./repo')} ${chalk_1.default.gray('Spawn a headless remote agent')}`,
66
+ ` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos')} ${chalk_1.default.gray('Start Claude Code with ekkOS memory (default)')}`,
67
+ ` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos codex')} ${chalk_1.default.gray('Start Codex (OpenAI) mode')}`,
68
+ ` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos daemon start')} ${chalk_1.default.gray('Start the background mobile sync service')}`,
69
+ ` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos docs watch')} ${chalk_1.default.gray('Keep local ekkOS_CONTEXT.md files updated on disk')}`,
70
+ ` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos connect gemini')} ${chalk_1.default.gray('Securely store your Gemini API key in the cloud')}`,
71
+ ` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos agent create --path ./repo')} ${chalk_1.default.gray('Spawn a headless remote agent in a directory')}`,
72
+ ` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos run --dashboard')} ${chalk_1.default.gray('Launch Claude with live usage dashboard')}`,
76
73
  '',
77
74
  chalk_1.default.gray(' Run ') + chalk_1.default.white('ekkos <command> --help') + chalk_1.default.gray(' for detailed options on any command.'),
78
75
  '',
@@ -137,53 +134,52 @@ commander_1.program
137
134
  }
138
135
  const groups = [
139
136
  {
140
- title: 'Launch',
141
- icon: '🚀',
137
+ title: 'Getting Started',
138
+ icon: '',
142
139
  commands: [
143
- { name: 'run', desc: 'Claude Code with memory, proxy, and mobile sync', note: 'default' },
144
- { name: 'pulse', desc: 'Pulse preset: bypass + guided selector + live dashboard' },
145
- { name: 'gemini', desc: 'Gemini CLI with memory and proxy' },
146
- { name: 'codex', desc: 'Codex (OpenAI) with mobile sync' },
147
- { name: 'acp', desc: 'Any ACP-compatible agent' },
140
+ { name: 'init', desc: 'First-time setup authenticate and configure IDEs' },
141
+ { name: 'scan', desc: 'Scan repo structure and seed ekkOS system registry' },
142
+ { name: 'docs', desc: 'Cortex: Watch and regenerate local ekkOS_CONTEXT.md files' },
143
+ { name: 'status', desc: 'Show overall system status (Memory, Sync Daemon, Auth)' },
144
+ { name: 'doctor', desc: 'Check system prerequisites and fix runaway processes' },
148
145
  ],
149
146
  },
150
147
  {
151
- title: 'Setup',
152
- icon: '⚙️',
148
+ title: 'Launch Agents (Local + Mobile Synced)',
149
+ icon: '',
153
150
  commands: [
154
- { name: 'init', desc: 'Authenticate and configure IDEs' },
155
- { name: 'scan', desc: 'Discover repo structure and seed system registry' },
156
- { name: 'docs', desc: 'Watch and regenerate ekkOS_CONTEXT.md files' },
157
- { name: 'connect', desc: 'Store AI vendor API keys in the cloud' },
151
+ { name: 'run', desc: 'Launch Claude Code with memory and mobile control', note: 'default' },
152
+ { name: 'codex', desc: 'Launch Codex (OpenAI) with mobile control' },
153
+ { name: 'gemini', desc: 'Launch Gemini mode (ACP) with mobile control' },
154
+ { name: 'acp', desc: 'Launch a generic ACP-compatible agent' },
158
155
  ],
159
156
  },
160
157
  {
161
- title: 'Agents & Swarm',
162
- icon: '🐝',
158
+ title: 'Mobile Sync & Cloud (Synk)',
159
+ icon: '',
163
160
  commands: [
164
- { name: 'agent', desc: 'Manage headless agents (create, list, stop)' },
165
- { name: 'swarm', desc: 'Parallel workers with Q-learning routing' },
166
- { name: 'sandbox', desc: 'OS-level sandboxing for agent execution' },
161
+ { name: 'daemon', desc: 'Manage background mobile sync service (start, stop, status)' },
162
+ { name: 'connect', desc: 'Securely store AI vendor API keys in the cloud' },
163
+ { name: 'sandbox', desc: 'Configure OS-level sandboxing for agent execution' },
164
+ { name: 'notify', desc: 'Send push notifications to your Synk mobile app' },
167
165
  ],
168
166
  },
169
167
  {
170
- title: 'Monitor',
171
- icon: '📊',
168
+ title: 'Headless Agents (Remote Control)',
169
+ icon: '',
172
170
  commands: [
173
- { name: 'status', desc: 'System status (Memory, Sync, Auth)' },
174
- { name: 'dashboard', desc: 'Live TUI dashboard for session monitoring' },
175
- { name: 'usage', desc: 'Token usage and cost tracking' },
176
- { name: 'sessions', desc: 'List active local CLI sessions' },
171
+ { name: 'agent', desc: 'Manage remote headless agents (create, list, stop)' },
172
+ { name: 'swarm', desc: 'Parallel workers, Q-learning routing, and swarm dashboard' },
177
173
  ],
178
174
  },
179
175
  {
180
- title: 'Utilities',
181
- icon: '🔧',
176
+ title: 'Monitoring & Usage',
177
+ icon: '',
182
178
  commands: [
183
- { name: 'auth', desc: 'Manage authentication tokens' },
184
- { name: 'daemon', desc: 'Background mobile sync service (start, stop)' },
185
- { name: 'notify', desc: 'Push notifications to Synk mobile app' },
186
- { name: 'doctor', desc: 'Check prerequisites and fix issues' },
179
+ { name: 'auth', desc: 'Manage authentication for Memory, Agents, and Mobile App' },
180
+ { name: 'usage', desc: 'Token usage and cost tracking (daily, weekly, monthly, session)' },
181
+ { name: 'dashboard', desc: 'Live TUI dashboard for session monitoring' },
182
+ { name: 'sessions', desc: 'List active local CLI sessions' },
187
183
  ],
188
184
  },
189
185
  ];
@@ -241,13 +237,28 @@ commander_1.program
241
237
  path: options.path,
242
238
  });
243
239
  });
244
- // Local living docs command
240
+ // ekkOS Cortex command group
245
241
  const docsCmd = commander_1.program
246
242
  .command('docs')
247
- .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
+ });
248
259
  docsCmd
249
260
  .command('watch')
250
- .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')
251
262
  .option('-p, --path <path>', 'Path to watch (default: current directory)')
252
263
  .option('--timezone <iana-tz>', 'IANA timezone for timestamps (default: local machine timezone)')
253
264
  .option('--poll-interval-ms <ms>', 'Polling interval fallback in milliseconds', (value) => parseInt(value, 10))
@@ -263,6 +274,30 @@ docsCmd
263
274
  noSeed: options.seed === false,
264
275
  });
265
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
+ });
266
301
  // Status command
267
302
  commander_1.program
268
303
  .command('status')
@@ -324,6 +359,10 @@ commander_1.program
324
359
  .description('Launch Claude Code with ekkOS memory proxy (IPC compression + pattern injection)')
325
360
  .option('-s, --session <name>', 'Session name to restore on clear')
326
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')
327
366
  .option('-v, --verbose', 'Show debug output')
328
367
  .option('-d, --doctor', 'Run diagnostics before starting')
329
368
  .option('-r, --research', 'Auto-run research agent on startup (scans arXiv for new AI papers)')
@@ -331,12 +370,9 @@ commander_1.program
331
370
  .option('--skip-proxy', 'Skip API proxy (use direct Anthropic API, disables seamless context eviction)')
332
371
  .option('--dashboard', 'Launch with live usage dashboard in an isolated 60/40 tmux split (requires tmux)')
333
372
  .option('--add-dir <dirs...>', 'Additional directories Claude Code can access (outside working directory)')
334
- .option('--model [model]', 'Claude launch model. Pass without a value to open the launch selector')
335
- .option('--context-window <mode>', 'Context window mode: auto, 200k, or 1m')
336
- .option('--continue-last', 'Continue the most recent Claude conversation')
337
- .option('--resume-session <id>', 'Resume a Claude conversation by session ID')
338
- .action((options) => {
339
- (0, run_1.run)({
373
+ .action(async (options) => {
374
+ const { run } = await Promise.resolve().then(() => __importStar(require('./commands/run')));
375
+ run({
340
376
  session: options.session,
341
377
  bypass: options.bypass,
342
378
  verbose: options.verbose,
@@ -354,32 +390,27 @@ commander_1.program
354
390
  });
355
391
  commander_1.program
356
392
  .command('pulse')
357
- .description('Launch ekkOS Pulse: bypass + selector + live Claude dashboard')
393
+ .description('Launch PULSE mode (equivalent to `ekkos -b --model --dashboard`)')
358
394
  .option('-s, --session <name>', 'Session name to restore on clear')
359
- .option('--no-bypass', 'Disable bypass permissions mode')
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')
360
398
  .option('-v, --verbose', 'Show debug output')
361
- .option('-d, --doctor', 'Run diagnostics before starting')
362
- .option('-r, --research', 'Auto-run research agent on startup (scans arXiv for new AI papers)')
363
399
  .option('--skip-inject', 'Monitor-only mode (detect context wall but print instructions instead of auto-inject)')
364
400
  .option('--skip-proxy', 'Skip API proxy (use direct Anthropic API, disables seamless context eviction)')
365
401
  .option('--add-dir <dirs...>', 'Additional directories Claude Code can access (outside working directory)')
366
- .option('--model [model]', 'Claude launch model. Omit the value to open the launch selector (default for pulse)')
367
- .option('--context-window <mode>', 'Context window mode: auto, 200k, or 1m')
368
- .option('--continue-last', 'Continue the most recent Claude conversation')
369
- .option('--resume-session <id>', 'Resume a Claude conversation by session ID')
370
- .action((options) => {
371
- (0, run_1.run)({
402
+ .action(async (options) => {
403
+ const { run } = await Promise.resolve().then(() => __importStar(require('./commands/run')));
404
+ run({
372
405
  session: options.session,
373
- bypass: options.bypass !== false,
406
+ bypass: true,
374
407
  pulse: true,
375
408
  verbose: options.verbose,
376
- doctor: options.doctor,
377
409
  noInject: options.skipInject,
378
- research: options.research,
379
410
  noProxy: options.skipProxy,
380
411
  dashboard: true,
381
412
  addDirs: options.addDir,
382
- model: options.model ?? true,
413
+ model: true,
383
414
  contextWindow: options.contextWindow,
384
415
  continueLast: options.continueLast,
385
416
  resumeSession: options.resumeSession,
@@ -392,12 +423,14 @@ commander_1.program
392
423
  .option('-s, --session <name>', 'Session name')
393
424
  .option('-v, --verbose', 'Show debug output')
394
425
  .option('--skip-proxy', 'Skip API proxy (use direct Gemini API)')
426
+ .option('--dashboard', 'Launch Gemini with a dedicated live dashboard split (requires tmux)')
395
427
  .action(async (options) => {
396
428
  const { gemini } = await Promise.resolve().then(() => __importStar(require('./commands/gemini')));
397
429
  gemini({
398
430
  session: options.session,
399
431
  verbose: options.verbose,
400
432
  noProxy: options.skipProxy,
433
+ dashboard: options.dashboard,
401
434
  });
402
435
  });
403
436
  // Test Claude — bare proxy test (no CLI wrapper)
@@ -465,19 +498,9 @@ commander_1.program
465
498
  console.log(chalk_1.default.gray(' the ekkOS proxy — no shell hooks required.'));
466
499
  console.log('');
467
500
  });
468
- // Usage command lightweight stub, heavy handler loaded lazily
469
- commander_1.program
470
- .command('usage')
471
- .description('Token usage and cost tracking (daily, weekly, monthly, session)')
472
- .allowUnknownOption()
473
- .action(async () => {
474
- const { registerUsageCommand } = await Promise.resolve().then(() => __importStar(require('./commands/usage/index')));
475
- const { Command } = await Promise.resolve().then(() => __importStar(require('commander')));
476
- const usageProgram = new Command('ekkos');
477
- registerUsageCommand(usageProgram);
478
- usageProgram.parse(['node', 'ekkos', 'usage', ...process.argv.slice(3)]);
479
- });
480
- // 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);
481
504
  commander_1.program.addCommand(dashboard_1.dashboardCommand);
482
505
  // Sessions command - list active Claude Code sessions (swarm support)
483
506
  commander_1.program
@@ -563,6 +586,7 @@ swarmCmd
563
586
  .option('--queen-strategy <strategy>', 'Queen strategy (adaptive-default, hierarchical-cascade, mesh-consensus)')
564
587
  .option('-v, --verbose', 'Show debug output')
565
588
  .action(async (options) => {
589
+ // Auto-open wizard when --task is missing
566
590
  if (!options.task) {
567
591
  const { swarmSetup } = await Promise.resolve().then(() => __importStar(require('./commands/swarm-setup')));
568
592
  swarmSetup();
@@ -582,7 +606,10 @@ swarmCmd
582
606
  swarmCmd
583
607
  .command('setup')
584
608
  .description('Interactive TUI wizard for configuring and launching a swarm')
585
- .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
+ });
586
613
  swarmCmd
587
614
  .command('swarm-dashboard')
588
615
  .description('Live swarm dashboard')
@@ -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,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekkos/cli",
3
- "version": "1.3.9",
3
+ "version": "1.4.1",
4
4
  "description": "ekkOS memory CLI — persistent memory for AI coding assistants (Claude Code, Gemini, Cursor, Windsurf)",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",