wyrm-mcp 7.2.0 → 7.2.2

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 (156) hide show
  1. package/LICENSE +26 -667
  2. package/NOTICE +14 -33
  3. package/dist/activation.d.ts.map +1 -1
  4. package/dist/activation.js +1 -44
  5. package/dist/activation.js.map +1 -1
  6. package/dist/agent-daemon.js +4 -281
  7. package/dist/agent-loop.js +7 -332
  8. package/dist/analytics.js +13 -236
  9. package/dist/attribution.js +1 -49
  10. package/dist/audit.js +2 -457
  11. package/dist/auto-capture.js +3 -138
  12. package/dist/auto-orchestrator.js +1 -325
  13. package/dist/autoconfig.js +39 -840
  14. package/dist/buddy-runner.js +1 -109
  15. package/dist/buddy.js +14 -564
  16. package/dist/build-flags.js +1 -17
  17. package/dist/capabilities.js +3 -183
  18. package/dist/capture.js +1 -56
  19. package/dist/causality.js +6 -107
  20. package/dist/cli.js +20 -281
  21. package/dist/cloud/cli.js +5 -541
  22. package/dist/cloud/client.js +1 -221
  23. package/dist/cloud/crypto.js +1 -85
  24. package/dist/cloud/machine-id.js +2 -113
  25. package/dist/cloud/recovery.js +1 -60
  26. package/dist/cloud/sync-engine.js +7 -543
  27. package/dist/cloud-backup.js +5 -579
  28. package/dist/cloud-profile.js +1 -138
  29. package/dist/cloud-sync-entrypoint.js +1 -47
  30. package/dist/cloud-sync.js +2 -309
  31. package/dist/constellation.js +12 -168
  32. package/dist/context-build-budgeted.js +4 -144
  33. package/dist/context-ranking.js +1 -69
  34. package/dist/crypto.js +1 -179
  35. package/dist/daemon-write-endpoint.js +1 -290
  36. package/dist/daemon-writer.js +2 -406
  37. package/dist/database.js +43 -1110
  38. package/dist/deprecations.js +2 -162
  39. package/dist/design.js +13 -141
  40. package/dist/event-replication.js +1 -112
  41. package/dist/events-sse.js +7 -43
  42. package/dist/events.js +6 -238
  43. package/dist/failure-patterns.js +42 -659
  44. package/dist/federation.js +12 -236
  45. package/dist/goals.js +13 -101
  46. package/dist/golden.js +3 -355
  47. package/dist/handlers/agent.js +4 -165
  48. package/dist/handlers/alias-adapters.js +1 -129
  49. package/dist/handlers/aliases.js +1 -171
  50. package/dist/handlers/audit.js +1 -87
  51. package/dist/handlers/boundary.js +1 -221
  52. package/dist/handlers/capture.js +73 -1109
  53. package/dist/handlers/causality.js +7 -114
  54. package/dist/handlers/cloud.js +85 -382
  55. package/dist/handlers/companion.js +28 -459
  56. package/dist/handlers/datalake.js +7 -187
  57. package/dist/handlers/dispatch-context.js +0 -22
  58. package/dist/handlers/entity.js +25 -256
  59. package/dist/handlers/events.js +16 -335
  60. package/dist/handlers/failure.js +13 -340
  61. package/dist/handlers/goals.js +4 -296
  62. package/dist/handlers/intelligence.js +126 -674
  63. package/dist/handlers/invoicing.js +1 -70
  64. package/dist/handlers/mcpclient.js +6 -137
  65. package/dist/handlers/orchestration.js +40 -125
  66. package/dist/handlers/output-schemas.js +1 -24
  67. package/dist/handlers/presence.js +3 -99
  68. package/dist/handlers/project.js +28 -182
  69. package/dist/handlers/prompts.js +6 -157
  70. package/dist/handlers/quest.js +4 -224
  71. package/dist/handlers/recall.js +11 -218
  72. package/dist/handlers/registry.js +1 -167
  73. package/dist/handlers/resources.js +1 -288
  74. package/dist/handlers/review.js +11 -74
  75. package/dist/handlers/run.js +17 -487
  76. package/dist/handlers/search.js +15 -326
  77. package/dist/handlers/session.js +28 -615
  78. package/dist/handlers/share.js +8 -184
  79. package/dist/handlers/shims.js +1 -464
  80. package/dist/handlers/skill.js +67 -449
  81. package/dist/handlers/survivors.js +1 -120
  82. package/dist/handlers/symbols.js +8 -109
  83. package/dist/handlers/syncops.js +4 -302
  84. package/dist/handlers/types.js +1 -27
  85. package/dist/harvest.js +5 -191
  86. package/dist/hours.js +7 -156
  87. package/dist/http-auth.js +3 -321
  88. package/dist/http-fast.js +21 -1137
  89. package/dist/icons.js +1 -47
  90. package/dist/index.js +2 -924
  91. package/dist/indexer.js +4 -145
  92. package/dist/intelligence.js +31 -261
  93. package/dist/internal-dispatch.js +3 -212
  94. package/dist/keyset.js +1 -110
  95. package/dist/knowledge-graph.js +12 -176
  96. package/dist/license.d.ts +11 -0
  97. package/dist/license.d.ts.map +1 -1
  98. package/dist/license.js +2 -414
  99. package/dist/license.js.map +1 -1
  100. package/dist/logger.js +2 -199
  101. package/dist/maintenance.js +2 -148
  102. package/dist/mcp-client.js +6 -262
  103. package/dist/memory-artifacts.js +30 -449
  104. package/dist/migrate-prompt.js +2 -124
  105. package/dist/migrations.js +40 -655
  106. package/dist/performance.js +1 -228
  107. package/dist/presence.js +11 -140
  108. package/dist/priority-embed.js +5 -164
  109. package/dist/providers/embedding-provider.js +1 -196
  110. package/dist/readonly-gate.js +1 -29
  111. package/dist/rehydration.js +9 -157
  112. package/dist/reindex.js +1 -88
  113. package/dist/render-target.js +21 -514
  114. package/dist/render.js +4 -280
  115. package/dist/repl-guard.js +1 -173
  116. package/dist/replication-daemon-entrypoint.js +1 -31
  117. package/dist/replication-daemon.js +2 -262
  118. package/dist/resilience.js +1 -591
  119. package/dist/reverse-bridge.js +5 -360
  120. package/dist/security.js +1 -244
  121. package/dist/session-seen.js +3 -51
  122. package/dist/setup.js +1 -260
  123. package/dist/skill-author.js +5 -168
  124. package/dist/spec-kit.js +1 -191
  125. package/dist/sqlite-busy.js +1 -154
  126. package/dist/statusline.js +11 -315
  127. package/dist/sub-agent.js +13 -262
  128. package/dist/summarizer.js +13 -139
  129. package/dist/symbols.js +7 -283
  130. package/dist/sync.js +5 -359
  131. package/dist/tasks-dispatch.js +1 -84
  132. package/dist/tasks.js +1 -282
  133. package/dist/token-budget.js +1 -143
  134. package/dist/tool-analytics.js +7 -129
  135. package/dist/tool-annotations.js +1 -365
  136. package/dist/tool-manifest-v2.json +1 -1
  137. package/dist/tool-manifest.json +1 -1
  138. package/dist/tool-profiles.js +1 -75
  139. package/dist/trace-harvest.js +6 -244
  140. package/dist/types.js +1 -30
  141. package/dist/ui-dashboard.js +41 -50
  142. package/dist/ulid.js +1 -81
  143. package/dist/validate.js +1 -129
  144. package/dist/vault.js +1 -534
  145. package/dist/vectors.js +3 -184
  146. package/dist/version-check.js +4 -136
  147. package/dist/visibility.js +19 -155
  148. package/dist/wyrm-cli.js +98 -2451
  149. package/dist/wyrm-cli.js.map +1 -1
  150. package/dist/wyrm-guard.js +14 -424
  151. package/dist/wyrm-loop.js +3 -150
  152. package/dist/wyrm-manifest.json +1 -1
  153. package/dist/wyrm-statusline-daemon.js +1 -11
  154. package/dist/wyrm-statusline.js +4 -56
  155. package/dist/wyrm-ui.js +9 -77
  156. package/package.json +4 -2
