@ekkos/cli 1.3.7 → 1.3.9

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,24 +38,11 @@ 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
- const init_1 = require("./commands/init");
42
- const test_1 = require("./commands/test");
43
- const status_1 = require("./commands/status");
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
44
  const run_1 = require("./commands/run");
45
- const gemini_1 = require("./commands/gemini");
46
- const test_claude_1 = require("./commands/test-claude");
47
- const doctor_1 = require("./commands/doctor");
48
- const stream_1 = require("./commands/stream");
49
- const claw_1 = require("./commands/claw");
50
- // DEPRECATED: Hooks removed in hookless architecture migration
51
- // hooksInstall, hooksVerify, hooksStatus no longer called — `hooks` command prints a deprecation notice.
52
- const state_1 = require("./utils/state");
53
- const index_1 = require("./commands/usage/index");
54
45
  const dashboard_1 = require("./commands/dashboard");
55
- const swarm_1 = require("./commands/swarm");
56
- const swarm_dashboard_1 = require("./commands/swarm-dashboard");
57
- const swarm_setup_1 = require("./commands/swarm-setup");
58
- const scan_1 = require("./commands/scan");
59
46
  const chalk_1 = __importDefault(require("chalk"));
60
47
  const fs = __importStar(require("fs"));
61
48
  const path = __importStar(require("path"));
