@ekkos/cli 0.2.17 → 0.2.18

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.
@@ -10,6 +10,7 @@ const fs_1 = require("fs");
10
10
  const chalk_1 = __importDefault(require("chalk"));
11
11
  const inquirer_1 = __importDefault(require("inquirer"));
12
12
  const ora_1 = __importDefault(require("ora"));
13
+ const hooks_js_1 = require("./hooks.js");
13
14
  const EKKOS_API_URL = 'https://mcp.ekkos.dev';
14
15
  const CONFIG_DIR = (0, path_1.join)((0, os_1.homedir)(), '.ekkos');
15
16
  const CONFIG_FILE = (0, path_1.join)(CONFIG_DIR, 'config.json');
@@ -207,24 +208,74 @@ async function setupIDE(ide, apiKey, config) {
207
208
  async function setupClaudeCode(apiKey) {
208
209
  const claudeDir = (0, path_1.join)((0, os_1.homedir)(), '.claude');
209
210
  const hooksDir = (0, path_1.join)(claudeDir, 'hooks');
210
- if (!(0, fs_1.existsSync)(hooksDir)) {
211
- (0, fs_1.mkdirSync)(hooksDir, { recursive: true });
211
+ const stateDir = (0, path_1.join)(claudeDir, 'state');
212
+ // Create directories
213
+ (0, fs_1.mkdirSync)(claudeDir, { recursive: true });
214
+ (0, fs_1.mkdirSync)(hooksDir, { recursive: true });
215
+ (0, fs_1.mkdirSync)(stateDir, { recursive: true });
216
+ // Check for existing custom hooks (don't have EKKOS_MANAGED=1 marker)
217
+ const isWindows = (0, os_1.platform)() === 'win32';
218
+ const hookExt = isWindows ? '.ps1' : '.sh';
219
+ const hookFiles = ['user-prompt-submit', 'stop', 'session-start', 'assistant-response'];
220
+ let hasCustomHooks = false;
221
+ for (const hookName of hookFiles) {
222
+ const hookPath = (0, path_1.join)(hooksDir, `${hookName}${hookExt}`);
223
+ if ((0, fs_1.existsSync)(hookPath)) {
224
+ const content = (0, fs_1.readFileSync)(hookPath, 'utf-8');
225
+ if (!content.includes('EKKOS_MANAGED=1')) {
226
+ hasCustomHooks = true;
227
+ break;
228
+ }
229
+ }
230
+ }
231
+ if (hasCustomHooks) {
232
+ // User has custom hooks - don't overwrite, use minimal approach
233
+ console.log(chalk_1.default.yellow(' Detected custom hooks - preserving your hooks'));
234
+ console.log(chalk_1.default.gray(' Run `ekkos hooks install --global` to upgrade to managed hooks'));
235
+ console.log(chalk_1.default.gray(' (This will overwrite existing hooks with full-featured versions)'));
236
+ // Still save API key for existing hooks to use
212
237
  }
238
+ else {
239
+ // No custom hooks OR all managed - safe to install full templates
240
+ try {
241
+ await (0, hooks_js_1.hooksInstall)({ global: true, verbose: false });
242
+ }
243
+ catch (err) {
244
+ // Fallback: if manifest-driven install fails, generate basic hooks
245
+ console.log(chalk_1.default.yellow(' Note: Could not install hooks from templates'));
246
+ console.log(chalk_1.default.gray(' Generating basic hooks instead...'));
247
+ await generateBasicHooks(hooksDir, apiKey);
248
+ }
249
+ }
250
+ // Save API key to config for hooks to use
251
+ const ekkosConfigDir = (0, path_1.join)((0, os_1.homedir)(), '.ekkos');
252
+ (0, fs_1.mkdirSync)(ekkosConfigDir, { recursive: true });
253
+ const configPath = (0, path_1.join)(ekkosConfigDir, 'config.json');
254
+ let existingConfig = {};
255
+ if ((0, fs_1.existsSync)(configPath)) {
256
+ try {
257
+ existingConfig = JSON.parse((0, fs_1.readFileSync)(configPath, 'utf-8'));
258
+ }
259
+ catch { }
260
+ }
261
+ // Update config with API key (hookApiKey for hooks, apiKey for compatibility)
262
+ existingConfig.hookApiKey = apiKey;
263
+ existingConfig.apiKey = apiKey;
264
+ existingConfig.updatedAt = new Date().toISOString();
265
+ (0, fs_1.writeFileSync)(configPath, JSON.stringify(existingConfig, null, 2));
266
+ }
267
+ /**
268
+ * Generate basic inline hooks as fallback when templates aren't available
269
+ */
270
+ async function generateBasicHooks(hooksDir, apiKey) {
213
271
  const isWindows = (0, os_1.platform)() === 'win32';
214
272
  if (isWindows) {
215
- // Windows: PowerShell hooks
216
273
  const promptSubmitHook = generatePromptSubmitHookPS(apiKey);
217
- const promptSubmitPath = (0, path_1.join)(hooksDir, 'user-prompt-submit.ps1');
218
- (0, fs_1.writeFileSync)(promptSubmitPath, promptSubmitHook);
274
+ (0, fs_1.writeFileSync)((0, path_1.join)(hooksDir, 'user-prompt-submit.ps1'), promptSubmitHook);
219
275
  const stopHook = generateStopHookPS(apiKey);
220
- const stopPath = (0, path_1.join)(hooksDir, 'stop.ps1');
221
- (0, fs_1.writeFileSync)(stopPath, stopHook);
222
- // Create wrapper batch files for Windows
223
- (0, fs_1.writeFileSync)((0, path_1.join)(hooksDir, 'user-prompt-submit.cmd'), `@echo off\npowershell -ExecutionPolicy Bypass -File "%~dp0user-prompt-submit.ps1"`);
224
- (0, fs_1.writeFileSync)((0, path_1.join)(hooksDir, 'stop.cmd'), `@echo off\npowershell -ExecutionPolicy Bypass -File "%~dp0stop.ps1"`);
276
+ (0, fs_1.writeFileSync)((0, path_1.join)(hooksDir, 'stop.ps1'), stopHook);
225
277
  }
226
278
  else {
227
- // Unix: Bash hooks
228
279
  const promptSubmitHook = generatePromptSubmitHook(apiKey);
229
280
  const promptSubmitPath = (0, path_1.join)(hooksDir, 'user-prompt-submit.sh');
230
281
  (0, fs_1.writeFileSync)(promptSubmitPath, promptSubmitHook);
@@ -234,11 +285,6 @@ async function setupClaudeCode(apiKey) {
234
285
  (0, fs_1.writeFileSync)(stopPath, stopHook);
235
286
  (0, fs_1.chmodSync)(stopPath, '755');
236
287
  }
237
- // Create state directory
238
- const stateDir = (0, path_1.join)(claudeDir, 'state');
239
- if (!(0, fs_1.existsSync)(stateDir)) {
240
- (0, fs_1.mkdirSync)(stateDir, { recursive: true });
241
- }
242
288
  }
243
289
  async function setupCursor(apiKey) {
244
290
  // Cursor uses .cursorrules for system prompt
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekkos/cli",
3
- "version": "0.2.17",
3
+ "version": "0.2.18",
4
4
  "description": "Setup ekkOS memory for AI coding assistants (Claude Code, Cursor, Windsurf)",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -159,6 +159,12 @@ if [ -z "$RAW_SESSION_ID" ] || [ "$RAW_SESSION_ID" = "unknown" ] || [ "$RAW_SESS
159
159
  if [ -f "$STATE_FILE" ] && [ -f "$JSON_PARSE_HELPER" ]; then
160
160
  RAW_SESSION_ID=$(node "$JSON_PARSE_HELPER" "$STATE_FILE" '.session_id' 2>/dev/null || echo "unknown")
161
161
  fi
162
+
163
+ # VSCode extension fallback: Extract session ID from transcript path
164
+ # Path format: ~/.claude/projects/<project>/<session-uuid>.jsonl
165
+ if [ "$RAW_SESSION_ID" = "unknown" ] && [ -n "$TRANSCRIPT_PATH" ] && [ -f "$TRANSCRIPT_PATH" ]; then
166
+ RAW_SESSION_ID=$(basename "$TRANSCRIPT_PATH" .jsonl)
167
+ fi
162
168
  fi
163
169
 
164
170
  # ═══════════════════════════════════════════════════════════════════════════