@ekkos/cli 1.4.2 → 1.5.1

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.
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ /**
3
+ * stdin-relay.ts — Universal Session Attach (Phase 1)
4
+ *
5
+ * Lightweight HTTP server bound to 127.0.0.1 that accepts remote input
6
+ * and injects it into the running PTY's stdin. The daemon (or any local
7
+ * process with the auth token) can POST text to be typed into Claude.
8
+ *
9
+ * Security:
10
+ * - Binds to 127.0.0.1 only (no network exposure)
11
+ * - Requires a one-time token generated at startup
12
+ * - Token is stored in active-sessions.json (filesystem-permission scoped)
13
+ */
14
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ var desc = Object.getOwnPropertyDescriptor(m, k);
17
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
18
+ desc = { enumerable: true, get: function() { return m[k]; } };
19
+ }
20
+ Object.defineProperty(o, k2, desc);
21
+ }) : (function(o, m, k, k2) {
22
+ if (k2 === undefined) k2 = k;
23
+ o[k2] = m[k];
24
+ }));
25
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
26
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
27
+ }) : function(o, v) {
28
+ o["default"] = v;
29
+ });
30
+ var __importStar = (this && this.__importStar) || (function () {
31
+ var ownKeys = function(o) {
32
+ ownKeys = Object.getOwnPropertyNames || function (o) {
33
+ var ar = [];
34
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
35
+ return ar;
36
+ };
37
+ return ownKeys(o);
38
+ };
39
+ return function (mod) {
40
+ if (mod && mod.__esModule) return mod;
41
+ var result = {};
42
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
43
+ __setModuleDefault(result, mod);
44
+ return result;
45
+ };
46
+ })();
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.startStdinRelay = startStdinRelay;
49
+ const http = __importStar(require("http"));
50
+ const crypto = __importStar(require("crypto"));
51
+ /**
52
+ * Start a stdin relay HTTP server on a random available port.
53
+ *
54
+ * Endpoints:
55
+ * POST /stdin — inject text into the PTY (body: { text: string })
56
+ * GET /health — check if session is alive
57
+ * GET /status — session metadata (attached clients, uptime)
58
+ */
59
+ async function startStdinRelay(options) {
60
+ const { write, onAttach, dlog } = options;
61
+ const token = crypto.randomBytes(24).toString('hex');
62
+ let attachedClients = 0;
63
+ const startTime = Date.now();
64
+ const server = http.createServer((req, res) => {
65
+ // CORS headers for local daemon communication
66
+ res.setHeader('Content-Type', 'application/json');
67
+ // Auth check (skip for health)
68
+ if (req.url !== '/health') {
69
+ const authHeader = req.headers.authorization;
70
+ if (authHeader !== `Bearer ${token}`) {
71
+ res.writeHead(401);
72
+ res.end(JSON.stringify({ error: 'Unauthorized' }));
73
+ return;
74
+ }
75
+ }
76
+ if (req.method === 'GET' && req.url === '/health') {
77
+ res.writeHead(200);
78
+ res.end(JSON.stringify({ alive: true, pid: process.pid }));
79
+ return;
80
+ }
81
+ if (req.method === 'GET' && req.url === '/status') {
82
+ res.writeHead(200);
83
+ res.end(JSON.stringify({
84
+ pid: process.pid,
85
+ uptime_ms: Date.now() - startTime,
86
+ attached_clients: attachedClients,
87
+ }));
88
+ return;
89
+ }
90
+ if (req.method === 'POST' && req.url === '/stdin') {
91
+ let body = '';
92
+ req.on('data', chunk => { body += chunk; });
93
+ req.on('end', () => {
94
+ try {
95
+ const { text } = JSON.parse(body);
96
+ if (typeof text !== 'string') {
97
+ res.writeHead(400);
98
+ res.end(JSON.stringify({ error: 'Missing "text" field' }));
99
+ return;
100
+ }
101
+ write(text);
102
+ dlog?.(`[stdin-relay] Injected ${text.length} chars from remote`);
103
+ res.writeHead(200);
104
+ res.end(JSON.stringify({ ok: true, length: text.length }));
105
+ }
106
+ catch (err) {
107
+ res.writeHead(400);
108
+ res.end(JSON.stringify({ error: 'Invalid JSON body' }));
109
+ }
110
+ });
111
+ return;
112
+ }
113
+ if (req.method === 'POST' && req.url === '/attach') {
114
+ attachedClients++;
115
+ const remoteInfo = req.headers['x-remote-info'] || 'unknown';
116
+ onAttach?.(remoteInfo);
117
+ dlog?.(`[stdin-relay] Remote client attached (${remoteInfo}), total: ${attachedClients}`);
118
+ res.writeHead(200);
119
+ res.end(JSON.stringify({ ok: true, attached_clients: attachedClients }));
120
+ return;
121
+ }
122
+ if (req.method === 'POST' && req.url === '/detach') {
123
+ attachedClients = Math.max(0, attachedClients - 1);
124
+ dlog?.(`[stdin-relay] Remote client detached, total: ${attachedClients}`);
125
+ res.writeHead(200);
126
+ res.end(JSON.stringify({ ok: true, attached_clients: attachedClients }));
127
+ return;
128
+ }
129
+ res.writeHead(404);
130
+ res.end(JSON.stringify({ error: 'Not found' }));
131
+ });
132
+ // Bind to localhost only — no network exposure
133
+ return new Promise((resolve, reject) => {
134
+ server.listen(0, '127.0.0.1', () => {
135
+ const addr = server.address();
136
+ if (!addr || typeof addr === 'string') {
137
+ reject(new Error('Failed to get server address'));
138
+ return;
139
+ }
140
+ const port = addr.port;
141
+ dlog?.(`[stdin-relay] Listening on 127.0.0.1:${port}`);
142
+ resolve({
143
+ port,
144
+ token,
145
+ stop: () => new Promise((resolveStop) => {
146
+ server.close(() => {
147
+ dlog?.('[stdin-relay] Server stopped');
148
+ resolveStop();
149
+ });
150
+ }),
151
+ });
152
+ });
153
+ server.on('error', reject);
154
+ });
155
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekkos/cli",
3
- "version": "1.4.2",
3
+ "version": "1.5.1",
4
4
  "description": "ekkOS memory CLI — persistent memory for AI coding assistants (Claude Code, Gemini, Cursor, Windsurf)",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -41,6 +41,7 @@
41
41
  "license": "UNLICENSED",
42
42
  "dependencies": {
43
43
  "@ekkos/agent": "^0.1.0",
44
+ "@ekkos/prometheus": "workspace:*",
44
45
  "@ekkos/remote": "^0.14.1",
45
46
  "@supabase/supabase-js": "^2.39.8",
46
47
  "blessed": "^0.1.81",
@@ -49,6 +50,7 @@
49
50
  "chalk": "^5.3.0",
50
51
  "commander": "^12.1.0",
51
52
  "inquirer": "^9.2.23",
53
+ "node-notifier": "^10.0.1",
52
54
  "node-pty": "1.2.0-beta.7",
53
55
  "open": "^10.0.0",
54
56
  "ora": "^8.0.1",
@@ -57,6 +59,7 @@
57
59
  "devDependencies": {
58
60
  "@types/blessed": "^0.1.27",
59
61
  "@types/node": "^20.11.0",
62
+ "@types/node-notifier": "^8.0.5",
60
63
  "typescript": "^5.3.3"
61
64
  },
62
65
  "engines": {
@@ -97,7 +97,9 @@ Every system directory contains an auto-generated `ekkOS_CONTEXT.md` file. These
97
97
 
98
98
  **Read the nearest `ekkOS_CONTEXT.md` when entering any directory you're about to work in.** Walk up the tree for broader context.
99
99
 
100
- **What they contain:** system architecture, dependencies, environment variables, file composition, activity status (`active` / `stale` / `dormant`), and recent git changes.
100
+ **What they contain:** system architecture, dependencies, environment variables, file composition, activity status (`active` / `stale` / `dormant`), and recent git changes.
101
+
102
+ **Semantic Analysis:** These files often contain a `## Active Work` or `## Semantic Analysis` section. This is high-fidelity memory of recent intent and architectural impacts (e.g. regressions, state changes). **Always prioritize these insights over your own assumptions about the current code state.**
101
103
 
102
104
  **When to read them:**
103
105
  - Before modifying code in a system you haven't touched yet
@@ -1,6 +0,0 @@
1
- interface SetupOptions {
2
- ide: string;
3
- key?: string;
4
- }
5
- export declare function setup(options: SetupOptions): Promise<void>;
6
- export {};
@@ -1,389 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.setup = setup;
7
- const os_1 = require("os");
8
- const path_1 = require("path");
9
- const fs_1 = require("fs");
10
- const chalk_1 = __importDefault(require("chalk"));
11
- const inquirer_1 = __importDefault(require("inquirer"));
12
- const ora_1 = __importDefault(require("ora"));
13
- // DEPRECATED: Hooks removed in hookless architecture migration
14
- // import { hooksInstall } from './hooks.js';
15
- const EKKOS_API_URL = 'https://mcp.ekkos.dev';
16
- const CONFIG_DIR = (0, path_1.join)((0, os_1.homedir)(), '.ekkos');
17
- const CONFIG_FILE = (0, path_1.join)(CONFIG_DIR, 'config.json');
18
- /**
19
- * Auto-detect which IDE the user is running in
20
- */
21
- function detectIDE() {
22
- // Check TERM_PROGRAM environment variable (set by many terminals/IDEs)
23
- const termProgram = process.env.TERM_PROGRAM?.toLowerCase() || '';
24
- const termEmulator = process.env.TERMINAL_EMULATOR?.toLowerCase() || '';
25
- const colorterm = process.env.COLORTERM?.toLowerCase() || '';
26
- // Cursor sets TERM_PROGRAM
27
- if (termProgram.includes('cursor')) {
28
- return 'cursor';
29
- }
30
- // VSCode sets TERM_PROGRAM to "vscode"
31
- if (termProgram.includes('vscode') || termEmulator.includes('vscode')) {
32
- return 'vscode';
33
- }
34
- // Windsurf/Codeium detection
35
- if (termProgram.includes('windsurf') || termProgram.includes('codeium')) {
36
- return 'windsurf';
37
- }
38
- // Check for Windsurf by config directory existence (if recently used)
39
- const windsurfConfig = (0, path_1.join)((0, os_1.homedir)(), '.codeium', 'windsurf');
40
- if ((0, fs_1.existsSync)(windsurfConfig)) {
41
- // Also check if there's activity (mcp_config.json exists or was modified recently)
42
- const mcpConfig = (0, path_1.join)(windsurfConfig, 'mcp_config.json');
43
- if ((0, fs_1.existsSync)(mcpConfig)) {
44
- // Windsurf is installed and configured - suggest it
45
- // But don't return it as definitive without env var
46
- }
47
- }
48
- // Check for Claude Code by looking at parent process or specific env vars
49
- // Claude Code typically runs in a terminal context
50
- if (process.env.CLAUDE_CODE || process.env.ANTHROPIC_API_KEY) {
51
- return 'claude-code';
52
- }
53
- // Check ~/.claude directory for Claude Code usage
54
- const claudeDir = (0, path_1.join)((0, os_1.homedir)(), '.claude');
55
- if ((0, fs_1.existsSync)(claudeDir)) {
56
- const hooksDir = (0, path_1.join)(claudeDir, 'hooks');
57
- if ((0, fs_1.existsSync)(hooksDir)) {
58
- // Claude Code has been set up before
59
- }
60
- }
61
- // Check for cursor config directory
62
- const cursorDir = (0, path_1.join)((0, os_1.homedir)(), '.cursor');
63
- if ((0, fs_1.existsSync)(cursorDir)) {
64
- // Cursor is installed
65
- }
66
- return null;
67
- }
68
- async function setup(options) {
69
- console.log('');
70
- console.log(chalk_1.default.cyan.bold('🧠 ekkOS Setup'));
71
- console.log(chalk_1.default.gray('─'.repeat(50)));
72
- console.log('');
73
- // Get API key
74
- let apiKey = options.key || process.env.EKKOS_API_KEY;
75
- if (!apiKey) {
76
- console.log(chalk_1.default.yellow('Get your API key from: https://ekkos.dev/dashboard/keys'));
77
- console.log('');
78
- const answers = await inquirer_1.default.prompt([
79
- {
80
- type: 'password',
81
- name: 'apiKey',
82
- message: 'Enter your ekkOS API key:',
83
- mask: '*',
84
- validate: (input) => {
85
- if (!input || input.length < 10) {
86
- return 'Please enter a valid API key';
87
- }
88
- return true;
89
- }
90
- }
91
- ]);
92
- apiKey = answers.apiKey;
93
- }
94
- // Verify API key
95
- const spinner = (0, ora_1.default)('Verifying API key...').start();
96
- try {
97
- const response = await fetch(`${EKKOS_API_URL}/api/v1/patterns/query`, {
98
- method: 'POST',
99
- headers: {
100
- 'Authorization': `Bearer ${apiKey}`,
101
- 'Content-Type': 'application/json'
102
- },
103
- body: JSON.stringify({ query: 'test', k: 1 })
104
- });
105
- if (!response.ok) {
106
- spinner.fail('Invalid API key');
107
- console.log(chalk_1.default.red('Please check your API key and try again.'));
108
- process.exit(1);
109
- }
110
- spinner.succeed('API key verified');
111
- }
112
- catch (error) {
113
- spinner.fail('Could not connect to ekkOS');
114
- console.log(chalk_1.default.red('Check your internet connection and try again.'));
115
- process.exit(1);
116
- }
117
- // Save config
118
- if (!(0, fs_1.existsSync)(CONFIG_DIR)) {
119
- (0, fs_1.mkdirSync)(CONFIG_DIR, { recursive: true });
120
- }
121
- const config = {
122
- apiKey: apiKey,
123
- installedIDEs: [],
124
- installedAt: new Date().toISOString()
125
- };
126
- // Determine which IDE to setup
127
- let ideToSetup;
128
- if (options.ide === 'all') {
129
- // Try to auto-detect the IDE
130
- const detectedIDE = detectIDE();
131
- if (detectedIDE) {
132
- console.log(chalk_1.default.green(`✓ Detected IDE: ${detectedIDE}`));
133
- console.log('');
134
- }
135
- const ideAnswers = await inquirer_1.default.prompt([
136
- {
137
- type: 'list',
138
- name: 'ide',
139
- message: 'Which IDE are you setting up?',
140
- choices: [
141
- { name: 'Claude Code (CLI)', value: 'claude-code' },
142
- { name: 'Claude Desktop (MCP)', value: 'claude-desktop' },
143
- { name: 'Cursor', value: 'cursor' },
144
- { name: 'Windsurf (Cascade)', value: 'windsurf' },
145
- { name: 'VSCode (Copilot)', value: 'vscode' }
146
- ],
147
- default: detectedIDE || 'cursor'
148
- }
149
- ]);
150
- ideToSetup = ideAnswers.ide;
151
- }
152
- else {
153
- ideToSetup = options.ide;
154
- }
155
- console.log('');
156
- // Setup the selected IDE
157
- await setupIDE(ideToSetup, apiKey, config);
158
- // Save final config
159
- (0, fs_1.writeFileSync)(CONFIG_FILE, JSON.stringify(config, null, 2));
160
- // Summary
161
- console.log('');
162
- console.log(chalk_1.default.green.bold('✅ Setup complete!'));
163
- console.log('');
164
- console.log(chalk_1.default.gray('─'.repeat(50)));
165
- console.log('');
166
- console.log('Configured IDEs:');
167
- for (const ide of config.installedIDEs) {
168
- console.log(chalk_1.default.cyan(` • ${ide}`));
169
- }
170
- console.log('');
171
- console.log('Next steps:');
172
- console.log(chalk_1.default.gray(' 1. Open your IDE'));
173
- console.log(chalk_1.default.gray(' 2. Start coding - memory is now active'));
174
- console.log(chalk_1.default.gray(' 3. Watch patterns form as you work'));
175
- console.log('');
176
- console.log(chalk_1.default.cyan('View your memory at: https://ekkos.dev/dashboard'));
177
- console.log('');
178
- }
179
- async function setupIDE(ide, apiKey, config) {
180
- const spinner = (0, ora_1.default)(`Setting up ${ide}...`).start();
181
- try {
182
- switch (ide) {
183
- case 'claude-code':
184
- await setupClaudeCode(apiKey);
185
- break;
186
- case 'claude-desktop':
187
- await setupClaudeDesktop(apiKey);
188
- break;
189
- case 'cursor':
190
- await setupCursor(apiKey);
191
- break;
192
- case 'windsurf':
193
- await setupWindsurf(apiKey);
194
- break;
195
- case 'vscode':
196
- await setupVSCode(apiKey);
197
- break;
198
- default:
199
- spinner.warn(`Unknown IDE: ${ide}`);
200
- return;
201
- }
202
- config.installedIDEs.push(ide);
203
- spinner.succeed(`${ide} configured`);
204
- }
205
- catch (error) {
206
- spinner.fail(`Failed to setup ${ide}: ${error}`);
207
- }
208
- }
209
- async function setupClaudeCode(apiKey) {
210
- const claudeDir = (0, path_1.join)((0, os_1.homedir)(), '.claude');
211
- // Create .claude directory (hooks directory is no longer created — hookless architecture)
212
- (0, fs_1.mkdirSync)(claudeDir, { recursive: true });
213
- // Save API key to ekkOS config
214
- const ekkosConfigDir = (0, path_1.join)((0, os_1.homedir)(), '.ekkos');
215
- (0, fs_1.mkdirSync)(ekkosConfigDir, { recursive: true });
216
- const configPath = (0, path_1.join)(ekkosConfigDir, 'config.json');
217
- let existingConfig = {};
218
- if ((0, fs_1.existsSync)(configPath)) {
219
- try {
220
- existingConfig = JSON.parse((0, fs_1.readFileSync)(configPath, 'utf-8'));
221
- }
222
- catch { }
223
- }
224
- existingConfig.apiKey = apiKey;
225
- existingConfig.updatedAt = new Date().toISOString();
226
- (0, fs_1.writeFileSync)(configPath, JSON.stringify(existingConfig, null, 2));
227
- }
228
- // DEPRECATED: Hooks removed in hookless architecture migration
229
- // generateBasicHooks() removed — hook generation is no longer performed.
230
- async function setupCursor(apiKey) {
231
- // Cursor uses .cursorrules for system prompt
232
- // and MCP servers for tools
233
- const cursorDir = (0, path_1.join)((0, os_1.homedir)(), '.cursor');
234
- if (!(0, fs_1.existsSync)(cursorDir)) {
235
- (0, fs_1.mkdirSync)(cursorDir, { recursive: true });
236
- }
237
- // Create/update MCP settings
238
- const mcpConfigPath = (0, path_1.join)(cursorDir, 'mcp.json');
239
- let mcpConfig = {};
240
- if ((0, fs_1.existsSync)(mcpConfigPath)) {
241
- try {
242
- mcpConfig = JSON.parse((0, fs_1.readFileSync)(mcpConfigPath, 'utf-8'));
243
- }
244
- catch { }
245
- }
246
- mcpConfig.mcpServers = mcpConfig.mcpServers || {};
247
- mcpConfig.mcpServers['ekkos-memory'] = {
248
- command: 'npx',
249
- args: ['@ekkos/mcp-server'],
250
- env: {
251
- EKKOS_API_KEY: apiKey
252
- }
253
- };
254
- (0, fs_1.writeFileSync)(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));
255
- // Create .cursorrules template in current directory
256
- const cursorRules = generateCursorRules();
257
- const cursorRulesPath = (0, path_1.join)(process.cwd(), '.cursorrules');
258
- if (!(0, fs_1.existsSync)(cursorRulesPath)) {
259
- (0, fs_1.writeFileSync)(cursorRulesPath, cursorRules);
260
- }
261
- }
262
- async function setupWindsurf(apiKey) {
263
- // Windsurf (Codeium) supports MCP servers like Cursor
264
- // Config location: ~/.codeium/windsurf/mcp_config.json
265
- const codeiumDir = (0, path_1.join)((0, os_1.homedir)(), '.codeium', 'windsurf');
266
- if (!(0, fs_1.existsSync)(codeiumDir)) {
267
- (0, fs_1.mkdirSync)(codeiumDir, { recursive: true });
268
- }
269
- // Create/update MCP config for Windsurf
270
- const mcpConfigPath = (0, path_1.join)(codeiumDir, 'mcp_config.json');
271
- let mcpConfig = { mcpServers: {} };
272
- if ((0, fs_1.existsSync)(mcpConfigPath)) {
273
- try {
274
- mcpConfig = JSON.parse((0, fs_1.readFileSync)(mcpConfigPath, 'utf-8'));
275
- mcpConfig.mcpServers = mcpConfig.mcpServers || {};
276
- }
277
- catch { }
278
- }
279
- // Add ekkOS cloud MCP server (HTTP transport)
280
- mcpConfig.mcpServers['ekkos-memory'] = {
281
- serverUrl: 'https://mcp.ekkos.dev',
282
- disabled: false,
283
- alwaysAllow: [],
284
- headers: {
285
- 'Authorization': `Bearer ${apiKey}`
286
- }
287
- };
288
- (0, fs_1.writeFileSync)(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));
289
- // Also save to legacy location for backwards compat
290
- const windsurfDir = (0, path_1.join)((0, os_1.homedir)(), '.windsurf');
291
- if (!(0, fs_1.existsSync)(windsurfDir)) {
292
- (0, fs_1.mkdirSync)(windsurfDir, { recursive: true });
293
- }
294
- (0, fs_1.writeFileSync)((0, path_1.join)(windsurfDir, 'ekkos.json'), JSON.stringify({ apiKey }, null, 2));
295
- // Create project rules template
296
- const cascadeRules = generateCascadeRules();
297
- const cascadeRulesPath = (0, path_1.join)(process.cwd(), '.windsurfrules');
298
- if (!(0, fs_1.existsSync)(cascadeRulesPath)) {
299
- (0, fs_1.writeFileSync)(cascadeRulesPath, cascadeRules);
300
- }
301
- }
302
- async function setupVSCode(apiKey) {
303
- // VSCode - create settings for Copilot/Continue
304
- const vscodeDir = (0, path_1.join)((0, os_1.homedir)(), '.vscode');
305
- if (!(0, fs_1.existsSync)(vscodeDir)) {
306
- (0, fs_1.mkdirSync)(vscodeDir, { recursive: true });
307
- }
308
- // Save config
309
- const configPath = (0, path_1.join)(vscodeDir, 'ekkos.json');
310
- (0, fs_1.writeFileSync)(configPath, JSON.stringify({ apiKey }, null, 2));
311
- // Note: Full VSCode integration requires extension
312
- console.log(chalk_1.default.yellow(' Note: VSCode requires the ekkOS extension for full integration'));
313
- }
314
- async function setupClaudeDesktop(apiKey) {
315
- // Claude Desktop - configure MCP server
316
- const claudeDir = (0, path_1.join)((0, os_1.homedir)(), 'Library', 'Application Support', 'Claude');
317
- if (!(0, fs_1.existsSync)(claudeDir)) {
318
- (0, fs_1.mkdirSync)(claudeDir, { recursive: true });
319
- }
320
- // Create/update MCP config
321
- const configPath = (0, path_1.join)(claudeDir, 'claude_desktop_config.json');
322
- let config = {};
323
- if ((0, fs_1.existsSync)(configPath)) {
324
- try {
325
- config = JSON.parse((0, fs_1.readFileSync)(configPath, 'utf-8'));
326
- }
327
- catch { }
328
- }
329
- // Preserve existing preferences
330
- config.mcpServers = config.mcpServers || {};
331
- config.mcpServers['ekkos-memory'] = {
332
- command: 'npx',
333
- args: ['-y', '@ekkos/mcp-server@latest'],
334
- env: {
335
- EKKOS_API_KEY: apiKey
336
- }
337
- };
338
- (0, fs_1.writeFileSync)(configPath, JSON.stringify(config, null, 2));
339
- console.log(chalk_1.default.yellow(' Note: Restart Claude Desktop to load the MCP server'));
340
- }
341
- // DEPRECATED: Hooks removed in hookless architecture migration
342
- // generatePromptSubmitHook() and generateStopHook() removed.
343
- function generateCursorRules() {
344
- return `# ekkOS Golden Loop Integration
345
-
346
- ## Memory System Active
347
-
348
- Before responding, check ekkOS memory for relevant patterns using the MCP tools:
349
- - \`ekkOS_Search\` - Find patterns that match the current problem
350
- - \`ekkOS_Forge\` - Save new patterns when you solve something
351
-
352
- ## When to FORGE New Patterns
353
-
354
- Use \`ekkOS_Forge\` when you:
355
- - Fixed a non-obvious bug
356
- - Found a better approach
357
- - Discovered a pitfall
358
- - User corrected you
359
-
360
- ## Pattern Application
361
-
362
- Apply relevant patterns silently in the answer.
363
- Do not emit control markers such as \`[ekkOS_APPLY]\` or \`[ekkOS_SKIP]\` unless the runtime explicitly asks for them.
364
- `;
365
- }
366
- function generateCascadeRules() {
367
- return `# ekkOS Memory Integration for Windsurf
368
-
369
- ## Active Learning
370
-
371
- This project uses ekkOS for persistent AI memory.
372
-
373
- When solving problems:
374
- 1. Check if similar patterns exist in memory
375
- 2. Apply proven solutions when they match
376
- 3. Create new patterns when you solve something novel
377
-
378
- ## Creating Patterns
379
-
380
- When you solve a problem that others might encounter:
381
- - Document the problem clearly
382
- - Explain the solution
383
- - Note any anti-patterns to avoid
384
-
385
- The system learns from every interaction.
386
- `;
387
- }
388
- // DEPRECATED: Hooks removed in hookless architecture migration
389
- // generatePromptSubmitHookPS() and generateStopHookPS() removed.