@ekkos/cli 1.3.6 → 1.3.8
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/commands/dashboard.js +96 -21
- package/dist/commands/gemini.js +46 -9
- package/dist/commands/init.js +92 -21
- package/dist/commands/living-docs.d.ts +8 -0
- package/dist/commands/living-docs.js +66 -0
- package/dist/commands/run.js +55 -6
- package/dist/commands/scan.d.ts +68 -0
- package/dist/commands/scan.js +318 -22
- package/dist/commands/setup.js +2 -6
- package/dist/deploy/index.d.ts +1 -0
- package/dist/deploy/index.js +1 -0
- package/dist/deploy/instructions.d.ts +10 -3
- package/dist/deploy/instructions.js +34 -7
- package/dist/index.js +192 -79
- package/dist/lib/usage-parser.js +18 -2
- package/dist/local/index.d.ts +4 -0
- package/dist/local/index.js +14 -1
- package/dist/local/language-config.d.ts +55 -0
- package/dist/local/language-config.js +729 -0
- package/dist/local/living-docs-manager.d.ts +59 -0
- package/dist/local/living-docs-manager.js +1084 -0
- package/dist/local/stack-detection.d.ts +21 -0
- package/dist/local/stack-detection.js +406 -0
- package/package.json +1 -1
- package/templates/CLAUDE.md +89 -99
|
@@ -6,25 +6,52 @@ exports.getInstructionsContent = getInstructionsContent;
|
|
|
6
6
|
const fs_1 = require("fs");
|
|
7
7
|
const platform_1 = require("../utils/platform");
|
|
8
8
|
const templates_1 = require("../utils/templates");
|
|
9
|
+
const BEGIN_MARKER = '<!-- ekkOS:begin — managed by ekkos init, do not remove this marker -->';
|
|
10
|
+
const END_MARKER = '<!-- ekkOS:end -->';
|
|
9
11
|
/**
|
|
10
|
-
* Deploy
|
|
12
|
+
* Deploy ekkOS block into ~/.claude/CLAUDE.md.
|
|
13
|
+
*
|
|
14
|
+
* Strategy:
|
|
15
|
+
* - If file doesn't exist → create it with just the ekkOS block.
|
|
16
|
+
* - If file exists and already has markers → replace the block between markers.
|
|
17
|
+
* - If file exists with no markers → prepend ekkOS block above existing content.
|
|
18
|
+
*
|
|
19
|
+
* Never destroys user content outside the markers.
|
|
11
20
|
*/
|
|
12
21
|
function deployInstructions() {
|
|
13
|
-
// Ensure .claude directory exists
|
|
14
22
|
if (!(0, fs_1.existsSync)(platform_1.CLAUDE_DIR)) {
|
|
15
23
|
(0, fs_1.mkdirSync)(platform_1.CLAUDE_DIR, { recursive: true });
|
|
16
24
|
}
|
|
17
|
-
|
|
18
|
-
(0,
|
|
25
|
+
const ekkosBlock = (0, templates_1.readTemplate)('CLAUDE.md');
|
|
26
|
+
if (!(0, fs_1.existsSync)(platform_1.CLAUDE_MD)) {
|
|
27
|
+
// No file — create fresh
|
|
28
|
+
(0, fs_1.writeFileSync)(platform_1.CLAUDE_MD, ekkosBlock);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const existing = (0, fs_1.readFileSync)(platform_1.CLAUDE_MD, 'utf-8');
|
|
32
|
+
const beginIdx = existing.indexOf(BEGIN_MARKER);
|
|
33
|
+
const endIdx = existing.indexOf(END_MARKER);
|
|
34
|
+
if (beginIdx !== -1 && endIdx !== -1 && endIdx > beginIdx) {
|
|
35
|
+
// Markers found — replace in-place
|
|
36
|
+
const before = existing.slice(0, beginIdx);
|
|
37
|
+
const after = existing.slice(endIdx + END_MARKER.length);
|
|
38
|
+
(0, fs_1.writeFileSync)(platform_1.CLAUDE_MD, before + ekkosBlock + after);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
// No markers — prepend ekkOS block, preserve everything below
|
|
42
|
+
(0, fs_1.writeFileSync)(platform_1.CLAUDE_MD, ekkosBlock + '\n\n' + existing);
|
|
19
43
|
}
|
|
20
44
|
/**
|
|
21
|
-
* Check if CLAUDE.md
|
|
45
|
+
* Check if CLAUDE.md has the ekkOS block
|
|
22
46
|
*/
|
|
23
47
|
function isInstructionsDeployed() {
|
|
24
|
-
|
|
48
|
+
if (!(0, fs_1.existsSync)(platform_1.CLAUDE_MD))
|
|
49
|
+
return false;
|
|
50
|
+
const content = (0, fs_1.readFileSync)(platform_1.CLAUDE_MD, 'utf-8');
|
|
51
|
+
return content.includes(BEGIN_MARKER) && content.includes(END_MARKER);
|
|
25
52
|
}
|
|
26
53
|
/**
|
|
27
|
-
* Get the CLAUDE.md content
|
|
54
|
+
* Get the ekkOS CLAUDE.md template content
|
|
28
55
|
*/
|
|
29
56
|
function getInstructionsContent() {
|
|
30
57
|
try {
|
package/dist/index.js
CHANGED
|
@@ -38,27 +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
|
-
|
|
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"));
|
|
49
|
+
const child_process_1 = require("child_process");
|
|
62
50
|
// Get version from package.json (CommonJS compatible)
|
|
63
51
|
const pkgPath = path.resolve(__dirname, '../package.json');
|
|
64
52
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
@@ -75,16 +63,13 @@ commander_1.program
|
|
|
75
63
|
.addHelpText('after', [
|
|
76
64
|
'',
|
|
77
65
|
chalk_1.default.cyan.bold('Examples:'),
|
|
78
|
-
` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos')} ${chalk_1.default.gray('Start Claude Code with ekkOS memory (default
|
|
79
|
-
` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos
|
|
80
|
-
` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos
|
|
81
|
-
` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos
|
|
82
|
-
` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos
|
|
83
|
-
` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos
|
|
84
|
-
` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos
|
|
85
|
-
` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos usage daily')} ${chalk_1.default.gray("View today's token usage and costs")}`,
|
|
86
|
-
` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos gemini')} ${chalk_1.default.gray('Launch Gemini CLI with ekkOS memory proxy')}`,
|
|
87
|
-
` ${chalk_1.default.gray('$')} ${chalk_1.default.white('ekkos swarm launch -t "build X"')} ${chalk_1.default.gray('Launch parallel workers on a task')}`,
|
|
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')}`,
|
|
88
73
|
'',
|
|
89
74
|
chalk_1.default.gray(' Run ') + chalk_1.default.white('ekkos <command> --help') + chalk_1.default.gray(' for detailed options on any command.'),
|
|
90
75
|
'',
|
|
@@ -152,38 +137,49 @@ commander_1.program
|
|
|
152
137
|
title: 'Getting Started',
|
|
153
138
|
icon: '▸',
|
|
154
139
|
commands: [
|
|
155
|
-
{ name: 'init', desc: '
|
|
140
|
+
{ name: 'init', desc: 'First-time setup — authenticate and configure IDEs' },
|
|
156
141
|
{ name: 'scan', desc: 'Scan repo structure and seed ekkOS system registry' },
|
|
157
|
-
{ name: '
|
|
158
|
-
{ name: '
|
|
159
|
-
{ name: 'doctor', desc: 'Check system prerequisites
|
|
142
|
+
{ name: 'docs', desc: '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' },
|
|
160
145
|
],
|
|
161
146
|
},
|
|
162
147
|
{
|
|
163
|
-
title: '
|
|
148
|
+
title: 'Launch Agents (Local + Mobile Synced)',
|
|
164
149
|
icon: '▸',
|
|
165
150
|
commands: [
|
|
166
|
-
{ name: 'run', desc: 'Launch Claude Code with
|
|
167
|
-
{ name: '
|
|
168
|
-
{ name: '
|
|
169
|
-
{ name: '
|
|
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' },
|
|
170
155
|
],
|
|
171
156
|
},
|
|
172
157
|
{
|
|
173
|
-
title: '
|
|
158
|
+
title: 'Mobile Sync & Cloud (Synk)',
|
|
174
159
|
icon: '▸',
|
|
175
160
|
commands: [
|
|
176
|
-
{ name: '
|
|
177
|
-
{ name: '
|
|
178
|
-
{ name: '
|
|
179
|
-
{ name: '
|
|
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' },
|
|
180
165
|
],
|
|
181
166
|
},
|
|
182
167
|
{
|
|
183
|
-
title: '
|
|
168
|
+
title: 'Headless Agents (Remote Control)',
|
|
184
169
|
icon: '▸',
|
|
185
170
|
commands: [
|
|
186
|
-
{ name: '
|
|
171
|
+
{ name: 'agent', desc: 'Manage remote headless agents (create, list, stop)' },
|
|
172
|
+
{ name: 'swarm', desc: 'Parallel workers, Q-learning routing, and swarm dashboard' },
|
|
173
|
+
],
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
title: 'Monitoring & Usage',
|
|
177
|
+
icon: '▸',
|
|
178
|
+
commands: [
|
|
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
|
];
|
|
@@ -225,7 +221,7 @@ commander_1.program
|
|
|
225
221
|
.option('-q, --quick', 'Quick setup: auto-detect IDE, use defaults, only prompt for API key if missing')
|
|
226
222
|
.option('--skip-hooks', '[DEPRECATED] Hooks are no longer deployed; this flag is a no-op')
|
|
227
223
|
.option('--skip-skills', 'Skip skills deployment')
|
|
228
|
-
.action(
|
|
224
|
+
.action(async (options) => { const { init } = await Promise.resolve().then(() => __importStar(require('./commands/init'))); await init(options); });
|
|
229
225
|
// Scan command — discover repo systems and seed registry
|
|
230
226
|
commander_1.program
|
|
231
227
|
.command('scan')
|
|
@@ -233,21 +229,45 @@ commander_1.program
|
|
|
233
229
|
.option('-c, --compile', 'Trigger a compile pass after seeding')
|
|
234
230
|
.option('-n, --dry-run', 'Show discovered systems without seeding')
|
|
235
231
|
.option('-p, --path <path>', 'Path to scan (default: current directory)')
|
|
236
|
-
.action((options) => {
|
|
237
|
-
(
|
|
232
|
+
.action(async (options) => {
|
|
233
|
+
const { scan } = await Promise.resolve().then(() => __importStar(require('./commands/scan')));
|
|
234
|
+
scan({
|
|
238
235
|
compile: options.compile,
|
|
239
236
|
dryRun: options.dryRun,
|
|
240
237
|
path: options.path,
|
|
241
238
|
});
|
|
242
239
|
});
|
|
240
|
+
// Local living docs command
|
|
241
|
+
const docsCmd = commander_1.program
|
|
242
|
+
.command('docs')
|
|
243
|
+
.description('Manage local ekkOS_CONTEXT.md living docs');
|
|
244
|
+
docsCmd
|
|
245
|
+
.command('watch')
|
|
246
|
+
.description('Watch the local workspace and keep ekkOS_CONTEXT.md files current on disk')
|
|
247
|
+
.option('-p, --path <path>', 'Path to watch (default: current directory)')
|
|
248
|
+
.option('--timezone <iana-tz>', 'IANA timezone for timestamps (default: local machine timezone)')
|
|
249
|
+
.option('--poll-interval-ms <ms>', 'Polling interval fallback in milliseconds', (value) => parseInt(value, 10))
|
|
250
|
+
.option('--debounce-ms <ms>', 'Debounce window before recompiling in milliseconds', (value) => parseInt(value, 10))
|
|
251
|
+
.option('--no-seed', 'Do not seed the platform registry while watching')
|
|
252
|
+
.action(async (options) => {
|
|
253
|
+
const { watchLivingDocs } = await Promise.resolve().then(() => __importStar(require('./commands/living-docs')));
|
|
254
|
+
watchLivingDocs({
|
|
255
|
+
path: options.path,
|
|
256
|
+
timeZone: options.timezone,
|
|
257
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
258
|
+
debounceMs: options.debounceMs,
|
|
259
|
+
noSeed: options.seed === false,
|
|
260
|
+
});
|
|
261
|
+
});
|
|
243
262
|
// Status command
|
|
244
263
|
commander_1.program
|
|
245
264
|
.command('status')
|
|
246
265
|
.description('Show live session metrics and memory status')
|
|
247
266
|
.option('-w, --watch', 'Watch mode — refresh session panel every 2s')
|
|
248
267
|
.option('--json', 'Output raw metrics JSON (no memory API call)')
|
|
249
|
-
.action((options) => {
|
|
250
|
-
|
|
268
|
+
.action(async (options) => {
|
|
269
|
+
const { status } = await Promise.resolve().then(() => __importStar(require('./commands/status')));
|
|
270
|
+
status({ watch: options.watch, json: options.json });
|
|
251
271
|
});
|
|
252
272
|
// OpenClaw integration commands
|
|
253
273
|
const clawCmd = commander_1.program
|
|
@@ -260,8 +280,9 @@ clawCmd
|
|
|
260
280
|
.option('--proxy-url <url>', 'Expected ekkOS proxy URL (default: https://proxy.ekkos.dev)')
|
|
261
281
|
.option('--model <model>', 'Expected primary OpenClaw model (default: ekkos-proxy/claude-sonnet-4-6)')
|
|
262
282
|
.option('--workspace <path>', 'Expected OpenClaw workspace path')
|
|
263
|
-
.action((options) => {
|
|
264
|
-
|
|
283
|
+
.action(async (options) => {
|
|
284
|
+
const { clawStatus } = await Promise.resolve().then(() => __importStar(require('./commands/claw')));
|
|
285
|
+
clawStatus({
|
|
265
286
|
json: options.json,
|
|
266
287
|
proxyUrl: options.proxyUrl,
|
|
267
288
|
model: options.model,
|
|
@@ -277,8 +298,9 @@ clawCmd
|
|
|
277
298
|
.option('--proxy-url <url>', 'Target ekkOS proxy URL')
|
|
278
299
|
.option('--model <model>', 'Target primary OpenClaw model')
|
|
279
300
|
.option('--workspace <path>', 'Expected OpenClaw workspace path for status verification')
|
|
280
|
-
.action((options) => {
|
|
281
|
-
|
|
301
|
+
.action(async (options) => {
|
|
302
|
+
const { clawUpgrade } = await Promise.resolve().then(() => __importStar(require('./commands/claw')));
|
|
303
|
+
clawUpgrade({
|
|
282
304
|
apply: options.apply,
|
|
283
305
|
force: options.force,
|
|
284
306
|
json: options.json,
|
|
@@ -291,7 +313,7 @@ clawCmd
|
|
|
291
313
|
commander_1.program
|
|
292
314
|
.command('test')
|
|
293
315
|
.description('Test connection to ekkOS memory')
|
|
294
|
-
.action(
|
|
316
|
+
.action(async () => { const { test } = await Promise.resolve().then(() => __importStar(require('./commands/test'))); await test(); });
|
|
295
317
|
// Run command - launches Claude Code with ekkOS proxy
|
|
296
318
|
commander_1.program
|
|
297
319
|
.command('run')
|
|
@@ -325,8 +347,9 @@ commander_1.program
|
|
|
325
347
|
.option('-s, --session <name>', 'Session name')
|
|
326
348
|
.option('-v, --verbose', 'Show debug output')
|
|
327
349
|
.option('--skip-proxy', 'Skip API proxy (use direct Gemini API)')
|
|
328
|
-
.action((options) => {
|
|
329
|
-
(
|
|
350
|
+
.action(async (options) => {
|
|
351
|
+
const { gemini } = await Promise.resolve().then(() => __importStar(require('./commands/gemini')));
|
|
352
|
+
gemini({
|
|
330
353
|
session: options.session,
|
|
331
354
|
verbose: options.verbose,
|
|
332
355
|
noProxy: options.skipProxy,
|
|
@@ -339,8 +362,9 @@ commander_1.program
|
|
|
339
362
|
.option('--no-proxy', 'Skip proxy too (completely vanilla Claude)')
|
|
340
363
|
.option('--no-hooks', 'Temporarily disable all hooks during test')
|
|
341
364
|
.option('-v, --verbose', 'Show debug output')
|
|
342
|
-
.action((options) => {
|
|
343
|
-
|
|
365
|
+
.action(async (options) => {
|
|
366
|
+
const { testClaude } = await Promise.resolve().then(() => __importStar(require('./commands/test-claude')));
|
|
367
|
+
testClaude({
|
|
344
368
|
noProxy: options.proxy === false,
|
|
345
369
|
noHooks: options.hooks === false,
|
|
346
370
|
verbose: options.verbose,
|
|
@@ -352,8 +376,9 @@ commander_1.program
|
|
|
352
376
|
.description('Check system prerequisites for ekkOS (Node, PTY, Claude, MCP)')
|
|
353
377
|
.option('-f, --fix', 'Attempt safe auto-fixes and show commands for manual fixes')
|
|
354
378
|
.option('-j, --json', 'Output machine-readable JSON report')
|
|
355
|
-
.action((options) => {
|
|
356
|
-
|
|
379
|
+
.action(async (options) => {
|
|
380
|
+
const { doctor } = await Promise.resolve().then(() => __importStar(require('./commands/doctor')));
|
|
381
|
+
doctor({ fix: options.fix, json: options.json });
|
|
357
382
|
});
|
|
358
383
|
// Stream command - stream capture status and management
|
|
359
384
|
const streamCmd = commander_1.program
|
|
@@ -365,8 +390,9 @@ streamCmd
|
|
|
365
390
|
.option('-s, --session <id>', 'Session ID to check')
|
|
366
391
|
.option('-w, --watch', 'Watch mode - refresh every second')
|
|
367
392
|
.option('-j, --json', 'Output machine-readable JSON')
|
|
368
|
-
.action((options) => {
|
|
369
|
-
|
|
393
|
+
.action(async (options) => {
|
|
394
|
+
const { streamStatus } = await Promise.resolve().then(() => __importStar(require('./commands/stream')));
|
|
395
|
+
streamStatus({
|
|
370
396
|
session: options.session,
|
|
371
397
|
watch: options.watch,
|
|
372
398
|
json: options.json
|
|
@@ -375,8 +401,9 @@ streamCmd
|
|
|
375
401
|
streamCmd
|
|
376
402
|
.command('list')
|
|
377
403
|
.description('List all sessions with stream data')
|
|
378
|
-
.action(() => {
|
|
379
|
-
|
|
404
|
+
.action(async () => {
|
|
405
|
+
const { streamList } = await Promise.resolve().then(() => __importStar(require('./commands/stream')));
|
|
406
|
+
streamList();
|
|
380
407
|
});
|
|
381
408
|
// DEPRECATED: Hooks removed in hookless architecture migration
|
|
382
409
|
// The `hooks` command prints a deprecation notice directing users to `ekkos run`.
|
|
@@ -393,17 +420,28 @@ commander_1.program
|
|
|
393
420
|
console.log(chalk_1.default.gray(' the ekkOS proxy — no shell hooks required.'));
|
|
394
421
|
console.log('');
|
|
395
422
|
});
|
|
396
|
-
// Usage command
|
|
397
|
-
|
|
398
|
-
|
|
423
|
+
// Usage command — lightweight stub, heavy handler loaded lazily
|
|
424
|
+
commander_1.program
|
|
425
|
+
.command('usage')
|
|
426
|
+
.description('Token usage and cost tracking (daily, weekly, monthly, session)')
|
|
427
|
+
.allowUnknownOption()
|
|
428
|
+
.action(async () => {
|
|
429
|
+
const { registerUsageCommand } = await Promise.resolve().then(() => __importStar(require('./commands/usage/index')));
|
|
430
|
+
const { Command } = await Promise.resolve().then(() => __importStar(require('commander')));
|
|
431
|
+
const usageProgram = new Command('ekkos');
|
|
432
|
+
registerUsageCommand(usageProgram);
|
|
433
|
+
usageProgram.parse(['node', 'ekkos', 'usage', ...process.argv.slice(3)]);
|
|
434
|
+
});
|
|
435
|
+
// Dashboard command — eager because `run --dashboard` spawns `ekkos dashboard` in tmux
|
|
399
436
|
commander_1.program.addCommand(dashboard_1.dashboardCommand);
|
|
400
437
|
// Sessions command - list active Claude Code sessions (swarm support)
|
|
401
438
|
commander_1.program
|
|
402
439
|
.command('sessions')
|
|
403
440
|
.description('List active Claude Code sessions (for swarm/multi-session support)')
|
|
404
441
|
.option('-j, --json', 'Output machine-readable JSON')
|
|
405
|
-
.action((options) => {
|
|
406
|
-
const
|
|
442
|
+
.action(async (options) => {
|
|
443
|
+
const { getActiveSessions } = await Promise.resolve().then(() => __importStar(require('./utils/state')));
|
|
444
|
+
const sessions = getActiveSessions();
|
|
407
445
|
if (options.json) {
|
|
408
446
|
console.log(JSON.stringify(sessions, null, 2));
|
|
409
447
|
return;
|
|
@@ -443,7 +481,8 @@ commander_1.program
|
|
|
443
481
|
console.log(chalk_1.default.gray('Running init with your options...'));
|
|
444
482
|
console.log('');
|
|
445
483
|
// Forward to init
|
|
446
|
-
await (
|
|
484
|
+
const { init } = await Promise.resolve().then(() => __importStar(require('./commands/init')));
|
|
485
|
+
await init({
|
|
447
486
|
ide: options.ide,
|
|
448
487
|
key: options.key
|
|
449
488
|
});
|
|
@@ -455,19 +494,19 @@ const swarmCmd = commander_1.program
|
|
|
455
494
|
swarmCmd
|
|
456
495
|
.command('status')
|
|
457
496
|
.description('Show Q-table stats (states, visits, epsilon, top actions)')
|
|
458
|
-
.action(
|
|
497
|
+
.action(async () => { const { swarmStatus } = await Promise.resolve().then(() => __importStar(require('./commands/swarm'))); swarmStatus(); });
|
|
459
498
|
swarmCmd
|
|
460
499
|
.command('reset')
|
|
461
500
|
.description('Clear Q-table from Redis (routing reverts to static rules)')
|
|
462
|
-
.action(
|
|
501
|
+
.action(async () => { const { swarmReset } = await Promise.resolve().then(() => __importStar(require('./commands/swarm'))); swarmReset(); });
|
|
463
502
|
swarmCmd
|
|
464
503
|
.command('export')
|
|
465
504
|
.description('Export Q-table to .swarm/q-learning-model.json')
|
|
466
|
-
.action(
|
|
505
|
+
.action(async () => { const { swarmExport } = await Promise.resolve().then(() => __importStar(require('./commands/swarm'))); swarmExport(); });
|
|
467
506
|
swarmCmd
|
|
468
507
|
.command('import')
|
|
469
508
|
.description('Import Q-table from .swarm/q-learning-model.json into Redis')
|
|
470
|
-
.action(
|
|
509
|
+
.action(async () => { const { swarmImport } = await Promise.resolve().then(() => __importStar(require('./commands/swarm'))); swarmImport(); });
|
|
471
510
|
swarmCmd
|
|
472
511
|
.command('launch')
|
|
473
512
|
.description('Launch parallel workers on a decomposed task (opens wizard if --task is omitted)')
|
|
@@ -478,13 +517,14 @@ swarmCmd
|
|
|
478
517
|
.option('--no-queen', 'Skip launching the Python Queen coordinator')
|
|
479
518
|
.option('--queen-strategy <strategy>', 'Queen strategy (adaptive-default, hierarchical-cascade, mesh-consensus)')
|
|
480
519
|
.option('-v, --verbose', 'Show debug output')
|
|
481
|
-
.action((options) => {
|
|
482
|
-
// Auto-open wizard when --task is missing
|
|
520
|
+
.action(async (options) => {
|
|
483
521
|
if (!options.task) {
|
|
484
|
-
|
|
522
|
+
const { swarmSetup } = await Promise.resolve().then(() => __importStar(require('./commands/swarm-setup')));
|
|
523
|
+
swarmSetup();
|
|
485
524
|
return;
|
|
486
525
|
}
|
|
487
|
-
|
|
526
|
+
const { swarmLaunch } = await Promise.resolve().then(() => __importStar(require('./commands/swarm')));
|
|
527
|
+
swarmLaunch({
|
|
488
528
|
workers: options.workers || 4,
|
|
489
529
|
task: options.task,
|
|
490
530
|
bypass: options.bypass !== false,
|
|
@@ -497,10 +537,83 @@ swarmCmd
|
|
|
497
537
|
swarmCmd
|
|
498
538
|
.command('setup')
|
|
499
539
|
.description('Interactive TUI wizard for configuring and launching a swarm')
|
|
500
|
-
.action(() => {
|
|
501
|
-
|
|
540
|
+
.action(async () => { const { swarmSetup } = await Promise.resolve().then(() => __importStar(require('./commands/swarm-setup'))); swarmSetup(); });
|
|
541
|
+
swarmCmd
|
|
542
|
+
.command('swarm-dashboard')
|
|
543
|
+
.description('Live swarm dashboard')
|
|
544
|
+
.allowUnknownOption()
|
|
545
|
+
.action(async () => {
|
|
546
|
+
const { swarmDashboardCommand } = await Promise.resolve().then(() => __importStar(require('./commands/swarm-dashboard')));
|
|
547
|
+
swarmDashboardCommand.parse(process.argv.slice(3));
|
|
502
548
|
});
|
|
503
|
-
|
|
549
|
+
// --- Remote & Agent Wrapper Helpers ---
|
|
550
|
+
function runRemoteCommand(command, ...args) {
|
|
551
|
+
const isAgent = command === 'agent';
|
|
552
|
+
const pkgName = isAgent ? 'ekkos-agent' : 'ekkos-remote';
|
|
553
|
+
const binName = isAgent ? 'ekkos-agent.mjs' : 'ekkos-synk.mjs';
|
|
554
|
+
// Try resolving local monorepo first
|
|
555
|
+
let binPath = path.resolve(__dirname, '../../' + pkgName + '/bin/' + binName);
|
|
556
|
+
// If we are installed globally (dist folder structure), resolve relatively
|
|
557
|
+
if (!fs.existsSync(binPath)) {
|
|
558
|
+
try {
|
|
559
|
+
const globalPath = require.resolve('@ekkos/' + (isAgent ? 'agent' : 'remote') + '/package.json');
|
|
560
|
+
binPath = path.join(path.dirname(globalPath), 'bin', binName);
|
|
561
|
+
}
|
|
562
|
+
catch (e) {
|
|
563
|
+
// Fallback relative to current dist
|
|
564
|
+
binPath = path.resolve(__dirname, '../../' + pkgName + '/bin/' + binName);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
const p = (0, child_process_1.spawn)('node', [binPath, ...args], {
|
|
568
|
+
stdio: 'inherit',
|
|
569
|
+
env: process.env
|
|
570
|
+
});
|
|
571
|
+
p.on('exit', (code) => {
|
|
572
|
+
process.exit(code || 0);
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
// Mobile Sync & Cloud (Synk) Commands
|
|
576
|
+
commander_1.program
|
|
577
|
+
.command('auth')
|
|
578
|
+
.description('Manage authentication for Memory, Agents, and Mobile App')
|
|
579
|
+
.allowUnknownOption()
|
|
580
|
+
.action(() => runRemoteCommand('synk', 'auth', ...process.argv.slice(3)));
|
|
581
|
+
commander_1.program
|
|
582
|
+
.command('codex')
|
|
583
|
+
.description('Launch Codex (OpenAI) with mobile control')
|
|
584
|
+
.allowUnknownOption()
|
|
585
|
+
.action(() => runRemoteCommand('synk', 'codex', ...process.argv.slice(3)));
|
|
586
|
+
commander_1.program
|
|
587
|
+
.command('acp')
|
|
588
|
+
.description('Launch a generic ACP-compatible agent')
|
|
589
|
+
.allowUnknownOption()
|
|
590
|
+
.action(() => runRemoteCommand('synk', 'acp', ...process.argv.slice(3)));
|
|
591
|
+
commander_1.program
|
|
592
|
+
.command('daemon')
|
|
593
|
+
.description('Manage background mobile sync service (start, stop, status)')
|
|
594
|
+
.allowUnknownOption()
|
|
595
|
+
.action(() => runRemoteCommand('synk', 'daemon', ...process.argv.slice(3)));
|
|
596
|
+
commander_1.program
|
|
597
|
+
.command('connect')
|
|
598
|
+
.description('Securely store AI vendor API keys in the cloud')
|
|
599
|
+
.allowUnknownOption()
|
|
600
|
+
.action(() => runRemoteCommand('synk', 'connect', ...process.argv.slice(3)));
|
|
601
|
+
commander_1.program
|
|
602
|
+
.command('sandbox')
|
|
603
|
+
.description('Configure OS-level sandboxing for agent execution')
|
|
604
|
+
.allowUnknownOption()
|
|
605
|
+
.action(() => runRemoteCommand('synk', 'sandbox', ...process.argv.slice(3)));
|
|
606
|
+
commander_1.program
|
|
607
|
+
.command('notify')
|
|
608
|
+
.description('Send push notifications to your Synk mobile app')
|
|
609
|
+
.allowUnknownOption()
|
|
610
|
+
.action(() => runRemoteCommand('synk', 'notify', ...process.argv.slice(3)));
|
|
611
|
+
// Headless Agents (Remote Control)
|
|
612
|
+
commander_1.program
|
|
613
|
+
.command('agent')
|
|
614
|
+
.description('Manage remote headless agents (create, list, stop)')
|
|
615
|
+
.allowUnknownOption()
|
|
616
|
+
.action(() => runRemoteCommand('agent', ...process.argv.slice(3)));
|
|
504
617
|
// Handle `-help` (single dash) — rewrite to `--help` for Commander compatibility
|
|
505
618
|
const helpIdx = process.argv.indexOf('-help');
|
|
506
619
|
if (helpIdx !== -1) {
|
package/dist/lib/usage-parser.js
CHANGED
|
@@ -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
|
-
|
|
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,
|
package/dist/local/index.d.ts
CHANGED
|
@@ -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';
|
package/dist/local/index.js
CHANGED
|
@@ -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[];
|