@ekkos/cli 1.2.17 → 1.3.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/cache/capture.js +0 -0
- package/dist/commands/dashboard.js +57 -49
- package/dist/commands/hooks.d.ts +25 -36
- package/dist/commands/hooks.js +43 -615
- package/dist/commands/init.js +7 -23
- package/dist/commands/run.js +97 -11
- package/dist/commands/setup.js +10 -352
- package/dist/deploy/hooks.d.ts +8 -5
- package/dist/deploy/hooks.js +12 -105
- package/dist/deploy/settings.d.ts +8 -2
- package/dist/deploy/settings.js +22 -51
- package/dist/index.js +17 -39
- package/dist/utils/state.js +7 -2
- package/package.json +1 -1
- package/templates/CLAUDE.md +82 -292
- package/templates/cursor-rules/ekkos-memory.md +48 -108
- package/templates/windsurf-rules/ekkos-memory.md +62 -64
- package/templates/cursor-hooks/after-agent-response.sh +0 -117
- package/templates/cursor-hooks/before-submit-prompt.sh +0 -419
- package/templates/cursor-hooks/hooks.json +0 -20
- package/templates/cursor-hooks/lib/contract.sh +0 -320
- package/templates/cursor-hooks/stop.sh +0 -75
- package/templates/hooks/assistant-response.ps1 +0 -256
- package/templates/hooks/assistant-response.sh +0 -160
- package/templates/hooks/hooks.json +0 -40
- package/templates/hooks/lib/contract.sh +0 -332
- package/templates/hooks/lib/count-tokens.cjs +0 -86
- package/templates/hooks/lib/ekkos-reminders.sh +0 -98
- package/templates/hooks/lib/state.sh +0 -210
- package/templates/hooks/session-start.ps1 +0 -146
- package/templates/hooks/session-start.sh +0 -353
- package/templates/hooks/stop.ps1 +0 -349
- package/templates/hooks/stop.sh +0 -382
- package/templates/hooks/user-prompt-submit.ps1 +0 -419
- package/templates/hooks/user-prompt-submit.sh +0 -516
- package/templates/project-stubs/session-start.ps1 +0 -63
- package/templates/project-stubs/session-start.sh +0 -55
- package/templates/project-stubs/stop.ps1 +0 -63
- package/templates/project-stubs/stop.sh +0 -55
- package/templates/project-stubs/user-prompt-submit.ps1 +0 -63
- package/templates/project-stubs/user-prompt-submit.sh +0 -55
- package/templates/windsurf-hooks/README.md +0 -212
- package/templates/windsurf-hooks/hooks.json +0 -17
- package/templates/windsurf-hooks/install.sh +0 -148
- package/templates/windsurf-hooks/lib/contract.sh +0 -322
- package/templates/windsurf-hooks/post-cascade-response.sh +0 -251
- package/templates/windsurf-hooks/pre-user-prompt.sh +0 -435
package/dist/commands/hooks.js
CHANGED
|
@@ -1,77 +1,63 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* ekkOS CLI: hooks subcommand
|
|
4
|
-
* Implements: ekkos hooks install | verify | status
|
|
3
|
+
* ekkOS CLI: hooks subcommand — DEPRECATED
|
|
5
4
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* Hooks are no longer needed. ekkOS CLI and proxy handle everything.
|
|
6
|
+
* Use `ekkos run` to start.
|
|
7
|
+
*
|
|
8
|
+
* This command is kept for backwards compatibility. The install/verify/status
|
|
9
|
+
* subcommands print a deprecation notice and exit cleanly. Utility functions
|
|
10
|
+
* (findProjectRoot, expandPath, loadManifest) are preserved for internal use
|
|
11
|
+
* by other commands (e.g. doctor).
|
|
10
12
|
*/
|
|
11
13
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
14
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
15
|
};
|
|
14
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.expandPath = expandPath;
|
|
18
|
+
exports.findProjectRoot = findProjectRoot;
|
|
15
19
|
exports.findManifest = findManifest;
|
|
16
20
|
exports.loadManifest = loadManifest;
|
|
17
21
|
exports.hooksInstall = hooksInstall;
|
|
18
22
|
exports.hooksVerify = hooksVerify;
|
|
19
23
|
exports.hooksStatus = hooksStatus;
|
|
20
|
-
exports.findProjectRoot = findProjectRoot;
|
|
21
|
-
exports.expandPath = expandPath;
|
|
22
24
|
const fs_1 = require("fs");
|
|
23
25
|
const path_1 = require("path");
|
|
24
26
|
const os_1 = require("os");
|
|
25
|
-
const crypto_1 = require("crypto");
|
|
26
27
|
const chalk_1 = __importDefault(require("chalk"));
|
|
27
28
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
28
|
-
//
|
|
29
|
+
// UTILITY FUNCTIONS (kept for internal use by doctor.ts etc.)
|
|
29
30
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
30
|
-
const isWindows = (0, os_1.platform)() === 'win32';
|
|
31
31
|
const HOME_DIR = (0, os_1.homedir)();
|
|
32
|
-
// Resolved paths
|
|
33
32
|
function expandPath(p) {
|
|
34
33
|
return p
|
|
35
34
|
.replace(/^~/, HOME_DIR)
|
|
36
35
|
.replace(/%USERPROFILE%/g, HOME_DIR);
|
|
37
36
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
37
|
+
function findProjectRoot(startDir = process.cwd()) {
|
|
38
|
+
let dir = startDir;
|
|
39
|
+
while (dir !== (0, path_1.dirname)(dir)) {
|
|
40
|
+
if ((0, fs_1.existsSync)((0, path_1.join)(dir, '.git')) || (0, fs_1.existsSync)((0, path_1.join)(dir, 'package.json'))) {
|
|
41
|
+
return dir;
|
|
42
|
+
}
|
|
43
|
+
dir = (0, path_1.dirname)(dir);
|
|
44
|
+
}
|
|
45
|
+
return startDir;
|
|
46
|
+
}
|
|
48
47
|
function findManifest() {
|
|
49
|
-
const candidates = [
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
candidates.push(distRoot);
|
|
56
|
-
// 3. Monorepo dev (presence of templates/ at repo root)
|
|
57
|
-
const repoRoot = (0, path_1.join)((0, path_1.dirname)((0, path_1.dirname)((0, path_1.dirname)((0, path_1.dirname)((0, path_1.dirname)(__dirname))))), 'templates', 'ekkos-manifest.json');
|
|
58
|
-
candidates.push(repoRoot);
|
|
59
|
-
// Also check from current __dirname variations
|
|
60
|
-
const fromDirname = (0, path_1.join)(__dirname, '..', '..', 'templates', 'ekkos-manifest.json');
|
|
61
|
-
candidates.push(fromDirname);
|
|
48
|
+
const candidates = [
|
|
49
|
+
(0, path_1.join)((0, path_1.dirname)((0, path_1.dirname)(__dirname)), 'templates', 'ekkos-manifest.json'),
|
|
50
|
+
(0, path_1.join)((0, path_1.dirname)((0, path_1.dirname)((0, path_1.dirname)(__dirname))), 'templates', 'ekkos-manifest.json'),
|
|
51
|
+
(0, path_1.join)((0, path_1.dirname)((0, path_1.dirname)((0, path_1.dirname)((0, path_1.dirname)((0, path_1.dirname)(__dirname))))), 'templates', 'ekkos-manifest.json'),
|
|
52
|
+
(0, path_1.join)(__dirname, '..', '..', 'templates', 'ekkos-manifest.json'),
|
|
53
|
+
];
|
|
62
54
|
for (const candidate of candidates) {
|
|
63
55
|
if ((0, fs_1.existsSync)(candidate)) {
|
|
64
|
-
return {
|
|
65
|
-
path: candidate,
|
|
66
|
-
templatesDir: (0, path_1.dirname)(candidate)
|
|
67
|
-
};
|
|
56
|
+
return { path: candidate, templatesDir: (0, path_1.dirname)(candidate) };
|
|
68
57
|
}
|
|
69
58
|
}
|
|
70
59
|
return null;
|
|
71
60
|
}
|
|
72
|
-
/**
|
|
73
|
-
* Load manifest from disk
|
|
74
|
-
*/
|
|
75
61
|
function loadManifest() {
|
|
76
62
|
const found = findManifest();
|
|
77
63
|
if (!found)
|
|
@@ -86,583 +72,25 @@ function loadManifest() {
|
|
|
86
72
|
}
|
|
87
73
|
}
|
|
88
74
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
89
|
-
//
|
|
90
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
91
|
-
function sha256File(filePath) {
|
|
92
|
-
const content = (0, fs_1.readFileSync)(filePath);
|
|
93
|
-
return (0, crypto_1.createHash)('sha256').update(content).digest('hex');
|
|
94
|
-
}
|
|
95
|
-
function sha256String(content) {
|
|
96
|
-
return (0, crypto_1.createHash)('sha256').update(content).digest('hex');
|
|
97
|
-
}
|
|
98
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
99
|
-
// PROJECT ROOT DETECTION
|
|
75
|
+
// DEPRECATION NOTICE
|
|
100
76
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
101
|
-
function
|
|
102
|
-
let dir = startDir;
|
|
103
|
-
// Walk up looking for .git or package.json
|
|
104
|
-
while (dir !== (0, path_1.dirname)(dir)) {
|
|
105
|
-
if ((0, fs_1.existsSync)((0, path_1.join)(dir, '.git')) || (0, fs_1.existsSync)((0, path_1.join)(dir, 'package.json'))) {
|
|
106
|
-
return dir;
|
|
107
|
-
}
|
|
108
|
-
dir = (0, path_1.dirname)(dir);
|
|
109
|
-
}
|
|
110
|
-
// Fallback to cwd
|
|
111
|
-
return startDir;
|
|
112
|
-
}
|
|
113
|
-
async function hooksInstall(options) {
|
|
114
|
-
const verbose = options.verbose || false;
|
|
115
|
-
const isGlobal = options.global !== false; // Default to global
|
|
116
|
-
const isProject = options.project === true;
|
|
77
|
+
function printDeprecationNotice() {
|
|
117
78
|
console.log('');
|
|
118
|
-
console.log(chalk_1.default.
|
|
119
|
-
console.log(chalk_1.default.gray('-'.repeat(50)));
|
|
79
|
+
console.log(chalk_1.default.yellow.bold(' Hooks are no longer needed'));
|
|
120
80
|
console.log('');
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (!manifestData) {
|
|
124
|
-
console.log(chalk_1.default.red('Error: Could not find ekkos-manifest.json'));
|
|
125
|
-
console.log(chalk_1.default.gray('Searched in:'));
|
|
126
|
-
console.log(chalk_1.default.gray(' - <packageRoot>/templates/'));
|
|
127
|
-
console.log(chalk_1.default.gray(' - <distRoot>/../templates/'));
|
|
128
|
-
console.log(chalk_1.default.gray(' - <repoRoot>/templates/ (monorepo dev)'));
|
|
129
|
-
process.exit(1);
|
|
130
|
-
}
|
|
131
|
-
const { manifest, templatesDir } = manifestData;
|
|
132
|
-
const platformConfig = manifest.platforms[(0, os_1.platform)()];
|
|
133
|
-
if (verbose) {
|
|
134
|
-
console.log(chalk_1.default.gray(`Manifest: ${manifestData.path}`));
|
|
135
|
-
console.log(chalk_1.default.gray(`Templates: ${templatesDir}`));
|
|
136
|
-
console.log(chalk_1.default.gray(`Platform: ${(0, os_1.platform)()}`));
|
|
137
|
-
console.log('');
|
|
138
|
-
}
|
|
139
|
-
const installedFiles = [];
|
|
140
|
-
let errorCount = 0;
|
|
141
|
-
// Resolve config and hooks directories
|
|
142
|
-
const configDir = expandPath(platformConfig.configDir);
|
|
143
|
-
const globalHooksDir = expandPath(platformConfig.globalHooksDir);
|
|
144
|
-
// Create directories
|
|
145
|
-
const defaultsDir = (0, path_1.join)(configDir, '.defaults');
|
|
146
|
-
const helpersDir = (0, path_1.join)(configDir, '.helpers');
|
|
147
|
-
(0, fs_1.mkdirSync)(configDir, { recursive: true });
|
|
148
|
-
(0, fs_1.mkdirSync)(defaultsDir, { recursive: true });
|
|
149
|
-
(0, fs_1.mkdirSync)(helpersDir, { recursive: true });
|
|
150
|
-
(0, fs_1.mkdirSync)(globalHooksDir, { recursive: true });
|
|
151
|
-
(0, fs_1.mkdirSync)((0, path_1.join)(globalHooksDir, 'lib'), { recursive: true });
|
|
152
|
-
if (verbose) {
|
|
153
|
-
console.log(chalk_1.default.gray(`Config dir: ${configDir}`));
|
|
154
|
-
console.log(chalk_1.default.gray(`Hooks dir: ${globalHooksDir}`));
|
|
155
|
-
console.log('');
|
|
156
|
-
}
|
|
157
|
-
// Helper to copy a file
|
|
158
|
-
const copyFile = (source, dest, description, options = {}) => {
|
|
159
|
-
const sourcePath = (0, path_1.join)(templatesDir, source);
|
|
160
|
-
if (!(0, fs_1.existsSync)(sourcePath)) {
|
|
161
|
-
console.log(chalk_1.default.yellow(` SKIP ${(0, path_1.basename)(dest)} - source not found`));
|
|
162
|
-
return false;
|
|
163
|
-
}
|
|
164
|
-
// Check overwrite policy
|
|
165
|
-
if (options.overwrite === 'createOnly' && (0, fs_1.existsSync)(dest)) {
|
|
166
|
-
if (verbose) {
|
|
167
|
-
console.log(chalk_1.default.gray(` SKIP ${(0, path_1.basename)(dest)} - already exists (user-editable)`));
|
|
168
|
-
}
|
|
169
|
-
return true; // Not an error
|
|
170
|
-
}
|
|
171
|
-
try {
|
|
172
|
-
(0, fs_1.mkdirSync)((0, path_1.dirname)(dest), { recursive: true });
|
|
173
|
-
(0, fs_1.copyFileSync)(sourcePath, dest);
|
|
174
|
-
// Make executable on Unix
|
|
175
|
-
if (options.executable && !isWindows) {
|
|
176
|
-
(0, fs_1.chmodSync)(dest, '755');
|
|
177
|
-
}
|
|
178
|
-
const checksum = sha256File(dest);
|
|
179
|
-
installedFiles.push({
|
|
180
|
-
path: dest,
|
|
181
|
-
checksum,
|
|
182
|
-
installedAt: new Date().toISOString()
|
|
183
|
-
});
|
|
184
|
-
console.log(chalk_1.default.green(` ✓ ${(0, path_1.basename)(dest)}`));
|
|
185
|
-
if (verbose) {
|
|
186
|
-
console.log(chalk_1.default.gray(` → ${dest}`));
|
|
187
|
-
}
|
|
188
|
-
return true;
|
|
189
|
-
}
|
|
190
|
-
catch (err) {
|
|
191
|
-
console.log(chalk_1.default.red(` ✗ ${(0, path_1.basename)(dest)} - ${err.message}`));
|
|
192
|
-
errorCount++;
|
|
193
|
-
return false;
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
197
|
-
// GLOBAL INSTALL
|
|
198
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
199
|
-
if (isGlobal) {
|
|
200
|
-
console.log(chalk_1.default.cyan('Installing global hooks...'));
|
|
201
|
-
console.log('');
|
|
202
|
-
// 1. Managed defaults (always overwrite)
|
|
203
|
-
console.log(chalk_1.default.gray('Managed defaults:'));
|
|
204
|
-
for (const file of manifest.files.managed) {
|
|
205
|
-
const dest = (0, path_1.join)(configDir, file.destination);
|
|
206
|
-
copyFile(file.source, dest, file.description, {
|
|
207
|
-
overwrite: 'always'
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
console.log('');
|
|
211
|
-
// 2. Helpers (always overwrite, executable)
|
|
212
|
-
console.log(chalk_1.default.gray('Helpers:'));
|
|
213
|
-
for (const file of manifest.files.helpers) {
|
|
214
|
-
const dest = (0, path_1.join)(configDir, file.destination);
|
|
215
|
-
copyFile(file.source, dest, file.description, {
|
|
216
|
-
executable: file.executable,
|
|
217
|
-
overwrite: 'always'
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
console.log('');
|
|
221
|
-
// 3. User-editable files (create if missing only)
|
|
222
|
-
console.log(chalk_1.default.gray('User-editable (create if missing):'));
|
|
223
|
-
for (const file of manifest.files.userEditable) {
|
|
224
|
-
const dest = (0, path_1.join)(configDir, file.destination);
|
|
225
|
-
copyFile(file.source, dest, file.description, {
|
|
226
|
-
overwrite: 'createOnly'
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
console.log('');
|
|
230
|
-
// 4. Hooks (platform-specific)
|
|
231
|
-
console.log(chalk_1.default.gray('Hooks:'));
|
|
232
|
-
const hookFiles = isWindows ? manifest.files.hooks.powershell : manifest.files.hooks.bash;
|
|
233
|
-
for (const file of hookFiles) {
|
|
234
|
-
const dest = (0, path_1.join)(globalHooksDir, file.destination);
|
|
235
|
-
copyFile(file.source, dest, file.description, {
|
|
236
|
-
executable: file.executable,
|
|
237
|
-
overwrite: 'always'
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
console.log('');
|
|
241
|
-
// 5. Lib files (Unix only)
|
|
242
|
-
if (!isWindows) {
|
|
243
|
-
console.log(chalk_1.default.gray('Hook libraries:'));
|
|
244
|
-
for (const file of manifest.files.hooks.lib) {
|
|
245
|
-
const dest = (0, path_1.join)(globalHooksDir, file.destination);
|
|
246
|
-
copyFile(file.source, dest, file.description, {
|
|
247
|
-
executable: file.executable,
|
|
248
|
-
overwrite: 'always'
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
console.log('');
|
|
252
|
-
}
|
|
253
|
-
// Write installed-state manifest (only on success)
|
|
254
|
-
if (errorCount === 0) {
|
|
255
|
-
const installedManifest = {
|
|
256
|
-
version: manifest.manifestVersion,
|
|
257
|
-
installedAt: new Date().toISOString(),
|
|
258
|
-
installedBy: `@ekkos/cli@${require('../../package.json').version}`,
|
|
259
|
-
sourceManifestChecksum: sha256File(manifestData.path),
|
|
260
|
-
installedFiles
|
|
261
|
-
};
|
|
262
|
-
const installedManifestPath = (0, path_1.join)(globalHooksDir, '.ekkos-manifest.json');
|
|
263
|
-
(0, fs_1.writeFileSync)(installedManifestPath, JSON.stringify(installedManifest, null, 2));
|
|
264
|
-
if (verbose) {
|
|
265
|
-
console.log(chalk_1.default.gray(`Installed manifest: ${installedManifestPath}`));
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
270
|
-
// PROJECT INSTALL
|
|
271
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
272
|
-
if (isProject) {
|
|
273
|
-
const projectRoot = findProjectRoot();
|
|
274
|
-
const projectHooksDir = (0, path_1.join)(projectRoot, '.claude', 'hooks');
|
|
275
|
-
const projectEkkosDir = (0, path_1.join)(projectRoot, '.ekkos');
|
|
276
|
-
console.log(chalk_1.default.cyan(`Installing project hooks for: ${projectRoot}`));
|
|
277
|
-
console.log('');
|
|
278
|
-
(0, fs_1.mkdirSync)(projectHooksDir, { recursive: true });
|
|
279
|
-
(0, fs_1.mkdirSync)(projectEkkosDir, { recursive: true });
|
|
280
|
-
// Use manifest.projectStubs if available, otherwise fallback to scanning directory
|
|
281
|
-
console.log(chalk_1.default.gray('Project stubs:'));
|
|
282
|
-
if (manifest.projectStubs) {
|
|
283
|
-
// Use manifest-driven installation
|
|
284
|
-
const stubFiles = isWindows ? manifest.projectStubs.powershell : manifest.projectStubs.bash;
|
|
285
|
-
for (const file of stubFiles) {
|
|
286
|
-
const sourcePath = (0, path_1.join)(templatesDir, file.source);
|
|
287
|
-
const destPath = (0, path_1.join)(projectHooksDir, file.destination);
|
|
288
|
-
if (!(0, fs_1.existsSync)(sourcePath)) {
|
|
289
|
-
console.log(chalk_1.default.yellow(` SKIP ${file.destination} - source not found`));
|
|
290
|
-
continue;
|
|
291
|
-
}
|
|
292
|
-
try {
|
|
293
|
-
(0, fs_1.copyFileSync)(sourcePath, destPath);
|
|
294
|
-
if (file.executable && !isWindows) {
|
|
295
|
-
(0, fs_1.chmodSync)(destPath, '755');
|
|
296
|
-
}
|
|
297
|
-
console.log(chalk_1.default.green(` ✓ ${file.destination}`));
|
|
298
|
-
installedFiles.push({
|
|
299
|
-
path: destPath,
|
|
300
|
-
checksum: sha256File(destPath),
|
|
301
|
-
installedAt: new Date().toISOString()
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
catch (err) {
|
|
305
|
-
console.log(chalk_1.default.red(` ✗ ${file.destination} - ${err.message}`));
|
|
306
|
-
errorCount++;
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
else {
|
|
311
|
-
// Fallback: scan project-stubs directory
|
|
312
|
-
const stubsDir = (0, path_1.join)(templatesDir, 'project-stubs');
|
|
313
|
-
if ((0, fs_1.existsSync)(stubsDir)) {
|
|
314
|
-
const stubExt = isWindows ? '.ps1' : '.sh';
|
|
315
|
-
const stubFileNames = (0, fs_1.readdirSync)(stubsDir).filter(f => f.endsWith(stubExt));
|
|
316
|
-
for (const stubFile of stubFileNames) {
|
|
317
|
-
const sourcePath = (0, path_1.join)(stubsDir, stubFile);
|
|
318
|
-
const destPath = (0, path_1.join)(projectHooksDir, stubFile);
|
|
319
|
-
try {
|
|
320
|
-
(0, fs_1.copyFileSync)(sourcePath, destPath);
|
|
321
|
-
if (!isWindows) {
|
|
322
|
-
(0, fs_1.chmodSync)(destPath, '755');
|
|
323
|
-
}
|
|
324
|
-
console.log(chalk_1.default.green(` ✓ ${stubFile}`));
|
|
325
|
-
installedFiles.push({
|
|
326
|
-
path: destPath,
|
|
327
|
-
checksum: sha256File(destPath),
|
|
328
|
-
installedAt: new Date().toISOString()
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
catch (err) {
|
|
332
|
-
console.log(chalk_1.default.yellow(` SKIP ${stubFile} - ${err.message}`));
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
else {
|
|
337
|
-
console.log(chalk_1.default.yellow(' No project stubs found - using global hooks'));
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
console.log('');
|
|
341
|
-
// Write project installed manifest
|
|
342
|
-
if (errorCount === 0) {
|
|
343
|
-
const projectInstalledManifest = {
|
|
344
|
-
version: manifest.manifestVersion,
|
|
345
|
-
installedAt: new Date().toISOString(),
|
|
346
|
-
installedBy: `@ekkos/cli@${require('../../package.json').version}`,
|
|
347
|
-
sourceManifestChecksum: sha256File(manifestData.path),
|
|
348
|
-
installedFiles: installedFiles.filter(f => f.path.startsWith(projectRoot))
|
|
349
|
-
};
|
|
350
|
-
const projectManifestPath = (0, path_1.join)(projectEkkosDir, '.ekkos-project-manifest.json');
|
|
351
|
-
(0, fs_1.writeFileSync)(projectManifestPath, JSON.stringify(projectInstalledManifest, null, 2));
|
|
352
|
-
if (verbose) {
|
|
353
|
-
console.log(chalk_1.default.gray(`Project manifest: ${projectManifestPath}`));
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
// Summary
|
|
358
|
-
console.log(chalk_1.default.gray('-'.repeat(50)));
|
|
359
|
-
if (errorCount === 0) {
|
|
360
|
-
console.log(chalk_1.default.green.bold(`✓ ${installedFiles.length} files installed successfully`));
|
|
361
|
-
console.log('');
|
|
362
|
-
console.log(chalk_1.default.gray('Run `ekkos hooks verify` to confirm installation.'));
|
|
363
|
-
}
|
|
364
|
-
else {
|
|
365
|
-
console.log(chalk_1.default.red.bold(`✗ ${errorCount} errors during installation`));
|
|
366
|
-
console.log('');
|
|
367
|
-
console.log(chalk_1.default.yellow('Some files may not have been installed correctly.'));
|
|
368
|
-
process.exit(1);
|
|
369
|
-
}
|
|
370
|
-
console.log('');
|
|
371
|
-
}
|
|
372
|
-
async function hooksVerify(options) {
|
|
373
|
-
const verbose = options.verbose || false;
|
|
374
|
-
const checkGlobal = options.global !== false;
|
|
375
|
-
const checkProject = options.project === true;
|
|
81
|
+
console.log(chalk_1.default.gray(' ekkOS has migrated to a hookless architecture.'));
|
|
82
|
+
console.log(chalk_1.default.gray(' The CLI and proxy now handle everything automatically.'));
|
|
376
83
|
console.log('');
|
|
377
|
-
console.log(chalk_1.default.cyan.bold('
|
|
378
|
-
console.log(chalk_1.default.gray('-'.repeat(50)));
|
|
84
|
+
console.log(chalk_1.default.white(' Use ' + chalk_1.default.cyan.bold('ekkos run') + ' to start Claude Code with ekkOS memory.'));
|
|
379
85
|
console.log('');
|
|
380
|
-
const issues = [];
|
|
381
|
-
// Load source manifest for reference
|
|
382
|
-
const manifestData = loadManifest();
|
|
383
|
-
if (!manifestData) {
|
|
384
|
-
issues.push({
|
|
385
|
-
severity: 'error',
|
|
386
|
-
message: 'Source manifest not found - cannot verify'
|
|
387
|
-
});
|
|
388
|
-
return { status: 'FAIL', issues };
|
|
389
|
-
}
|
|
390
|
-
const { manifest } = manifestData;
|
|
391
|
-
const platformConfig = manifest.platforms[(0, os_1.platform)()];
|
|
392
|
-
const configDir = expandPath(platformConfig.configDir);
|
|
393
|
-
const globalHooksDir = expandPath(platformConfig.globalHooksDir);
|
|
394
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
395
|
-
// GLOBAL VERIFICATION
|
|
396
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
397
|
-
if (checkGlobal) {
|
|
398
|
-
console.log(chalk_1.default.cyan('Verifying global installation...'));
|
|
399
|
-
console.log('');
|
|
400
|
-
// Check installed-state manifest
|
|
401
|
-
const installedManifestPath = (0, path_1.join)(globalHooksDir, '.ekkos-manifest.json');
|
|
402
|
-
if (!(0, fs_1.existsSync)(installedManifestPath)) {
|
|
403
|
-
issues.push({
|
|
404
|
-
severity: 'error',
|
|
405
|
-
file: installedManifestPath,
|
|
406
|
-
message: 'Installed-state manifest not found. Run: ekkos hooks install --global'
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
else {
|
|
410
|
-
try {
|
|
411
|
-
const installedManifest = JSON.parse((0, fs_1.readFileSync)(installedManifestPath, 'utf-8'));
|
|
412
|
-
console.log(chalk_1.default.gray(`Installed version: ${installedManifest.version}`));
|
|
413
|
-
console.log(chalk_1.default.gray(`Installed at: ${installedManifest.installedAt}`));
|
|
414
|
-
console.log(chalk_1.default.gray(`Installed by: ${installedManifest.installedBy}`));
|
|
415
|
-
console.log('');
|
|
416
|
-
// Verify each installed file
|
|
417
|
-
console.log(chalk_1.default.gray('Checking files:'));
|
|
418
|
-
for (const file of installedManifest.installedFiles) {
|
|
419
|
-
if (!(0, fs_1.existsSync)(file.path)) {
|
|
420
|
-
console.log(chalk_1.default.red(` ✗ ${(0, path_1.basename)(file.path)} - missing`));
|
|
421
|
-
issues.push({
|
|
422
|
-
severity: 'error',
|
|
423
|
-
file: file.path,
|
|
424
|
-
message: 'File missing'
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
else {
|
|
428
|
-
const currentChecksum = sha256File(file.path);
|
|
429
|
-
if (currentChecksum !== file.checksum) {
|
|
430
|
-
console.log(chalk_1.default.yellow(` ○ ${(0, path_1.basename)(file.path)} - modified`));
|
|
431
|
-
issues.push({
|
|
432
|
-
severity: 'warning',
|
|
433
|
-
file: file.path,
|
|
434
|
-
message: 'File has been modified since installation'
|
|
435
|
-
});
|
|
436
|
-
}
|
|
437
|
-
else {
|
|
438
|
-
console.log(chalk_1.default.green(` ✓ ${(0, path_1.basename)(file.path)}`));
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
console.log('');
|
|
443
|
-
}
|
|
444
|
-
catch (err) {
|
|
445
|
-
issues.push({
|
|
446
|
-
severity: 'error',
|
|
447
|
-
file: installedManifestPath,
|
|
448
|
-
message: `Invalid installed manifest: ${err.message}`
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
// Check required files
|
|
453
|
-
console.log(chalk_1.default.gray('Required files:'));
|
|
454
|
-
// Helper
|
|
455
|
-
const helperPath = (0, path_1.join)(configDir, '.helpers', 'json-parse.cjs');
|
|
456
|
-
if (!(0, fs_1.existsSync)(helperPath)) {
|
|
457
|
-
console.log(chalk_1.default.red(` ✗ json-parse.cjs - missing`));
|
|
458
|
-
issues.push({
|
|
459
|
-
severity: 'error',
|
|
460
|
-
file: helperPath,
|
|
461
|
-
message: 'JSON parse helper missing. Run: ekkos hooks install --global'
|
|
462
|
-
});
|
|
463
|
-
}
|
|
464
|
-
else {
|
|
465
|
-
console.log(chalk_1.default.green(` ✓ json-parse.cjs`));
|
|
466
|
-
}
|
|
467
|
-
// Defaults
|
|
468
|
-
const defaultsPath = (0, path_1.join)(configDir, '.defaults', 'session-words.json');
|
|
469
|
-
if (!(0, fs_1.existsSync)(defaultsPath)) {
|
|
470
|
-
console.log(chalk_1.default.red(` ✗ session-words.json (defaults) - missing`));
|
|
471
|
-
issues.push({
|
|
472
|
-
severity: 'error',
|
|
473
|
-
file: defaultsPath,
|
|
474
|
-
message: 'Default session words missing. Run: ekkos hooks install --global'
|
|
475
|
-
});
|
|
476
|
-
}
|
|
477
|
-
else {
|
|
478
|
-
console.log(chalk_1.default.green(` ✓ session-words.json (defaults)`));
|
|
479
|
-
}
|
|
480
|
-
// Check EKKOS_MANAGED header in hooks
|
|
481
|
-
console.log('');
|
|
482
|
-
console.log(chalk_1.default.gray('Hook fingerprints:'));
|
|
483
|
-
const hookFiles = isWindows ? manifest.files.hooks.powershell : manifest.files.hooks.bash;
|
|
484
|
-
for (const hookFile of hookFiles) {
|
|
485
|
-
const hookPath = (0, path_1.join)(globalHooksDir, hookFile.destination);
|
|
486
|
-
if ((0, fs_1.existsSync)(hookPath)) {
|
|
487
|
-
const content = (0, fs_1.readFileSync)(hookPath, 'utf-8');
|
|
488
|
-
if (content.includes('EKKOS_MANAGED=1')) {
|
|
489
|
-
console.log(chalk_1.default.green(` ✓ ${(0, path_1.basename)(hookPath)} - managed`));
|
|
490
|
-
}
|
|
491
|
-
else {
|
|
492
|
-
console.log(chalk_1.default.yellow(` ○ ${(0, path_1.basename)(hookPath)} - custom (not managed)`));
|
|
493
|
-
issues.push({
|
|
494
|
-
severity: 'warning',
|
|
495
|
-
file: hookPath,
|
|
496
|
-
message: 'Hook does not have EKKOS_MANAGED=1 fingerprint'
|
|
497
|
-
});
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
else {
|
|
501
|
-
console.log(chalk_1.default.red(` ✗ ${(0, path_1.basename)(hookFile.destination)} - missing`));
|
|
502
|
-
issues.push({
|
|
503
|
-
severity: 'error',
|
|
504
|
-
file: hookPath,
|
|
505
|
-
message: 'Hook file missing'
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
console.log('');
|
|
510
|
-
}
|
|
511
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
512
|
-
// PROJECT VERIFICATION
|
|
513
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
514
|
-
if (checkProject) {
|
|
515
|
-
const projectRoot = findProjectRoot();
|
|
516
|
-
const projectManifestPath = (0, path_1.join)(projectRoot, '.ekkos', '.ekkos-project-manifest.json');
|
|
517
|
-
console.log(chalk_1.default.cyan(`Verifying project installation: ${projectRoot}`));
|
|
518
|
-
console.log('');
|
|
519
|
-
if (!(0, fs_1.existsSync)(projectManifestPath)) {
|
|
520
|
-
issues.push({
|
|
521
|
-
severity: 'warning',
|
|
522
|
-
file: projectManifestPath,
|
|
523
|
-
message: 'No project-level hooks installed (using global)'
|
|
524
|
-
});
|
|
525
|
-
console.log(chalk_1.default.yellow(' No project-level hooks installed'));
|
|
526
|
-
console.log(chalk_1.default.gray(' Using global hooks from ~/.claude/hooks/'));
|
|
527
|
-
}
|
|
528
|
-
else {
|
|
529
|
-
const projectManifest = JSON.parse((0, fs_1.readFileSync)(projectManifestPath, 'utf-8'));
|
|
530
|
-
console.log(chalk_1.default.gray(`Project hooks version: ${projectManifest.version}`));
|
|
531
|
-
for (const file of projectManifest.installedFiles) {
|
|
532
|
-
if (!(0, fs_1.existsSync)(file.path)) {
|
|
533
|
-
console.log(chalk_1.default.red(` ✗ ${(0, path_1.basename)(file.path)} - missing`));
|
|
534
|
-
issues.push({ severity: 'error', file: file.path, message: 'Missing' });
|
|
535
|
-
}
|
|
536
|
-
else {
|
|
537
|
-
console.log(chalk_1.default.green(` ✓ ${(0, path_1.basename)(file.path)}`));
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
console.log('');
|
|
542
|
-
}
|
|
543
|
-
// Summary
|
|
544
|
-
console.log(chalk_1.default.gray('-'.repeat(50)));
|
|
545
|
-
const errors = issues.filter(i => i.severity === 'error').length;
|
|
546
|
-
const warnings = issues.filter(i => i.severity === 'warning').length;
|
|
547
|
-
let status = 'PASS';
|
|
548
|
-
if (errors > 0) {
|
|
549
|
-
status = 'FAIL';
|
|
550
|
-
console.log(chalk_1.default.red.bold(`FAIL: ${errors} error(s), ${warnings} warning(s)`));
|
|
551
|
-
}
|
|
552
|
-
else if (warnings > 0) {
|
|
553
|
-
status = 'WARN';
|
|
554
|
-
console.log(chalk_1.default.yellow.bold(`WARN: ${warnings} warning(s)`));
|
|
555
|
-
}
|
|
556
|
-
else {
|
|
557
|
-
console.log(chalk_1.default.green.bold('PASS: All checks passed'));
|
|
558
|
-
}
|
|
559
|
-
console.log('');
|
|
560
|
-
return { status, issues };
|
|
561
86
|
}
|
|
562
|
-
async function
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
const globalHooksDir = platformConfig ? expandPath(platformConfig.globalHooksDir) : (0, path_1.join)(HOME_DIR, '.claude', 'hooks');
|
|
572
|
-
const projectRoot = findProjectRoot();
|
|
573
|
-
const projectHooksDir = (0, path_1.join)(projectRoot, '.claude', 'hooks');
|
|
574
|
-
// Display directories
|
|
575
|
-
console.log(chalk_1.default.gray('Directories:'));
|
|
576
|
-
console.log(` Config: ${configDir}`);
|
|
577
|
-
console.log(` Global hooks: ${globalHooksDir}`);
|
|
578
|
-
console.log(` Project: ${projectRoot}`);
|
|
579
|
-
console.log('');
|
|
580
|
-
// Check global installation
|
|
581
|
-
console.log(chalk_1.default.cyan('Global Installation:'));
|
|
582
|
-
const globalManifestPath = (0, path_1.join)(globalHooksDir, '.ekkos-manifest.json');
|
|
583
|
-
if ((0, fs_1.existsSync)(globalManifestPath)) {
|
|
584
|
-
const globalManifest = JSON.parse((0, fs_1.readFileSync)(globalManifestPath, 'utf-8'));
|
|
585
|
-
console.log(chalk_1.default.green(` ✓ Installed (v${globalManifest.version})`));
|
|
586
|
-
console.log(chalk_1.default.gray(` Installed: ${new Date(globalManifest.installedAt).toLocaleString()}`));
|
|
587
|
-
console.log(chalk_1.default.gray(` Files: ${globalManifest.installedFiles.length}`));
|
|
588
|
-
}
|
|
589
|
-
else {
|
|
590
|
-
console.log(chalk_1.default.yellow(' ○ Not installed'));
|
|
591
|
-
console.log(chalk_1.default.gray(' Run: ekkos hooks install --global'));
|
|
592
|
-
}
|
|
593
|
-
console.log('');
|
|
594
|
-
// Check hooks enablement
|
|
595
|
-
console.log(chalk_1.default.cyan('Hook Enablement:'));
|
|
596
|
-
const hooksEnabledPath = (0, path_1.join)(configDir, 'hooks-enabled.json');
|
|
597
|
-
if ((0, fs_1.existsSync)(hooksEnabledPath)) {
|
|
598
|
-
try {
|
|
599
|
-
const hooksEnabled = JSON.parse((0, fs_1.readFileSync)(hooksEnabledPath, 'utf-8'));
|
|
600
|
-
const targets = hooksEnabled.targets || {};
|
|
601
|
-
for (const [target, config] of Object.entries(targets)) {
|
|
602
|
-
const hooks = config || {};
|
|
603
|
-
const enabled = Object.entries(hooks)
|
|
604
|
-
.filter(([, v]) => v === true)
|
|
605
|
-
.map(([k]) => k);
|
|
606
|
-
const disabled = Object.entries(hooks)
|
|
607
|
-
.filter(([, v]) => v === false)
|
|
608
|
-
.map(([k]) => k);
|
|
609
|
-
console.log(` ${target}:`);
|
|
610
|
-
if (enabled.length > 0) {
|
|
611
|
-
console.log(chalk_1.default.green(` Enabled: ${enabled.join(', ')}`));
|
|
612
|
-
}
|
|
613
|
-
if (disabled.length > 0) {
|
|
614
|
-
console.log(chalk_1.default.yellow(` Disabled: ${disabled.join(', ')}`));
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
catch {
|
|
619
|
-
console.log(chalk_1.default.yellow(' ○ Could not read hooks-enabled.json'));
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
else {
|
|
623
|
-
console.log(chalk_1.default.gray(' Using defaults (all enabled)'));
|
|
624
|
-
}
|
|
625
|
-
console.log('');
|
|
626
|
-
// List hooks with modification times
|
|
627
|
-
console.log(chalk_1.default.cyan('Installed Hooks:'));
|
|
628
|
-
const hookExt = isWindows ? '.ps1' : '.sh';
|
|
629
|
-
if ((0, fs_1.existsSync)(globalHooksDir)) {
|
|
630
|
-
const hookFiles = (0, fs_1.readdirSync)(globalHooksDir)
|
|
631
|
-
.filter(f => f.endsWith(hookExt))
|
|
632
|
-
.sort();
|
|
633
|
-
for (const hookFile of hookFiles) {
|
|
634
|
-
const hookPath = (0, path_1.join)(globalHooksDir, hookFile);
|
|
635
|
-
const stat = (0, fs_1.statSync)(hookPath);
|
|
636
|
-
const mtime = stat.mtime.toLocaleString();
|
|
637
|
-
const content = (0, fs_1.readFileSync)(hookPath, 'utf-8');
|
|
638
|
-
const isManaged = content.includes('EKKOS_MANAGED=1');
|
|
639
|
-
const icon = isManaged ? chalk_1.default.green('✓') : chalk_1.default.yellow('○');
|
|
640
|
-
const tag = isManaged ? chalk_1.default.gray('[managed]') : chalk_1.default.yellow('[custom]');
|
|
641
|
-
console.log(` ${icon} ${hookFile} ${tag}`);
|
|
642
|
-
if (verbose) {
|
|
643
|
-
console.log(chalk_1.default.gray(` Modified: ${mtime}`));
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
else {
|
|
648
|
-
console.log(chalk_1.default.gray(' No hooks directory found'));
|
|
649
|
-
}
|
|
650
|
-
console.log('');
|
|
651
|
-
// Check project-level hooks
|
|
652
|
-
console.log(chalk_1.default.cyan('Project Hooks:'));
|
|
653
|
-
if ((0, fs_1.existsSync)(projectHooksDir) && projectHooksDir !== globalHooksDir) {
|
|
654
|
-
const projectHookFiles = (0, fs_1.readdirSync)(projectHooksDir).filter(f => f.endsWith(hookExt));
|
|
655
|
-
if (projectHookFiles.length > 0) {
|
|
656
|
-
for (const hookFile of projectHookFiles) {
|
|
657
|
-
console.log(chalk_1.default.green(` ✓ ${hookFile}`));
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
else {
|
|
661
|
-
console.log(chalk_1.default.gray(' Using global hooks'));
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
else {
|
|
665
|
-
console.log(chalk_1.default.gray(' Using global hooks'));
|
|
666
|
-
}
|
|
667
|
-
console.log('');
|
|
87
|
+
async function hooksInstall(_options) {
|
|
88
|
+
printDeprecationNotice();
|
|
89
|
+
}
|
|
90
|
+
async function hooksVerify(_options) {
|
|
91
|
+
printDeprecationNotice();
|
|
92
|
+
return { status: 'PASS', issues: [] };
|
|
93
|
+
}
|
|
94
|
+
async function hooksStatus(_options) {
|
|
95
|
+
printDeprecationNotice();
|
|
668
96
|
}
|