wyrm-mcp 7.2.1 → 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.
- package/LICENSE +26 -667
- package/NOTICE +14 -33
- package/dist/activation.js +1 -60
- package/dist/agent-daemon.js +4 -281
- package/dist/agent-loop.js +7 -332
- package/dist/analytics.js +13 -236
- package/dist/attribution.js +1 -49
- package/dist/audit.js +2 -457
- package/dist/auto-capture.js +3 -138
- package/dist/auto-orchestrator.js +1 -325
- package/dist/autoconfig.js +39 -840
- package/dist/buddy-runner.js +1 -109
- package/dist/buddy.js +14 -564
- package/dist/build-flags.js +1 -17
- package/dist/capabilities.js +3 -183
- package/dist/capture.js +1 -56
- package/dist/causality.js +6 -107
- package/dist/cli.js +20 -281
- package/dist/cloud/cli.js +5 -541
- package/dist/cloud/client.js +1 -221
- package/dist/cloud/crypto.js +1 -85
- package/dist/cloud/machine-id.js +2 -113
- package/dist/cloud/recovery.js +1 -60
- package/dist/cloud/sync-engine.js +7 -543
- package/dist/cloud-backup.js +5 -579
- package/dist/cloud-profile.js +1 -138
- package/dist/cloud-sync-entrypoint.js +1 -47
- package/dist/cloud-sync.js +2 -309
- package/dist/constellation.js +12 -168
- package/dist/context-build-budgeted.js +4 -144
- package/dist/context-ranking.js +1 -69
- package/dist/crypto.js +1 -179
- package/dist/daemon-write-endpoint.js +1 -290
- package/dist/daemon-writer.js +2 -406
- package/dist/database.js +43 -1110
- package/dist/deprecations.js +2 -162
- package/dist/design.js +13 -141
- package/dist/event-replication.js +1 -112
- package/dist/events-sse.js +7 -43
- package/dist/events.js +6 -238
- package/dist/failure-patterns.js +42 -659
- package/dist/federation.js +12 -236
- package/dist/goals.js +13 -101
- package/dist/golden.js +3 -355
- package/dist/handlers/agent.js +4 -165
- package/dist/handlers/alias-adapters.js +1 -129
- package/dist/handlers/aliases.js +1 -171
- package/dist/handlers/audit.js +1 -87
- package/dist/handlers/boundary.js +1 -221
- package/dist/handlers/capture.js +73 -1109
- package/dist/handlers/causality.js +7 -114
- package/dist/handlers/cloud.js +85 -382
- package/dist/handlers/companion.js +28 -459
- package/dist/handlers/datalake.js +7 -187
- package/dist/handlers/dispatch-context.js +0 -22
- package/dist/handlers/entity.js +25 -256
- package/dist/handlers/events.js +16 -335
- package/dist/handlers/failure.js +13 -340
- package/dist/handlers/goals.js +4 -296
- package/dist/handlers/intelligence.js +126 -674
- package/dist/handlers/invoicing.js +1 -70
- package/dist/handlers/mcpclient.js +6 -137
- package/dist/handlers/orchestration.js +40 -125
- package/dist/handlers/output-schemas.js +1 -24
- package/dist/handlers/presence.js +3 -99
- package/dist/handlers/project.js +28 -182
- package/dist/handlers/prompts.js +6 -157
- package/dist/handlers/quest.js +4 -224
- package/dist/handlers/recall.js +11 -218
- package/dist/handlers/registry.js +1 -167
- package/dist/handlers/resources.js +1 -288
- package/dist/handlers/review.js +11 -74
- package/dist/handlers/run.js +17 -487
- package/dist/handlers/search.js +15 -326
- package/dist/handlers/session.js +28 -615
- package/dist/handlers/share.js +8 -184
- package/dist/handlers/shims.js +1 -464
- package/dist/handlers/skill.js +67 -449
- package/dist/handlers/survivors.js +1 -120
- package/dist/handlers/symbols.js +8 -109
- package/dist/handlers/syncops.js +4 -302
- package/dist/handlers/types.js +1 -27
- package/dist/harvest.js +5 -191
- package/dist/hours.js +7 -156
- package/dist/http-auth.js +3 -321
- package/dist/http-fast.js +21 -1137
- package/dist/icons.js +1 -47
- package/dist/index.js +2 -924
- package/dist/indexer.js +4 -145
- package/dist/intelligence.js +31 -261
- package/dist/internal-dispatch.js +3 -212
- package/dist/keyset.js +1 -110
- package/dist/knowledge-graph.js +12 -176
- package/dist/license.js +2 -441
- package/dist/logger.js +2 -199
- package/dist/maintenance.js +2 -148
- package/dist/mcp-client.js +6 -262
- package/dist/memory-artifacts.js +30 -449
- package/dist/migrate-prompt.js +2 -124
- package/dist/migrations.js +40 -655
- package/dist/performance.js +1 -228
- package/dist/presence.js +11 -140
- package/dist/priority-embed.js +5 -164
- package/dist/providers/embedding-provider.js +1 -196
- package/dist/readonly-gate.js +1 -29
- package/dist/rehydration.js +9 -157
- package/dist/reindex.js +1 -88
- package/dist/render-target.js +21 -514
- package/dist/render.js +4 -280
- package/dist/repl-guard.js +1 -173
- package/dist/replication-daemon-entrypoint.js +1 -31
- package/dist/replication-daemon.js +2 -262
- package/dist/resilience.js +1 -591
- package/dist/reverse-bridge.js +5 -360
- package/dist/security.js +1 -244
- package/dist/session-seen.js +3 -51
- package/dist/setup.js +1 -260
- package/dist/skill-author.js +5 -168
- package/dist/spec-kit.js +1 -191
- package/dist/sqlite-busy.js +1 -154
- package/dist/statusline.js +11 -315
- package/dist/sub-agent.js +13 -262
- package/dist/summarizer.js +13 -139
- package/dist/symbols.js +7 -283
- package/dist/sync.js +5 -359
- package/dist/tasks-dispatch.js +1 -84
- package/dist/tasks.js +1 -282
- package/dist/token-budget.js +1 -143
- package/dist/tool-analytics.js +7 -129
- package/dist/tool-annotations.js +1 -365
- package/dist/tool-manifest-v2.json +1 -1
- package/dist/tool-manifest.json +1 -1
- package/dist/tool-profiles.js +1 -75
- package/dist/trace-harvest.js +6 -244
- package/dist/types.js +1 -30
- package/dist/ui-dashboard.js +41 -50
- package/dist/ulid.js +1 -81
- package/dist/validate.js +1 -129
- package/dist/vault.js +1 -534
- package/dist/vectors.js +3 -184
- package/dist/version-check.js +4 -136
- package/dist/visibility.js +19 -155
- package/dist/wyrm-cli.js +98 -2464
- package/dist/wyrm-guard.js +14 -424
- package/dist/wyrm-loop.js +3 -150
- package/dist/wyrm-manifest.json +1 -1
- package/dist/wyrm-statusline-daemon.js +1 -11
- package/dist/wyrm-statusline.js +4 -56
- package/dist/wyrm-ui.js +9 -77
- package/package.json +4 -2
package/dist/autoconfig.js
CHANGED
|
@@ -1,851 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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**
|
|
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**
|
|
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**
|
|
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**
|
|
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**
|
|
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**
|
|
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**
|
|
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**
|
|
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**
|
|
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**
|
|
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**
|
|
749
|
-
${
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
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};
|