@ekkos/cli 0.2.9 → 0.2.11

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 (44) hide show
  1. package/dist/agent/daemon.d.ts +86 -0
  2. package/dist/agent/daemon.js +297 -0
  3. package/dist/agent/pty-runner.d.ts +51 -0
  4. package/dist/agent/pty-runner.js +184 -0
  5. package/dist/cache/LocalSessionStore.d.ts +34 -21
  6. package/dist/cache/LocalSessionStore.js +169 -53
  7. package/dist/cache/capture.d.ts +19 -11
  8. package/dist/cache/capture.js +243 -76
  9. package/dist/cache/types.d.ts +14 -1
  10. package/dist/commands/agent.d.ts +44 -0
  11. package/dist/commands/agent.js +300 -0
  12. package/dist/commands/doctor.d.ts +10 -0
  13. package/dist/commands/doctor.js +175 -87
  14. package/dist/commands/hooks.d.ts +109 -0
  15. package/dist/commands/hooks.js +668 -0
  16. package/dist/commands/run.d.ts +2 -0
  17. package/dist/commands/run.js +357 -85
  18. package/dist/commands/setup-remote.d.ts +20 -0
  19. package/dist/commands/setup-remote.js +467 -0
  20. package/dist/index.js +116 -1
  21. package/dist/restore/RestoreOrchestrator.d.ts +17 -3
  22. package/dist/restore/RestoreOrchestrator.js +64 -22
  23. package/dist/utils/paths.d.ts +125 -0
  24. package/dist/utils/paths.js +283 -0
  25. package/dist/utils/state.d.ts +2 -0
  26. package/package.json +1 -1
  27. package/templates/ekkos-manifest.json +223 -0
  28. package/templates/helpers/json-parse.cjs +101 -0
  29. package/templates/hooks/assistant-response.ps1 +256 -0
  30. package/templates/hooks/assistant-response.sh +124 -64
  31. package/templates/hooks/session-start.ps1 +107 -2
  32. package/templates/hooks/session-start.sh +201 -166
  33. package/templates/hooks/stop.ps1 +124 -3
  34. package/templates/hooks/stop.sh +470 -843
  35. package/templates/hooks/user-prompt-submit.ps1 +107 -22
  36. package/templates/hooks/user-prompt-submit.sh +403 -393
  37. package/templates/project-stubs/session-start.ps1 +63 -0
  38. package/templates/project-stubs/session-start.sh +55 -0
  39. package/templates/project-stubs/stop.ps1 +63 -0
  40. package/templates/project-stubs/stop.sh +55 -0
  41. package/templates/project-stubs/user-prompt-submit.ps1 +63 -0
  42. package/templates/project-stubs/user-prompt-submit.sh +55 -0
  43. package/templates/shared/hooks-enabled.json +22 -0
  44. package/templates/shared/session-words.json +45 -0
@@ -1,4 +1,14 @@
1
1
  "use strict";
2
+ /**
3
+ * ekkOS CLI: doctor command
4
+ * Checks system prerequisites for ekkOS
5
+ *
6
+ * Per ekkOS Onboarding Spec v1.2 + Addendum:
7
+ * - Node gate: Node >= 18 required (FAIL if missing)
8
+ * - PTY gate: WARN on Windows if missing (monitor-only mode available)
9
+ * - Hooks gate: Verifies hook installation
10
+ * - NO jq checks (jq dependency eliminated)
11
+ */
2
12
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
13
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
14
  };
@@ -6,8 +16,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
16
  exports.runDiagnostics = runDiagnostics;
7
17
  exports.doctor = doctor;
8
18
  const os_1 = require("os");
19
+ const path_1 = require("path");
20
+ const fs_1 = require("fs");
9
21
  const child_process_1 = require("child_process");
10
22
  const chalk_1 = __importDefault(require("chalk"));
23
+ const hooks_1 = require("./hooks");
11
24
  /**
12
25
  * Check if a command exists in PATH
13
26
  */