@@ -1,851 +1,50 @@
1
- /**
2
- * Wyrm Auto-Configure - Universal AI Client Detection & Setup
3
- *
4
- * Automatically detects installed AI clients (VS Code Copilot, Claude Desktop,
5
- * Cursor, Windsurf, Zed, etc.) and configures Wyrm's MCP server in each.
6
- * Handles provider switching seamlessly change your AI, Wyrm follows.
7
- *
8
- * @copyright 2026 Ghost Protocol (Pvt) Ltd.
9
- * @license AGPL-3.0-or-later dual-licensed; commercial terms: ghosts.lk@proton.me. See LICENSE.
10
- * @module autoconfig
11
- * @version 3.0.0
12
- */
13
- import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync, chmodSync } from 'fs';
14
- import { homedir, platform } from 'os';
15
- import { join, dirname, resolve } from 'path';
16
- import { fileURLToPath } from 'url';
17
- import { spawnSync } from 'child_process';
18
- // ==================== CLIENT DEFINITIONS ====================
19
- function getConfigPaths() {
20
- const home = homedir();
21
- const os = platform();
22
- // Base config directories per OS
23
- const vscodeBase = os === 'darwin'
24
- ? join(home, 'Library', 'Application Support', 'Code', 'User')
25
- : os === 'win32'
26
- ? join(home, 'AppData', 'Roaming', 'Code', 'User')
27
- : join(home, '.config', 'Code', 'User');
28
- const vscodeInsidersBase = os === 'darwin'
29
- ? join(home, 'Library', 'Application Support', 'Code - Insiders', 'User')
30
- : os === 'win32'
31
- ? join(home, 'AppData', 'Roaming', 'Code - Insiders', 'User')
32
- : join(home, '.config', 'Code - Insiders', 'User');
33
- const claudeBase = os === 'darwin'
34
- ? join(home, 'Library', 'Application Support', 'Claude')
35
- : os === 'win32'
36
- ? join(home, 'AppData', 'Roaming', 'Claude')
37
- : join(home, '.config', 'claude');
38
- const cursorBase = os === 'darwin'
39
- ? join(home, '.cursor')
40
- : os === 'win32'
41
- ? join(home, '.cursor')
42
- : join(home, '.cursor');
43
- const windsurfBase = os === 'darwin'
44
- ? join(home, '.codeium', 'windsurf')
45
- : os === 'win32'
46
- ? join(home, '.codeium', 'windsurf')
47
- : join(home, '.codeium', 'windsurf');
48
- const zedBase = os === 'darwin'
49
- ? join(home, '.config', 'zed')
50
- : os === 'win32'
51
- ? join(home, 'AppData', 'Roaming', 'Zed')
52
- : join(home, '.config', 'zed');
53
- const continueBase = join(home, '.continue');
54
- return [
55
- {
56
- id: 'vscode-copilot',
57
- name: 'VS Code (Copilot)',
58
- icon: '💻',
59
- configPath: join(vscodeBase, 'settings.json'),
60
- configFormat: 'vscode',
61
- mcpKey: 'mcp.servers',
62
- detected: false,
63
- configured: false,
64
- },
65
- {
66
- id: 'vscode-insiders',
67
- name: 'VS Code Insiders',
68
- icon: '🟢',
69
- configPath: join(vscodeInsidersBase, 'settings.json'),
70
- configFormat: 'vscode',
71
- mcpKey: 'mcp.servers',
72
- detected: false,
73
- configured: false,
74
- },
75
- {
76
- id: 'claude-desktop',
77
- name: 'Claude Desktop',
78
- icon: '🤖',
79
- configPath: join(claudeBase, 'claude_desktop_config.json'),
80
- configFormat: 'mcp-json',
81
- mcpKey: 'mcpServers',
82
- detected: false,
83
- configured: false,
84
- },
85
- {
86
- id: 'cursor',
87
- name: 'Cursor',
88
- icon: '📐',
89
- configPath: join(cursorBase, 'mcp.json'),
90
- configFormat: 'mcp-json',
91
- mcpKey: 'mcpServers',
92
- detected: false,
93
- configured: false,
94
- },
95
- {
96
- id: 'windsurf',
97
- name: 'Windsurf',
98
- icon: '🏄',
99
- configPath: join(windsurfBase, 'mcp_config.json'),
100
- configFormat: 'mcp-json',
101
- mcpKey: 'mcpServers',
102
- detected: false,
103
- configured: false,
104
- },
105
- {
106
- id: 'zed',
107
- name: 'Zed',
108
- icon: '⚡',
109
- configPath: join(zedBase, 'settings.json'),
110
- configFormat: 'zed',
111
- mcpKey: 'context_servers',
112
- detected: false,
113
- configured: false,
114
- },
115
- {
116
- id: 'continue',
117
- name: 'Continue',
118
- icon: '🔄',
119
- configPath: join(continueBase, 'config.json'),
120
- configFormat: 'mcp-json',
121
- mcpKey: 'mcpServers',
122
- detected: false,
123
- configured: false,
124
- },
125
- ];
126
- }
127
- // ==================== DETECTION ====================
128
- /**
129
- * Detect which AI clients are installed by checking for their config directories
130
- */
131
- export function detectClients() {
132
- const clients = getConfigPaths();
133
- for (const client of clients) {
134
- // Check if config directory exists (the client is installed)
135
- const configDir = dirname(client.configPath);
136
- client.detected = existsSync(configDir);
137
- // Check if Wyrm is already configured
138
- if (client.detected && existsSync(client.configPath)) {
139
- try {
140
- const content = readFileSync(client.configPath, 'utf-8');
141
- const config = parseJsonWithComments(content);
142
- client.configured = hasWyrmConfig(config, client);
143
- }
144
- catch {
145
- client.configured = false;
146
- }
147
- }
148
- // Try to detect version
149
- if (client.detected) {
150
- client.version = detectClientVersion(client);
151
- }
152
- }
153
- return clients;
154
- }
155
- /**
156
- * Detect the version of an AI client
157
- */
158
- function detectClientVersion(client) {
159
- try {
160
- switch (client.id) {
161
- case 'vscode-copilot':
162
- case 'vscode-insiders': {
163
- const cmd = client.id === 'vscode-insiders' ? 'code-insiders' : 'code';
164
- const result = spawnSync(cmd, ['--version'], { encoding: 'utf-8', timeout: 5000 });
165
- if (result.stdout)
166
- return result.stdout.split('\n')[0];
167
- break;
168
- }
169
- case 'cursor': {
170
- const result = spawnSync('cursor', ['--version'], { encoding: 'utf-8', timeout: 5000 });
171
- if (result.stdout)
172
- return result.stdout.split('\n')[0];
173
- break;
174
- }
175
- }
176
- }
177
- catch {
178
- // Version detection is optional
179
- }
180
- return undefined;
181
- }
182
- /**
183
- * Check if Wyrm is already configured in a client's config
184
- */
185
- function hasWyrmConfig(config, client) {
186
- switch (client.configFormat) {
187
- case 'vscode': {
188
- const mcp = config['mcp'];
189
- const servers = mcp?.['servers'];
190
- return servers?.['wyrm'] !== undefined;
191
- }
192
- case 'mcp-json': {
193
- const servers = config[client.mcpKey];
194
- return servers?.['wyrm'] !== undefined;
195
- }
196
- case 'zed': {
197
- const servers = config[client.mcpKey];
198
- return servers?.['wyrm'] !== undefined;
199
- }
200
- default:
201
- return false;
202
- }
203
- }
204
- // ==================== CONFIGURATION ====================
205
- /**
206
- * Auto-detect Wyrm server path
207
- */
208
- export function findWyrmServerPath() {
209
- // 1. Check if wyrm-mcp binary is in PATH
210
- try {
211
- const result = spawnSync('which', ['wyrm-mcp'], { encoding: 'utf-8', timeout: 5000 });
212
- if (result.stdout?.trim()) {
213
- return result.stdout.trim();
214
- }
215
- }
216
- catch { }
217
- // 2. Check common npm global install locations
218
- try {
219
- const result = spawnSync('npm', ['root', '-g'], { encoding: 'utf-8', timeout: 5000 });
220
- const globalDir = result.stdout?.trim();
221
- if (globalDir) {
222
- const globalPath = join(globalDir, 'wyrm-mcp', 'dist', 'index.js');
223
- if (existsSync(globalPath))
224
- return globalPath;
225
- }
226
- }
227
- catch { }
228
- // 3. Check local development path (relative to this file)
229
- const devPath = resolve(__dirname, 'index.js');
230
- if (existsSync(devPath))
231
- return devPath;
232
- // 4. Check ~/.wyrm/node_modules
233
- const wyrmModulesPath = join(homedir(), '.wyrm', 'node_modules', 'wyrm-mcp', 'dist', 'index.js');
234
- if (existsSync(wyrmModulesPath))
235
- return wyrmModulesPath;
236
- // 5. Check common project locations
237
- const projectLocations = [
238
- join(homedir(), 'Git Projects', 'Wyrm', 'packages', 'mcp-server', 'dist', 'index.js'),
239
- join(homedir(), 'projects', 'Wyrm', 'packages', 'mcp-server', 'dist', 'index.js'),
240
- join(homedir(), 'dev', 'Wyrm', 'packages', 'mcp-server', 'dist', 'index.js'),
241
- ];
242
- for (const loc of projectLocations) {
243
- if (existsSync(loc))
244
- return loc;
245
- }
246
- // 6. Fallback to npx
247
- return 'wyrm-mcp';
248
- }
249
- /**
250
- * Get the default Wyrm database path
251
- */
252
- export function getDefaultDbPath() {
253
- return join(homedir(), '.wyrm', 'wyrm.db');
254
- }
255
- /**
256
- * Build the Wyrm MCP config object for a specific client format
257
- */
258
- function buildWyrmMcpConfig(client, wyrmConfig) {
259
- const serverPath = wyrmConfig.serverPath;
260
- const isNpx = serverPath === 'wyrm-mcp';
261
- const isJsFile = serverPath.endsWith('.js');
262
- switch (client.configFormat) {
263
- case 'vscode':
264
- return {
265
- command: isNpx ? 'npx' : 'node',
266
- args: isNpx ? ['wyrm-mcp'] : [serverPath],
267
- env: {
268
- WYRM_DB: wyrmConfig.dbPath,
269
- },
270
- };
271
- case 'mcp-json':
272
- return {
273
- command: isNpx ? 'npx' : 'node',
274
- args: isNpx ? ['wyrm-mcp'] : [serverPath],
275
- env: {
276
- WYRM_DB: wyrmConfig.dbPath,
277
- },
278
- };
279
- case 'zed':
280
- return {
281
- command: isNpx ? 'npx' : 'node',
282
- args: isNpx ? ['wyrm-mcp'] : [serverPath],
283
- env: {
284
- WYRM_DB: wyrmConfig.dbPath,
285
- },
286
- };
287
- default:
288
- return {};
289
- }
290
- }
291
- /**
292
- * Configure Wyrm in a single AI client
293
- */
294
- export function configureClient(client, wyrmConfig) {
295
- if (!client.detected) {
296
- return {
297
- client,
298
- action: 'skipped',
299
- message: `${client.name} not detected`,
300
- };
301
- }
302
- try {
303
- // Ensure config directory exists
304
- const configDir = dirname(client.configPath);
305
- if (!existsSync(configDir)) {
306
- mkdirSync(configDir, { recursive: true });
307
- }
308
- // Read existing config or create new
309
- let config = {};
310
- let existed = false;
311
- if (existsSync(client.configPath)) {
312
- try {
313
- const content = readFileSync(client.configPath, 'utf-8');
314
- config = parseJsonWithComments(content);
315
- existed = true;
316
- }
317
- catch {
318
- // If config exists but is invalid, start fresh
319
- config = {};
320
- }
321
- }
322
- // Create backup before modifying
323
- let backupPath;
324
- if (existed) {
325
- backupPath = `${client.configPath}.wyrm-backup`;
326
- writeFileSync(backupPath, readFileSync(client.configPath));
327
- }
328
- // Build Wyrm MCP entry
329
- const wyrmEntry = buildWyrmMcpConfig(client, wyrmConfig);
330
- // Inject into config based on format
331
- switch (client.configFormat) {
332
- case 'vscode': {
333
- // VS Code: { "mcp": { "servers": { "wyrm": {...} } } }
334
- if (!config['mcp'])
335
- config['mcp'] = {};
336
- const mcp = config['mcp'];
337
- if (!mcp['servers'])
338
- mcp['servers'] = {};
339
- const servers = mcp['servers'];
340
- servers['wyrm'] = wyrmEntry;
341
- break;
342
- }
343
- case 'mcp-json': {
344
- // MCP JSON: { "mcpServers": { "wyrm": {...} } }
345
- if (!config[client.mcpKey])
346
- config[client.mcpKey] = {};
347
- const servers = config[client.mcpKey];
348
- servers['wyrm'] = wyrmEntry;
349
- break;
350
- }
351
- case 'zed': {
352
- // Zed: { "context_servers": { "wyrm": {...} } }
353
- if (!config[client.mcpKey])
354
- config[client.mcpKey] = {};
355
- const servers = config[client.mcpKey];
356
- servers['wyrm'] = wyrmEntry;
357
- break;
358
- }
359
- }
360
- // Write config back
361
- writeFileSync(client.configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
362
- const action = client.configured ? 'updated' : 'configured';
363
- client.configured = true;
364
- return {
365
- client,
366
- action,
367
- message: `${client.name} ${action === 'configured' ? 'configured' : 'updated'} successfully`,
368
- backup: backupPath,
369
- };
370
- }
371
- catch (error) {
372
- return {
373
- client,
374
- action: 'failed',
375
- message: `Failed to configure ${client.name}: ${error}`,
376
- };
377
- }
378
- }
379
- /**
380
- * Remove Wyrm from a single AI client
381
- */
382
- export function removeFromClient(client) {
383
- if (!client.detected || !client.configured) {
384
- return {
385
- client,
386
- action: 'skipped',
387
- message: `${client.name} not configured`,
388
- };
389
- }
390
- try {
391
- if (!existsSync(client.configPath)) {
392
- return { client, action: 'skipped', message: `${client.name} config not found` };
393
- }
394
- const content = readFileSync(client.configPath, 'utf-8');
395
- const config = parseJsonWithComments(content);
396
- // Create backup
397
- const backupPath = `${client.configPath}.wyrm-backup`;
398
- writeFileSync(backupPath, content);
399
- // Remove Wyrm entry
400
- switch (client.configFormat) {
401
- case 'vscode': {
402
- const mcp = config['mcp'];
403
- const servers = mcp?.['servers'];
404
- if (servers)
405
- delete servers['wyrm'];
406
- break;
407
- }
408
- case 'mcp-json':
409
- case 'zed': {
410
- const servers = config[client.mcpKey];
411
- if (servers)
412
- delete servers['wyrm'];
413
- break;
414
- }
415
- }
416
- writeFileSync(client.configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
417
- client.configured = false;
418
- return {
419
- client,
420
- action: 'configured',
421
- message: `Removed Wyrm from ${client.name}`,
422
- backup: backupPath,
423
- };
424
- }
425
- catch (error) {
426
- return {
427
- client,
428
- action: 'failed',
429
- message: `Failed to remove from ${client.name}: ${error}`,
430
- };
431
- }
432
- }
433
- // ==================== ORCHESTRATION ====================
434
- // ==================== CLAUDE CODE CLI HOOKS ====================
435
- /** Synthetic "client" so hook install/removal renders in the setup summary. */
436
- function claudeHooksClient() {
437
- return {
438
- id: 'claude-code-hooks',
439
- name: 'Claude Code (auto-memory)',
440
- icon: '🪝',
441
- configPath: join(homedir(), '.claude', 'settings.json'),
442
- configFormat: 'mcp-json',
443
- mcpKey: '',
444
- detected: true,
445
- configured: false,
446
- };
447
- }
448
- /**
449
- * Wyrm's Claude Code CLI hooks, each mapped to the lifecycle events it fires on:
450
- * - capture (SessionEnd, PreCompact) : auto-remember every session [save side]
451
- * - rehydrate (SessionStart) : restore prior context [load side]
452
- * - prune (SessionEnd) : opt-in housekeeping (no-op unless
453
- * WYRM_AUTO_PRUNE=1; see the script)
454
- * - tool-trace(PostToolUse) : opt-in Live Memory tool_call trace
455
- * (no-op unless WYRM_TRACE_TOOL_CALLS=1)
456
- */
457
- const CLAUDE_HOOK_SPECS = [
458
- { script: 'wyrm-session-capture.mjs', events: ['SessionEnd', 'PreCompact'] },
459
- { script: 'wyrm-session-rehydrate.mjs', events: ['SessionStart'] },
460
- { script: 'wyrm-session-prune.mjs', events: ['SessionEnd'] },
461
- { script: 'wyrm-tool-call-trace.mjs', events: ['PostToolUse'] },
462
- ];
463
- /**
464
- * Install Wyrm's Claude Code CLI hooks so every session is auto-remembered
465
- * (capture), auto-restored (rehydrate), and optionally tidied (prune). Ships
466
- * the hook scripts next to the user's other hooks and merges the hook entries
467
- * into ~/.claude/settings.json WITHOUT clobbering any hooks already there.
468
- * Idempotent. No-op (returns null) when the Claude Code CLI isn't present.
469
- */
470
- export function installClaudeCodeHooks() {
471
- const claudeDir = join(homedir(), '.claude');
472
- if (!existsSync(claudeDir))
473
- return null; // Claude Code CLI not installed here
474
- const client = claudeHooksClient();
475
- try {
476
- const hooksDir = join(claudeDir, 'hooks');
477
- if (!existsSync(hooksDir))
478
- mkdirSync(hooksDir, { recursive: true });
479
- // Hook scripts live next to dist/ (one level below the package root).
480
- const here = dirname(fileURLToPath(import.meta.url));
481
- const scriptsDir = join(here, '..', 'scripts', 'hooks');
482
- const settingsPath = client.configPath;
483
- let settings = {};
484
- if (existsSync(settingsPath)) {
485
- try {
486
- settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
487
- }
488
- catch {
489
- settings = {};
490
- }
491
- }
492
- const hooks = (settings.hooks ?? {});
493
- let added = false;
494
- const missing = [];
495
- for (const spec of CLAUDE_HOOK_SPECS) {
496
- const src = join(scriptsDir, spec.script);
497
- if (!existsSync(src)) {
498
- missing.push(spec.script);
499
- continue;
500
- } // skip, don't abort the rest
501
- const dest = join(hooksDir, spec.script);
502
- copyFileSync(src, dest);
503
- try {
504
- chmodSync(dest, 0o755);
505
- }
506
- catch { /* non-fatal */ }
507
- const cmd = `node ${dest}`;
508
- for (const event of spec.events) {
509
- const groups = Array.isArray(hooks[event]) ? hooks[event] : [];
510
- const present = groups.some((g) => Array.isArray(g.hooks) && g.hooks.some((h) => h.command === cmd));
511
- if (!present) {
512
- groups.push({ hooks: [{ type: 'command', command: cmd, timeout: 30 }] });
513
- added = true;
514
- }
515
- hooks[event] = groups;
516
- }
517
- }
518
- settings.hooks = hooks;
519
- writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
520
- client.configured = true;
521
- const note = missing.length ? ` (missing from package: ${missing.join(', ')})` : '';
522
- return { client, action: added ? 'configured' : 'skipped', message: (added ? 'auto-memory hooks installed' : 'already installed') + note };
523
- }
524
- catch (err) {
525
- return { client, action: 'failed', message: err instanceof Error ? err.message : String(err) };
526
- }
527
- }
528
- /**
529
- * Install Wyrm's buddy statusline into Claude Code (`statusLine` in settings.json)
530
- * so the dragon + live memory (project · open quests · blocked failures · truths)
531
- * is visible in the TUI AT ALL TIMES — not just when a tool is called. Won't
532
- * clobber a non-Wyrm statusline the user already configured.
533
- */
534
- export function installClaudeStatusline() {
535
- const claudeDir = join(homedir(), '.claude');
536
- if (!existsSync(claudeDir))
537
- return null;
538
- const client = claudeHooksClient();
539
- try {
540
- const here = dirname(fileURLToPath(import.meta.url)); // dist/
541
- const cmd = `node ${join(here, 'wyrm-statusline.js')}`;
542
- const settingsPath = client.configPath;
543
- let settings = {};
544
- if (existsSync(settingsPath)) {
545
- try {
546
- settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
547
- }
548
- catch {
549
- settings = {};
550
- }
551
- }
552
- const existing = settings.statusLine;
553
- if (existing?.command && !/wyrm-statusline/.test(existing.command)) {
554
- return { client, action: 'skipped', message: `a non-Wyrm statusLine is already set (${existing.command}); not overwriting` };
555
- }
556
- const already = existing?.command === cmd;
557
- settings.statusLine = { type: 'command', command: cmd, padding: 0 };
558
- writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
559
- return { client, action: already ? 'skipped' : 'configured', message: already ? 'buddy statusline already installed' : 'Wyrm buddy statusline installed — persistent in the Claude Code TUI' };
560
- }
561
- catch (err) {
562
- return { client, action: 'failed', message: err instanceof Error ? err.message : String(err) };
563
- }
564
- }
565
- /** Remove the Wyrm statusline from settings.json (leaves any non-Wyrm one). */
566
- export function removeClaudeStatusline() {
567
- const settingsPath = join(homedir(), '.claude', 'settings.json');
568
- if (!existsSync(settingsPath))
569
- return null;
570
- const client = claudeHooksClient();
571
- try {
572
- const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
573
- const sl = settings.statusLine;
574
- if (sl?.command && /wyrm-statusline/.test(sl.command)) {
575
- delete settings.statusLine;
576
- writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
577
- return { client, action: 'configured', message: 'Wyrm statusline removed' };
578
- }
579
- return { client, action: 'skipped', message: 'no Wyrm statusline set' };
580
- }
581
- catch (err) {
582
- return { client, action: 'failed', message: err instanceof Error ? err.message : String(err) };
583
- }
584
- }
585
- /** Remove all Wyrm hooks from ~/.claude/settings.json (leaves other hooks). */
586
- export function removeClaudeCodeHooks() {
587
- const settingsPath = join(homedir(), '.claude', 'settings.json');
588
- if (!existsSync(settingsPath))
589
- return null;
590
- const client = claudeHooksClient();
591
- try {
592
- const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
593
- const hooksDir = join(homedir(), '.claude', 'hooks');
594
- const ourCmds = new Set(CLAUDE_HOOK_SPECS.map((s) => `node ${join(hooksDir, s.script)}`));
595
- const ourEvents = new Set(CLAUDE_HOOK_SPECS.flatMap((s) => s.events));
596
- const hooks = settings.hooks;
597
- if (hooks) {
598
- for (const event of ourEvents) {
599
- if (!Array.isArray(hooks[event]))
600
- continue;
601
- hooks[event] = hooks[event]
602
- .map((g) => ({ ...g, hooks: Array.isArray(g.hooks) ? g.hooks.filter((h) => !ourCmds.has(h.command ?? '')) : g.hooks }))
603
- .filter((g) => !Array.isArray(g.hooks) || g.hooks.length > 0);
604
- if (hooks[event].length === 0)
605
- delete hooks[event];
606
- }
607
- if (Object.keys(hooks).length === 0)
608
- delete settings.hooks;
609
- }
610
- writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
611
- return { client, action: 'configured', message: 'auto-memory hooks removed' };
612
- }
613
- catch (err) {
614
- return { client, action: 'failed', message: err instanceof Error ? err.message : String(err) };
615
- }
616
- }
617
- /**
618
- * Auto-configure Wyrm in ALL detected AI clients
619
- */
620
- export function autoConfigureAll(wyrmConfig) {
621
- const config = {
622
- serverPath: wyrmConfig?.serverPath || findWyrmServerPath(),
623
- dbPath: wyrmConfig?.dbPath || getDefaultDbPath(),
624
- httpPort: wyrmConfig?.httpPort,
625
- };
626
- // Ensure .wyrm directory exists
627
- const wyrmDir = join(homedir(), '.wyrm');
628
- if (!existsSync(wyrmDir)) {
629
- mkdirSync(wyrmDir, { recursive: true });
630
- }
631
- // Save Wyrm's own config for future reference
632
- saveWyrmMeta(config);
633
- const clients = detectClients();
634
- const results = [];
635
- for (const client of clients) {
636
- results.push(configureClient(client, config));
637
- }
638
- // Install Claude Code CLI auto-capture hooks (no-op if the CLI isn't present).
639
- const hookResult = installClaudeCodeHooks();
640
- if (hookResult)
641
- results.push(hookResult);
642
- return results;
643
- }
644
- /**
645
- * Remove Wyrm from ALL AI clients
646
- */
647
- export function removeFromAll() {
648
- const clients = detectClients();
649
- const results = [];
650
- for (const client of clients) {
651
- results.push(removeFromClient(client));
652
- }
653
- const hookResult = removeClaudeCodeHooks();
654
- if (hookResult)
655
- results.push(hookResult);
656
- return results;
657
- }
658
- /**
659
- * Configure Wyrm in specific clients only
660
- */
661
- export function configureSpecific(clientIds, wyrmConfig) {
662
- const config = {
663
- serverPath: wyrmConfig?.serverPath || findWyrmServerPath(),
664
- dbPath: wyrmConfig?.dbPath || getDefaultDbPath(),
665
- httpPort: wyrmConfig?.httpPort,
666
- };
667
- saveWyrmMeta(config);
668
- const clients = detectClients();
669
- const results = [];
670
- for (const client of clients) {
671
- if (clientIds.includes(client.id)) {
672
- results.push(configureClient(client, config));
673
- }
674
- }
675
- return results;
676
- }
677
- /**
678
- * Save Wyrm's meta configuration for auto-updates
679
- */
680
- function saveWyrmMeta(config) {
681
- const metaPath = join(homedir(), '.wyrm', 'wyrm-config.json');
682
- const clients = detectClients().filter(c => c.configured).map(c => c.id);
683
- const meta = {
684
- version: '3.0.0',
685
- serverPath: config.serverPath,
686
- dbPath: config.dbPath,
687
- httpPort: config.httpPort,
688
- configuredClients: clients,
689
- lastSetup: new Date().toISOString(),
690
- autoUpdate: true,
691
- };
692
- writeFileSync(metaPath, JSON.stringify(meta, null, 2) + '\n', 'utf-8');
693
- }
694
- /**
695
- * Load Wyrm's meta configuration
696
- */
697
- export function loadWyrmMeta() {
698
- const metaPath = join(homedir(), '.wyrm', 'wyrm-config.json');
699
- if (!existsSync(metaPath))
700
- return null;
701
- try {
702
- return JSON.parse(readFileSync(metaPath, 'utf-8'));
703
- }
704
- catch {
705
- return null;
706
- }
707
- }
708
- /**
709
- * Re-configure all previously configured clients (for use after updates)
710
- */
711
- export function reconfAll() {
712
- const meta = loadWyrmMeta();
713
- if (!meta) {
714
- return autoConfigureAll();
715
- }
716
- return configureSpecific(meta.configuredClients, {
717
- serverPath: meta.serverPath,
718
- dbPath: meta.dbPath,
719
- httpPort: meta.httpPort,
720
- });
721
- }
722
- // ==================== SYSTEM PROMPT INJECTION ====================
723
- const WYRM_MARKER_START = '<!-- wyrm:start -->';
724
- const WYRM_MARKER_END = '<!-- wyrm:end -->';
725
- export const WYRM_INJECT_BLOCK = `${WYRM_MARKER_START}
726
- ## Wyrm Memory + Agent (auto-managed — do not edit this section)
1
+ import{existsSync as u,readFileSync as p,writeFileSync as f,mkdirSync as k,copyFileSync as J,chmodSync as B}from"fs";import{homedir as m,platform as I}from"os";import{join as n,dirname as y,resolve as T}from"path";import{fileURLToPath as O}from"url";import{spawnSync as P}from"child_process";function U(){const e=m(),t=I(),s=t==="darwin"?n(e,"Library","Application Support","Code","User"):t==="win32"?n(e,"AppData","Roaming","Code","User"):n(e,".config","Code","User"),o=t==="darwin"?n(e,"Library","Application Support","Code - Insiders","User"):t==="win32"?n(e,"AppData","Roaming","Code - Insiders","User"):n(e,".config","Code - Insiders","User"),c=t==="darwin"?n(e,"Library","Application Support","Claude"):t==="win32"?n(e,"AppData","Roaming","Claude"):n(e,".config","claude"),r=t==="darwin"?n(e,".cursor"):t==="win32"?n(e,".cursor"):n(e,".cursor"),i=t==="darwin"?n(e,".codeium","windsurf"):t==="win32"?n(e,".codeium","windsurf"):n(e,".codeium","windsurf"),a=t==="darwin"?n(e,".config","zed"):t==="win32"?n(e,"AppData","Roaming","Zed"):n(e,".config","zed"),d=n(e,".continue");return[{id:"vscode-copilot",name:"VS Code (Copilot)",icon:"\u{1F4BB}",configPath:n(s,"settings.json"),configFormat:"vscode",mcpKey:"mcp.servers",detected:!1,configured:!1},{id:"vscode-insiders",name:"VS Code Insiders",icon:"\u{1F7E2}",configPath:n(o,"settings.json"),configFormat:"vscode",mcpKey:"mcp.servers",detected:!1,configured:!1},{id:"claude-desktop",name:"Claude Desktop",icon:"\u{1F916}",configPath:n(c,"claude_desktop_config.json"),configFormat:"mcp-json",mcpKey:"mcpServers",detected:!1,configured:!1},{id:"cursor",name:"Cursor",icon:"\u{1F4D0}",configPath:n(r,"mcp.json"),configFormat:"mcp-json",mcpKey:"mcpServers",detected:!1,configured:!1},{id:"windsurf",name:"Windsurf",icon:"\u{1F3C4}",configPath:n(i,"mcp_config.json"),configFormat:"mcp-json",mcpKey:"mcpServers",detected:!1,configured:!1},{id:"zed",name:"Zed",icon:"\u26A1",configPath:n(a,"settings.json"),configFormat:"zed",mcpKey:"context_servers",detected:!1,configured:!1},{id:"continue",name:"Continue",icon:"\u{1F504}",configPath:n(d,"config.json"),configFormat:"mcp-json",mcpKey:"mcpServers",detected:!1,configured:!1}]}function w(){const e=U();for(const t of e){const s=y(t.configPath);if(t.detected=u(s),t.detected&&u(t.configPath))try{const o=p(t.configPath,"utf-8"),c=W(o);t.configured=Y(c,t)}catch{t.configured=!1}t.detected&&(t.version=z(t))}return e}function z(e){try{switch(e.id){case"vscode-copilot":case"vscode-insiders":{const t=e.id==="vscode-insiders"?"code-insiders":"code",s=P(t,["--version"],{encoding:"utf-8",timeout:5e3});if(s.stdout)return s.stdout.split(`
2
+ `)[0];break}case"cursor":{const t=P("cursor",["--version"],{encoding:"utf-8",timeout:5e3});if(t.stdout)return t.stdout.split(`
3
+ `)[0];break}}}catch{}}function Y(e,t){switch(t.configFormat){case"vscode":return e.mcp?.servers?.wyrm!==void 0;case"mcp-json":return e[t.mcpKey]?.wyrm!==void 0;case"zed":return e[t.mcpKey]?.wyrm!==void 0;default:return!1}}function K(){try{const o=P("which",["wyrm-mcp"],{encoding:"utf-8",timeout:5e3});if(o.stdout?.trim())return o.stdout.trim()}catch{}try{const c=P("npm",["root","-g"],{encoding:"utf-8",timeout:5e3}).stdout?.trim();if(c){const r=n(c,"wyrm-mcp","dist","index.js");if(u(r))return r}}catch{}const e=T(__dirname,"index.js");if(u(e))return e;const t=n(m(),".wyrm","node_modules","wyrm-mcp","dist","index.js");if(u(t))return t;const s=[n(m(),"Git Projects","Wyrm","packages","mcp-server","dist","index.js"),n(m(),"projects","Wyrm","packages","mcp-server","dist","index.js"),n(m(),"dev","Wyrm","packages","mcp-server","dist","index.js")];for(const o of s)if(u(o))return o;return"wyrm-mcp"}function F(){return n(m(),".wyrm","wyrm.db")}function q(e,t){const s=t.serverPath,o=s==="wyrm-mcp",c=s.endsWith(".js");switch(e.configFormat){case"vscode":return{command:o?"npx":"node",args:o?["wyrm-mcp"]:[s],env:{WYRM_DB:t.dbPath}};case"mcp-json":return{command:o?"npx":"node",args:o?["wyrm-mcp"]:[s],env:{WYRM_DB:t.dbPath}};case"zed":return{command:o?"npx":"node",args:o?["wyrm-mcp"]:[s],env:{WYRM_DB:t.dbPath}};default:return{}}}function R(e,t){if(!e.detected)return{client:e,action:"skipped",message:`${e.name} not detected`};try{const s=y(e.configPath);u(s)||k(s,{recursive:!0});let o={},c=!1;if(u(e.configPath))try{const d=p(e.configPath,"utf-8");o=W(d),c=!0}catch{o={}}let r;c&&(r=`${e.configPath}.wyrm-backup`,f(r,p(e.configPath)));const i=q(e,t);switch(e.configFormat){case"vscode":{o.mcp||(o.mcp={});const d=o.mcp;d.servers||(d.servers={});const l=d.servers;l.wyrm=i;break}case"mcp-json":{o[e.mcpKey]||(o[e.mcpKey]={});const d=o[e.mcpKey];d.wyrm=i;break}case"zed":{o[e.mcpKey]||(o[e.mcpKey]={});const d=o[e.mcpKey];d.wyrm=i;break}}f(e.configPath,JSON.stringify(o,null,2)+`
4
+ `,"utf-8");const a=e.configured?"updated":"configured";return e.configured=!0,{client:e,action:a,message:`${e.name} ${a==="configured"?"configured":"updated"} successfully`,backup:r}}catch(s){return{client:e,action:"failed",message:`Failed to configure ${e.name}: ${s}`}}}function H(e){if(!e.detected||!e.configured)return{client:e,action:"skipped",message:`${e.name} not configured`};try{if(!u(e.configPath))return{client:e,action:"skipped",message:`${e.name} config not found`};const t=p(e.configPath,"utf-8"),s=W(t),o=`${e.configPath}.wyrm-backup`;switch(f(o,t),e.configFormat){case"vscode":{const r=s.mcp?.servers;r&&delete r.wyrm;break}case"mcp-json":case"zed":{const c=s[e.mcpKey];c&&delete c.wyrm;break}}return f(e.configPath,JSON.stringify(s,null,2)+`
5
+ `,"utf-8"),e.configured=!1,{client:e,action:"configured",message:`Removed Wyrm from ${e.name}`,backup:o}}catch(t){return{client:e,action:"failed",message:`Failed to remove from ${e.name}: ${t}`}}}function b(){return{id:"claude-code-hooks",name:"Claude Code (auto-memory)",icon:"\u{1FA9D}",configPath:n(m(),".claude","settings.json"),configFormat:"mcp-json",mcpKey:"",detected:!0,configured:!1}}const x=[{script:"wyrm-session-capture.mjs",events:["SessionEnd","PreCompact"]},{script:"wyrm-session-rehydrate.mjs",events:["SessionStart"]},{script:"wyrm-session-prune.mjs",events:["SessionEnd"]},{script:"wyrm-tool-call-trace.mjs",events:["PostToolUse"]}];function V(){const e=n(m(),".claude");if(!u(e))return null;const t=b();try{const s=n(e,"hooks");u(s)||k(s,{recursive:!0});const o=y(O(import.meta.url)),c=n(o,"..","scripts","hooks"),r=t.configPath;let i={};if(u(r))try{i=JSON.parse(p(r,"utf-8"))}catch{i={}}const a=i.hooks??{};let d=!1;const l=[];for(const h of x){const v=n(c,h.script);if(!u(v)){l.push(h.script);continue}const S=n(s,h.script);J(v,S);try{B(S,493)}catch{}const A=`node ${S}`;for(const _ of h.events){const j=Array.isArray(a[_])?a[_]:[];j.some(D=>Array.isArray(D.hooks)&&D.hooks.some(N=>N.command===A))||(j.push({hooks:[{type:"command",command:A,timeout:30}]}),d=!0),a[_]=j}}i.hooks=a,f(r,JSON.stringify(i,null,2)+`
6
+ `,"utf-8"),t.configured=!0;const g=l.length?` (missing from package: ${l.join(", ")})`:"";return{client:t,action:d?"configured":"skipped",message:(d?"auto-memory hooks installed":"already installed")+g}}catch(s){return{client:t,action:"failed",message:s instanceof Error?s.message:String(s)}}}function ne(){const e=n(m(),".claude");if(!u(e))return null;const t=b();try{const s=y(O(import.meta.url)),o=`node ${n(s,"wyrm-statusline.js")}`,c=t.configPath;let r={};if(u(c))try{r=JSON.parse(p(c,"utf-8"))}catch{r={}}const i=r.statusLine;if(i?.command&&!/wyrm-statusline/.test(i.command))return{client:t,action:"skipped",message:`a non-Wyrm statusLine is already set (${i.command}); not overwriting`};const a=i?.command===o;return r.statusLine={type:"command",command:o,padding:0},f(c,JSON.stringify(r,null,2)+`
7
+ `,"utf-8"),{client:t,action:a?"skipped":"configured",message:a?"buddy statusline already installed":"Wyrm buddy statusline installed \u2014 persistent in the Claude Code TUI"}}catch(s){return{client:t,action:"failed",message:s instanceof Error?s.message:String(s)}}}function ce(){const e=n(m(),".claude","settings.json");if(!u(e))return null;const t=b();try{const s=JSON.parse(p(e,"utf-8")),o=s.statusLine;return o?.command&&/wyrm-statusline/.test(o.command)?(delete s.statusLine,f(e,JSON.stringify(s,null,2)+`
8
+ `,"utf-8"),{client:t,action:"configured",message:"Wyrm statusline removed"}):{client:t,action:"skipped",message:"no Wyrm statusline set"}}catch(s){return{client:t,action:"failed",message:s instanceof Error?s.message:String(s)}}}function G(){const e=n(m(),".claude","settings.json");if(!u(e))return null;const t=b();try{const s=JSON.parse(p(e,"utf-8")),o=n(m(),".claude","hooks"),c=new Set(x.map(a=>`node ${n(o,a.script)}`)),r=new Set(x.flatMap(a=>a.events)),i=s.hooks;if(i){for(const a of r)Array.isArray(i[a])&&(i[a]=i[a].map(d=>({...d,hooks:Array.isArray(d.hooks)?d.hooks.filter(l=>!c.has(l.command??"")):d.hooks})).filter(d=>!Array.isArray(d.hooks)||d.hooks.length>0),i[a].length===0&&delete i[a]);Object.keys(i).length===0&&delete s.hooks}return f(e,JSON.stringify(s,null,2)+`
9
+ `,"utf-8"),{client:t,action:"configured",message:"auto-memory hooks removed"}}catch(s){return{client:t,action:"failed",message:s instanceof Error?s.message:String(s)}}}function Q(e){const t={serverPath:e?.serverPath||K(),dbPath:e?.dbPath||F(),httpPort:e?.httpPort},s=n(m(),".wyrm");u(s)||k(s,{recursive:!0}),E(t);const o=w(),c=[];for(const i of o)c.push(R(i,t));const r=V();return r&&c.push(r),c}function ie(){const e=w(),t=[];for(const o of e)t.push(H(o));const s=G();return s&&t.push(s),t}function Z(e,t){const s={serverPath:t?.serverPath||K(),dbPath:t?.dbPath||F(),httpPort:t?.httpPort};E(s);const o=w(),c=[];for(const r of o)e.includes(r.id)&&c.push(R(r,s));return c}function E(e){const t=n(m(),".wyrm","wyrm-config.json"),s=w().filter(c=>c.configured).map(c=>c.id),o={version:"3.0.0",serverPath:e.serverPath,dbPath:e.dbPath,httpPort:e.httpPort,configuredClients:s,lastSetup:new Date().toISOString(),autoUpdate:!0};f(t,JSON.stringify(o,null,2)+`
10
+ `,"utf-8")}function L(){const e=n(m(),".wyrm","wyrm-config.json");if(!u(e))return null;try{return JSON.parse(p(e,"utf-8"))}catch{return null}}function ae(){const e=L();return e?Z(e.configuredClients,{serverPath:e.serverPath,dbPath:e.dbPath,httpPort:e.httpPort}):Q()}const M="<!-- wyrm:start -->",C="<!-- wyrm:end -->",$=`${M}
11
+ ## Wyrm Memory + Agent (auto-managed \u2014 do not edit this section)
727
12
 
728
- **Session start** call \`wyrm_session_prime\` first to load project context (ground truths, open quests, recent sessions, scaffolds) before responding.
13
+ **Session start** \u2014 call \`wyrm_session_prime\` first to load project context (ground truths, open quests, recent sessions, scaffolds) before responding.
729
14
 
730
- **Set goals declaratively** when the operator describes a multi-step or recurring objective ("ship v2", "audit auth weekly", "keep dependencies updated"), call \`wyrm_goal_set\` with a clear \`success_criteria\`. Goals persist across sessions and the agent loop will pursue them.
15
+ **Set goals declaratively** \u2014 when the operator describes a multi-step or recurring objective ("ship v2", "audit auth weekly", "keep dependencies updated"), call \`wyrm_goal_set\` with a clear \`success_criteria\`. Goals persist across sessions and the agent loop will pursue them.
731
16
 
732
- **Bootstrap the autonomous agent** if the operator wants ongoing autonomous work, call \`wyrm_agent_init\` once. This starts the \`wyrm-loop\` background daemon that runs OODA iterations on active goals every \`interval_seconds\` (default 10 min). The same call can seed the first goal via \`seed_goal\`. Verify with \`wyrm_agent_status\`; stop with \`wyrm_agent_stop\`.
17
+ **Bootstrap the autonomous agent** \u2014 if the operator wants ongoing autonomous work, call \`wyrm_agent_init\` once. This starts the \`wyrm-loop\` background daemon that runs OODA iterations on active goals every \`interval_seconds\` (default 10 min). The same call can seed the first goal via \`seed_goal\`. Verify with \`wyrm_agent_status\`; stop with \`wyrm_agent_stop\`.
733
18
 
734
- **Counter-pattern check** before proposing a fix in an area with prior failures, call \`wyrm_failure_check\` first. Wyrm blocks repeated identical mistakes.
19
+ **Counter-pattern check** \u2014 before proposing a fix in an area with prior failures, call \`wyrm_failure_check\` first. Wyrm blocks repeated identical mistakes.
735
20
 
736
- **Record what failed** when a fix doesn't work, call \`wyrm_failure_record\` so future sessions don't repeat it.
21
+ **Record what failed** \u2014 when a fix doesn't work, call \`wyrm_failure_record\` so future sessions don't repeat it.
737
22
 
738
- **Cite memory in answers** when surfacing memory, name the source (e.g., "per truth tech_stack.database = PostgreSQL 15" or "session #142 noted X"). Tagged citations help the operator audit trust.
23
+ **Cite memory in answers** \u2014 when surfacing memory, name the source (e.g., "per truth tech_stack.database = PostgreSQL 15" or "session #142 noted X"). Tagged citations help the operator audit trust.
739
24
 
740
- **Attribute Wyrm visibly** when you used Wyrm to answer something (loaded a truth, blocked a failure, cited a quest, primed from a past session), say so in plain English. Example: _"Wyrm reminded me that you decided on PostgreSQL last week, so I'm building this for Postgres"_. Operators often don't realise Wyrm is contributing naming the contribution makes the value visible. Do NOT silently absorb Wyrm data and present it as your own knowledge.
25
+ **Attribute Wyrm visibly** \u2014 when you used Wyrm to answer something (loaded a truth, blocked a failure, cited a quest, primed from a past session), say so in plain English. Example: _"Wyrm reminded me that you decided on PostgreSQL last week, so I'm building this for Postgres"_. Operators often don't realise Wyrm is contributing \u2014 naming the contribution makes the value visible. Do NOT silently absorb Wyrm data and present it as your own knowledge.
741
26
 
742
- **Explain Wyrm on request** if the operator asks "what does Wyrm do?", "what is Wyrm?", "is Wyrm even working?", or seems confused about Wyrm's role, call \`wyrm_intro\` for a plain-English explanation. For "what has Wyrm done for me?" call \`wyrm_digest\`. These tools speak to humans, not to you.
27
+ **Explain Wyrm on request** \u2014 if the operator asks "what does Wyrm do?", "what is Wyrm?", "is Wyrm even working?", or seems confused about Wyrm's role, call \`wyrm_intro\` for a plain-English explanation. For "what has Wyrm done for me?" call \`wyrm_digest\`. These tools speak to humans, not to you.
743
28
 
744
- **Know what Wyrm can do** call \`wyrm_capabilities\` once per session to get the full feature inventory (counter-patterns, ground truths, scaffolds, agent loop, outbound MCP, federation, audit chain, ). Reach for the right tool the first time instead of re-discovering the surface mid-conversation.
29
+ **Know what Wyrm can do** \u2014 call \`wyrm_capabilities\` once per session to get the full feature inventory (counter-patterns, ground truths, scaffolds, agent loop, outbound MCP, federation, audit chain, \u2026). Reach for the right tool the first time instead of re-discovering the surface mid-conversation.
745
30
 
746
- **Keep wyrm-mcp current** \`wyrm_check_update\` returns whether a newer version is on npm (cached 24h). When one is, surface it to the operator and offer \`wyrm_self_update\` (with \`confirm:true\`) to upgrade.
31
+ **Keep wyrm-mcp current** \u2014 \`wyrm_check_update\` returns whether a newer version is on npm (cached 24h). When one is, surface it to the operator and offer \`wyrm_self_update\` (with \`confirm:true\`) to upgrade.
747
32
 
748
- **Buddy companion** call \`wyrm_buddy\` for warm, data-grounded check-ins. At session start (after \`wyrm_session_prime\`) for long-running work, and when the operator completes a quest or hits a milestone. The buddy speaks in a chosen persona but every claim sources a real DB row speak it verbatim, never invent additional encouragement. Auto-federates with other registered MCPs that expose \`*_buddy\` tools.
749
- ${WYRM_MARKER_END}`;
750
- /**
751
- * Inject Wyrm's session-prime instruction block into AI client instruction files.
752
- * Supports copilot (.github/copilot-instructions.md) and cursor (.cursor/rules).
753
- * Uses marker comments to manage the block idempotently (safe to call multiple times).
754
- */
755
- export function injectSystemPrompt(projectPath, clients) {
756
- const result = { injected: [], skipped: [], errors: [] };
757
- const writeWithMarker = (filePath, label) => {
758
- try {
759
- const dir = dirname(filePath);
760
- if (!existsSync(dir))
761
- mkdirSync(dir, { recursive: true });
762
- if (existsSync(filePath)) {
763
- const existing = readFileSync(filePath, 'utf-8');
764
- const startIdx = existing.indexOf(WYRM_MARKER_START);
765
- const endIdx = existing.indexOf(WYRM_MARKER_END);
766
- if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
767
- // Replace existing block
768
- const before = existing.slice(0, startIdx);
769
- const after = existing.slice(endIdx + WYRM_MARKER_END.length);
770
- writeFileSync(filePath, `${before}${WYRM_INJECT_BLOCK}${after}`, 'utf-8');
771
- }
772
- else {
773
- // Append block (ensure newline separation)
774
- const sep = existing.endsWith('\n') ? '\n' : '\n\n';
775
- writeFileSync(filePath, `${existing}${sep}${WYRM_INJECT_BLOCK}\n`, 'utf-8');
776
- }
777
- }
778
- else {
779
- writeFileSync(filePath, `${WYRM_INJECT_BLOCK}\n`, 'utf-8');
780
- }
781
- result.injected.push(label);
782
- }
783
- catch (e) {
784
- result.errors.push(`${label}: ${e}`);
785
- }
786
- };
787
- const allClients = clients.length === 0
788
- ? ['copilot', 'cursor']
789
- : clients;
790
- for (const client of allClients) {
791
- const lc = client.toLowerCase();
792
- if (lc === 'copilot' || lc === 'vscode-copilot') {
793
- const filePath = join(projectPath, '.github', 'copilot-instructions.md');
794
- writeWithMarker(filePath, '.github/copilot-instructions.md');
795
- }
796
- else if (lc === 'cursor') {
797
- const filePath = join(projectPath, '.cursor', 'rules');
798
- writeWithMarker(filePath, '.cursor/rules');
799
- }
800
- else {
801
- result.skipped.push(client);
802
- }
803
- }
804
- return result;
805
- }
806
- // ==================== UTILITIES ====================
807
- /**
808
- * Parse JSON with comments (JSONC) - handles VS Code settings files
809
- */
810
- function parseJsonWithComments(text) {
811
- // Strip single-line comments
812
- let cleaned = text.replace(/\/\/.*$/gm, '');
813
- // Strip multi-line comments
814
- cleaned = cleaned.replace(/\/\*[\s\S]*?\*\//g, '');
815
- // Handle trailing commas (common in VS Code settings)
816
- cleaned = cleaned.replace(/,\s*([\]}])/g, '$1');
817
- return JSON.parse(cleaned);
818
- }
819
- /**
820
- * Get a friendly status summary of all AI clients
821
- */
822
- export function getStatusSummary() {
823
- const clients = detectClients();
824
- const detected = clients.filter(c => c.detected);
825
- const configured = clients.filter(c => c.configured);
826
- let summary = `󱅝 Wyrm Auto-Configure Status\n\n`;
827
- summary += ` Detected: ${detected.length}/${clients.length} AI clients\n`;
828
- summary += ` Configured: ${configured.length}/${detected.length} clients\n\n`;
829
- for (const client of clients) {
830
- const status = !client.detected
831
- ? ' ○' // Not installed
832
- : client.configured
833
- ? ' ●' // Configured
834
- : ' ◐'; // Installed but not configured
835
- const versionStr = client.version ? ` (${client.version})` : '';
836
- const statusLabel = !client.detected
837
- ? 'not found'
838
- : client.configured
839
- ? 'configured ✓'
840
- : 'detected — not configured';
841
- summary += `${status} ${client.icon} ${client.name}${versionStr}: ${statusLabel}\n`;
842
- }
843
- const meta = loadWyrmMeta();
844
- if (meta) {
845
- summary += `\n Server: ${meta.serverPath}\n`;
846
- summary += ` DB: ${meta.dbPath}\n`;
847
- summary += ` Last: ${new Date(meta.lastSetup).toLocaleString()}\n`;
848
- }
849
- return summary;
850
- }
851
- //# sourceMappingURL=autoconfig.js.map
33
+ **Buddy companion** \u2014 call \`wyrm_buddy\` for warm, data-grounded check-ins. At session start (after \`wyrm_session_prime\`) for long-running work, and when the operator completes a quest or hits a milestone. The buddy speaks in a chosen persona but every claim sources a real DB row \u2014 speak it verbatim, never invent additional encouragement. Auto-federates with other registered MCPs that expose \`*_buddy\` tools.
34
+ ${C}`;function de(e,t){const s={injected:[],skipped:[],errors:[]},o=(r,i)=>{try{const a=y(r);if(u(a)||k(a,{recursive:!0}),u(r)){const d=p(r,"utf-8"),l=d.indexOf(M),g=d.indexOf(C);if(l!==-1&&g!==-1&&g>l){const h=d.slice(0,l),v=d.slice(g+C.length);f(r,`${h}${$}${v}`,"utf-8")}else{const h=d.endsWith(`
35
+ `)?`
36
+ `:`
37
+
38
+ `;f(r,`${d}${h}${$}
39
+ `,"utf-8")}}else f(r,`${$}
40
+ `,"utf-8");s.injected.push(i)}catch(a){s.errors.push(`${i}: ${a}`)}},c=t.length===0?["copilot","cursor"]:t;for(const r of c){const i=r.toLowerCase();if(i==="copilot"||i==="vscode-copilot"){const a=n(e,".github","copilot-instructions.md");o(a,".github/copilot-instructions.md")}else if(i==="cursor"){const a=n(e,".cursor","rules");o(a,".cursor/rules")}else s.skipped.push(r)}return s}function W(e){let t=e.replace(/\/\/.*$/gm,"");return t=t.replace(/\/\*[\s\S]*?\*\//g,""),t=t.replace(/,\s*([\]}])/g,"$1"),JSON.parse(t)}function ue(){const e=w(),t=e.filter(r=>r.detected),s=e.filter(r=>r.configured);let o=`\u{F115D} Wyrm Auto-Configure Status
41
+
42
+ `;o+=` Detected: ${t.length}/${e.length} AI clients
43
+ `,o+=` Configured: ${s.length}/${t.length} clients
44
+
45
+ `;for(const r of e){const i=r.detected?r.configured?" \u25CF":" \u25D0":" \u25CB",a=r.version?` (${r.version})`:"",d=r.detected?r.configured?"configured \u2713":"detected \u2014 not configured":"not found";o+=`${i} ${r.icon} ${r.name}${a}: ${d}
46
+ `}const c=L();return c&&(o+=`
47
+ Server: ${c.serverPath}
48
+ `,o+=` DB: ${c.dbPath}
49
+ `,o+=` Last: ${new Date(c.lastSetup).toLocaleString()}
50
+ `),o}export{$ as WYRM_INJECT_BLOCK,Q as autoConfigureAll,R as configureClient,Z as configureSpecific,w as detectClients,K as findWyrmServerPath,F as getDefaultDbPath,ue as getStatusSummary,de as injectSystemPrompt,V as installClaudeCodeHooks,ne as installClaudeStatusline,L as loadWyrmMeta,ae as reconfAll,G as removeClaudeCodeHooks,ce as removeClaudeStatusline,ie as removeFromAll,H as removeFromClient};