@ekkos/cli 0.2.18 → 1.0.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/README.md +57 -0
- package/dist/agent/daemon.d.ts +27 -0
- package/dist/agent/daemon.js +254 -29
- package/dist/agent/health-check.d.ts +35 -0
- package/dist/agent/health-check.js +243 -0
- package/dist/agent/pty-runner.d.ts +1 -0
- package/dist/agent/pty-runner.js +6 -1
- package/dist/capture/eviction-client.d.ts +139 -0
- package/dist/capture/eviction-client.js +454 -0
- package/dist/capture/index.d.ts +2 -0
- package/dist/capture/index.js +2 -0
- package/dist/capture/jsonl-rewriter.d.ts +96 -0
- package/dist/capture/jsonl-rewriter.js +1369 -0
- package/dist/capture/transcript-repair.d.ts +51 -0
- package/dist/capture/transcript-repair.js +319 -0
- package/dist/commands/agent.d.ts +6 -0
- package/dist/commands/agent.js +244 -0
- package/dist/commands/dashboard.d.ts +25 -0
- package/dist/commands/dashboard.js +1175 -0
- package/dist/commands/doctor.js +23 -1
- package/dist/commands/run.d.ts +5 -0
- package/dist/commands/run.js +1605 -516
- package/dist/commands/setup-remote.js +146 -37
- package/dist/commands/swarm-dashboard.d.ts +20 -0
- package/dist/commands/swarm-dashboard.js +735 -0
- package/dist/commands/swarm-setup.d.ts +10 -0
- package/dist/commands/swarm-setup.js +956 -0
- package/dist/commands/swarm.d.ts +46 -0
- package/dist/commands/swarm.js +441 -0
- package/dist/commands/test-claude.d.ts +16 -0
- package/dist/commands/test-claude.js +156 -0
- package/dist/commands/usage/blocks.d.ts +8 -0
- package/dist/commands/usage/blocks.js +60 -0
- package/dist/commands/usage/daily.d.ts +9 -0
- package/dist/commands/usage/daily.js +96 -0
- package/dist/commands/usage/dashboard.d.ts +8 -0
- package/dist/commands/usage/dashboard.js +104 -0
- package/dist/commands/usage/formatters.d.ts +41 -0
- package/dist/commands/usage/formatters.js +147 -0
- package/dist/commands/usage/index.d.ts +13 -0
- package/dist/commands/usage/index.js +87 -0
- package/dist/commands/usage/monthly.d.ts +8 -0
- package/dist/commands/usage/monthly.js +66 -0
- package/dist/commands/usage/session.d.ts +11 -0
- package/dist/commands/usage/session.js +193 -0
- package/dist/commands/usage/weekly.d.ts +9 -0
- package/dist/commands/usage/weekly.js +61 -0
- package/dist/commands/usage.d.ts +7 -0
- package/dist/commands/usage.js +214 -0
- package/dist/cron/index.d.ts +7 -0
- package/dist/cron/index.js +13 -0
- package/dist/cron/promoter.d.ts +70 -0
- package/dist/cron/promoter.js +403 -0
- package/dist/deploy/instructions.d.ts +5 -2
- package/dist/deploy/instructions.js +11 -8
- package/dist/index.js +262 -5
- package/dist/lib/tmux-scrollbar.d.ts +14 -0
- package/dist/lib/tmux-scrollbar.js +296 -0
- package/dist/lib/usage-monitor.d.ts +47 -0
- package/dist/lib/usage-monitor.js +124 -0
- package/dist/lib/usage-parser.d.ts +162 -0
- package/dist/lib/usage-parser.js +583 -0
- package/dist/restore/RestoreOrchestrator.d.ts +4 -0
- package/dist/restore/RestoreOrchestrator.js +118 -30
- package/dist/utils/log-rotate.d.ts +18 -0
- package/dist/utils/log-rotate.js +74 -0
- package/dist/utils/platform.d.ts +2 -0
- package/dist/utils/platform.js +3 -1
- package/dist/utils/session-binding.d.ts +5 -0
- package/dist/utils/session-binding.js +46 -0
- package/dist/utils/state.js +4 -0
- package/dist/utils/verify-remote-terminal.d.ts +10 -0
- package/dist/utils/verify-remote-terminal.js +415 -0
- package/package.json +9 -2
- package/templates/CLAUDE.md +135 -23
- package/templates/ekkos-manifest.json +5 -5
- package/templates/hooks/lib/contract.sh +43 -31
- package/templates/hooks/lib/count-tokens.cjs +86 -0
- package/templates/hooks/lib/ekkos-reminders.sh +98 -0
- package/templates/hooks/lib/state.sh +53 -1
- package/templates/hooks/stop.sh +150 -388
- package/templates/hooks/user-prompt-submit.sh +353 -443
- package/templates/windsurf-hooks/README.md +212 -0
- package/templates/windsurf-hooks/hooks.json +9 -2
- package/templates/windsurf-hooks/install.sh +148 -0
- package/templates/windsurf-hooks/lib/contract.sh +2 -0
- package/templates/windsurf-hooks/post-cascade-response.sh +251 -0
- package/templates/windsurf-hooks/pre-user-prompt.sh +435 -0
- package/templates/windsurf-skills/ekkos-memory/SKILL.md +219 -0
- package/templates/agents/README.md +0 -182
- package/templates/agents/code-reviewer.md +0 -166
- package/templates/agents/debug-detective.md +0 -169
- package/templates/agents/ekkOS_Vercel.md +0 -99
- package/templates/agents/extension-manager.md +0 -229
- package/templates/agents/git-companion.md +0 -185
- package/templates/agents/github-test-agent.md +0 -321
- package/templates/agents/railway-manager.md +0 -215
- package/templates/windsurf-hooks/before-submit-prompt.sh +0 -238
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Remote Terminal Verification Script
|
|
4
|
+
*
|
|
5
|
+
* Systematically verifies that all remote terminal components are properly configured
|
|
6
|
+
* and working correctly. Run this after setup-remote to validate the installation.
|
|
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.verifyRemoteTerminal = verifyRemoteTerminal;
|
|
46
|
+
const fs = __importStar(require("fs"));
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
const os = __importStar(require("os"));
|
|
49
|
+
const child_process_1 = require("child_process");
|
|
50
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
51
|
+
const EKKOS_DIR = path.join(os.homedir(), '.ekkos');
|
|
52
|
+
const results = [];
|
|
53
|
+
/**
|
|
54
|
+
* Test 1: Device configuration file exists
|
|
55
|
+
*/
|
|
56
|
+
function testDeviceConfig() {
|
|
57
|
+
const devicePath = path.join(EKKOS_DIR, 'device.json');
|
|
58
|
+
if (fs.existsSync(devicePath)) {
|
|
59
|
+
try {
|
|
60
|
+
const data = JSON.parse(fs.readFileSync(devicePath, 'utf-8'));
|
|
61
|
+
if (data.deviceId && data.deviceToken && data.deviceName) {
|
|
62
|
+
results.push({
|
|
63
|
+
name: 'Device Configuration',
|
|
64
|
+
status: 'pass',
|
|
65
|
+
message: 'Device config found and valid',
|
|
66
|
+
details: `Device: ${data.deviceName} (${data.deviceId.slice(0, 8)}...)`,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
results.push({
|
|
71
|
+
name: 'Device Configuration',
|
|
72
|
+
status: 'fail',
|
|
73
|
+
message: 'Device config exists but is incomplete',
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
results.push({
|
|
79
|
+
name: 'Device Configuration',
|
|
80
|
+
status: 'fail',
|
|
81
|
+
message: 'Device config is invalid JSON',
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
results.push({
|
|
87
|
+
name: 'Device Configuration',
|
|
88
|
+
status: 'fail',
|
|
89
|
+
message: 'Device not configured - run ekkos setup-remote',
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Test 2: Service is installed
|
|
95
|
+
*/
|
|
96
|
+
function testServiceInstalled() {
|
|
97
|
+
const platform = os.platform();
|
|
98
|
+
try {
|
|
99
|
+
if (platform === 'darwin') {
|
|
100
|
+
// macOS: check launchctl
|
|
101
|
+
const output = (0, child_process_1.execSync)('launchctl list | grep dev.ekkos.agent || true', {
|
|
102
|
+
encoding: 'utf-8',
|
|
103
|
+
});
|
|
104
|
+
if (output.includes('dev.ekkos.agent')) {
|
|
105
|
+
results.push({
|
|
106
|
+
name: 'Service Installation',
|
|
107
|
+
status: 'pass',
|
|
108
|
+
message: 'Service is installed on macOS',
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
results.push({
|
|
113
|
+
name: 'Service Installation',
|
|
114
|
+
status: 'fail',
|
|
115
|
+
message: 'Service not found - run ekkos agent start',
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else if (platform === 'win32') {
|
|
120
|
+
// Windows: check scheduled task
|
|
121
|
+
const output = (0, child_process_1.execSync)('schtasks /query /tn "ekkOS Agent" 2>nul || echo NOT_FOUND', {
|
|
122
|
+
encoding: 'utf-8',
|
|
123
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
124
|
+
});
|
|
125
|
+
if (!output.includes('NOT_FOUND')) {
|
|
126
|
+
results.push({
|
|
127
|
+
name: 'Service Installation',
|
|
128
|
+
status: 'pass',
|
|
129
|
+
message: 'Service is installed on Windows',
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
results.push({
|
|
134
|
+
name: 'Service Installation',
|
|
135
|
+
status: 'fail',
|
|
136
|
+
message: 'Service not found - run ekkos agent start',
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else if (platform === 'linux') {
|
|
141
|
+
// Linux: check systemd
|
|
142
|
+
const output = (0, child_process_1.execSync)('systemctl --user is-active ekkos-agent 2>/dev/null || echo INACTIVE', {
|
|
143
|
+
encoding: 'utf-8',
|
|
144
|
+
});
|
|
145
|
+
if (output.includes('active')) {
|
|
146
|
+
results.push({
|
|
147
|
+
name: 'Service Installation',
|
|
148
|
+
status: 'pass',
|
|
149
|
+
message: 'Service is installed on Linux',
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
results.push({
|
|
154
|
+
name: 'Service Installation',
|
|
155
|
+
status: 'fail',
|
|
156
|
+
message: 'Service not active - run ekkos agent start',
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
results.push({
|
|
163
|
+
name: 'Service Installation',
|
|
164
|
+
status: 'fail',
|
|
165
|
+
message: 'Failed to check service status',
|
|
166
|
+
details: String(err).slice(0, 100),
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Test 3: Service is running
|
|
172
|
+
*/
|
|
173
|
+
function testServiceRunning() {
|
|
174
|
+
const pidPath = path.join(EKKOS_DIR, 'agent.pid');
|
|
175
|
+
if (!fs.existsSync(pidPath)) {
|
|
176
|
+
results.push({
|
|
177
|
+
name: 'Service Running',
|
|
178
|
+
status: 'fail',
|
|
179
|
+
message: 'No PID file - service may not have started',
|
|
180
|
+
});
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
const pid = parseInt(fs.readFileSync(pidPath, 'utf-8').trim());
|
|
185
|
+
(0, child_process_1.execSync)(`kill -0 ${pid} 2>/dev/null`);
|
|
186
|
+
results.push({
|
|
187
|
+
name: 'Service Running',
|
|
188
|
+
status: 'pass',
|
|
189
|
+
message: `Process running (PID ${pid})`,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
results.push({
|
|
194
|
+
name: 'Service Running',
|
|
195
|
+
status: 'fail',
|
|
196
|
+
message: 'PID file exists but process is not running - restart with ekkos agent restart',
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Test 4: Logs are being written
|
|
202
|
+
*/
|
|
203
|
+
function testLogsWriting() {
|
|
204
|
+
const logPath = path.join(EKKOS_DIR, 'agent.log');
|
|
205
|
+
if (!fs.existsSync(logPath)) {
|
|
206
|
+
results.push({
|
|
207
|
+
name: 'Log Output',
|
|
208
|
+
status: 'warn',
|
|
209
|
+
message: 'No logs yet - service may not have started or run long enough',
|
|
210
|
+
});
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
const stats = fs.statSync(logPath);
|
|
215
|
+
const lastModified = Date.now() - stats.mtime.getTime();
|
|
216
|
+
const fiveMinutes = 5 * 60 * 1000;
|
|
217
|
+
if (lastModified < fiveMinutes) {
|
|
218
|
+
results.push({
|
|
219
|
+
name: 'Log Output',
|
|
220
|
+
status: 'pass',
|
|
221
|
+
message: `Logs updating (last write ${Math.round(lastModified / 1000)}s ago)`,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
results.push({
|
|
226
|
+
name: 'Log Output',
|
|
227
|
+
status: 'warn',
|
|
228
|
+
message: `No logs written for ${Math.round(lastModified / 1000 / 60)}m - check for errors`,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
results.push({
|
|
234
|
+
name: 'Log Output',
|
|
235
|
+
status: 'fail',
|
|
236
|
+
message: 'Failed to read log file',
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Test 5: Network connectivity to relay
|
|
242
|
+
*/
|
|
243
|
+
async function testNetworkConnectivity() {
|
|
244
|
+
try {
|
|
245
|
+
const RELAY_URL = 'wss://ekkos-relay-production.up.railway.app';
|
|
246
|
+
const controller = new AbortController();
|
|
247
|
+
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
248
|
+
const response = await fetch(`${RELAY_URL}/api/v1/health`, {
|
|
249
|
+
signal: controller.signal,
|
|
250
|
+
});
|
|
251
|
+
clearTimeout(timeout);
|
|
252
|
+
if (response.ok) {
|
|
253
|
+
results.push({
|
|
254
|
+
name: 'Network Connectivity',
|
|
255
|
+
status: 'pass',
|
|
256
|
+
message: 'Relay server is reachable',
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
results.push({
|
|
261
|
+
name: 'Network Connectivity',
|
|
262
|
+
status: 'warn',
|
|
263
|
+
message: `Relay server returned ${response.status}`,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
catch (err) {
|
|
268
|
+
if (err.name === 'AbortError') {
|
|
269
|
+
results.push({
|
|
270
|
+
name: 'Network Connectivity',
|
|
271
|
+
status: 'fail',
|
|
272
|
+
message: 'Timeout connecting to relay (>5s) - check internet connection',
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
results.push({
|
|
277
|
+
name: 'Network Connectivity',
|
|
278
|
+
status: 'fail',
|
|
279
|
+
message: `Cannot reach relay server: ${err.message}`,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Test 6: WebSocket connection from daemon
|
|
286
|
+
*/
|
|
287
|
+
function testDaemonConnection() {
|
|
288
|
+
const logPath = path.join(EKKOS_DIR, 'agent.log');
|
|
289
|
+
if (!fs.existsSync(logPath)) {
|
|
290
|
+
results.push({
|
|
291
|
+
name: 'Daemon WebSocket',
|
|
292
|
+
status: 'warn',
|
|
293
|
+
message: 'Cannot check - no logs yet',
|
|
294
|
+
});
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
298
|
+
const content = fs.readFileSync(logPath, 'utf-8');
|
|
299
|
+
const lines = content.split('\n');
|
|
300
|
+
const lastLines = lines.slice(-50);
|
|
301
|
+
const hasConnected = lastLines.some(l => l.includes('Connected to relay server'));
|
|
302
|
+
const hasRegistered = lastLines.some(l => l.includes('Registered with relay'));
|
|
303
|
+
const hasError = lastLines.some(l => l.includes('WebSocket error'));
|
|
304
|
+
const hasDisconnected = lastLines.some(l => l.includes('Disconnected'));
|
|
305
|
+
if (hasConnected && hasRegistered && !hasError) {
|
|
306
|
+
results.push({
|
|
307
|
+
name: 'Daemon WebSocket',
|
|
308
|
+
status: 'pass',
|
|
309
|
+
message: 'Daemon connected and registered with relay',
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
else if (hasDisconnected && !hasConnected) {
|
|
313
|
+
results.push({
|
|
314
|
+
name: 'Daemon WebSocket',
|
|
315
|
+
status: 'warn',
|
|
316
|
+
message: 'Daemon disconnected - reconnecting, check again in 30s',
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
else if (hasError) {
|
|
320
|
+
results.push({
|
|
321
|
+
name: 'Daemon WebSocket',
|
|
322
|
+
status: 'fail',
|
|
323
|
+
message: 'Daemon has WebSocket errors - check network and firewall',
|
|
324
|
+
details: lastLines
|
|
325
|
+
.filter(l => l.includes('error'))
|
|
326
|
+
.slice(-1)[0],
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
results.push({
|
|
331
|
+
name: 'Daemon WebSocket',
|
|
332
|
+
status: 'warn',
|
|
333
|
+
message: 'Unknown connection state - run ekkos agent logs -f to monitor',
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
catch (err) {
|
|
338
|
+
results.push({
|
|
339
|
+
name: 'Daemon WebSocket',
|
|
340
|
+
status: 'fail',
|
|
341
|
+
message: 'Failed to read logs',
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Print verification results
|
|
347
|
+
*/
|
|
348
|
+
function printResults() {
|
|
349
|
+
console.log('');
|
|
350
|
+
console.log(chalk_1.default.cyan.bold(' Remote Terminal Verification'));
|
|
351
|
+
console.log('');
|
|
352
|
+
let passed = 0;
|
|
353
|
+
let warned = 0;
|
|
354
|
+
let failed = 0;
|
|
355
|
+
for (const result of results) {
|
|
356
|
+
let icon = '';
|
|
357
|
+
let color = chalk_1.default.gray;
|
|
358
|
+
if (result.status === 'pass') {
|
|
359
|
+
icon = chalk_1.default.green('✓');
|
|
360
|
+
color = chalk_1.default.green;
|
|
361
|
+
passed++;
|
|
362
|
+
}
|
|
363
|
+
else if (result.status === 'warn') {
|
|
364
|
+
icon = chalk_1.default.yellow('⚠');
|
|
365
|
+
color = chalk_1.default.yellow;
|
|
366
|
+
warned++;
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
icon = chalk_1.default.red('✗');
|
|
370
|
+
color = chalk_1.default.red;
|
|
371
|
+
failed++;
|
|
372
|
+
}
|
|
373
|
+
console.log(` ${icon} ${color(result.name)}`);
|
|
374
|
+
console.log(` ${result.message}`);
|
|
375
|
+
if (result.details) {
|
|
376
|
+
console.log(` ${chalk_1.default.gray(result.details.slice(0, 80))}`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
console.log('');
|
|
380
|
+
console.log(chalk_1.default.gray(` Results: ${chalk_1.default.green(`${passed} pass`)}, ${chalk_1.default.yellow(`${warned} warn`)}, ${chalk_1.default.red(`${failed} fail`)}`));
|
|
381
|
+
console.log('');
|
|
382
|
+
// Overall status
|
|
383
|
+
if (failed === 0) {
|
|
384
|
+
if (warned === 0) {
|
|
385
|
+
console.log(chalk_1.default.green(' ✓ All checks passed!'));
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
console.log(chalk_1.default.yellow(' ⚠ Most checks passed, but review warnings above.'));
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
console.log(chalk_1.default.red(' ✗ Some checks failed. See above for fixes.'));
|
|
393
|
+
}
|
|
394
|
+
console.log('');
|
|
395
|
+
// Next steps
|
|
396
|
+
if (failed > 0) {
|
|
397
|
+
console.log(chalk_1.default.cyan(' Next steps:'));
|
|
398
|
+
console.log(' 1. Review failed checks above');
|
|
399
|
+
console.log(' 2. Run suggested commands');
|
|
400
|
+
console.log(' 3. Wait 30s and run this verification again');
|
|
401
|
+
console.log('');
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Run all verification tests
|
|
406
|
+
*/
|
|
407
|
+
async function verifyRemoteTerminal() {
|
|
408
|
+
testDeviceConfig();
|
|
409
|
+
testServiceInstalled();
|
|
410
|
+
testServiceRunning();
|
|
411
|
+
testLogsWriting();
|
|
412
|
+
await testNetworkConnectivity();
|
|
413
|
+
testDaemonConnection();
|
|
414
|
+
printResults();
|
|
415
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ekkos/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Setup ekkOS memory for AI coding assistants (Claude Code, Cursor, Windsurf)",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"ekkos": "dist/index.js",
|
|
8
8
|
"cli": "dist/index.js",
|
|
9
|
-
"ekkos-capture": "dist/cache/capture.js"
|
|
9
|
+
"ekkos-capture": "dist/cache/capture.js",
|
|
10
|
+
"ekkos-promote": "dist/cron/promoter.js"
|
|
10
11
|
},
|
|
11
12
|
"scripts": {
|
|
12
13
|
"build": "tsc",
|
|
@@ -28,6 +29,11 @@
|
|
|
28
29
|
"author": "ekkOS",
|
|
29
30
|
"license": "MIT",
|
|
30
31
|
"dependencies": {
|
|
32
|
+
"@ekkos/prometheus": "*",
|
|
33
|
+
"@supabase/supabase-js": "^2.39.8",
|
|
34
|
+
"blessed": "^0.1.81",
|
|
35
|
+
"blessed-contrib": "^4.11.0",
|
|
36
|
+
"ccusage": "^18.0.5",
|
|
31
37
|
"chalk": "^5.3.0",
|
|
32
38
|
"commander": "^12.1.0",
|
|
33
39
|
"inquirer": "^9.2.23",
|
|
@@ -37,6 +43,7 @@
|
|
|
37
43
|
"ws": "^8.19.0"
|
|
38
44
|
},
|
|
39
45
|
"devDependencies": {
|
|
46
|
+
"@types/blessed": "^0.1.27",
|
|
40
47
|
"@types/node": "^20.11.0",
|
|
41
48
|
"typescript": "^5.3.3"
|
|
42
49
|
},
|
package/templates/CLAUDE.md
CHANGED
|
@@ -53,6 +53,28 @@ After `/clear`, when you see restored turns in `<system-reminder>` (pattern: `**
|
|
|
53
53
|
```
|
|
54
54
|
This is the ONLY way users see that restoration worked. Hook output is invisible to them.
|
|
55
55
|
|
|
56
|
+
### RULE 6: AUTO-RESTORE AFTER COMPACTION (CONTEXT GUARDIAN)
|
|
57
|
+
When you see `<ekkos-context-preserved>` in the conversation (appears after auto-compaction):
|
|
58
|
+
1. **IMMEDIATELY** call `ekkOS_RestoreContext` with the `restoration_token` from the tag
|
|
59
|
+
2. This restores your working memory that was preserved before compaction
|
|
60
|
+
3. Start your response with:
|
|
61
|
+
```
|
|
62
|
+
✓ **Context restored** - ekkOS preserved X turns before compaction
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**This is how seamless context continuity works:**
|
|
66
|
+
- PreCompact hook fires → ekkOS_PreserveContext saves your state
|
|
67
|
+
- Claude Code compacts → Summary includes the preservation tag
|
|
68
|
+
- You see the tag → Call ekkOS_RestoreContext → Full context restored
|
|
69
|
+
- User experiences zero knowledge loss despite compaction
|
|
70
|
+
|
|
71
|
+
**Example restoration call:**
|
|
72
|
+
```
|
|
73
|
+
ekkOS_RestoreContext({
|
|
74
|
+
restoration_token: "<token from tag>"
|
|
75
|
+
})
|
|
76
|
+
```
|
|
77
|
+
|
|
56
78
|
---
|
|
57
79
|
|
|
58
80
|
## SKILL TRIGGERS (MANDATORY)
|
|
@@ -79,8 +101,7 @@ Call skills using `Skill(skill: "name")` when you detect these triggers:
|
|
|
79
101
|
| Trigger Keywords | Agent | When to Use |
|
|
80
102
|
|------------------|-------|-------------|
|
|
81
103
|
| extension, vsix, publish, marketplace, ekkos-connect | `extension-manager` | Version bumps, VSIX builds, publishing |
|
|
82
|
-
| vercel, deploy
|
|
83
|
-
| deploy, railway, workers, pm2, restart, logs, queue | `railway-manager` | Railway deployments, service management |
|
|
104
|
+
| vercel, deploy, apps/*, ekkos.dev, api.ekkos.dev | `devops` | Vercel deployments — ALL apps/ deploy to Vercel |
|
|
84
105
|
| commit, push, branch, merge, git, pull request, rebase | `git-companion` | All git operations |
|
|
85
106
|
| error, bug, broken, not working, failing, crash | `debug-detective` | Systematic debugging |
|
|
86
107
|
| review, PR, check this code, code quality | `code-reviewer` | Code reviews |
|
|
@@ -89,6 +110,25 @@ Call skills using `Skill(skill: "name")` when you detect these triggers:
|
|
|
89
110
|
| backend, API, database, Supabase, RLS | `backend` | API and database work |
|
|
90
111
|
| test, QA, quality, coverage | `qa` | Testing and quality assurance |
|
|
91
112
|
| plan, architect, design, implement feature | `tech-lead` | Complex planning and coordination |
|
|
113
|
+
| research, papers, arXiv, cutting edge, latest AI | `research-scout` | Monitor AI research, update roadmap |
|
|
114
|
+
|
|
115
|
+
**Vercel Project Mapping** (apps/ folder → Vercel project → URL):
|
|
116
|
+
|
|
117
|
+
| apps/ folder | Vercel project | Production URL |
|
|
118
|
+
|-------------|----------------|----------------|
|
|
119
|
+
| `apps/web` | **platform** | platform.ekkos.dev |
|
|
120
|
+
| `apps/memory` | memory | api.ekkos.dev |
|
|
121
|
+
| `apps/proxy` | proxy | proxy.ekkos.dev |
|
|
122
|
+
| `apps/docs` | docs | docs.ekkos.dev |
|
|
123
|
+
| `apps/marketing` | marketing | ekkos.dev |
|
|
124
|
+
| `apps/ekkosca` | ekkosca | ekkos.ca |
|
|
125
|
+
| `apps/blog` | blog | blog.ekkos.dev |
|
|
126
|
+
| `apps/support` | support | support.ekkos.dev |
|
|
127
|
+
| `apps/labs` | labs | ekkoslabs.com |
|
|
128
|
+
| `apps/admin` | admin | admin.ekkos.ca |
|
|
129
|
+
| `apps/sdk` | sdk | sdk-ekkos.vercel.app |
|
|
130
|
+
|
|
131
|
+
**NOTE:** `apps/web` → Vercel project name is `platform`, NOT `web`.
|
|
92
132
|
|
|
93
133
|
**How it works:**
|
|
94
134
|
1. Detect trigger keywords in user request
|
|
@@ -109,39 +149,55 @@ You: Task(subagent_type="extension-manager", prompt="Bump version...")
|
|
|
109
149
|
|
|
110
150
|
---
|
|
111
151
|
|
|
112
|
-
## MCP Tools (
|
|
152
|
+
## MCP Tools (68 Total)
|
|
113
153
|
|
|
114
|
-
### Core Memory Tools
|
|
154
|
+
### Core Memory Tools (7)
|
|
115
155
|
| Tool | Description |
|
|
116
156
|
|------|-------------|
|
|
117
157
|
| `ekkOS_Search` | 🔴 REQUIRED: Search all 11 layers before answering |
|
|
118
|
-
| `
|
|
158
|
+
| `ekkOS_ExpandPattern` | Get full details of a pattern from compact search results |
|
|
119
159
|
| `ekkOS_Capture` | Capture memory events |
|
|
120
160
|
| `ekkOS_Forge` | 🔴 REQUIRED: Create pattern from solution |
|
|
121
|
-
| `
|
|
161
|
+
| `ekkOS_Track` | Track when pattern is applied |
|
|
122
162
|
| `ekkOS_Outcome` | Track if pattern worked or failed |
|
|
123
|
-
| `ekkOS_Detect` | 🔴 REQUIRED: Auto-detect which patterns were used |
|
|
124
|
-
| `ekkOS_Summary` | 🔴 REQUIRED: Get summary of MCP activity |
|
|
125
|
-
| `ekkOS_Conflict` | 🔴 REQUIRED: Check for conflicts before destructive actions |
|
|
126
|
-
| `ekkOS_Recall` | Recall past conversations by time |
|
|
127
|
-
| `ekkOS_Codebase` | Search project code embeddings |
|
|
128
163
|
| `ekkOS_Stats` | Get statistics for all layers |
|
|
129
|
-
| `ekkOS_Track` | Track when pattern is applied |
|
|
130
|
-
| `ekkOS_Reflect` | Analyze response for improvement opportunities |
|
|
131
164
|
|
|
132
|
-
###
|
|
165
|
+
### Context & Retrieval Tools (5)
|
|
133
166
|
| Tool | Description |
|
|
134
167
|
|------|-------------|
|
|
135
|
-
| `
|
|
136
|
-
| `
|
|
168
|
+
| `ekkOS_Context` | Get relevant context for a task |
|
|
169
|
+
| `ekkOS_Codebase` | Search project code embeddings |
|
|
170
|
+
| `ekkOS_Recall` | Recall past conversations by time |
|
|
171
|
+
| `ekkOS_PreserveContext` | 🔴 REQUIRED: Preserve working memory before compaction |
|
|
172
|
+
| `ekkOS_RestoreContext` | 🔴 REQUIRED: Restore context after compaction (RULE 6) |
|
|
137
173
|
|
|
138
|
-
###
|
|
174
|
+
### Utility Tools (8)
|
|
139
175
|
| Tool | Description |
|
|
140
176
|
|------|-------------|
|
|
177
|
+
| `ekkOS_Summary` | 🔴 REQUIRED: Get summary of MCP activity |
|
|
178
|
+
| `ekkOS_Conflict` | 🔴 REQUIRED: Check for conflicts before destructive actions |
|
|
179
|
+
| `ekkOS_Reflect` | Analyze response for improvement opportunities |
|
|
180
|
+
| `ekkOS_Detect` | 🔴 REQUIRED: Auto-detect which patterns were used |
|
|
141
181
|
| `ekkOS_Export` | Export your patterns, directives, plans as portable JSON backup |
|
|
142
182
|
| `ekkOS_Import` | Import memory from backup (auto-deduplication) |
|
|
183
|
+
| `ekkOS_Why` | Explain why a pattern or decision was made |
|
|
184
|
+
| `ekkOS_Health` | System health check for ekkOS infrastructure |
|
|
185
|
+
|
|
186
|
+
### Directive Tools (4)
|
|
187
|
+
| Tool | Description |
|
|
188
|
+
|------|-------------|
|
|
189
|
+
| `ekkOS_Directive` | 🔴 REQUIRED: Create MUST/NEVER/PREFER/AVOID rules |
|
|
190
|
+
| `ekkOS_UpdateDirective` | Update existing directive |
|
|
191
|
+
| `ekkOS_DeleteDirective` | Remove a directive |
|
|
192
|
+
| `ekkOS_UniversalDirectives` | Get directives that apply to all users (constitutional) |
|
|
193
|
+
|
|
194
|
+
### Schema Awareness Tools (2)
|
|
195
|
+
| Tool | Description |
|
|
196
|
+
|------|-------------|
|
|
197
|
+
| `ekkOS_IndexSchema` | Index database schemas (Supabase, Prisma, TypeScript) |
|
|
198
|
+
| `ekkOS_GetSchema` | Get schema for a specific table/type |
|
|
143
199
|
|
|
144
|
-
### Plan Management
|
|
200
|
+
### Plan Management (8)
|
|
145
201
|
| Tool | Description |
|
|
146
202
|
|------|-------------|
|
|
147
203
|
| `ekkOS_Plan` | Create structured task plan |
|
|
@@ -153,7 +209,7 @@ You: Task(subagent_type="extension-manager", prompt="Bump version...")
|
|
|
153
209
|
| `ekkOS_Templates` | List available templates |
|
|
154
210
|
| `ekkOS_FromTemplate` | Create plan from template |
|
|
155
211
|
|
|
156
|
-
### Secrets Management (
|
|
212
|
+
### Secrets Management (5)
|
|
157
213
|
| Tool | Description |
|
|
158
214
|
|------|-------------|
|
|
159
215
|
| `ekkOS_StoreSecret` | Encrypt and store sensitive data (AES-256-GCM) |
|
|
@@ -162,6 +218,39 @@ You: Task(subagent_type="extension-manager", prompt="Bump version...")
|
|
|
162
218
|
| `ekkOS_DeleteSecret` | Permanently delete a secret |
|
|
163
219
|
| `ekkOS_RotateSecret` | Update secret with new value |
|
|
164
220
|
|
|
221
|
+
### Project/Sync Tools (4)
|
|
222
|
+
| Tool | Description |
|
|
223
|
+
|------|-------------|
|
|
224
|
+
| `ekkOS_ProjectInit` | Initialize ekkOS for a new project |
|
|
225
|
+
| `ekkOS_Ingest` | Bulk ingest data into memory layers |
|
|
226
|
+
| `ekkOS_Snapshot` | Create point-in-time snapshot of memory state |
|
|
227
|
+
| `ekkOS_Sync` | Sync local and cloud memory state |
|
|
228
|
+
|
|
229
|
+
### Session Management (1)
|
|
230
|
+
| Tool | Description |
|
|
231
|
+
|------|-------------|
|
|
232
|
+
| `ekkOS_Session` | Manage L1 Working Memory sessions (start/resume/end) |
|
|
233
|
+
|
|
234
|
+
### Learning Tools (2)
|
|
235
|
+
| Tool | Description |
|
|
236
|
+
|------|-------------|
|
|
237
|
+
| `ekkOS_Learn` | Auto-learning from corrections, successes, observations |
|
|
238
|
+
| `ekkOS_ReviewLearning` | Review pending learning candidates, approve/reject patterns |
|
|
239
|
+
|
|
240
|
+
### Relationship Tools (2)
|
|
241
|
+
| Tool | Description |
|
|
242
|
+
|------|-------------|
|
|
243
|
+
| `ekkOS_Link` | Create relationships between patterns |
|
|
244
|
+
| `ekkOS_Playbook` | Manage ordered sequences of patterns (workflows) |
|
|
245
|
+
|
|
246
|
+
### PROMETHEUS Tools (4)
|
|
247
|
+
| Tool | Description |
|
|
248
|
+
|------|-------------|
|
|
249
|
+
| `ekkOS_Delta` | Compute improvement score using Δ_prometheus formula |
|
|
250
|
+
| `ekkOS_MetaState` | System introspection ("I know what I know") |
|
|
251
|
+
| `ekkOS_Goal` | Manage persistent objectives and success criteria |
|
|
252
|
+
| `ekkOS_Strategy` | Context-aware strategy selection for tasks |
|
|
253
|
+
|
|
165
254
|
---
|
|
166
255
|
|
|
167
256
|
## Proactive Tool Triggers (MEMORIZE THESE)
|
|
@@ -204,6 +293,28 @@ You: Task(subagent_type="extension-manager", prompt="Bump version...")
|
|
|
204
293
|
- Need to retrieve stored credentials
|
|
205
294
|
- User asks "do you have my X key?"
|
|
206
295
|
|
|
296
|
+
### Always Use Context Preservation When:
|
|
297
|
+
- PreCompact hook fires (auto-triggered)
|
|
298
|
+
- See `<ekkos-context-preserved>` tag (auto-restore)
|
|
299
|
+
- RULE 6 activation
|
|
300
|
+
|
|
301
|
+
### Always Use Learning Tools When:
|
|
302
|
+
- User corrects you (ekkOS_Learn mode: correction)
|
|
303
|
+
- Solution succeeds (ekkOS_Learn mode: success)
|
|
304
|
+
- Discover insight (ekkOS_Learn mode: observe)
|
|
305
|
+
- Review pending patterns (ekkOS_ReviewLearning)
|
|
306
|
+
|
|
307
|
+
### Always Use Relationship Tools When:
|
|
308
|
+
- Patterns are sequential (ekkOS_Link type: leads_to)
|
|
309
|
+
- Creating workflows (ekkOS_Playbook)
|
|
310
|
+
- Patterns are alternatives (ekkOS_Link type: alternative)
|
|
311
|
+
|
|
312
|
+
### Always Use PROMETHEUS Tools When:
|
|
313
|
+
- Evaluating system improvement (ekkOS_Delta)
|
|
314
|
+
- Self-assessment needed (ekkOS_MetaState)
|
|
315
|
+
- Setting objectives (ekkOS_Goal)
|
|
316
|
+
- Choosing approach (ekkOS_Strategy)
|
|
317
|
+
|
|
207
318
|
---
|
|
208
319
|
|
|
209
320
|
## 11-Layer Memory Architecture
|
|
@@ -253,20 +364,21 @@ Call `ekkOS_Directive` when user says:
|
|
|
253
364
|
**EVERY response MUST end with this footer:**
|
|
254
365
|
```
|
|
255
366
|
---
|
|
256
|
-
{IDE} ({Model}) · 🧠 **ekkOS_™** · Turn {N} · 📅 {Timestamp}
|
|
367
|
+
{IDE} ({Model}) · 🧠 **ekkOS_™** · Turn {N} · {SessionName} · 📅 {Timestamp}
|
|
257
368
|
```
|
|
258
369
|
|
|
259
370
|
**How to detect values:**
|
|
260
371
|
- **IDE**: Claude Code, Cursor, Windsurf, etc. (from environment)
|
|
261
372
|
- **Model**: Sonnet 4.5, Opus 4.5, etc. (from your model name)
|
|
262
373
|
- **Turn Number**: From hook header (e.g., "Turn 47") - starts at 0 for each new session
|
|
374
|
+
- **Session Name**: From hook header (e.g., "sol-gem-dig") - human-readable session identifier
|
|
263
375
|
- **Timestamp**: From hook header (accurate local time in EST)
|
|
264
376
|
|
|
265
377
|
**Examples:**
|
|
266
|
-
- `Claude Code (Sonnet 4.5) · 🧠 **ekkOS_™** · Turn 12 · 📅 2026-01-09 4:50 PM EST`
|
|
267
|
-
- `Cursor (Claude Sonnet 4) · 🧠 **ekkOS_™** · Turn 5 · 📅 2026-01-09 10:15 AM EST`
|
|
378
|
+
- `Claude Code (Sonnet 4.5) · 🧠 **ekkOS_™** · Turn 12 · cosmic-penguin-runs · 📅 2026-01-09 4:50 PM EST`
|
|
379
|
+
- `Cursor (Claude Sonnet 4) · 🧠 **ekkOS_™** · Turn 5 · bright-falcon-soars · 📅 2026-01-09 10:15 AM EST`
|
|
268
380
|
|
|
269
|
-
**The hook header shows:** `🧠 ekkOS Memory | Turn {N} |
|
|
381
|
+
**The hook header shows:** `🧠 ekkOS Memory | Turn {N} | {Context%} | {SessionName} | {timestamp}`
|
|
270
382
|
|
|
271
383
|
---
|
|
272
384
|
|