@@ -76,12 +63,16 @@ commander_1.program
76
63
  .addHelpText('after', [
77
64
  '',
78
65
  chalk_1.default.cyan.bold('Examples:'),
79
- ` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos')} ${chalk_1.default.gray('Start Claude Code with ekkOS memory (default)')}`,
80
- ` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos codex')} ${chalk_1.default.gray('Start Codex (OpenAI) mode')}`,
81
- ` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos daemon start')} ${chalk_1.default.gray('Start the background mobile sync service')}`,
82
- ` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos connect gemini')} ${chalk_1.default.gray('Securely store your Gemini API key in the cloud')}`,
83
- ` ${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')}`,
84
- ` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos run --dashboard')} ${chalk_1.default.gray('Launch Claude with live usage dashboard')}`,
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')}`,
85
76
  '',
86
77
  chalk_1.default.gray(' Run ') + chalk_1.default.white('ekkos <command> --help') + chalk_1.default.gray(' for detailed options on any command.'),
87
78
  '',
@@ -146,51 +137,53 @@ commander_1.program
146
137
  }
147
138
  const groups = [
148
139
  {
149
- title: 'Getting Started',
150
- icon: '',
140
+ title: 'Launch',
141
+ icon: '🚀',
151
142
  commands: [
152
- { name: 'init', desc: 'First-time setup authenticate and configure IDEs' },
153
- { name: 'scan', desc: 'Scan repo structure and seed ekkOS system registry' },
154
- { name: 'status', desc: 'Show overall system status (Memory, Sync Daemon, Auth)' },
155
- { name: 'doctor', desc: 'Check system prerequisites and fix runaway processes' },
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' },
156
148
  ],
157
149
  },
158
150
  {
159
- title: 'Launch Agents (Local + Mobile Synced)',
160
- icon: '',
151
+ title: 'Setup',
152
+ icon: '⚙️',
161
153
  commands: [
162
- { name: 'run', desc: 'Launch Claude Code with memory and mobile control', note: 'default' },
163
- { name: 'codex', desc: 'Launch Codex (OpenAI) with mobile control' },
164
- { name: 'gemini', desc: 'Launch Gemini mode (ACP) with mobile control' },
165
- { name: 'acp', desc: 'Launch a generic ACP-compatible agent' },
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' },
166
158
  ],
167
159
  },
168
160
  {
169
- title: 'Mobile Sync & Cloud (Synk)',
170
- icon: '',
161
+ title: 'Agents & Swarm',
162
+ icon: '🐝',
171
163
  commands: [
172
- { name: 'daemon', desc: 'Manage background mobile sync service (start, stop, status)' },
173
- { name: 'connect', desc: 'Securely store AI vendor API keys in the cloud' },
174
- { name: 'sandbox', desc: 'Configure OS-level sandboxing for agent execution' },
175
- { name: 'notify', desc: 'Send push notifications to your Synk mobile app' },
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' },
176
167
  ],
177
168
  },
178
169
  {
179
- title: 'Headless Agents (Remote Control)',
180
- icon: '',
170
+ title: 'Monitor',
171
+ icon: '📊',
181
172
  commands: [
182
- { name: 'agent', desc: 'Manage remote headless agents (create, list, stop)' },
183
- { name: 'swarm', desc: 'Parallel workers, Q-learning routing, and swarm dashboard' },
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' },
184
177
  ],
185
178
  },
186
179
  {
187
- title: 'Monitoring & Usage',
188
- icon: '',
180
+ title: 'Utilities',
181
+ icon: '🔧',
189
182
  commands: [
190
- { name: 'auth', desc: 'Manage authentication for Memory, Agents, and Mobile App' },
191
- { name: 'usage', desc: 'Token usage and cost tracking (daily, weekly, monthly, session)' },
192
- { name: 'dashboard', desc: 'Live TUI dashboard for session monitoring' },
193
- { name: 'sessions', desc: 'List active local CLI sessions' },
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' },
194
187
  ],
195
188
  },
196
189
  ];
@@ -232,7 +225,7 @@ commander_1.program
232
225
  .option('-q, --quick', 'Quick setup: auto-detect IDE, use defaults, only prompt for API key if missing')
233
226
  .option('--skip-hooks', '[DEPRECATED] Hooks are no longer deployed; this flag is a no-op')
234
227
  .option('--skip-skills', 'Skip skills deployment')
235
- .action(init_1.init);
228
+ .action(async (options) => { const { init } = await Promise.resolve().then(() => __importStar(require('./commands/init'))); await init(options); });
236
229
  // Scan command — discover repo systems and seed registry
237
230
  commander_1.program
238
231
  .command('scan')
@@ -240,21 +233,45 @@ commander_1.program
240
233
  .option('-c, --compile', 'Trigger a compile pass after seeding')
241
234
  .option('-n, --dry-run', 'Show discovered systems without seeding')
242
235
  .option('-p, --path <path>', 'Path to scan (default: current directory)')
243
- .action((options) => {
244
- (0, scan_1.scan)({
236
+ .action(async (options) => {
237
+ const { scan } = await Promise.resolve().then(() => __importStar(require('./commands/scan')));
238
+ scan({
245
239
  compile: options.compile,
246
240
  dryRun: options.dryRun,
247
241
  path: options.path,
248
242
  });
249
243
  });
244
+ // Local living docs command
245
+ const docsCmd = commander_1.program
246
+ .command('docs')
247
+ .description('Manage local ekkOS_CONTEXT.md living docs');
248
+ docsCmd
249
+ .command('watch')
250
+ .description('Watch the local workspace and keep ekkOS_CONTEXT.md files current on disk')
251
+ .option('-p, --path <path>', 'Path to watch (default: current directory)')
252
+ .option('--timezone <iana-tz>', 'IANA timezone for timestamps (default: local machine timezone)')
253
+ .option('--poll-interval-ms <ms>', 'Polling interval fallback in milliseconds', (value) => parseInt(value, 10))
254
+ .option('--debounce-ms <ms>', 'Debounce window before recompiling in milliseconds', (value) => parseInt(value, 10))
255
+ .option('--no-seed', 'Do not seed the platform registry while watching')
256
+ .action(async (options) => {
257
+ const { watchLivingDocs } = await Promise.resolve().then(() => __importStar(require('./commands/living-docs')));
258
+ watchLivingDocs({
259
+ path: options.path,
260
+ timeZone: options.timezone,
261
+ pollIntervalMs: options.pollIntervalMs,
262
+ debounceMs: options.debounceMs,
263
+ noSeed: options.seed === false,
264
+ });
265
+ });
250
266
  // Status command
251
267
  commander_1.program
252
268
  .command('status')
253
269
  .description('Show live session metrics and memory status')
254
270
  .option('-w, --watch', 'Watch mode — refresh session panel every 2s')
255
271
  .option('--json', 'Output raw metrics JSON (no memory API call)')
256
- .action((options) => {
257
- (0, status_1.status)({ watch: options.watch, json: options.json });
272
+ .action(async (options) => {
273
+ const { status } = await Promise.resolve().then(() => __importStar(require('./commands/status')));
274
+ status({ watch: options.watch, json: options.json });
258
275
  });
259
276
  // OpenClaw integration commands
260
277
  const clawCmd = commander_1.program
@@ -267,8 +284,9 @@ clawCmd
267
284
  .option('--proxy-url <url>', 'Expected ekkOS proxy URL (default: https://proxy.ekkos.dev)')
268
285
  .option('--model <model>', 'Expected primary OpenClaw model (default: ekkos-proxy/claude-sonnet-4-6)')
269
286
  .option('--workspace <path>', 'Expected OpenClaw workspace path')
270
- .action((options) => {
271
- (0, claw_1.clawStatus)({
287
+ .action(async (options) => {
288
+ const { clawStatus } = await Promise.resolve().then(() => __importStar(require('./commands/claw')));
289
+ clawStatus({
272
290
  json: options.json,
273
291
  proxyUrl: options.proxyUrl,
274
292
  model: options.model,
@@ -284,8 +302,9 @@ clawCmd
284
302
  .option('--proxy-url <url>', 'Target ekkOS proxy URL')
285
303
  .option('--model <model>', 'Target primary OpenClaw model')
286
304
  .option('--workspace <path>', 'Expected OpenClaw workspace path for status verification')
287
- .action((options) => {
288
- (0, claw_1.clawUpgrade)({
305
+ .action(async (options) => {
306
+ const { clawUpgrade } = await Promise.resolve().then(() => __importStar(require('./commands/claw')));
307
+ clawUpgrade({
289
308
  apply: options.apply,
290
309
  force: options.force,
291
310
  json: options.json,
@@ -298,7 +317,7 @@ clawCmd
298
317
  commander_1.program
299
318
  .command('test')
300
319
  .description('Test connection to ekkOS memory')
301
- .action(test_1.test);
320
+ .action(async () => { const { test } = await Promise.resolve().then(() => __importStar(require('./commands/test'))); await test(); });
302
321
  // Run command - launches Claude Code with ekkOS proxy
303
322
  commander_1.program
304
323
  .command('run')
@@ -312,6 +331,10 @@ commander_1.program
312
331
  .option('--skip-proxy', 'Skip API proxy (use direct Anthropic API, disables seamless context eviction)')
313
332
  .option('--dashboard', 'Launch with live usage dashboard in an isolated 60/40 tmux split (requires tmux)')
314
333
  .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')
315
338
  .action((options) => {
316
339
  (0, run_1.run)({
317
340
  session: options.session,
@@ -323,6 +346,43 @@ commander_1.program
323
346
  noProxy: options.skipProxy,
324
347
  dashboard: options.dashboard,
325
348
  addDirs: options.addDir,
349
+ model: options.model,
350
+ contextWindow: options.contextWindow,
351
+ continueLast: options.continueLast,
352
+ resumeSession: options.resumeSession,
353
+ });
354
+ });
355
+ commander_1.program
356
+ .command('pulse')
357
+ .description('Launch ekkOS Pulse: bypass + selector + live Claude dashboard')
358
+ .option('-s, --session <name>', 'Session name to restore on clear')
359
+ .option('--no-bypass', 'Disable bypass permissions mode')
360
+ .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
+ .option('--skip-inject', 'Monitor-only mode (detect context wall but print instructions instead of auto-inject)')
364
+ .option('--skip-proxy', 'Skip API proxy (use direct Anthropic API, disables seamless context eviction)')
365
+ .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)({
372
+ session: options.session,
373
+ bypass: options.bypass !== false,
374
+ pulse: true,
375
+ verbose: options.verbose,
376
+ doctor: options.doctor,
377
+ noInject: options.skipInject,
378
+ research: options.research,
379
+ noProxy: options.skipProxy,
380
+ dashboard: true,
381
+ addDirs: options.addDir,
382
+ model: options.model ?? true,
383
+ contextWindow: options.contextWindow,
384
+ continueLast: options.continueLast,
385
+ resumeSession: options.resumeSession,
326
386
  });
327
387
  });
328
388
  // Gemini CLI — launch Gemini with ekkOS proxy
@@ -332,8 +392,9 @@ commander_1.program
332
392
  .option('-s, --session <name>', 'Session name')
333
393
  .option('-v, --verbose', 'Show debug output')
334
394
  .option('--skip-proxy', 'Skip API proxy (use direct Gemini API)')
335
- .action((options) => {
336
- (0, gemini_1.gemini)({
395
+ .action(async (options) => {
396
+ const { gemini } = await Promise.resolve().then(() => __importStar(require('./commands/gemini')));
397
+ gemini({
337
398
  session: options.session,
338
399
  verbose: options.verbose,
339
400
  noProxy: options.skipProxy,
@@ -346,8 +407,9 @@ commander_1.program
346
407
  .option('--no-proxy', 'Skip proxy too (completely vanilla Claude)')
347
408
  .option('--no-hooks', 'Temporarily disable all hooks during test')
348
409
  .option('-v, --verbose', 'Show debug output')
349
- .action((options) => {
350
- (0, test_claude_1.testClaude)({
410
+ .action(async (options) => {
411
+ const { testClaude } = await Promise.resolve().then(() => __importStar(require('./commands/test-claude')));
412
+ testClaude({
351
413
  noProxy: options.proxy === false,
352
414
  noHooks: options.hooks === false,
353
415
  verbose: options.verbose,
@@ -359,8 +421,9 @@ commander_1.program
359
421
  .description('Check system prerequisites for ekkOS (Node, PTY, Claude, MCP)')
360
422
  .option('-f, --fix', 'Attempt safe auto-fixes and show commands for manual fixes')
361
423
  .option('-j, --json', 'Output machine-readable JSON report')
362
- .action((options) => {
363
- (0, doctor_1.doctor)({ fix: options.fix, json: options.json });
424
+ .action(async (options) => {
425
+ const { doctor } = await Promise.resolve().then(() => __importStar(require('./commands/doctor')));
426
+ doctor({ fix: options.fix, json: options.json });
364
427
  });
365
428
  // Stream command - stream capture status and management
366
429
  const streamCmd = commander_1.program
@@ -372,8 +435,9 @@ streamCmd
372
435
  .option('-s, --session <id>', 'Session ID to check')
373
436
  .option('-w, --watch', 'Watch mode - refresh every second')
374
437
  .option('-j, --json', 'Output machine-readable JSON')
375
- .action((options) => {
376
- (0, stream_1.streamStatus)({
438
+ .action(async (options) => {
439
+ const { streamStatus } = await Promise.resolve().then(() => __importStar(require('./commands/stream')));
440
+ streamStatus({
377
441
  session: options.session,
378
442
  watch: options.watch,
379
443
  json: options.json
@@ -382,8 +446,9 @@ streamCmd
382
446
  streamCmd
383
447
  .command('list')
384
448
  .description('List all sessions with stream data')
385
- .action(() => {
386
- (0, stream_1.streamList)();
449
+ .action(async () => {
450
+ const { streamList } = await Promise.resolve().then(() => __importStar(require('./commands/stream')));
451
+ streamList();
387
452
  });
388
453
  // DEPRECATED: Hooks removed in hookless architecture migration
389
454
  // The `hooks` command prints a deprecation notice directing users to `ekkos run`.
@@ -400,17 +465,28 @@ commander_1.program
400
465
  console.log(chalk_1.default.gray(' the ekkOS proxy — no shell hooks required.'));
401
466
  console.log('');
402
467
  });
403
- // Usage command - track Claude Code token usage and costs (powered by ccusage)
404
- (0, index_1.registerUsageCommand)(commander_1.program);
405
- // Dashboard command - live TUI for monitoring session usage
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
406
481
  commander_1.program.addCommand(dashboard_1.dashboardCommand);
407
482
  // Sessions command - list active Claude Code sessions (swarm support)
408
483
  commander_1.program
409
484
  .command('sessions')
410
485
  .description('List active Claude Code sessions (for swarm/multi-session support)')
411
486
  .option('-j, --json', 'Output machine-readable JSON')
412
- .action((options) => {
413
- const sessions = (0, state_1.getActiveSessions)();
487
+ .action(async (options) => {
488
+ const { getActiveSessions } = await Promise.resolve().then(() => __importStar(require('./utils/state')));
489
+ const sessions = getActiveSessions();
414
490
  if (options.json) {
415
491
  console.log(JSON.stringify(sessions, null, 2));
416
492
  return;
@@ -450,7 +526,8 @@ commander_1.program
450
526
  console.log(chalk_1.default.gray('Running init with your options...'));
451
527
  console.log('');
452
528
  // Forward to init
453
- await (0, init_1.init)({
529
+ const { init } = await Promise.resolve().then(() => __importStar(require('./commands/init')));
530
+ await init({
454
531
  ide: options.ide,
455
532
  key: options.key
456
533
  });
@@ -462,19 +539,19 @@ const swarmCmd = commander_1.program
462
539
  swarmCmd
463
540
  .command('status')
464
541
  .description('Show Q-table stats (states, visits, epsilon, top actions)')
465
- .action(swarm_1.swarmStatus);
542
+ .action(async () => { const { swarmStatus } = await Promise.resolve().then(() => __importStar(require('./commands/swarm'))); swarmStatus(); });
466
543
  swarmCmd
467
544
  .command('reset')
468
545
  .description('Clear Q-table from Redis (routing reverts to static rules)')
469
- .action(swarm_1.swarmReset);
546
+ .action(async () => { const { swarmReset } = await Promise.resolve().then(() => __importStar(require('./commands/swarm'))); swarmReset(); });
470
547
  swarmCmd
471
548
  .command('export')
472
549
  .description('Export Q-table to .swarm/q-learning-model.json')
473
- .action(swarm_1.swarmExport);
550
+ .action(async () => { const { swarmExport } = await Promise.resolve().then(() => __importStar(require('./commands/swarm'))); swarmExport(); });
474
551
  swarmCmd
475
552
  .command('import')
476
553
  .description('Import Q-table from .swarm/q-learning-model.json into Redis')
477
- .action(swarm_1.swarmImport);
554
+ .action(async () => { const { swarmImport } = await Promise.resolve().then(() => __importStar(require('./commands/swarm'))); swarmImport(); });
478
555
  swarmCmd
479
556
  .command('launch')
480
557
  .description('Launch parallel workers on a decomposed task (opens wizard if --task is omitted)')
@@ -485,13 +562,14 @@ swarmCmd
485
562
  .option('--no-queen', 'Skip launching the Python Queen coordinator')
486
563
  .option('--queen-strategy <strategy>', 'Queen strategy (adaptive-default, hierarchical-cascade, mesh-consensus)')
487
564
  .option('-v, --verbose', 'Show debug output')
488
- .action((options) => {
489
- // Auto-open wizard when --task is missing
565
+ .action(async (options) => {
490
566
  if (!options.task) {
491
- (0, swarm_setup_1.swarmSetup)();
567
+ const { swarmSetup } = await Promise.resolve().then(() => __importStar(require('./commands/swarm-setup')));
568
+ swarmSetup();
492
569
  return;
493
570
  }
494
- (0, swarm_1.swarmLaunch)({
571
+ const { swarmLaunch } = await Promise.resolve().then(() => __importStar(require('./commands/swarm')));
572
+ swarmLaunch({
495
573
  workers: options.workers || 4,
496
574
  task: options.task,
497
575
  bypass: options.bypass !== false,
@@ -504,10 +582,15 @@ swarmCmd
504
582
  swarmCmd
505
583
  .command('setup')
506
584
  .description('Interactive TUI wizard for configuring and launching a swarm')
507
- .action(() => {
508
- (0, swarm_setup_1.swarmSetup)();
585
+ .action(async () => { const { swarmSetup } = await Promise.resolve().then(() => __importStar(require('./commands/swarm-setup'))); swarmSetup(); });
586
+ swarmCmd
587
+ .command('swarm-dashboard')
588
+ .description('Live swarm dashboard')
589
+ .allowUnknownOption()
590
+ .action(async () => {
591
+ const { swarmDashboardCommand } = await Promise.resolve().then(() => __importStar(require('./commands/swarm-dashboard')));
592
+ swarmDashboardCommand.parse(process.argv.slice(3));
509
593
  });
510
- swarmCmd.addCommand(swarm_dashboard_1.swarmDashboardCommand);
511
594
  // --- Remote & Agent Wrapper Helpers ---
512
595
  function runRemoteCommand(command, ...args) {
513
596
  const isAgent = command === 'agent';
@@ -91,8 +91,24 @@ function resolveSessionName(name) {
91
91
  if (fs.existsSync(activeSessionsPath)) {
92
92
  try {
93
93
  const sessions = JSON.parse(fs.readFileSync(activeSessionsPath, 'utf-8'));
94
- // Find first entry with a valid UUID sessionId (skip "unknown" or empty)
95
- const match = sessions.find(s => s.sessionName === name && s.sessionId && s.sessionId !== 'unknown' && s.sessionId.length > 8);
94
+ // Find first entry with a valid UUID sessionId (skip "unknown" or empty).
95
+ // Also skip entries whose PID is dead (stale from prior runs / restarts).
96
+ const match = sessions.find(s => {
97
+ if (s.sessionName !== name)
98
+ return false;
99
+ if (!s.sessionId || s.sessionId === 'unknown' || s.sessionId.length <= 8)
100
+ return false;
101
+ // Prune dead PIDs — prevents stale cross-binding after restart
102
+ if (s.pid > 1) {
103
+ try {
104
+ process.kill(s.pid, 0);
105
+ }
106
+ catch {
107
+ return false;
108
+ }
109
+ }
110
+ return true;
111
+ });
96
112
  if (match) {
97
113
  return {
98
114
  uuid: match.sessionId,
@@ -12,3 +12,7 @@ export { SyncEngine, createSyncEngine, } from './sync-engine';
12
12
  export type { SyncResult, } from './sync-engine';
13
13
  export { generateLocalEmbedding, cosineSimilarity, searchByEmbedding, isEmbeddingAvailable, } from './local-embeddings';
14
14
  export type { SimilarityResult, } from './local-embeddings';
15
+ export { detectStack, } from './stack-detection';
16
+ export type { StackInfo, } from './stack-detection';
17
+ export { LANGUAGE_CONFIGS, BASE_KEY_FILES, BASE_EXCLUDED_DIRS, getKeyFilesForStack, getExcludedDirsForStack, getPromptHintsForStack, getSourceExtensionsForStack, getSystemBoundaryMarkersForStack, } from './language-config';
18
+ export type { LanguageConfig, } from './language-config';
@@ -6,7 +6,7 @@
6
6
  * Provides offline-capable memory, fallback logic, sync, and local embeddings.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.isEmbeddingAvailable = exports.searchByEmbedding = exports.cosineSimilarity = exports.generateLocalEmbedding = exports.createSyncEngine = exports.SyncEngine = exports.OFFLINE_CAPABLE_TOOLS = exports.isOfflineFallbackReady = exports.callWithFallback = exports.localStore = exports.LocalMemoryStore = void 0;
9
+ exports.getSystemBoundaryMarkersForStack = exports.getSourceExtensionsForStack = exports.getPromptHintsForStack = exports.getExcludedDirsForStack = exports.getKeyFilesForStack = exports.BASE_EXCLUDED_DIRS = exports.BASE_KEY_FILES = exports.LANGUAGE_CONFIGS = exports.detectStack = exports.isEmbeddingAvailable = exports.searchByEmbedding = exports.cosineSimilarity = exports.generateLocalEmbedding = exports.createSyncEngine = exports.SyncEngine = exports.OFFLINE_CAPABLE_TOOLS = exports.isOfflineFallbackReady = exports.callWithFallback = exports.localStore = exports.LocalMemoryStore = void 0;
10
10
  // SQLite memory store (Phase 6A)
11
11
  var sqlite_store_1 = require("./sqlite-store");
12
12
  Object.defineProperty(exports, "LocalMemoryStore", { enumerable: true, get: function () { return sqlite_store_1.LocalMemoryStore; } });
@@ -26,3 +26,16 @@ Object.defineProperty(exports, "generateLocalEmbedding", { enumerable: true, get
26
26
  Object.defineProperty(exports, "cosineSimilarity", { enumerable: true, get: function () { return local_embeddings_1.cosineSimilarity; } });
27
27
  Object.defineProperty(exports, "searchByEmbedding", { enumerable: true, get: function () { return local_embeddings_1.searchByEmbedding; } });
28
28
  Object.defineProperty(exports, "isEmbeddingAvailable", { enumerable: true, get: function () { return local_embeddings_1.isEmbeddingAvailable; } });
29
+ // Stack detection (Living Docs V4 — Phase 4)
30
+ var stack_detection_1 = require("./stack-detection");
31
+ Object.defineProperty(exports, "detectStack", { enumerable: true, get: function () { return stack_detection_1.detectStack; } });
32
+ // Language configuration (Living Docs V4 — Phase 4)
33
+ var language_config_1 = require("./language-config");
34
+ Object.defineProperty(exports, "LANGUAGE_CONFIGS", { enumerable: true, get: function () { return language_config_1.LANGUAGE_CONFIGS; } });
35
+ Object.defineProperty(exports, "BASE_KEY_FILES", { enumerable: true, get: function () { return language_config_1.BASE_KEY_FILES; } });
36
+ Object.defineProperty(exports, "BASE_EXCLUDED_DIRS", { enumerable: true, get: function () { return language_config_1.BASE_EXCLUDED_DIRS; } });
37
+ Object.defineProperty(exports, "getKeyFilesForStack", { enumerable: true, get: function () { return language_config_1.getKeyFilesForStack; } });
38
+ Object.defineProperty(exports, "getExcludedDirsForStack", { enumerable: true, get: function () { return language_config_1.getExcludedDirsForStack; } });
39
+ Object.defineProperty(exports, "getPromptHintsForStack", { enumerable: true, get: function () { return language_config_1.getPromptHintsForStack; } });
40
+ Object.defineProperty(exports, "getSourceExtensionsForStack", { enumerable: true, get: function () { return language_config_1.getSourceExtensionsForStack; } });
41
+ Object.defineProperty(exports, "getSystemBoundaryMarkersForStack", { enumerable: true, get: function () { return language_config_1.getSystemBoundaryMarkersForStack; } });
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Language Configuration — data-driven config for each language ecosystem
3
+ *
4
+ * Pure functions with zero dependencies. Provides key files, excluded dirs,
5
+ * source extensions, system boundary markers, and Gemini prompt hints
6
+ * for each supported language.
7
+ *
8
+ * Living Docs V4 — Phase 4: Language-Adapted Discovery
9
+ */
10
+ import type { StackInfo } from './stack-detection.js';
11
+ export interface LanguageConfig {
12
+ /** Key file names to prioritize reading (for both CLI and server compiler) */
13
+ keyFiles: string[];
14
+ /** Directories to exclude from scanning */
15
+ excludedDirs: string[];
16
+ /** File extensions that count as source files */
17
+ sourceExtensions: string[];
18
+ /** Files that mark a system boundary */
19
+ systemBoundaryMarkers: string[];
20
+ /** Prompt hints for Gemini (what to ask about) */
21
+ promptHints: {
22
+ architecture: string[];
23
+ buildSystem: string[];
24
+ deployTarget: string[];
25
+ testFramework: string[];
26
+ conventions: string[];
27
+ };
28
+ }
29
+ export declare const BASE_KEY_FILES: string[];
30
+ export declare const BASE_EXCLUDED_DIRS: string[];
31
+ export declare const LANGUAGE_CONFIGS: Record<string, LanguageConfig>;
32
+ /**
33
+ * Get the merged key files list for a detected stack.
34
+ * Returns BASE_KEY_FILES + language-specific files (deduplicated).
35
+ */
36
+ export declare function getKeyFilesForStack(stack: StackInfo): string[];
37
+ /**
38
+ * Get the merged excluded dirs for a detected stack.
39
+ * Returns BASE_EXCLUDED_DIRS + language-specific dirs.
40
+ */
41
+ export declare function getExcludedDirsForStack(stack: StackInfo): Set<string>;
42
+ /**
43
+ * Get prompt hints for the Gemini compiler.
44
+ * Falls back to TypeScript hints for unknown languages.
45
+ */
46
+ export declare function getPromptHintsForStack(stack: StackInfo): LanguageConfig['promptHints'];
47
+ /**
48
+ * Get the source extensions for a detected stack.
49
+ */
50
+ export declare function getSourceExtensionsForStack(stack: StackInfo): Set<string>;
51
+ /**
52
+ * Get system boundary markers for a detected stack.
53
+ * These are files that indicate a "system root" directory.
54
+ */
55
+ export declare function getSystemBoundaryMarkersForStack(stack: StackInfo): string[];