@ekkos/cli 1.4.2 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/dashboard.js +309 -93
- package/dist/commands/init.js +59 -4
- package/dist/commands/living-docs.d.ts +1 -0
- package/dist/commands/living-docs.js +5 -2
- package/dist/commands/logout.d.ts +9 -0
- package/dist/commands/logout.js +104 -0
- package/dist/commands/run.js +56 -23
- package/dist/commands/workspaces.d.ts +4 -0
- package/dist/commands/workspaces.js +153 -0
- package/dist/index.js +82 -83
- package/dist/local/diff-engine.d.ts +19 -0
- package/dist/local/diff-engine.js +81 -0
- package/dist/local/entity-extractor.d.ts +18 -0
- package/dist/local/entity-extractor.js +67 -0
- package/dist/local/git-utils.d.ts +37 -0
- package/dist/local/git-utils.js +169 -0
- package/dist/local/living-docs-manager.d.ts +6 -0
- package/dist/local/living-docs-manager.js +180 -139
- package/dist/utils/notifier.d.ts +15 -0
- package/dist/utils/notifier.js +40 -0
- package/dist/utils/paths.d.ts +4 -0
- package/dist/utils/paths.js +7 -0
- package/dist/utils/state.d.ts +3 -0
- package/dist/utils/stdin-relay.d.ts +37 -0
- package/dist/utils/stdin-relay.js +155 -0
- package/package.json +4 -1
- package/templates/CLAUDE.md +3 -1
- package/dist/commands/setup.d.ts +0 -6
- package/dist/commands/setup.js +0 -389
package/dist/commands/init.js
CHANGED
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
@@ -104,7 +137,7 @@ async function deviceAuth() {
|
|
|
104
137
|
}
|
|
105
138
|
console.log('');
|
|
106
139
|
// Open browser
|
|
107
|
-
const verificationUrl = deviceCode.verificationUrl || `${platform_1.PLATFORM_URL}/
|
|
140
|
+
const verificationUrl = deviceCode.verificationUrl || `${platform_1.PLATFORM_URL}/verify/${deviceCode.code}`;
|
|
108
141
|
console.log(chalk_1.default.gray(` Opening browser → ${verificationUrl}`));
|
|
109
142
|
try {
|
|
110
143
|
await (0, open_1.default)(verificationUrl);
|
|
@@ -502,6 +535,18 @@ async function init(options) {
|
|
|
502
535
|
projectRoot = (0, child_process_1.execSync)('git rev-parse --show-toplevel', { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
503
536
|
}
|
|
504
537
|
catch { /* not a git repo — use cwd */ }
|
|
538
|
+
console.log(chalk_1.default.cyan('Step 4/4: Cortex Background Guardian'));
|
|
539
|
+
console.log(chalk_1.default.gray('─'.repeat(40)));
|
|
540
|
+
console.log('');
|
|
541
|
+
const { confirmRoot } = await inquirer_1.default.prompt([
|
|
542
|
+
{
|
|
543
|
+
type: 'input',
|
|
544
|
+
name: 'confirmRoot',
|
|
545
|
+
message: 'Confirm the root directory to watch for this project:',
|
|
546
|
+
default: projectRoot
|
|
547
|
+
}
|
|
548
|
+
]);
|
|
549
|
+
projectRoot = (0, path_1.resolve)(confirmRoot);
|
|
505
550
|
const ekkosYmlPath = (0, path_1.join)(projectRoot, 'ekkos.yml');
|
|
506
551
|
if (!(0, fs_1.existsSync)(ekkosYmlPath)) {
|
|
507
552
|
const projectName = (0, path_1.basename)(projectRoot);
|
|
@@ -525,9 +570,16 @@ async function init(options) {
|
|
|
525
570
|
ymlLines.push(` lint: "${lintCmd}"`);
|
|
526
571
|
ymlLines.push('');
|
|
527
572
|
(0, fs_1.writeFileSync)(ekkosYmlPath, ymlLines.join('\n'), 'utf-8');
|
|
528
|
-
console.log('');
|
|
529
|
-
|
|
530
|
-
|
|
573
|
+
console.log(chalk_1.default.green(` ✓ Created ${chalk_1.default.bold('ekkos.yml')} in ${projectRoot}`));
|
|
574
|
+
}
|
|
575
|
+
// Register with local daemon for background watching
|
|
576
|
+
try {
|
|
577
|
+
const { addWorkspace } = await Promise.resolve().then(() => __importStar(require('./workspaces')));
|
|
578
|
+
console.log(chalk_1.default.gray(' Registering with background guardian...'));
|
|
579
|
+
await addWorkspace(projectRoot);
|
|
580
|
+
}
|
|
581
|
+
catch {
|
|
582
|
+
console.log(chalk_1.default.yellow(' Note: Background guardian not active. Project will be watched on next CLI run.'));
|
|
531
583
|
}
|
|
532
584
|
// Summary with prominent next step
|
|
533
585
|
const ideNames = installedIDEs.map(id => id === 'claude' ? 'Claude Code' : id === 'cursor' ? 'Cursor' : 'Windsurf');
|
|
@@ -559,6 +611,9 @@ async function init(options) {
|
|
|
559
611
|
console.log(chalk_1.default.white(' 2. Run ') + chalk_1.default.cyan.bold('ekkos scan') + chalk_1.default.white(' to map your project systems'));
|
|
560
612
|
console.log(chalk_1.default.white(' 3. Run ') + chalk_1.default.cyan.bold('ekkos') + chalk_1.default.white(' to start coding with memory'));
|
|
561
613
|
console.log('');
|
|
614
|
+
console.log(chalk_1.default.cyan(' ✨ Cortex Guardian is now active in the background.'));
|
|
615
|
+
console.log(chalk_1.default.gray(' Every save will be semantically analyzed for regressions.'));
|
|
616
|
+
console.log('');
|
|
562
617
|
console.log(chalk_1.default.gray(` Dashboard: https://platform.ekkos.dev/dashboard`));
|
|
563
618
|
console.log(chalk_1.default.gray(` Configure ANS: https://platform.ekkos.dev/dashboard/settings/project`));
|
|
564
619
|
console.log(chalk_1.default.gray(` Connect vitals: https://platform.ekkos.dev/dashboard/settings/vitals`));
|
|
@@ -7,7 +7,7 @@ exports.watchLivingDocs = watchLivingDocs;
|
|
|
7
7
|
const chalk_1 = __importDefault(require("chalk"));
|
|
8
8
|
const path_1 = require("path");
|
|
9
9
|
const living_docs_manager_js_1 = require("../local/living-docs-manager.js");
|
|
10
|
-
const
|
|
10
|
+
const state_js_1 = require("../utils/state.js");
|
|
11
11
|
const platform_js_1 = require("../utils/platform.js");
|
|
12
12
|
function printStartupSummary(options) {
|
|
13
13
|
console.log('');
|
|
@@ -16,6 +16,7 @@ function printStartupSummary(options) {
|
|
|
16
16
|
console.log(chalk_1.default.gray(` Path: ${options.targetPath}`));
|
|
17
17
|
console.log(chalk_1.default.gray(` Timezone: ${options.timeZone}`));
|
|
18
18
|
console.log(chalk_1.default.gray(` Registry seed: ${options.seedingEnabled ? 'enabled' : 'disabled'}`));
|
|
19
|
+
console.log(chalk_1.default.gray(` Rich Analysis: ${options.richEnabled ? chalk_1.default.green('enabled') : 'disabled'}`));
|
|
19
20
|
console.log('');
|
|
20
21
|
console.log(chalk_1.default.gray(' Watching local files and updating Cortex docs (ekkOS_CONTEXT.md) on change.'));
|
|
21
22
|
console.log(chalk_1.default.gray(' Press Ctrl+C to stop.'));
|
|
@@ -24,7 +25,7 @@ function printStartupSummary(options) {
|
|
|
24
25
|
async function watchLivingDocs(options) {
|
|
25
26
|
const targetPath = (0, path_1.resolve)(options.path || process.cwd());
|
|
26
27
|
const timeZone = options.timeZone || process.env.EKKOS_USER_TIMEZONE || Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
27
|
-
const apiKey = options.noSeed ? null : (0,
|
|
28
|
+
const apiKey = options.noSeed ? null : (0, state_js_1.getAuthToken)();
|
|
28
29
|
const apiUrl = options.noSeed ? undefined : (process.env.EKKOS_API_URL || platform_js_1.MCP_API_URL);
|
|
29
30
|
const manager = new living_docs_manager_js_1.LocalLivingDocsManager({
|
|
30
31
|
targetPath,
|
|
@@ -33,12 +34,14 @@ async function watchLivingDocs(options) {
|
|
|
33
34
|
timeZone,
|
|
34
35
|
pollIntervalMs: options.pollIntervalMs,
|
|
35
36
|
flushDebounceMs: options.debounceMs,
|
|
37
|
+
richAnalysis: options.rich !== false,
|
|
36
38
|
onLog: message => console.log(chalk_1.default.gray(` ${message}`)),
|
|
37
39
|
});
|
|
38
40
|
printStartupSummary({
|
|
39
41
|
targetPath,
|
|
40
42
|
timeZone,
|
|
41
43
|
seedingEnabled: !!(apiUrl && apiKey),
|
|
44
|
+
richEnabled: options.rich !== false,
|
|
42
45
|
});
|
|
43
46
|
manager.start();
|
|
44
47
|
await new Promise((resolvePromise) => {
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* logout.ts — Log out of ekkOS
|
|
4
|
+
*
|
|
5
|
+
* Clears API credentials from ~/.ekkos/config.json and optionally
|
|
6
|
+
* clears Synk mobile sync credentials from ~/.ekkos/synk/.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
42
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.logout = logout;
|
|
46
|
+
const fs = __importStar(require("fs"));
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
const readline = __importStar(require("readline"));
|
|
49
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
50
|
+
const state_1 = require("../utils/state");
|
|
51
|
+
const SYNK_DIR = path.join(state_1.EKKOS_DIR, 'synk');
|
|
52
|
+
async function logout(options) {
|
|
53
|
+
const config = (0, state_1.getConfig)();
|
|
54
|
+
if (!config?.apiKey && !config?.hookApiKey) {
|
|
55
|
+
console.log(chalk_1.default.yellow('Not currently logged in.'));
|
|
56
|
+
console.log(chalk_1.default.gray('Run "ekkos init" to authenticate.'));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
console.log('');
|
|
60
|
+
console.log(chalk_1.default.blue('Current session:'));
|
|
61
|
+
if (config.userId)
|
|
62
|
+
console.log(chalk_1.default.gray(` User ID: ${config.userId}`));
|
|
63
|
+
console.log('');
|
|
64
|
+
console.log(chalk_1.default.yellow('This will clear your ekkOS API credentials from this machine.'));
|
|
65
|
+
const hasSynk = fs.existsSync(SYNK_DIR);
|
|
66
|
+
if (options.all && hasSynk) {
|
|
67
|
+
console.log(chalk_1.default.yellow(' --all flag: Synk mobile sync credentials will also be cleared.'));
|
|
68
|
+
}
|
|
69
|
+
console.log('');
|
|
70
|
+
const rl = readline.createInterface({
|
|
71
|
+
input: process.stdin,
|
|
72
|
+
output: process.stdout,
|
|
73
|
+
});
|
|
74
|
+
const answer = await new Promise((resolve) => {
|
|
75
|
+
rl.question(chalk_1.default.yellow('Are you sure you want to log out? (y/N): '), resolve);
|
|
76
|
+
});
|
|
77
|
+
rl.close();
|
|
78
|
+
if (answer.toLowerCase() !== 'y' && answer.toLowerCase() !== 'yes') {
|
|
79
|
+
console.log(chalk_1.default.blue('Logout cancelled.'));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
// Clear main ekkOS config
|
|
83
|
+
try {
|
|
84
|
+
if (fs.existsSync(state_1.CONFIG_FILE)) {
|
|
85
|
+
fs.unlinkSync(state_1.CONFIG_FILE);
|
|
86
|
+
}
|
|
87
|
+
console.log(chalk_1.default.green('✓ ekkOS API credentials cleared'));
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
console.error(chalk_1.default.red(`Failed to clear config: ${err.message}`));
|
|
91
|
+
}
|
|
92
|
+
// Optionally clear Synk credentials
|
|
93
|
+
if (options.all && hasSynk) {
|
|
94
|
+
try {
|
|
95
|
+
fs.rmSync(SYNK_DIR, { recursive: true, force: true });
|
|
96
|
+
console.log(chalk_1.default.green('✓ Synk mobile sync credentials cleared'));
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
console.error(chalk_1.default.red(`Failed to clear Synk data: ${err.message}`));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
console.log('');
|
|
103
|
+
console.log(chalk_1.default.gray('Run "ekkos init" to log in again.'));
|
|
104
|
+
}
|
package/dist/commands/run.js
CHANGED
|
@@ -45,6 +45,7 @@ const os = __importStar(require("os"));
|
|
|
45
45
|
const child_process_1 = require("child_process");
|
|
46
46
|
const state_1 = require("../utils/state");
|
|
47
47
|
const session_binding_1 = require("../utils/session-binding");
|
|
48
|
+
const stdin_relay_1 = require("../utils/stdin-relay");
|
|
48
49
|
const doctor_1 = require("./doctor");
|
|
49
50
|
const stream_tailer_1 = require("../capture/stream-tailer");
|
|
50
51
|
const jsonl_rewriter_1 = require("../capture/jsonl-rewriter");
|
|
@@ -87,7 +88,7 @@ function getConfig(options) {
|
|
|
87
88
|
/* eslint-enable no-restricted-syntax */
|
|
88
89
|
}
|
|
89
90
|
const MAX_OUTPUT_1M_MODELS = '64000';
|
|
90
|
-
const MAX_OUTPUT_200K_OPUS_SONNET = '
|
|
91
|
+
const MAX_OUTPUT_200K_OPUS_SONNET = '16384';
|
|
91
92
|
let runtimeClaudeCodeVersion = null;
|
|
92
93
|
let runtimeClaudeContextWindow = 'auto';
|
|
93
94
|
let runtimeClaudeLaunchModel;
|
|
@@ -239,9 +240,9 @@ function renderClaudeLaunchSelectorIntro() {
|
|
|
239
240
|
console.log(neonCyan('╔══════════════════════════════════════════════════════════════════════╗'));
|
|
240
241
|
console.log(line(chalk_1.default.white.bold('ekkOS.dev // PULSE')));
|
|
241
242
|
console.log(neonCyan('╠══════════════════════════════════════════════════════════════════════╣'));
|
|
242
|
-
console.log(line(`${acidGreen('200K')} ${steel('compat lane')} ${chalk_1.default.white('//
|
|
243
|
-
console.log(line(`${signalAmber(' 1M')} ${steel('wide lane')} ${chalk_1.default.white('//
|
|
244
|
-
console.log(line(steel('
|
|
243
|
+
console.log(line(`${acidGreen('200K')} ${steel('compat lane')} ${chalk_1.default.white('// 16,384 output // eviction + replay at 90%')}`));
|
|
244
|
+
console.log(line(`${signalAmber(' 1M')} ${steel('wide lane')} ${chalk_1.default.white('// 64,000 output // 5x headroom')}`));
|
|
245
|
+
console.log(line(steel('98%+ cache hit rate // 30 msg keep on eviction // 50% target')));
|
|
245
246
|
console.log(neonCyan('╚══════════════════════════════════════════════════════════════════════╝'));
|
|
246
247
|
console.log('');
|
|
247
248
|
}
|
|
@@ -267,31 +268,31 @@ function buildLaunchModelChoices() {
|
|
|
267
268
|
const green = chalk_1.default.hex('#00ff88');
|
|
268
269
|
return [
|
|
269
270
|
{
|
|
270
|
-
name: `${cyan.bold('Auto route')} ${chalk_1.default.gray('//
|
|
271
|
+
name: `${cyan.bold('Auto route')} ${chalk_1.default.gray('// Opus 4.6 · 200K · 16,384 output')}`,
|
|
271
272
|
value: 'default',
|
|
272
273
|
},
|
|
273
274
|
{
|
|
274
|
-
name: `${cyan.bold('Claude Opus 4.5')} ${green('[200K]')} ${chalk_1.default.gray('//
|
|
275
|
+
name: `${cyan.bold('Claude Opus 4.5')} ${green('[200K]')} ${chalk_1.default.gray('// 16,384 output')}`,
|
|
275
276
|
value: 'claude-opus-4-5',
|
|
276
277
|
},
|
|
277
278
|
{
|
|
278
|
-
name: `${cyan.bold('Claude Opus 4.6')} ${green('[200K]')} ${chalk_1.default.gray('//
|
|
279
|
+
name: `${cyan.bold('Claude Opus 4.6')} ${green('[200K]')} ${chalk_1.default.gray('// 16,384 output')}`,
|
|
279
280
|
value: 'claude-opus-4-6-200k',
|
|
280
281
|
},
|
|
281
282
|
{
|
|
282
|
-
name: `${cyan.bold('Claude Opus 4.6')} ${amber('[1M]')} ${chalk_1.default.gray('//
|
|
283
|
+
name: `${cyan.bold('Claude Opus 4.6')} ${amber('[1M]')} ${chalk_1.default.gray('// 64,000 output')}`,
|
|
283
284
|
value: 'claude-opus-4-6-1m',
|
|
284
285
|
},
|
|
285
286
|
{
|
|
286
|
-
name: `${cyan.bold('Claude Sonnet 4.5')} ${green('[200K]')} ${chalk_1.default.gray('//
|
|
287
|
+
name: `${cyan.bold('Claude Sonnet 4.5')} ${green('[200K]')} ${chalk_1.default.gray('// 16,384 output')}`,
|
|
287
288
|
value: 'claude-sonnet-4-5',
|
|
288
289
|
},
|
|
289
290
|
{
|
|
290
|
-
name: `${cyan.bold('Claude Sonnet 4.6')} ${green('[200K]')} ${chalk_1.default.gray('//
|
|
291
|
+
name: `${cyan.bold('Claude Sonnet 4.6')} ${green('[200K]')} ${chalk_1.default.gray('// 16,384 output')}`,
|
|
291
292
|
value: 'claude-sonnet-4-6-200k',
|
|
292
293
|
},
|
|
293
294
|
{
|
|
294
|
-
name: `${cyan.bold('Claude Sonnet 4.6')} ${amber('[1M]')} ${chalk_1.default.gray('//
|
|
295
|
+
name: `${cyan.bold('Claude Sonnet 4.6')} ${amber('[1M]')} ${chalk_1.default.gray('// 64,000 output')}`,
|
|
295
296
|
value: 'claude-sonnet-4-6-1m',
|
|
296
297
|
},
|
|
297
298
|
{
|
|
@@ -312,7 +313,12 @@ async function resolveClaudeLaunchSelection(options) {
|
|
|
312
313
|
&& process.stdout.isTTY === true
|
|
313
314
|
&& process.env.EKKOS_DISABLE_LAUNCH_WINDOW !== '1';
|
|
314
315
|
if (!shouldShowLaunchWindow) {
|
|
315
|
-
|
|
316
|
+
// Default to newest Opus 200k when no model explicitly set
|
|
317
|
+
if (!model) {
|
|
318
|
+
model = 'claude-opus-4-6';
|
|
319
|
+
contextWindow = '200k';
|
|
320
|
+
}
|
|
321
|
+
else if (!modelSupportsOneMillionContext(model)) {
|
|
316
322
|
contextWindow = '200k';
|
|
317
323
|
}
|
|
318
324
|
return {
|
|
@@ -346,7 +352,7 @@ async function resolveClaudeLaunchSelection(options) {
|
|
|
346
352
|
},
|
|
347
353
|
]);
|
|
348
354
|
const selectedProfile = normalizeRequestedLaunchModel(firstPrompt.model);
|
|
349
|
-
model = firstPrompt.model === 'default' ?
|
|
355
|
+
model = firstPrompt.model === 'default' ? 'claude-opus-4-6' : selectedProfile.model;
|
|
350
356
|
continueLast = firstPrompt.launchMode === 'continue';
|
|
351
357
|
resumeSession = firstPrompt.launchMode === 'resume' ? resumeSession : '';
|
|
352
358
|
if (firstPrompt.launchMode === 'resume') {
|
|
@@ -362,7 +368,9 @@ async function resolveClaudeLaunchSelection(options) {
|
|
|
362
368
|
resumeSession = resumePrompt.resumeSession.trim();
|
|
363
369
|
}
|
|
364
370
|
if (firstPrompt.model === 'default') {
|
|
365
|
-
|
|
371
|
+
// Auto route → default to newest Opus with 200k context
|
|
372
|
+
model = 'claude-opus-4-6';
|
|
373
|
+
contextWindow = '200k';
|
|
366
374
|
}
|
|
367
375
|
else if (selectedProfile.syntheticContextWindow) {
|
|
368
376
|
contextWindow = selectedProfile.syntheticContextWindow;
|
|
@@ -624,7 +632,7 @@ function getEkkosEnv() {
|
|
|
624
632
|
// Only ekkOS-wrapped sessions get this; vanilla `claude` keeps autocompact on.
|
|
625
633
|
DISABLE_AUTO_COMPACT: 'true',
|
|
626
634
|
// Align Claude's advertised output ceiling with the chosen model/context profile.
|
|
627
|
-
// 1M Opus/Sonnet gets
|
|
635
|
+
// 1M Opus/Sonnet gets 64K; 200K Opus/Sonnet 4.5/4.6 is forced to 16,384.
|
|
628
636
|
// The proxy also enforces this server-side in case Claude Code ignores the env var.
|
|
629
637
|
CLAUDE_CODE_MAX_OUTPUT_TOKENS: maxOutputTokens,
|
|
630
638
|
};
|
|
@@ -913,12 +921,12 @@ function formatPulseModel(model) {
|
|
|
913
921
|
}
|
|
914
922
|
function formatPulseContextLine(contextWindow) {
|
|
915
923
|
if (contextWindow === '200k') {
|
|
916
|
-
return '200K // 200,000
|
|
924
|
+
return '200K // 200,000 tokens // 16,384 max output // eviction at 90%';
|
|
917
925
|
}
|
|
918
926
|
if (contextWindow === '1m') {
|
|
919
|
-
return '1M // 1,000,000
|
|
927
|
+
return '1M // 1,000,000 tokens // 64,000 max output // no eviction until 90%';
|
|
920
928
|
}
|
|
921
|
-
return 'AUTO //
|
|
929
|
+
return 'AUTO // resolved from model profile';
|
|
922
930
|
}
|
|
923
931
|
async function showPulseLaunchLoader(options) {
|
|
924
932
|
const cyan = chalk_1.default.hex('#00f0ff');
|
|
@@ -962,8 +970,9 @@ async function showPulseLaunchLoader(options) {
|
|
|
962
970
|
console.log(buildPanelLine(amber, 'WINDOW', formatPulseContextLine(normalizeContextWindowOption(options.contextWindow))));
|
|
963
971
|
console.log(buildPanelLine(steel, 'PATH', 'selector -> dashboard -> claude -> proxy'));
|
|
964
972
|
console.log(cyan(' ╚══════════════════════════════════════════════════════════════════════════════╝'));
|
|
965
|
-
console.log(` ${green('200K')} ${steel('//
|
|
966
|
-
console.log(` ${amber('1M ')} ${steel('//
|
|
973
|
+
console.log(` ${green('200K')} ${steel('// 16,384 output // eviction + replay at 90% // target 50%')}`);
|
|
974
|
+
console.log(` ${amber('1M ')} ${steel('// 64,000 output // 5x headroom // eviction deferred')}`);
|
|
975
|
+
console.log(` ${steel(' // 98%+ cache hit rate // 30 msg keep on eviction')}`);
|
|
967
976
|
console.log('');
|
|
968
977
|
for (const stage of stages) {
|
|
969
978
|
for (let filled = 0; filled <= barWidth; filled += 1) {
|
|
@@ -1418,7 +1427,7 @@ async function run(options) {
|
|
|
1418
1427
|
proxyModeEnabled = !(options.noProxy || false);
|
|
1419
1428
|
if (proxyModeEnabled) {
|
|
1420
1429
|
if (!suppressPreClaudeOutput && !options.pulse) {
|
|
1421
|
-
console.log(chalk_1.default.cyan(' 🧠
|
|
1430
|
+
console.log(chalk_1.default.cyan(' 🧠 ekkOS_Pulse // proxy active'));
|
|
1422
1431
|
}
|
|
1423
1432
|
}
|
|
1424
1433
|
else if (verbose && !suppressPreClaudeOutput) {
|
|
@@ -1803,7 +1812,7 @@ async function run(options) {
|
|
|
1803
1812
|
// ══════════════════════════════════════════════════════════════════════════
|
|
1804
1813
|
// MAGIC MOMENT: Morning dreams right after sparkle, before Claude appears
|
|
1805
1814
|
// ══════════════════════════════════════════════════════════════════════════
|
|
1806
|
-
if (!suppressPreClaudeOutput) {
|
|
1815
|
+
if (!suppressPreClaudeOutput && !options.pulse) {
|
|
1807
1816
|
await showMorningDreamsIfNeeded();
|
|
1808
1817
|
}
|
|
1809
1818
|
// ══════════════════════════════════════════════════════════════════════════
|
|
@@ -1855,6 +1864,7 @@ async function run(options) {
|
|
|
1855
1864
|
let isAutoClearInProgress = false;
|
|
1856
1865
|
let transcriptPath = null;
|
|
1857
1866
|
let currentSessionId = null;
|
|
1867
|
+
let stdinRelayHandle = null;
|
|
1858
1868
|
// ══════════════════════════════════════════════════════════════════════════
|
|
1859
1869
|
// PER-TURN BANNER STATE
|
|
1860
1870
|
// Tracks idle→active transitions to print the session banner once per turn
|
|
@@ -2010,7 +2020,10 @@ async function run(options) {
|
|
|
2010
2020
|
// and shown in "Continuum Loaded". Re-deriving from JSONL UUID produces a
|
|
2011
2021
|
// different name since Claude Code's UUID ≠ the CLI-generated UUID.
|
|
2012
2022
|
currentSession = cliSessionName || (0, state_1.uuidToWords)(sessionId);
|
|
2013
|
-
(0, state_1.updateCurrentProcessSession)(currentSessionId, currentSession,
|
|
2023
|
+
(0, state_1.updateCurrentProcessSession)(currentSessionId, currentSession, {
|
|
2024
|
+
...getClaudeSessionMetadata(options),
|
|
2025
|
+
transcriptPath: fullPath,
|
|
2026
|
+
});
|
|
2014
2027
|
(0, state_1.updateState)({ sessionId: currentSessionId, sessionName: currentSession });
|
|
2015
2028
|
writeSessionFiles(currentSessionId, currentSession, getClaudeSessionMetadata(options));
|
|
2016
2029
|
bindRealSessionToProxy(currentSession, 'fast-transcript', currentSessionId);
|
|
@@ -2423,6 +2436,24 @@ async function run(options) {
|
|
|
2423
2436
|
}
|
|
2424
2437
|
process.stdin.resume();
|
|
2425
2438
|
process.stdin.on('data', onStdinData);
|
|
2439
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
2440
|
+
// UNIVERSAL SESSION ATTACH — stdin relay for remote input injection
|
|
2441
|
+
// Starts a localhost HTTP server so the daemon can pipe in remote input
|
|
2442
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
2443
|
+
(0, stdin_relay_1.startStdinRelay)({
|
|
2444
|
+
write: (text) => shell.write(text),
|
|
2445
|
+
onAttach: (remoteInfo) => {
|
|
2446
|
+
dlog(`[session-attach] Remote client attached: ${remoteInfo}`);
|
|
2447
|
+
},
|
|
2448
|
+
dlog,
|
|
2449
|
+
}).then(handle => {
|
|
2450
|
+
stdinRelayHandle = handle;
|
|
2451
|
+
dlog(`[session-attach] stdin relay started on port ${handle.port}`);
|
|
2452
|
+
// Update active session with relay info
|
|
2453
|
+
(0, state_1.updateCurrentProcessSession)(currentSessionId || initialSessionId, currentSession || initialSessionName, { stdinPort: handle.port, stdinToken: handle.token });
|
|
2454
|
+
}).catch(err => {
|
|
2455
|
+
dlog(`[session-attach] stdin relay failed to start: ${err.message}`);
|
|
2456
|
+
});
|
|
2426
2457
|
// Helper to get current output buffer (for readiness checks)
|
|
2427
2458
|
const getOutputBuffer = () => outputBuffer;
|
|
2428
2459
|
// Handle context wall detection
|
|
@@ -3233,6 +3264,7 @@ Use Perplexity for deep research. Be thorough but efficient. Start now.`;
|
|
|
3233
3264
|
(0, state_1.clearAutoClearFlag)();
|
|
3234
3265
|
localLivingDocsManager?.stop();
|
|
3235
3266
|
stopStreamTailer(); // Stop stream capture
|
|
3267
|
+
stdinRelayHandle?.stop().catch(() => { }); // Stop stdin relay
|
|
3236
3268
|
(0, state_1.unregisterActiveSession)(); // Remove from active sessions registry
|
|
3237
3269
|
cleanupInstanceFile(instanceId); // Clean up instance file
|
|
3238
3270
|
// NOTE: No restore needed - ekkOS uses separate installation from homebrew
|
|
@@ -3253,6 +3285,7 @@ Use Perplexity for deep research. Be thorough but efficient. Start now.`;
|
|
|
3253
3285
|
(0, state_1.clearAutoClearFlag)();
|
|
3254
3286
|
localLivingDocsManager?.stop();
|
|
3255
3287
|
stopStreamTailer(); // Stop stream capture
|
|
3288
|
+
stdinRelayHandle?.stop().catch(() => { }); // Stop stdin relay
|
|
3256
3289
|
(0, state_1.unregisterActiveSession)(); // Remove from active sessions registry
|
|
3257
3290
|
cleanupInstanceFile(instanceId); // Clean up instance file
|
|
3258
3291
|
// NOTE: No restore needed - ekkOS uses separate installation from homebrew
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.listWorkspaces = listWorkspaces;
|
|
7
|
+
exports.addWorkspace = addWorkspace;
|
|
8
|
+
exports.removeWorkspace = removeWorkspace;
|
|
9
|
+
exports.manageWorkspaces = manageWorkspaces;
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
+
const fs_1 = __importDefault(require("fs"));
|
|
12
|
+
const paths_js_1 = require("../utils/paths.js");
|
|
13
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
14
|
+
/**
|
|
15
|
+
* ekkos cortex workspaces
|
|
16
|
+
*
|
|
17
|
+
* Tool for managing projects watched by the Cortex universal background watcher.
|
|
18
|
+
*/
|
|
19
|
+
async function getDaemonPort() {
|
|
20
|
+
const portPath = (0, paths_js_1.getDaemonPortPath)();
|
|
21
|
+
if (!fs_1.default.existsSync(portPath))
|
|
22
|
+
return null;
|
|
23
|
+
const port = fs_1.default.readFileSync(portPath, 'utf-8').trim();
|
|
24
|
+
return port ? Number(port) : null;
|
|
25
|
+
}
|
|
26
|
+
async function listWorkspaces() {
|
|
27
|
+
const port = await getDaemonPort();
|
|
28
|
+
if (!port) {
|
|
29
|
+
console.log(chalk_1.default.yellow(' Daemon is not running. Start it with `ekkos daemon start`.'));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const res = await fetch(`http://127.0.0.1:${port}/list-workspaces`);
|
|
34
|
+
if (!res.ok)
|
|
35
|
+
throw new Error(`HTTP ${res.status}`);
|
|
36
|
+
const { workspaces } = await res.json();
|
|
37
|
+
console.log('');
|
|
38
|
+
console.log(chalk_1.default.cyan.bold(' Watched Workspaces (Cortex)'));
|
|
39
|
+
console.log(chalk_1.default.gray(' ───────────────────────────'));
|
|
40
|
+
if (workspaces.length === 0) {
|
|
41
|
+
console.log(chalk_1.default.gray(' No workspaces currently being watched.'));
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
for (const w of workspaces) {
|
|
45
|
+
console.log(` ${chalk_1.default.green('•')} ${chalk_1.default.white(w.path)}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
console.log('');
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
console.log(chalk_1.default.red(` Failed to list workspaces: ${err.message}`));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async function addWorkspace(path) {
|
|
55
|
+
const port = await getDaemonPort();
|
|
56
|
+
if (!port) {
|
|
57
|
+
console.log(chalk_1.default.yellow(' Daemon is not running. Cannot register workspace.'));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const res = await fetch(`http://127.0.0.1:${port}/register-workspace`, {
|
|
62
|
+
method: 'POST',
|
|
63
|
+
headers: { 'Content-Type': 'application/json' },
|
|
64
|
+
body: JSON.stringify({ path }),
|
|
65
|
+
});
|
|
66
|
+
if (res.ok) {
|
|
67
|
+
console.log(chalk_1.default.green(` ✓ Successfully registered workspace: ${path}`));
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
const data = await res.json();
|
|
71
|
+
console.log(chalk_1.default.red(` ✗ Failed to register workspace: ${data.error || res.statusText}`));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
console.log(chalk_1.default.red(` ✗ Error connecting to daemon: ${err.message}`));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async function removeWorkspace(path) {
|
|
79
|
+
const port = await getDaemonPort();
|
|
80
|
+
if (!port) {
|
|
81
|
+
console.log(chalk_1.default.yellow(' Daemon is not running. Cannot unregister workspace.'));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
const res = await fetch(`http://127.0.0.1:${port}/unregister-workspace`, {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
headers: { 'Content-Type': 'application/json' },
|
|
88
|
+
body: JSON.stringify({ path }),
|
|
89
|
+
});
|
|
90
|
+
if (res.ok) {
|
|
91
|
+
console.log(chalk_1.default.green(` ✓ Successfully unregistered workspace: ${path}`));
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
const data = await res.json();
|
|
95
|
+
console.log(chalk_1.default.red(` ✗ Failed to unregister workspace: ${data.error || res.statusText}`));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
console.log(chalk_1.default.red(` ✗ Error connecting to daemon: ${err.message}`));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function manageWorkspaces() {
|
|
103
|
+
const port = await getDaemonPort();
|
|
104
|
+
if (!port) {
|
|
105
|
+
console.log(chalk_1.default.yellow('\n Daemon is not running. Start it with `ekkos daemon start` first.'));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const res = await fetch(`http://127.0.0.1:${port}/list-workspaces`);
|
|
110
|
+
if (!res.ok)
|
|
111
|
+
throw new Error(`HTTP ${res.status}`);
|
|
112
|
+
const { workspaces } = await res.json();
|
|
113
|
+
const { action } = await inquirer_1.default.prompt([
|
|
114
|
+
{
|
|
115
|
+
type: 'list',
|
|
116
|
+
name: 'action',
|
|
117
|
+
message: 'Manage Cortex Workspaces',
|
|
118
|
+
choices: [
|
|
119
|
+
{ name: 'List all watched projects', value: 'list' },
|
|
120
|
+
{ name: 'Stop watching a project', value: 'remove' },
|
|
121
|
+
{ name: 'Add current directory', value: 'add_cwd' },
|
|
122
|
+
{ name: 'Exit', value: 'exit' }
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
]);
|
|
126
|
+
if (action === 'list') {
|
|
127
|
+
await listWorkspaces();
|
|
128
|
+
}
|
|
129
|
+
else if (action === 'remove') {
|
|
130
|
+
if (workspaces.length === 0) {
|
|
131
|
+
console.log(chalk_1.default.yellow('\n No workspaces to remove.'));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const { toRemove } = await inquirer_1.default.prompt([
|
|
135
|
+
{
|
|
136
|
+
type: 'checkbox',
|
|
137
|
+
name: 'toRemove',
|
|
138
|
+
message: 'Select workspaces to stop watching:',
|
|
139
|
+
choices: workspaces.map(w => w.path)
|
|
140
|
+
}
|
|
141
|
+
]);
|
|
142
|
+
for (const p of toRemove) {
|
|
143
|
+
await removeWorkspace(p);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else if (action === 'add_cwd') {
|
|
147
|
+
await addWorkspace(process.cwd());
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
console.log(chalk_1.default.red(`\n Error: ${err.message}`));
|
|
152
|
+
}
|
|
153
|
+
}
|