@ekkos/cli 1.3.1 → 1.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/dist/capture/jsonl-rewriter.d.ts +1 -1
  2. package/dist/capture/jsonl-rewriter.js +3 -3
  3. package/dist/capture/transcript-repair.d.ts +2 -2
  4. package/dist/capture/transcript-repair.js +2 -2
  5. package/dist/commands/claw.d.ts +13 -0
  6. package/dist/commands/claw.js +253 -0
  7. package/dist/commands/dashboard.js +742 -118
  8. package/dist/commands/doctor.d.ts +3 -3
  9. package/dist/commands/doctor.js +6 -79
  10. package/dist/commands/gemini.d.ts +19 -0
  11. package/dist/commands/gemini.js +193 -0
  12. package/dist/commands/init.d.ts +1 -0
  13. package/dist/commands/init.js +56 -41
  14. package/dist/commands/run.d.ts +0 -1
  15. package/dist/commands/run.js +288 -263
  16. package/dist/commands/scan.d.ts +21 -0
  17. package/dist/commands/scan.js +386 -0
  18. package/dist/commands/status.d.ts +4 -1
  19. package/dist/commands/status.js +165 -27
  20. package/dist/commands/swarm-dashboard.js +156 -28
  21. package/dist/commands/swarm.d.ts +1 -1
  22. package/dist/commands/swarm.js +1 -1
  23. package/dist/commands/test-claude.d.ts +2 -2
  24. package/dist/commands/test-claude.js +3 -3
  25. package/dist/deploy/index.d.ts +0 -2
  26. package/dist/deploy/index.js +0 -2
  27. package/dist/deploy/settings.d.ts +6 -5
  28. package/dist/deploy/settings.js +64 -16
  29. package/dist/deploy/skills.js +1 -2
  30. package/dist/index.js +86 -96
  31. package/dist/lib/usage-parser.d.ts +1 -1
  32. package/dist/lib/usage-parser.js +9 -6
  33. package/dist/local/index.d.ts +14 -0
  34. package/dist/local/index.js +28 -0
  35. package/dist/local/local-embeddings.d.ts +49 -0
  36. package/dist/local/local-embeddings.js +232 -0
  37. package/dist/local/offline-fallback.d.ts +44 -0
  38. package/dist/local/offline-fallback.js +159 -0
  39. package/dist/local/sqlite-store.d.ts +126 -0
  40. package/dist/local/sqlite-store.js +393 -0
  41. package/dist/local/sync-engine.d.ts +42 -0
  42. package/dist/local/sync-engine.js +223 -0
  43. package/dist/utils/platform.d.ts +5 -1
  44. package/dist/utils/platform.js +24 -4
  45. package/dist/utils/proxy-url.d.ts +21 -0
  46. package/dist/utils/proxy-url.js +34 -0
  47. package/dist/utils/state.d.ts +1 -1
  48. package/dist/utils/state.js +11 -3
  49. package/dist/utils/templates.js +1 -1
  50. package/package.json +11 -4
  51. package/templates/CLAUDE.md +49 -107
  52. package/dist/agent/daemon.d.ts +0 -130
  53. package/dist/agent/daemon.js +0 -606
  54. package/dist/agent/health-check.d.ts +0 -35
  55. package/dist/agent/health-check.js +0 -243
  56. package/dist/agent/pty-runner.d.ts +0 -53
  57. package/dist/agent/pty-runner.js +0 -190
  58. package/dist/commands/agent.d.ts +0 -50
  59. package/dist/commands/agent.js +0 -544
  60. package/dist/commands/setup-remote.d.ts +0 -20
  61. package/dist/commands/setup-remote.js +0 -582
  62. package/dist/utils/verify-remote-terminal.d.ts +0 -10
  63. package/dist/utils/verify-remote-terminal.js +0 -415
  64. package/templates/README.md +0 -378
  65. package/templates/claude-plugins/PHASE2_COMPLETION.md +0 -346
  66. package/templates/claude-plugins/PLUGIN_PROPOSALS.md +0 -1776
  67. package/templates/claude-plugins/README.md +0 -587
  68. package/templates/claude-plugins/agents/code-reviewer.json +0 -14
  69. package/templates/claude-plugins/agents/debug-detective.json +0 -15
  70. package/templates/claude-plugins/agents/git-companion.json +0 -14
  71. package/templates/claude-plugins/blog-manager/.claude-plugin/plugin.json +0 -8
  72. package/templates/claude-plugins/blog-manager/commands/blog.md +0 -691
  73. package/templates/claude-plugins/golden-loop-monitor/.claude-plugin/plugin.json +0 -8
  74. package/templates/claude-plugins/golden-loop-monitor/commands/loop-status.md +0 -434
  75. package/templates/claude-plugins/learning-tracker/.claude-plugin/plugin.json +0 -8
  76. package/templates/claude-plugins/learning-tracker/commands/my-patterns.md +0 -282
  77. package/templates/claude-plugins/memory-lens/.claude-plugin/plugin.json +0 -8
  78. package/templates/claude-plugins/memory-lens/commands/memory-search.md +0 -181
  79. package/templates/claude-plugins/pattern-coach/.claude-plugin/plugin.json +0 -8
  80. package/templates/claude-plugins/pattern-coach/commands/forge.md +0 -365
  81. package/templates/claude-plugins/project-schema-validator/.claude-plugin/plugin.json +0 -8
  82. package/templates/claude-plugins/project-schema-validator/commands/validate-schema.md +0 -582
  83. package/templates/claude-plugins-admin/AGENT_TEAM_PROPOSALS.md +0 -819
  84. package/templates/claude-plugins-admin/README.md +0 -446
  85. package/templates/claude-plugins-admin/autonomous-admin-agent/.claude-plugin/plugin.json +0 -8
  86. package/templates/claude-plugins-admin/autonomous-admin-agent/commands/agent.md +0 -595
  87. package/templates/claude-plugins-admin/backend-agent/.claude-plugin/plugin.json +0 -8
  88. package/templates/claude-plugins-admin/backend-agent/commands/backend.md +0 -798
  89. package/templates/claude-plugins-admin/deploy-guardian/.claude-plugin/plugin.json +0 -8
  90. package/templates/claude-plugins-admin/deploy-guardian/commands/deploy.md +0 -554
  91. package/templates/claude-plugins-admin/frontend-agent/.claude-plugin/plugin.json +0 -8
  92. package/templates/claude-plugins-admin/frontend-agent/commands/frontend.md +0 -881
  93. package/templates/claude-plugins-admin/mcp-server-manager/.claude-plugin/plugin.json +0 -8
  94. package/templates/claude-plugins-admin/mcp-server-manager/commands/mcp.md +0 -85
  95. package/templates/claude-plugins-admin/memory-system-monitor/.claude-plugin/plugin.json +0 -8
  96. package/templates/claude-plugins-admin/memory-system-monitor/commands/memory-health.md +0 -569
  97. package/templates/claude-plugins-admin/qa-agent/.claude-plugin/plugin.json +0 -8
  98. package/templates/claude-plugins-admin/qa-agent/commands/qa.md +0 -863
  99. package/templates/claude-plugins-admin/tech-lead-agent/.claude-plugin/plugin.json +0 -8
  100. package/templates/claude-plugins-admin/tech-lead-agent/commands/lead.md +0 -732
  101. package/templates/commands/continue.md +0 -47
  102. package/templates/cursor-rules/ekkos-memory.md +0 -127
  103. package/templates/ekkos-manifest.json +0 -223
  104. package/templates/helpers/json-parse.cjs +0 -101
  105. package/templates/hooks-node/lib/state.js +0 -187
  106. package/templates/hooks-node/stop.js +0 -416
  107. package/templates/hooks-node/user-prompt-submit.js +0 -337
  108. package/templates/plan-template.md +0 -306
  109. package/templates/rules/00-hooks-contract.mdc +0 -89
  110. package/templates/rules/30-ekkos-core.mdc +0 -188
  111. package/templates/rules/31-ekkos-messages.mdc +0 -78
  112. package/templates/shared/hooks-enabled.json +0 -22
  113. package/templates/shared/session-words.json +0 -45
  114. package/templates/skills/ekkOS_Deep_Recall/Skill.md +0 -282
  115. package/templates/skills/ekkOS_Learn/Skill.md +0 -265
  116. package/templates/skills/ekkOS_Memory_First/Skill.md +0 -206
  117. package/templates/skills/ekkOS_Plan_Assist/Skill.md +0 -302
  118. package/templates/skills/ekkOS_Preferences/Skill.md +0 -247
  119. package/templates/skills/ekkOS_Reflect/Skill.md +0 -257
  120. package/templates/skills/ekkOS_Safety/Skill.md +0 -265
  121. package/templates/skills/ekkOS_Schema/Skill.md +0 -251
  122. package/templates/skills/ekkOS_Summary/Skill.md +0 -257
  123. package/templates/spec-template.md +0 -159
  124. package/templates/windsurf-rules/ekkos-memory.md +0 -127
  125. package/templates/windsurf-skills/README.md +0 -58
  126. package/templates/windsurf-skills/ekkos-continue/SKILL.md +0 -81
  127. package/templates/windsurf-skills/ekkos-golden-loop/SKILL.md +0 -225
  128. package/templates/windsurf-skills/ekkos-insights/SKILL.md +0 -138
  129. package/templates/windsurf-skills/ekkos-recall/SKILL.md +0 -96
  130. package/templates/windsurf-skills/ekkos-safety/SKILL.md +0 -89
  131. package/templates/windsurf-skills/ekkos-vault/SKILL.md +0 -86
