@ekkos/cli 0.3.3 → 1.0.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.
Files changed (81) hide show
  1. package/README.md +57 -0
  2. package/dist/agent/daemon.d.ts +27 -0
  3. package/dist/agent/daemon.js +254 -29
  4. package/dist/agent/health-check.d.ts +35 -0
  5. package/dist/agent/health-check.js +243 -0
  6. package/dist/agent/pty-runner.d.ts +1 -0
  7. package/dist/agent/pty-runner.js +6 -1
  8. package/dist/capture/transcript-repair.d.ts +1 -0
  9. package/dist/capture/transcript-repair.js +12 -1
  10. package/dist/commands/agent.d.ts +6 -0
  11. package/dist/commands/agent.js +244 -0
  12. package/dist/commands/dashboard.d.ts +25 -0
  13. package/dist/commands/dashboard.js +1175 -0
  14. package/dist/commands/run.d.ts +3 -0
  15. package/dist/commands/run.js +503 -350
  16. package/dist/commands/setup-remote.js +146 -37
  17. package/dist/commands/swarm-dashboard.d.ts +20 -0
  18. package/dist/commands/swarm-dashboard.js +735 -0
  19. package/dist/commands/swarm-setup.d.ts +10 -0
  20. package/dist/commands/swarm-setup.js +956 -0
  21. package/dist/commands/swarm.d.ts +46 -0
  22. package/dist/commands/swarm.js +441 -0
  23. package/dist/commands/test-claude.d.ts +16 -0
  24. package/dist/commands/test-claude.js +156 -0
  25. package/dist/commands/usage/blocks.d.ts +8 -0
  26. package/dist/commands/usage/blocks.js +60 -0
  27. package/dist/commands/usage/daily.d.ts +9 -0
  28. package/dist/commands/usage/daily.js +96 -0
  29. package/dist/commands/usage/dashboard.d.ts +8 -0
  30. package/dist/commands/usage/dashboard.js +104 -0
  31. package/dist/commands/usage/formatters.d.ts +41 -0
  32. package/dist/commands/usage/formatters.js +147 -0
  33. package/dist/commands/usage/index.d.ts +13 -0
  34. package/dist/commands/usage/index.js +87 -0
  35. package/dist/commands/usage/monthly.d.ts +8 -0
  36. package/dist/commands/usage/monthly.js +66 -0
  37. package/dist/commands/usage/session.d.ts +11 -0
  38. package/dist/commands/usage/session.js +193 -0
  39. package/dist/commands/usage/weekly.d.ts +9 -0
  40. package/dist/commands/usage/weekly.js +61 -0
  41. package/dist/deploy/instructions.d.ts +5 -2
  42. package/dist/deploy/instructions.js +11 -8
  43. package/dist/index.js +256 -20
  44. package/dist/lib/tmux-scrollbar.d.ts +14 -0
  45. package/dist/lib/tmux-scrollbar.js +296 -0
  46. package/dist/lib/usage-parser.d.ts +95 -5
  47. package/dist/lib/usage-parser.js +416 -71
  48. package/dist/utils/log-rotate.d.ts +18 -0
  49. package/dist/utils/log-rotate.js +74 -0
  50. package/dist/utils/platform.d.ts +2 -0
  51. package/dist/utils/platform.js +3 -1
  52. package/dist/utils/session-binding.d.ts +5 -0
  53. package/dist/utils/session-binding.js +46 -0
  54. package/dist/utils/state.js +4 -0
  55. package/dist/utils/verify-remote-terminal.d.ts +10 -0
  56. package/dist/utils/verify-remote-terminal.js +415 -0
  57. package/package.json +16 -11
  58. package/templates/CLAUDE.md +135 -23
  59. package/templates/cursor-hooks/after-agent-response.sh +0 -0
  60. package/templates/cursor-hooks/before-submit-prompt.sh +0 -0
  61. package/templates/cursor-hooks/stop.sh +0 -0
  62. package/templates/ekkos-manifest.json +5 -5
  63. package/templates/hooks/assistant-response.sh +0 -0
  64. package/templates/hooks/lib/contract.sh +43 -31
  65. package/templates/hooks/lib/count-tokens.cjs +86 -0
  66. package/templates/hooks/lib/ekkos-reminders.sh +98 -0
  67. package/templates/hooks/lib/state.sh +53 -1
  68. package/templates/hooks/session-start.sh +0 -0
  69. package/templates/hooks/stop.sh +150 -388
  70. package/templates/hooks/user-prompt-submit.sh +353 -443
  71. package/templates/plan-template.md +0 -0
  72. package/templates/spec-template.md +0 -0
  73. package/templates/windsurf-hooks/README.md +212 -0
  74. package/templates/windsurf-hooks/hooks.json +9 -2
  75. package/templates/windsurf-hooks/install.sh +148 -0
  76. package/templates/windsurf-hooks/lib/contract.sh +2 -0
  77. package/templates/windsurf-hooks/post-cascade-response.sh +251 -0
  78. package/templates/windsurf-hooks/pre-user-prompt.sh +435 -0
  79. package/templates/windsurf-skills/ekkos-memory/SKILL.md +219 -0
  80. package/LICENSE +0 -21
  81. package/templates/windsurf-hooks/before-submit-prompt.sh +0 -238