@@ -65,25 +78,22 @@ function checkPty() {
65
78
  */
66
79
  function checkMcpConfig() {
67
80
  try {
68
- const output = (0, child_process_1.execSync)('claude mcp list', {
81
+ const output = (0, child_process_1.execSync)('claude mcp list 2>&1', {
69
82
  encoding: 'utf-8',
70
- timeout: 10000,
83
+ timeout: 30000, // 30s - health checks can be slow
71
84
  stdio: ['pipe', 'pipe', 'pipe']
72
85
  });
73
- // Parse server names from output
74
- const servers = [];
75
- const lines = output.split('\n');
76
- for (const line of lines) {
77
- // Look for server names (varies by Claude version)
78
- if (line.includes('ekkos') || line.includes('memory')) {
79
- servers.push(line.trim());
80
- }
81
- }
82
- const hasEkkos = output.toLowerCase().includes('ekkos') ||
83
- output.toLowerCase().includes('memory');
84
- return { configured: hasEkkos, servers };
86
+ return parseMcpOutput(output);
85
87
  }
86
88
  catch (error) {
89
+ // Even on timeout/error, stdout may contain partial output
90
+ const partialOutput = error?.stdout || error?.output?.[1] || '';
91
+ if (partialOutput) {
92
+ const result = parseMcpOutput(partialOutput);
93
+ if (result.configured) {
94
+ return result; // Got what we needed despite timeout
95
+ }
96
+ }
87
97
  return {
88
98
  configured: false,
89
99
  servers: [],
@@ -92,18 +102,58 @@ function checkMcpConfig() {
92
102
  }
93
103
  }
94
104
  /**
95
- * Check if bash is available (for hooks on Windows)
105
+ * Parse MCP list output to find ekkOS servers
96
106
  */
97
- function checkBash() {
98
- if ((0, os_1.platform)() !== 'win32')
99
- return true;
100
- try {
101
- (0, child_process_1.execSync)('bash --version', { stdio: 'ignore', timeout: 5000 });
102
- return true;
107
+ function parseMcpOutput(output) {
108
+ const servers = [];
109
+ const lines = output.split('\n');
110
+ for (const line of lines) {
111
+ // Look for server names (varies by Claude version)
112
+ if (line.includes('ekkos') || line.includes('memory')) {
113
+ servers.push(line.trim());
114
+ }
103
115
  }
104
- catch {
105
- return false;
116
+ const hasEkkos = output.toLowerCase().includes('ekkos') ||
117
+ output.toLowerCase().includes('memory');
118
+ return { configured: hasEkkos, servers };
119
+ }
120
+ /**
121
+ * Check hooks installation status
122
+ */
123
+ function checkHooksInstallation() {
124
+ const isWindows = (0, os_1.platform)() === 'win32';
125
+ const manifestData = (0, hooks_1.loadManifest)();
126
+ if (!manifestData) {
127
+ return { installed: false, details: 'Source manifest not found' };
128
+ }
129
+ const platformConfig = manifestData.manifest.platforms[(0, os_1.platform)()];
130
+ const globalHooksDir = (0, hooks_1.expandPath)(platformConfig.globalHooksDir);
131
+ const configDir = (0, hooks_1.expandPath)(platformConfig.configDir);
132
+ // Check installed manifest
133
+ const installedManifestPath = (0, path_1.join)(globalHooksDir, '.ekkos-manifest.json');
134
+ if (!(0, fs_1.existsSync)(installedManifestPath)) {
135
+ return { installed: false, details: 'No installed manifest found' };
136
+ }
137
+ // Check helper
138
+ const helperPath = (0, path_1.join)(configDir, '.helpers', 'json-parse.cjs');
139
+ if (!(0, fs_1.existsSync)(helperPath)) {
140
+ return { installed: false, details: 'json-parse.cjs helper missing' };
106
141
  }
142
+ // Check defaults
143
+ const defaultsPath = (0, path_1.join)(configDir, '.defaults', 'session-words.json');
144
+ if (!(0, fs_1.existsSync)(defaultsPath)) {
145
+ return { installed: false, details: 'Default session words missing' };
146
+ }
147
+ // Check required hooks
148
+ const hookExt = isWindows ? '.ps1' : '.sh';
149
+ const requiredHooks = ['user-prompt-submit', 'stop', 'session-start'];
150
+ for (const hookName of requiredHooks) {
151
+ const hookPath = (0, path_1.join)(globalHooksDir, `${hookName}${hookExt}`);
152
+ if (!(0, fs_1.existsSync)(hookPath)) {
153
+ return { installed: false, details: `${hookName}${hookExt} missing` };
154
+ }
155
+ }
156
+ return { installed: true, details: 'All hooks and helpers installed' };
107
157
  }
108
158
  /**
109
159
  * Run diagnostic checks and return report
@@ -115,11 +165,38 @@ function runDiagnostics() {
115
165
  const nodeVersion = getVersion('node') || 'Not found';
116
166
  const claudeVersion = getVersion('claude');
117
167
  // ═══════════════════════════════════════════════════════════════════════════
118
- // GATE 1: Interactive Claude Works
168
+ // GATE 1: Node.js Version (>= 18 required per spec)
169
+ // ═══════════════════════════════════════════════════════════════════════════
170
+ const nodeChecks = [];
171
+ let nodeGatePass = true;
172
+ const nodeMatch = nodeVersion.match(/v?(\d+)\./);
173
+ const nodeMajor = nodeMatch ? parseInt(nodeMatch[1], 10) : 0;
174
+ const nodeOk = nodeMajor >= 18;
175
+ nodeChecks.push({
176
+ name: 'Node.js version >= 18',
177
+ passed: nodeOk,
178
+ detail: nodeOk
179
+ ? `${nodeVersion} (OK)`
180
+ : `${nodeVersion} (Need Node.js 18 or higher)`
181
+ });
182
+ if (!nodeOk) {
183
+ nodeGatePass = false;
184
+ }
185
+ gates.push({
186
+ id: 'node',
187
+ title: 'Node.js',
188
+ status: nodeGatePass ? 'PASS' : 'FAIL',
189
+ checks: nodeChecks,
190
+ fix: nodeGatePass ? undefined : isWindows
191
+ ? 'winget install OpenJS.NodeJS.LTS'
192
+ : 'Install Node.js 20 or 22 LTS from nodejs.org'
193
+ });
194
+ // ═══════════════════════════════════════════════════════════════════════════
195
+ // GATE 2: Interactive Claude Works
119
196
  // ═══════════════════════════════════════════════════════════════════════════
120
197
  const claudeChecks = [];
121
198
  let claudeGatePass = true;
122
- // Check 1.1: claude command exists
199
+ // Check 2.1: claude command exists
123
200
  const claudeExists = commandExists('claude');
124
201
  claudeChecks.push({
125
202
  name: 'Claude CLI installed',
@@ -128,7 +205,7 @@ function runDiagnostics() {
128
205
  });
129
206
  if (!claudeExists)
130
207
  claudeGatePass = false;
131
- // Check 1.2: claude --version works
208
+ // Check 2.2: claude --version works
132
209
  if (claudeExists) {
133
210
  const versionWorks = claudeVersion !== null;
134
211
  claudeChecks.push({
@@ -139,15 +216,6 @@ function runDiagnostics() {
139
216
  if (!versionWorks)
140
217
  claudeGatePass = false;
141
218
  }
142
- // Check 1.3: On Windows, PTY is required for interactive mode
143
- // This is checked in Gate 2, but we note it here
144
- if (isWindows) {
145
- claudeChecks.push({
146
- name: 'Interactive mode (requires PTY)',
147
- passed: true, // Will be validated in Gate 2
148
- detail: 'See PTY gate below'
149
- });
150
- }
151
219
  gates.push({
152
220
  id: 'interactive-claude',
153
221
  title: 'Interactive Claude',
@@ -156,52 +224,71 @@ function runDiagnostics() {
156
224
  fix: claudeGatePass ? undefined : 'npm install -g @anthropic-ai/claude-code'
157
225
  });
158
226
  // ═══════════════════════════════════════════════════════════════════════════
159
- // GATE 2: PTY Works (Windows-critical)
227
+ // GATE 3: PTY Works (WARN on Windows if missing per spec v1.2 Addendum)
160
228
  // ═══════════════════════════════════════════════════════════════════════════
161
229
  const ptyChecks = [];
162
- let ptyGatePass = true;
163
- // Check Node version (20.x or 22.x recommended)
164
- const nodeMatch = nodeVersion.match(/v?(\d+)\./);
165
- const nodeMajor = nodeMatch ? parseInt(nodeMatch[1], 10) : 0;
166
- const nodeOk = nodeMajor >= 18 && nodeMajor <= 22;
167
- ptyChecks.push({
168
- name: 'Node.js version',
169
- passed: nodeOk,
170
- detail: nodeOk
171
- ? `${nodeVersion} (OK)`
172
- : `${nodeVersion} (Need 20.x or 22.x LTS for PTY support)`
173
- });
174
- if (!nodeOk && isWindows)
175
- ptyGatePass = false;
230
+ let ptyGateStatus = 'PASS';
176
231
  // Check PTY availability
177
232
  const ptyResult = checkPty();
178
233
  ptyChecks.push({
179
234
  name: 'node-pty loadable',
180
235
  passed: ptyResult.available,
181
- detail: ptyResult.available ? 'ConPTY available' : ptyResult.error
236
+ detail: ptyResult.available
237
+ ? isWindows ? 'ConPTY available' : 'PTY available'
238
+ : ptyResult.error
182
239
  });
183
- if (!ptyResult.available && isWindows)
184
- ptyGatePass = false;
185
- // On macOS/Linux, PTY is typically available, mark as PASS with note
186
- if (!isWindows && !ptyResult.available) {
187
- ptyChecks.push({
188
- name: 'Fallback available',
189
- passed: true,
190
- detail: 'Unix script(1) fallback available'
191
- });
192
- ptyGatePass = true; // Unix has fallback
240
+ if (!ptyResult.available) {
241
+ if (isWindows) {
242
+ // Per spec: Windows without PTY is WARN (monitor-only mode available)
243
+ ptyGateStatus = 'WARN';
244
+ ptyChecks.push({
245
+ name: 'Monitor-only mode',
246
+ passed: true,
247
+ detail: 'Auto-continue disabled; manual /clear + /continue required'
248
+ });
249
+ }
250
+ else {
251
+ // Unix has script(1) fallback
252
+ ptyChecks.push({
253
+ name: 'Unix fallback',
254
+ passed: true,
255
+ detail: 'script(1) fallback available'
256
+ });
257
+ ptyGateStatus = 'PASS';
258
+ }
193
259
  }
194
260
  gates.push({
195
261
  id: 'pty',
196
262
  title: 'PTY (Terminal)',
197
- status: ptyGatePass ? 'PASS' : (isWindows ? 'FAIL' : 'WARN'),
263
+ status: ptyGateStatus,
198
264
  checks: ptyChecks,
199
- fix: ptyGatePass ? undefined : isWindows
200
- ? 'npm install node-pty-prebuilt-multiarch OR install VS Build Tools and run: npm rebuild node-pty'
265
+ fix: ptyGateStatus === 'WARN'
266
+ ? 'npm install node-pty-prebuilt-multiarch (optional for auto-continue)'
201
267
  : undefined
202
268
  });
203
269
  // ═══════════════════════════════════════════════════════════════════════════
204
- // GATE 3: MCP Works
270
+ // GATE 4: Hooks Installation (per spec v1.2)
271
+ // ═══════════════════════════════════════════════════════════════════════════
272
+ const hooksChecks = [];
273
+ let hooksGateStatus = 'PASS';
274
+ const hooksResult = checkHooksInstallation();
275
+ hooksChecks.push({
276
+ name: 'ekkOS hooks installed',
277
+ passed: hooksResult.installed,
278
+ detail: hooksResult.details
279
+ });
280
+ if (!hooksResult.installed) {
281
+ hooksGateStatus = 'WARN';
282
+ }
283
+ gates.push({
284
+ id: 'hooks',
285
+ title: 'ekkOS Hooks',
286
+ status: hooksGateStatus,
287
+ checks: hooksChecks,
288
+ fix: hooksGateStatus === 'WARN' ? 'ekkos hooks install --global' : undefined
289
+ });
290
+ // ═══════════════════════════════════════════════════════════════════════════
291
+ // GATE 5: MCP Works
205
292
  // ═══════════════════════════════════════════════════════════════════════════
206
293
  const mcpChecks = [];
207
294
  let mcpGatePass = true;
@@ -227,16 +314,6 @@ function runDiagnostics() {
227
314
  if (!mcpResult.configured)
228
315
  mcpGatePass = false;
229
316
  }
230
- // Check bash on Windows (for hooks, WARN only)
231
- if (isWindows) {
232
- const bashOk = checkBash();
233
- mcpChecks.push({
234
- name: 'Bash available (for hooks)',
235
- passed: bashOk,
236
- detail: bashOk ? 'Git Bash/WSL detected' : 'PowerShell hooks will be used'
237
- });
238
- // Don't fail gate for missing bash, just note it
239
- }
240
317
  gates.push({
241
318
  id: 'mcp',
242
319
  title: 'MCP Configuration',
@@ -248,7 +325,6 @@ function runDiagnostics() {
248
325
  // ═══════════════════════════════════════════════════════════════════════════
249
326
  // Calculate overall status
250
327
  // ═══════════════════════════════════════════════════════════════════════════
251
- const allPass = gates.every(g => g.status === 'PASS');
252
328
  const hasBlocker = gates.some(g => g.status === 'FAIL');
253
329
  return {
254
330
  platform: isWindows ? 'Windows' : (0, os_1.platform)() === 'darwin' ? 'macOS' : 'Linux',
@@ -263,8 +339,8 @@ function runDiagnostics() {
263
339
  */
264
340
  function displayReport(report) {
265
341
  console.log('');
266
- console.log(chalk_1.default.cyan.bold('🩺 ekkOS Doctor'));
267
- console.log(chalk_1.default.gray('─'.repeat(60)));
342
+ console.log(chalk_1.default.cyan.bold('ekkOS Doctor'));
343
+ console.log(chalk_1.default.gray('-'.repeat(60)));
268
344
  console.log('');
269
345
  // Platform info
270
346
  console.log(chalk_1.default.gray(`Platform: ${report.platform}`));
@@ -289,13 +365,13 @@ function displayReport(report) {
289
365
  console.log(chalk_1.default.gray(` ${check.detail}`));
290
366
  }
291
367
  }
292
- if (gate.fix && gate.status === 'FAIL') {
368
+ if (gate.fix && (gate.status === 'FAIL' || gate.status === 'WARN')) {
293
369
  console.log(chalk_1.default.yellow(` → Fix: ${gate.fix}`));
294
370
  }
295
371
  console.log('');
296
372
  }
297
373
  // Summary
298
- console.log(chalk_1.default.gray('─'.repeat(60)));
374
+ console.log(chalk_1.default.gray('-'.repeat(60)));
299
375
  const blockers = report.gates.filter(g => g.status === 'FAIL').length;
300
376
  const warnings = report.gates.filter(g => g.status === 'WARN').length;
301
377
  if (blockers > 0) {
@@ -307,7 +383,7 @@ function displayReport(report) {
307
383
  console.log(chalk_1.default.green('ekkOS can run, but some features may be limited.'));
308
384
  }
309
385
  else {
310
- console.log(chalk_1.default.green.bold('āœ“ All systems operational'));
386
+ console.log(chalk_1.default.green.bold('All systems operational'));
311
387
  console.log(chalk_1.default.green('ekkOS is ready to use.'));
312
388
  }
313
389
  console.log('');
@@ -340,7 +416,6 @@ async function attemptAutoFixes(report) {
340
416
  // Try prebuilt PTY first (safe, non-admin)
341
417
  console.log(chalk_1.default.yellow('\nAttempting to install prebuilt PTY...'));
342
418
  try {
343
- // Try homebridge prebuilt first
344
419
  (0, child_process_1.execSync)('npm install -g @homebridge/node-pty-prebuilt-multiarch', {
345
420
  stdio: 'inherit',
346
421
  timeout: 60000
@@ -352,6 +427,19 @@ async function attemptAutoFixes(report) {
352
427
  manual.push('If that fails: Install VS Build Tools, then: npm rebuild node-pty');
353
428
  }
354
429
  break;
430
+ case 'hooks':
431
+ // Try to install hooks
432
+ console.log(chalk_1.default.yellow('\nAttempting to install hooks...'));
433
+ try {
434
+ // Import and call directly
435
+ const { hooksInstall } = require('./hooks');
436
+ await hooksInstall({ global: true, verbose: false });
437
+ fixed.push('Hooks installed');
438
+ }
439
+ catch {
440
+ manual.push('ekkos hooks install --global');
441
+ }
442
+ break;
355
443
  case 'mcp':
356
444
  // Try to configure MCP (safe)
357
445
  console.log(chalk_1.default.yellow('\nAttempting to configure MCP...'));
@@ -388,26 +476,26 @@ async function doctor(options = {}) {
388
476
  displayReport(report);
389
477
  // Fix mode - attempt safe auto-fixes
390
478
  if (options.fix && !report.canProceed) {
391
- console.log(chalk_1.default.cyan.bold('\nšŸ”§ Attempting auto-fixes...\n'));
479
+ console.log(chalk_1.default.cyan.bold('\nAttempting auto-fixes...\n'));
392
480
  const { fixed, manual } = await attemptAutoFixes(report);
393
481
  if (fixed.length > 0) {
394
482
  console.log(chalk_1.default.green('\nāœ“ Auto-fixed:'));
395
483
  for (const f of fixed) {
396
- console.log(chalk_1.default.green(` • ${f}`));
484
+ console.log(chalk_1.default.green(` - ${f}`));
397
485
  }
398
486
  }
399
487
  if (manual.length > 0) {
400
- console.log(chalk_1.default.yellow('\n⚠ Manual steps required:'));
488
+ console.log(chalk_1.default.yellow('\nManual steps required:'));
401
489
  for (const m of manual) {
402
490
  console.log(chalk_1.default.white(` ${m}`));
403
491
  }
404
492
  }
405
493
  // Re-run diagnostics to check if fixes worked
406
- console.log(chalk_1.default.cyan('\nšŸ”„ Re-checking...'));
494
+ console.log(chalk_1.default.cyan('\nRe-checking...'));
407
495
  const recheck = runDiagnostics();
408
496
  displayReport(recheck);
409
497
  if (recheck.canProceed) {
410
- console.log(chalk_1.default.green.bold('āœ“ All issues resolved!'));
498
+ console.log(chalk_1.default.green.bold('All issues resolved!'));
411
499
  }
412
500
  process.exit(recheck.canProceed ? 0 : 1);
413
501
  }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * ekkOS CLI: hooks subcommand
3
+ * Implements: ekkos hooks install | verify | status
4
+ *
5
+ * Per ekkOS Onboarding Spec v1.2 + Addendum:
6
+ * - Manifest-driven deployment
7
+ * - SHA256 checksum verification
8
+ * - Installed-state manifest tracking
9
+ */
10
+ interface ManifestFile {
11
+ source: string;
12
+ destination: string;
13
+ description: string;
14
+ checksum: string | null;
15
+ overwrite?: 'always' | 'createOnly';
16
+ executable?: boolean;
17
+ }
18
+ interface Manifest {
19
+ $schema?: string;
20
+ manifestVersion: string;
21
+ generatedAt: string | null;
22
+ platforms: {
23
+ darwin: {
24
+ configDir: string;
25
+ globalHooksDir: string;
26
+ shell: string;
27
+ };
28
+ linux: {
29
+ configDir: string;
30
+ globalHooksDir: string;
31
+ shell: string;
32
+ };
33
+ win32: {
34
+ configDir: string;
35
+ globalHooksDir: string;
36
+ shell: string;
37
+ };
38
+ };
39
+ files: {
40
+ managed: ManifestFile[];
41
+ helpers: ManifestFile[];
42
+ userEditable: ManifestFile[];
43
+ hooks: {
44
+ bash: ManifestFile[];
45
+ powershell: ManifestFile[];
46
+ lib: ManifestFile[];
47
+ };
48
+ };
49
+ projectStubs?: {
50
+ description?: string;
51
+ bash: ManifestFile[];
52
+ powershell: ManifestFile[];
53
+ };
54
+ cli: {
55
+ minVersion: string;
56
+ legacySupported: boolean;
57
+ requiredCommands: Record<string, string[]>;
58
+ };
59
+ validation: {
60
+ requiredFiles: string[];
61
+ checksumAlgorithm: string;
62
+ };
63
+ }
64
+ interface VerifyResult {
65
+ status: 'PASS' | 'WARN' | 'FAIL';
66
+ issues: Array<{
67
+ severity: 'error' | 'warning';
68
+ file?: string;
69
+ message: string;
70
+ }>;
71
+ }
72
+ declare function expandPath(p: string): string;
73
+ /**
74
+ * Find the ekkos-manifest.json file
75
+ * Search order per spec:
76
+ * 1. <packageRoot>/templates/ekkos-manifest.json
77
+ * 2. <distRoot>/../templates/ekkos-manifest.json
78
+ * 3. In monorepo dev: <repoRoot>/templates/ekkos-manifest.json
79
+ */
80
+ export declare function findManifest(): {
81
+ path: string;
82
+ templatesDir: string;
83
+ } | null;
84
+ /**
85
+ * Load manifest from disk
86
+ */
87
+ export declare function loadManifest(): {
88
+ manifest: Manifest;
89
+ path: string;
90
+ templatesDir: string;
91
+ } | null;
92
+ declare function findProjectRoot(startDir?: string): string;
93
+ interface InstallOptions {
94
+ global?: boolean;
95
+ project?: boolean;
96
+ verbose?: boolean;
97
+ }
98
+ export declare function hooksInstall(options: InstallOptions): Promise<void>;
99
+ interface VerifyOptions {
100
+ global?: boolean;
101
+ project?: boolean;
102
+ verbose?: boolean;
103
+ }
104
+ export declare function hooksVerify(options: VerifyOptions): Promise<VerifyResult>;
105
+ interface StatusOptions {
106
+ verbose?: boolean;
107
+ }
108
+ export declare function hooksStatus(options: StatusOptions): Promise<void>;
109
+ export { findProjectRoot, expandPath };