@@ -0,0 +1,21 @@
1
+ /**
2
+ * ekkos scan
3
+ *
4
+ * Scans the current directory's repo structure, discovers systems,
5
+ * and seeds them into ekkOS system_registry via the memory API.
6
+ *
7
+ * Usage:
8
+ * ekkos scan Scan cwd and seed registry
9
+ * ekkos scan --compile Also trigger a compile pass after seeding
10
+ * ekkos scan --dry-run Show what would be seeded without calling API
11
+ * ekkos scan --path /my/repo Scan a specific directory
12
+ *
13
+ * Reuses discovery logic from apps/memory/workers/context-compiler/registry-seed.ts
14
+ */
15
+ interface ScanOptions {
16
+ compile?: boolean;
17
+ dryRun?: boolean;
18
+ path?: string;
19
+ }
20
+ export declare function scan(options: ScanOptions): Promise<void>;
21
+ export {};
@@ -0,0 +1,386 @@
1
+ "use strict";
2
+ /**
3
+ * ekkos scan
4
+ *
5
+ * Scans the current directory's repo structure, discovers systems,
6
+ * and seeds them into ekkOS system_registry via the memory API.
7
+ *
8
+ * Usage:
9
+ * ekkos scan Scan cwd and seed registry
10
+ * ekkos scan --compile Also trigger a compile pass after seeding
11
+ * ekkos scan --dry-run Show what would be seeded without calling API
12
+ * ekkos scan --path /my/repo Scan a specific directory
13
+ *
14
+ * Reuses discovery logic from apps/memory/workers/context-compiler/registry-seed.ts
15
+ */
16
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.scan = scan;
21
+ const fs_1 = require("fs");
22
+ const path_1 = require("path");
23
+ const chalk_1 = __importDefault(require("chalk"));
24
+ const ora_1 = __importDefault(require("ora"));
25
+ const platform_js_1 = require("../utils/platform.js");
26
+ // ── Excluded directories ─────────────────────────────────────────────────
27
+ const EXCLUDED_DIRS = new Set([
28
+ 'node_modules', '.next', 'dist', 'out', 'build', '.turbo', '.cache',
29
+ '.git', '.github', '.vscode', '.idea', '.claude', '.windsurf',
30
+ 'coverage', '__pycache__', '.pytest_cache', '.mypy_cache',
31
+ 'vendor', '.vercel', '.svelte-kit', 'target', 'tmp', 'temp',
32
+ 'logs', 'downloads', 'favicon', 'public', 'resources',
33
+ ]);
34
+ const EXCLUDED_PREFIXES = ['.', '_'];
35
+ // ── Domain detection ─────────────────────────────────────────────────────
36
+ const DOMAIN_MAP = {
37
+ apps: 'platform',
38
+ packages: 'packages',
39
+ workers: 'workers',
40
+ extensions: 'extensions',
41
+ scripts: 'infra',
42
+ services: 'services',
43
+ lib: 'memory',
44
+ supabase: 'infra',
45
+ 'mcp-servers': 'tools',
46
+ templates: 'tools',
47
+ docs: 'content',
48
+ content: 'content',
49
+ monitoring: 'infra',
50
+ tests: 'infra',
51
+ e2e: 'infra',
52
+ src: 'core',
53
+ api: 'api',
54
+ components: 'frontend',
55
+ pages: 'frontend',
56
+ };
57
+ function detectDomain(dirPath) {
58
+ const topLevel = dirPath.split('/')[0];
59
+ return DOMAIN_MAP[topLevel] || 'other';
60
+ }
61
+ // ── System ID generation ─────────────────────────────────────────────────
62
+ function toSystemId(dirPath) {
63
+ return dirPath
64
+ .toLowerCase()
65
+ .replace(/\//g, '-')
66
+ .replace(/[^a-z0-9-]/g, '')
67
+ .replace(/-+/g, '-')
68
+ .replace(/^-|-$/g, '')
69
+ .slice(0, 64);
70
+ }
71
+ function toHumanName(dirPath) {
72
+ const parts = dirPath.split('/');
73
+ return parts[parts.length - 1]
74
+ .replace(/-/g, ' ')
75
+ .replace(/\b\w/g, c => c.toUpperCase());
76
+ }
77
+ // ── Directory scanner ────────────────────────────────────────────────────
78
+ function isIncludable(fullPath) {
79
+ const name = (0, path_1.basename)(fullPath);
80
+ if (EXCLUDED_DIRS.has(name))
81
+ return false;
82
+ if (EXCLUDED_PREFIXES.some(p => name.startsWith(p)))
83
+ return false;
84
+ // Must contain at least 1 source file or meaningful config
85
+ try {
86
+ const entries = (0, fs_1.readdirSync)(fullPath);
87
+ const sourceFiles = entries.filter(e => {
88
+ const ext = e.split('.').pop()?.toLowerCase();
89
+ return ['ts', 'tsx', 'js', 'jsx', 'mjs', 'py', 'rs', 'go', 'sql', 'mts'].includes(ext || '');
90
+ });
91
+ const configFiles = entries.filter(e => ['package.json', 'tsconfig.json', 'Cargo.toml', 'pyproject.toml', 'wrangler.toml', 'go.mod'].includes(e));
92
+ return sourceFiles.length >= 1 || configFiles.length >= 1;
93
+ }
94
+ catch {
95
+ return false;
96
+ }
97
+ }
98
+ // Container directories that always scan children
99
+ const CONTAINER_DIRS = new Set([
100
+ 'apps', 'packages', 'workers', 'extensions', 'services', 'mcp-servers',
101
+ ]);
102
+ function scanDirectory(repoRoot, currentPath, depth, maxDepth, parentId) {
103
+ if (depth > maxDepth)
104
+ return [];
105
+ const results = [];
106
+ const relPath = (0, path_1.relative)(repoRoot, currentPath) || '.';
107
+ const dirName = (0, path_1.basename)(currentPath);
108
+ // Skip root — just descend into children
109
+ if (relPath === '.') {
110
+ let entries;
111
+ try {
112
+ entries = (0, fs_1.readdirSync)(currentPath);
113
+ }
114
+ catch {
115
+ return [];
116
+ }
117
+ for (const entry of entries) {
118
+ const full = (0, path_1.join)(currentPath, entry);
119
+ try {
120
+ if ((0, fs_1.statSync)(full).isDirectory() && !EXCLUDED_DIRS.has(entry) && !entry.startsWith('.')) {
121
+ results.push(...scanDirectory(repoRoot, full, depth + 1, maxDepth, null));
122
+ }
123
+ }
124
+ catch { /* permission denied, etc. */ }
125
+ }
126
+ return results;
127
+ }
128
+ // Container directories (apps/, packages/, etc.) — always scan children, never register themselves
129
+ const isContainer = CONTAINER_DIRS.has(relPath) || CONTAINER_DIRS.has(dirName);
130
+ if (isContainer && depth <= 1) {
131
+ try {
132
+ const entries = (0, fs_1.readdirSync)(currentPath);
133
+ for (const e of entries) {
134
+ const full = (0, path_1.join)(currentPath, e);
135
+ try {
136
+ if ((0, fs_1.statSync)(full).isDirectory() && !EXCLUDED_DIRS.has(e) && !e.startsWith('.')) {
137
+ results.push(...scanDirectory(repoRoot, full, depth + 1, maxDepth, null));
138
+ }
139
+ }
140
+ catch { /* ignore */ }
141
+ }
142
+ }
143
+ catch { /* ignore */ }
144
+ return results;
145
+ }
146
+ if (!isIncludable(currentPath))
147
+ return [];
148
+ const systemId = toSystemId(relPath);
149
+ const domain = detectDomain(relPath);
150
+ // Detect description from package.json if available
151
+ let description = `System at ${relPath}`;
152
+ const pkgPath = (0, path_1.join)(currentPath, 'package.json');
153
+ if ((0, fs_1.existsSync)(pkgPath)) {
154
+ try {
155
+ const pkg = JSON.parse((0, fs_1.readFileSync)(pkgPath, 'utf-8'));
156
+ if (pkg.description)
157
+ description = pkg.description;
158
+ }
159
+ catch { /* ignore */ }
160
+ }
161
+ const entry = {
162
+ system_id: systemId,
163
+ name: toHumanName(relPath),
164
+ description,
165
+ directory_path: relPath,
166
+ domain,
167
+ status: 'active',
168
+ parent_system_id: parentId,
169
+ metadata: {},
170
+ tags: [domain],
171
+ aliases: [],
172
+ };
173
+ results.push(entry);
174
+ // Scan subdirectories for nested systems
175
+ const topLevel = relPath.split('/')[0];
176
+ const isNestedContainer = CONTAINER_DIRS.has(topLevel);
177
+ if (isNestedContainer && depth <= 3) {
178
+ try {
179
+ const entries = (0, fs_1.readdirSync)(currentPath);
180
+ for (const e of entries) {
181
+ const full = (0, path_1.join)(currentPath, e);
182
+ try {
183
+ if ((0, fs_1.statSync)(full).isDirectory() && !EXCLUDED_DIRS.has(e) && !e.startsWith('.')) {
184
+ results.push(...scanDirectory(repoRoot, full, depth + 1, maxDepth, systemId));
185
+ }
186
+ }
187
+ catch { /* ignore */ }
188
+ }
189
+ }
190
+ catch { /* ignore */ }
191
+ }
192
+ return results;
193
+ }
194
+ // ── Boundary Precedence ──────────────────────────────────────────────────
195
+ function assignParentSystems(systems) {
196
+ const sorted = [...systems].sort((a, b) => {
197
+ const depthA = a.directory_path.split('/').length;
198
+ const depthB = b.directory_path.split('/').length;
199
+ if (depthA !== depthB)
200
+ return depthA - depthB;
201
+ return a.system_id.localeCompare(b.system_id);
202
+ });
203
+ for (let i = 0; i < sorted.length; i++) {
204
+ const system = sorted[i];
205
+ if (system.parent_system_id)
206
+ continue;
207
+ let nearestParent = null;
208
+ let nearestDepth = -1;
209
+ for (let j = 0; j < sorted.length; j++) {
210
+ if (i === j)
211
+ continue;
212
+ const candidate = sorted[j];
213
+ const candidateDepth = candidate.directory_path.split('/').length;
214
+ if (system.directory_path.startsWith(candidate.directory_path + '/') &&
215
+ candidateDepth > nearestDepth) {
216
+ nearestParent = candidate;
217
+ nearestDepth = candidateDepth;
218
+ }
219
+ }
220
+ if (nearestParent) {
221
+ system.parent_system_id = nearestParent.system_id;
222
+ }
223
+ }
224
+ return sorted;
225
+ }
226
+ // ── Load API key from config ─────────────────────────────────────────────
227
+ function loadApiKey() {
228
+ try {
229
+ if ((0, fs_1.existsSync)(platform_js_1.EKKOS_CONFIG)) {
230
+ const config = JSON.parse((0, fs_1.readFileSync)(platform_js_1.EKKOS_CONFIG, 'utf-8'));
231
+ return config.apiKey || null;
232
+ }
233
+ }
234
+ catch { /* ignore */ }
235
+ // Fall back to env var
236
+ return process.env.EKKOS_API_KEY || null;
237
+ }
238
+ // ── Detect git repo root ─────────────────────────────────────────────────
239
+ function findGitRoot(startPath) {
240
+ let current = (0, path_1.resolve)(startPath);
241
+ while (current !== '/') {
242
+ if ((0, fs_1.existsSync)((0, path_1.join)(current, '.git'))) {
243
+ return current;
244
+ }
245
+ const parent = (0, path_1.resolve)(current, '..');
246
+ if (parent === current)
247
+ break;
248
+ current = parent;
249
+ }
250
+ return null;
251
+ }
252
+ // ── Main scan command ────────────────────────────────────────────────────
253
+ async function scan(options) {
254
+ const startTime = Date.now();
255
+ const targetPath = (0, path_1.resolve)(options.path || process.cwd());
256
+ const isDryRun = options.dryRun ?? false;
257
+ const shouldCompile = options.compile ?? false;
258
+ console.log('');
259
+ console.log(chalk_1.default.cyan.bold(' ekkOS Scan'));
260
+ console.log(chalk_1.default.gray(' ─'.repeat(25)));
261
+ console.log('');
262
+ // Check if in a git repo
263
+ const gitRoot = findGitRoot(targetPath);
264
+ const repoRoot = gitRoot || targetPath;
265
+ if (gitRoot) {
266
+ console.log(chalk_1.default.gray(` Git root: ${gitRoot}`));
267
+ }
268
+ else {
269
+ console.log(chalk_1.default.yellow(` No git repo found — scanning ${targetPath}`));
270
+ }
271
+ console.log('');
272
+ // Phase 1: Scan repo structure
273
+ const scanSpinner = (0, ora_1.default)('Scanning repo structure...').start();
274
+ let systems;
275
+ try {
276
+ const rawSystems = scanDirectory(repoRoot, repoRoot, 0, 4, null);
277
+ systems = assignParentSystems(rawSystems);
278
+ scanSpinner.succeed(`Found ${chalk_1.default.bold(systems.length.toString())} systems`);
279
+ }
280
+ catch (err) {
281
+ scanSpinner.fail('Scan failed');
282
+ console.error(chalk_1.default.red(` ${err instanceof Error ? err.message : err}`));
283
+ process.exit(1);
284
+ }
285
+ // Show discovered systems
286
+ if (systems.length > 0) {
287
+ console.log('');
288
+ // Group by domain
289
+ const byDomain = new Map();
290
+ for (const s of systems) {
291
+ const group = byDomain.get(s.domain) || [];
292
+ group.push(s);
293
+ byDomain.set(s.domain, group);
294
+ }
295
+ for (const [domain, entries] of byDomain) {
296
+ console.log(chalk_1.default.cyan(` ${domain}`));
297
+ for (const s of entries) {
298
+ const indent = s.parent_system_id ? ' ' : ' ';
299
+ const arrow = s.parent_system_id ? '└─' : '──';
300
+ console.log(chalk_1.default.gray(`${indent}${arrow} `) + chalk_1.default.white(s.system_id) + chalk_1.default.gray(` → ${s.directory_path}`));
301
+ }
302
+ }
303
+ console.log('');
304
+ }
305
+ // Dry run — stop here
306
+ if (isDryRun) {
307
+ console.log(chalk_1.default.yellow(' Dry run — no changes made'));
308
+ const duration = ((Date.now() - startTime) / 1000).toFixed(1);
309
+ console.log(chalk_1.default.gray(` Done in ${duration}s`));
310
+ console.log('');
311
+ return;
312
+ }
313
+ // Phase 2: Load API key
314
+ const apiKey = loadApiKey();
315
+ if (!apiKey) {
316
+ console.log(chalk_1.default.red(' No API key found.'));
317
+ console.log(chalk_1.default.gray(' Run `ekkos init` first, or set EKKOS_API_KEY env var.'));
318
+ console.log('');
319
+ process.exit(1);
320
+ }
321
+ // Phase 3: Seed registry via API
322
+ const seedSpinner = (0, ora_1.default)('Seeding registry...').start();
323
+ try {
324
+ const apiUrl = process.env.EKKOS_API_URL || platform_js_1.MCP_API_URL;
325
+ const response = await fetch(`${apiUrl}/api/v1/living-docs/seed`, {
326
+ method: 'POST',
327
+ headers: {
328
+ 'Authorization': `Bearer ${apiKey}`,
329
+ 'Content-Type': 'application/json',
330
+ },
331
+ body: JSON.stringify({
332
+ systems,
333
+ compile: shouldCompile,
334
+ }),
335
+ });
336
+ if (!response.ok) {
337
+ const errBody = await response.text();
338
+ seedSpinner.fail('Seed failed');
339
+ console.error(chalk_1.default.red(` API returned ${response.status}: ${errBody}`));
340
+ process.exit(1);
341
+ }
342
+ const result = await response.json();
343
+ if (!result.ok) {
344
+ seedSpinner.fail('Seed failed');
345
+ console.error(chalk_1.default.red(` ${result.error || 'Unknown error'}`));
346
+ process.exit(1);
347
+ }
348
+ seedSpinner.succeed(`Seeded ${chalk_1.default.bold(result.total.toString())} systems` +
349
+ (result.inserted > 0 ? chalk_1.default.green(` (${result.inserted} new)`) : '') +
350
+ (result.updated > 0 ? chalk_1.default.gray(` (${result.updated} updated)`) : ''));
351
+ // Show any errors
352
+ if (result.errors && result.errors.length > 0) {
353
+ console.log(chalk_1.default.yellow(` ${result.errors.length} errors:`));
354
+ for (const e of result.errors.slice(0, 5)) {
355
+ console.log(chalk_1.default.gray(` - ${e}`));
356
+ }
357
+ if (result.errors.length > 5) {
358
+ console.log(chalk_1.default.gray(` ... and ${result.errors.length - 5} more`));
359
+ }
360
+ }
361
+ // Compile pass result
362
+ if (result.compile) {
363
+ if (result.compile.triggered) {
364
+ console.log(chalk_1.default.green(' Compile pass triggered'));
365
+ }
366
+ else {
367
+ console.log(chalk_1.default.yellow(` Compile skipped: ${result.compile.reason || result.compile.error || 'unknown'}`));
368
+ }
369
+ }
370
+ }
371
+ catch (err) {
372
+ seedSpinner.fail('Seed failed');
373
+ if (err instanceof TypeError && err.cause?.code === 'ECONNREFUSED') {
374
+ console.error(chalk_1.default.red(' Could not connect to ekkOS API. Is it running?'));
375
+ }
376
+ else {
377
+ console.error(chalk_1.default.red(` ${err instanceof Error ? err.message : err}`));
378
+ }
379
+ process.exit(1);
380
+ }
381
+ // Summary
382
+ const duration = ((Date.now() - startTime) / 1000).toFixed(1);
383
+ console.log('');
384
+ console.log(chalk_1.default.green(` Done in ${duration}s`));
385
+ console.log('');
386
+ }
@@ -1 +1,4 @@
1
- export declare function status(): Promise<void>;
1
+ export declare function status(opts?: {
2
+ watch?: boolean;
3
+ json?: boolean;
4
+ }): Promise<void>;
@@ -11,40 +11,108 @@ const chalk_1 = __importDefault(require("chalk"));
11
11
  const ora_1 = __importDefault(require("ora"));
12
12
  const EKKOS_API_URL = 'https://mcp.ekkos.dev';
13
13
  const CONFIG_FILE = (0, path_1.join)((0, os_1.homedir)(), '.ekkos', 'config.json');
14
- async function status() {
15
- console.log('');
16
- console.log(chalk_1.default.cyan.bold('📊 ekkOS Memory Status'));
17
- console.log(chalk_1.default.gray('─'.repeat(50)));
18
- console.log('');
19
- // Check config exists
20
- if (!(0, fs_1.existsSync)(CONFIG_FILE)) {
21
- console.log(chalk_1.default.red('✗ Not configured'));
22
- console.log(chalk_1.default.gray(' Run: npx @ekkos/cli setup'));
23
- process.exit(1);
24
- }
25
- let config;
14
+ const METRICS_FILE = (0, path_1.join)((0, os_1.homedir)(), '.ekkos', 'session-metrics.json');
15
+ /** Age (ms) after which the metrics file is considered stale (no active session). */
16
+ const METRICS_STALE_MS = 5 * 60 * 1000; // 5 minutes
17
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
18
+ function readMetrics() {
19
+ if (!(0, fs_1.existsSync)(METRICS_FILE))
20
+ return null;
26
21
  try {
27
- config = JSON.parse((0, fs_1.readFileSync)(CONFIG_FILE, 'utf-8'));
22
+ return JSON.parse((0, fs_1.readFileSync)(METRICS_FILE, 'utf-8'));
28
23
  }
29
24
  catch {
30
- console.log(chalk_1.default.red('✗ Invalid configuration'));
31
- process.exit(1);
25
+ return null;
32
26
  }
33
- const apiKey = config.apiKey || process.env.EKKOS_API_KEY;
34
- if (!apiKey) {
35
- console.log(chalk_1.default.red('✗ No API key'));
36
- process.exit(1);
27
+ }
28
+ function isMetricsStale(m) {
29
+ const updatedAt = new Date(m.updated_at).getTime();
30
+ return Date.now() - updatedAt > METRICS_STALE_MS;
31
+ }
32
+ function formatDuration(isoStart) {
33
+ const ms = Date.now() - new Date(isoStart).getTime();
34
+ const totalSec = Math.floor(ms / 1000);
35
+ const h = Math.floor(totalSec / 3600);
36
+ const m = Math.floor((totalSec % 3600) / 60);
37
+ const s = totalSec % 60;
38
+ if (h > 0)
39
+ return `${h}h ${m}m`;
40
+ if (m > 0)
41
+ return `${m}m ${s}s`;
42
+ return `${s}s`;
43
+ }
44
+ function formatTokens(n) {
45
+ if (n >= 1000000)
46
+ return `${(n / 1000000).toFixed(1)}M`;
47
+ if (n >= 1000)
48
+ return `${Math.round(n / 1000)}K`;
49
+ return String(n);
50
+ }
51
+ // ─── Session panel ────────────────────────────────────────────────────────────
52
+ function printSessionPanel(m) {
53
+ const sessionLabel = m.session_name || m.session_id;
54
+ const duration = formatDuration(m.started_at);
55
+ const turnLabel = m.turn > 0 ? `Turn ${m.turn}` : `${m.tool_calls} calls`;
56
+ const lines = [
57
+ `ekkOS Session: ${sessionLabel}`,
58
+ `Duration: ${duration} · ${turnLabel}`,
59
+ null, // separator
60
+ `Patterns recalled: ${m.patterns_recalled}`,
61
+ `Patterns forged: ${m.patterns_forged}`,
62
+ `Directives active: ${m.directives_active}`,
63
+ `Cache: ${m.cache_backend} (${m.cache_patterns} patterns)`,
64
+ null, // separator
65
+ `Tokens: ${formatTokens(m.tokens_in)} in · ${formatTokens(m.tokens_out)} out`,
66
+ ];
67
+ // Compute box width from longest content line
68
+ const contentLines = lines.filter(Boolean);
69
+ const maxLen = Math.max(...contentLines.map(l => l.length));
70
+ const innerWidth = Math.max(39, maxLen + 4); // 2 spaces padding each side
71
+ const pad = (text) => {
72
+ const spaces = innerWidth - text.length - 2;
73
+ return ` ${text}${' '.repeat(Math.max(0, spaces))}`;
74
+ };
75
+ const top = `┌${'─'.repeat(innerWidth)}┐`;
76
+ const sep = `├${'─'.repeat(innerWidth)}┤`;
77
+ const bottom = `└${'─'.repeat(innerWidth)}┘`;
78
+ const row = (text) => `│${pad(text)}│`;
79
+ const sepRow = sep;
80
+ console.log('');
81
+ console.log(chalk_1.default.cyan(top));
82
+ let firstSep = false;
83
+ for (const line of lines) {
84
+ if (line === null) {
85
+ // Separator after header block, then again before token block
86
+ if (!firstSep) {
87
+ console.log(chalk_1.default.cyan(sepRow));
88
+ firstSep = true;
89
+ }
90
+ else {
91
+ console.log(chalk_1.default.cyan(sepRow));
92
+ }
93
+ }
94
+ else if (!firstSep) {
95
+ // Header rows — bold
96
+ console.log(chalk_1.default.cyan('│') + chalk_1.default.bold(pad(line)) + chalk_1.default.cyan('│'));
97
+ }
98
+ else {
99
+ console.log(chalk_1.default.cyan(row(line)));
100
+ }
37
101
  }
102
+ console.log(chalk_1.default.cyan(bottom));
103
+ console.log('');
104
+ }
105
+ // ─── Memory API stats (original status content) ───────────────────────────────
106
+ async function printMemoryStats(apiKey, config) {
38
107
  const spinner = (0, ora_1.default)('Fetching memory stats...').start();
39
108
  try {
40
- // Get pattern stats
41
109
  const patternsResponse = await fetch(`${EKKOS_API_URL}/api/v1/patterns/query`, {
42
110
  method: 'POST',
43
111
  headers: {
44
112
  'Authorization': `Bearer ${apiKey}`,
45
- 'Content-Type': 'application/json'
113
+ 'Content-Type': 'application/json',
46
114
  },
47
- body: JSON.stringify({ query: '', k: 100 })
115
+ body: JSON.stringify({ query: '', k: 100 }),
48
116
  });
49
117
  let patternCount = 0;
50
118
  let avgSuccessRate = 0;
@@ -60,13 +128,11 @@ async function status() {
60
128
  }
61
129
  }
62
130
  spinner.stop();
63
- // Display stats
64
131
  console.log(chalk_1.default.cyan('Patterns:'));
65
132
  console.log(` Total: ${chalk_1.default.bold(patternCount)}`);
66
133
  console.log(` Success Rate: ${chalk_1.default.bold((avgSuccessRate * 100).toFixed(1) + '%')}`);
67
134
  console.log(` Applications: ${chalk_1.default.bold(totalApplications)}`);
68
135
  console.log('');
69
- // IDE status
70
136
  console.log(chalk_1.default.cyan('Connected IDEs:'));
71
137
  const ides = config.installedIDEs || [];
72
138
  if (ides.length === 0) {
@@ -78,13 +144,11 @@ async function status() {
78
144
  }
79
145
  }
80
146
  console.log('');
81
- // Config info
82
147
  console.log(chalk_1.default.cyan('Configuration:'));
83
148
  console.log(` Config File: ${chalk_1.default.gray(CONFIG_FILE)}`);
84
149
  console.log(` Installed: ${chalk_1.default.gray(config.installedAt || 'Unknown')}`);
85
150
  console.log(` API Key: ${chalk_1.default.gray(apiKey.substring(0, 10) + '...')}`);
86
151
  console.log('');
87
- // Golden Loop status
88
152
  console.log(chalk_1.default.cyan('Golden Loop:'));
89
153
  const loopActive = patternCount > 0 || totalApplications > 0;
90
154
  if (loopActive) {
@@ -95,7 +159,6 @@ async function status() {
95
159
  console.log(chalk_1.default.yellow(' ○ INITIALIZING - Start coding to build memory'));
96
160
  }
97
161
  console.log('');
98
- // Footer
99
162
  console.log(chalk_1.default.gray('─'.repeat(50)));
100
163
  console.log('');
101
164
  console.log(`Dashboard: ${chalk_1.default.cyan('https://ekkos.dev/dashboard')}`);
@@ -107,3 +170,78 @@ async function status() {
107
170
  process.exit(1);
108
171
  }
109
172
  }
173
+ // ─── Single render pass ───────────────────────────────────────────────────────
174
+ function renderOnce(opts) {
175
+ const metrics = readMetrics();
176
+ if (opts.json) {
177
+ if (!metrics || isMetricsStale(metrics)) {
178
+ console.log(JSON.stringify({ active: false, reason: 'No active session or session stale' }));
179
+ }
180
+ else {
181
+ console.log(JSON.stringify({ active: true, ...metrics }));
182
+ }
183
+ return;
184
+ }
185
+ if (!metrics || isMetricsStale(metrics)) {
186
+ console.log('');
187
+ console.log(chalk_1.default.gray(' No active ekkOS session detected.'));
188
+ console.log(chalk_1.default.gray(' Run `ekkos run` to start a session.'));
189
+ console.log('');
190
+ return;
191
+ }
192
+ printSessionPanel(metrics);
193
+ }
194
+ // ─── Main export ──────────────────────────────────────────────────────────────
195
+ async function status(opts = {}) {
196
+ // Watch mode: render every 2s, clear between renders
197
+ if (opts.watch) {
198
+ // Initial render
199
+ process.stdout.write('\x1B[2J\x1B[0f'); // clear screen
200
+ renderOnce({ json: false, skipMemory: true });
201
+ setInterval(() => {
202
+ process.stdout.write('\x1B[2J\x1B[0f');
203
+ renderOnce({ json: false, skipMemory: true });
204
+ }, 2000);
205
+ // Keep alive until Ctrl-C
206
+ return;
207
+ }
208
+ // JSON mode: just dump the metrics file
209
+ if (opts.json) {
210
+ renderOnce({ json: true, skipMemory: false });
211
+ return;
212
+ }
213
+ // Normal mode: show session panel (if active) then memory stats
214
+ console.log('');
215
+ console.log(chalk_1.default.cyan.bold('ekkOS Memory Status'));
216
+ console.log(chalk_1.default.gray('─'.repeat(50)));
217
+ // Session panel (live metrics from MCP server)
218
+ const metrics = readMetrics();
219
+ if (metrics && !isMetricsStale(metrics)) {
220
+ printSessionPanel(metrics);
221
+ }
222
+ else {
223
+ console.log('');
224
+ console.log(chalk_1.default.gray(' No active session'));
225
+ console.log('');
226
+ }
227
+ // Memory API stats (requires config + network)
228
+ if (!(0, fs_1.existsSync)(CONFIG_FILE)) {
229
+ console.log(chalk_1.default.red(' ✗ Not configured — run: ekkos init'));
230
+ console.log('');
231
+ return;
232
+ }
233
+ let config;
234
+ try {
235
+ config = JSON.parse((0, fs_1.readFileSync)(CONFIG_FILE, 'utf-8'));
236
+ }
237
+ catch {
238
+ console.log(chalk_1.default.red(' ✗ Invalid configuration'));
239
+ return;
240
+ }
241
+ const apiKey = config.apiKey || process.env.EKKOS_API_KEY;
242
+ if (!apiKey) {
243
+ console.log(chalk_1.default.red(' ✗ No API key — run: ekkos init'));
244
+ return;
245
+ }
246
+ await printMemoryStats(apiKey, config);
247
+ }