@@ -189,68 +189,177 @@ async function installService(verbose) {
189
189
  async function installMacOSService(verbose) {
190
190
  const plistName = 'dev.ekkos.agent';
191
191
  const plistPath = path.join(os.homedir(), 'Library', 'LaunchAgents', `${plistName}.plist`);
192
- // Find node path
192
+ // Find node path - use execPath of current process
193
193
  const nodePath = process.execPath;
194
- // Find ekkos CLI path (assuming it's in node_modules/.bin or globally installed)
194
+ if (verbose) {
195
+ console.log(chalk_1.default.gray(` Node path: ${nodePath}`));
196
+ }
197
+ // Find ekkos CLI path
195
198
  let ekkosPath;
199
+ let useNpx = false;
196
200
  try {
197
201
  ekkosPath = (0, child_process_1.execSync)('which ekkos', { encoding: 'utf-8' }).trim();
202
+ if (!ekkosPath)
203
+ throw new Error('Empty path');
198
204
  }
199
205
  catch {
200
206
  // Fall back to npx
201
- ekkosPath = 'npx @ekkos/cli';
207
+ ekkosPath = 'npx';
208
+ useNpx = true;
202
209
  }
203
- const plistContent = `<?xml version="1.0" encoding="UTF-8"?>
210
+ if (verbose) {
211
+ console.log(chalk_1.default.gray(` ekkOS path: ${ekkosPath}${useNpx ? ' (via npx)' : ''}`));
212
+ }
213
+ // Build program arguments
214
+ const programArgs = [nodePath];
215
+ if (useNpx) {
216
+ programArgs.push(ekkosPath);
217
+ programArgs.push('@ekkos/cli');
218
+ }
219
+ else {
220
+ programArgs.push(ekkosPath);
221
+ }
222
+ programArgs.push('agent');
223
+ programArgs.push('daemon');
224
+ // Build plist XML programmatically to avoid string template issues
225
+ const plistContent = buildPlist(plistName, programArgs, nodePath);
226
+ // Ensure directory exists
227
+ const launchAgentsDir = path.dirname(plistPath);
228
+ if (!fs.existsSync(launchAgentsDir)) {
229
+ fs.mkdirSync(launchAgentsDir, { recursive: true });
230
+ }
231
+ // Unload if already loaded
232
+ try {
233
+ (0, child_process_1.execSync)(`launchctl unload "${plistPath}" 2>/dev/null`, { encoding: 'utf-8' });
234
+ if (verbose) {
235
+ console.log(chalk_1.default.gray(` Unloaded existing: ${plistName}`));
236
+ }
237
+ }
238
+ catch {
239
+ // Ignore if not loaded
240
+ }
241
+ // Write plist
242
+ fs.writeFileSync(plistPath, plistContent);
243
+ if (verbose) {
244
+ console.log(chalk_1.default.gray(` Created: ${plistPath}`));
245
+ }
246
+ // Load the service
247
+ try {
248
+ (0, child_process_1.execSync)(`launchctl load "${plistPath}"`, { encoding: 'utf-8' });
249
+ if (verbose) {
250
+ console.log(chalk_1.default.gray(` Loaded: ${plistName}`));
251
+ }
252
+ }
253
+ catch (err) {
254
+ throw new Error(`Failed to load launchd service: ${err.message}`);
255
+ }
256
+ // Verify it's loaded
257
+ try {
258
+ const output = (0, child_process_1.execSync)('launchctl list | grep dev.ekkos.agent', { encoding: 'utf-8' });
259
+ if (verbose && output.trim()) {
260
+ console.log(chalk_1.default.gray(` Service status: ${output.trim()}`));
261
+ }
262
+ }
263
+ catch {
264
+ // Ignore if grep finds nothing
265
+ }
266
+ }
267
+ /**
268
+ * Build plist content with proper XML escaping
269
+ */
270
+ function buildPlist(label, args, nodePath) {
271
+ const homeDir = os.homedir();
272
+ const ekkosDir = state_1.EKKOS_DIR;
273
+ const nodeBinDir = path.dirname(nodePath);
274
+ const launchPath = `${nodeBinDir}:${path.join(homeDir, '.local', 'bin')}:/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin`;
275
+ // Build ProgramArguments array
276
+ let argXml = '';
277
+ for (const arg of args) {
278
+ argXml += ` <string>${escapeXml(arg)}</string>\n`;
279
+ }
280
+ return `<?xml version="1.0" encoding="UTF-8"?>
204
281
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
205
282
  <plist version="1.0">
206
283
  <dict>
207
284
  <key>Label</key>
208
- <string>${plistName}</string>
285
+ <string>${escapeXml(label)}</string>
286
+
209
287
  <key>ProgramArguments</key>
210
288
  <array>
211
- <string>${nodePath}</string>
212
- <string>${ekkosPath.includes('npx') ? ekkosPath.split(' ')[0] : ekkosPath}</string>
213
- ${ekkosPath.includes('npx') ? '<string>@ekkos/cli</string>' : ''}
214
- <string>agent</string>
215
- <string>daemon</string>
216
- </array>
289
+ ${argXml} </array>
290
+
291
+ <!-- Run at system login and on demand -->
217
292
  <key>RunAtLoad</key>
218
293
  <true/>
294
+
295
+ <!-- Keep alive: restart immediately if it exits or crashes -->
219
296
  <key>KeepAlive</key>
220
- <true/>
297
+ <dict>
298
+ <!-- Restart even if exit code is 0 (successful exit) -->
299
+ <key>SuccessfulExit</key>
300
+ <false/>
301
+ <!-- Restart if process is terminated by signal -->
302
+ <key>Crashed</key>
303
+ <true/>
304
+ </dict>
305
+
306
+ <!-- Logging -->
221
307
  <key>StandardErrorPath</key>
222
- <string>${path.join(state_1.EKKOS_DIR, 'agent.err.log')}</string>
308
+ <string>${escapeXml(path.join(ekkosDir, 'agent.err.log'))}</string>
309
+
223
310
  <key>StandardOutPath</key>
224
- <string>${path.join(state_1.EKKOS_DIR, 'agent.out.log')}</string>
311
+ <string>${escapeXml(path.join(ekkosDir, 'agent.out.log'))}</string>
312
+
313
+ <!-- Environment variables -->
225
314
  <key>EnvironmentVariables</key>
226
315
  <dict>
227
316
  <key>PATH</key>
228
- <string>/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin</string>
317
+ <string>${escapeXml(launchPath)}</string>
318
+ <key>NODE_ENV</key>
319
+ <string>production</string>
320
+ <key>HOME</key>
321
+ <string>${escapeXml(homeDir)}</string>
229
322
  </dict>
323
+
324
+ <!-- Process management -->
325
+ <key>ProcessType</key>
326
+ <string>Background</string>
327
+
328
+ <!-- Critical: Allow daemon to handle network reconnections -->
329
+ <key>AbandonProcessGroup</key>
330
+ <true/>
331
+
332
+ <!-- Restart throttle: wait 10s before restarting crashed process -->
333
+ <key>ThrottleInterval</key>
334
+ <integer>10</integer>
335
+
336
+ <!-- Higher priority for reliable connectivity -->
337
+ <key>Nice</key>
338
+ <integer>-5</integer>
339
+
340
+ <!-- File descriptor limits -->
341
+ <key>SoftResourceLimits</key>
342
+ <dict>
343
+ <key>NumberOfFiles</key>
344
+ <integer>2048</integer>
345
+ </dict>
346
+
347
+ <!-- Working directory -->
348
+ <key>WorkingDirectory</key>
349
+ <string>${escapeXml(homeDir)}</string>
230
350
  </dict>
231
351
  </plist>`;
232
- // Ensure directory exists
233
- const launchAgentsDir = path.dirname(plistPath);
234
- if (!fs.existsSync(launchAgentsDir)) {
235
- fs.mkdirSync(launchAgentsDir, { recursive: true });
236
- }
237
- // Write plist
238
- fs.writeFileSync(plistPath, plistContent);
239
- if (verbose) {
240
- console.log(chalk_1.default.gray(` Created: ${plistPath}`));
241
- }
242
- // Unload if already loaded
243
- try {
244
- (0, child_process_1.execSync)(`launchctl unload "${plistPath}" 2>/dev/null`, { encoding: 'utf-8' });
245
- }
246
- catch {
247
- // Ignore if not loaded
248
- }
249
- // Load the service
250
- (0, child_process_1.execSync)(`launchctl load "${plistPath}"`, { encoding: 'utf-8' });
251
- if (verbose) {
252
- console.log(chalk_1.default.gray(` Loaded: ${plistName}`));
253
- }
352
+ }
353
+ /**
354
+ * Escape XML special characters
355
+ */
356
+ function escapeXml(str) {
357
+ return str
358
+ .replace(/&/g, '&amp;')
359
+ .replace(/</g, '&lt;')
360
+ .replace(/>/g, '&gt;')
361
+ .replace(/"/g, '&quot;')
362
+ .replace(/'/g, '&apos;');
254
363
  }
255
364
  /**
256
365
  * Install Windows service
@@ -0,0 +1,20 @@
1
+ /**
2
+ * ekkos swarm dashboard
3
+ *
4
+ * Live TUI dashboard for monitoring a swarm of parallel Claude Code workers.
5
+ * Uses blessed-contrib for rich terminal widgets.
6
+ *
7
+ * Layout:
8
+ * Header: Swarm name + worker count + total cost + duration
9
+ * Workers: Per-worker status cards (context bar, cost, turns, model)
10
+ * Chart: Combined token usage from all workers
11
+ * Table: All turns from all workers (with worker column)
12
+ * Usage: Anthropic rate limit windows
13
+ * Footer: Aggregate totals + routing breakdown + keybindings
14
+ *
15
+ * Usage:
16
+ * ekkos swarm dashboard Auto-detect active swarm
17
+ * ekkos swarm dashboard --launch-ts 123 Use specific launch timestamp
18
+ */
19
+ import { Command } from 'commander';
20
+ export declare const swarmDashboardCommand: Command;