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.
- package/LICENSE +26 -667
- package/NOTICE +14 -33
- package/dist/activation.d.ts.map +1 -1
- package/dist/activation.js +1 -44
- package/dist/activation.js.map +1 -1
- 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.d.ts +11 -0
- package/dist/license.d.ts.map +1 -1
- package/dist/license.js +2 -414
- package/dist/license.js.map +1 -1
- 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 -2451
- package/dist/wyrm-cli.js.map +1 -1
- 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
|
@@ -1,674 +1,126 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
? `<!-- cache-stable -->\n${stableParts.join('\n\n---\n\n')}\n<!-- /cache-stable -->\n\n---\n\n`
|
|
128
|
-
: '';
|
|
129
|
-
const volatileBody = brief.sections.length > 0 ? brief.text.slice(0, 2000) : '';
|
|
130
|
-
let text = `${stableHeader}${stablePreamble}${volatileBody}`;
|
|
131
|
-
text += `\n\n_Brief: ${truthStats.current} ground truth${truthStats.current !== 1 ? 's' : ''}, scaffold: ${scaffoldMatch ? scaffoldMatch.scaffold.problem_type : 'none'}, ${stats.total} memory artifact${stats.total !== 1 ? 's' : ''}.`;
|
|
132
|
-
if (brief.sourceIds.length > 0) {
|
|
133
|
-
text += ` Memory IDs: [${brief.sourceIds.join(', ')}] — use \`wyrm_feedback\` to rate._`;
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
text += `_`;
|
|
137
|
-
}
|
|
138
|
-
// Log cached-preamble savings (best-effort).
|
|
139
|
-
try {
|
|
140
|
-
const { logSavings } = await import('../statusline.js');
|
|
141
|
-
const rawDb = db.getDatabase();
|
|
142
|
-
const sess = rawDb.prepare(`SELECT id FROM sessions WHERE project_id = ? ORDER BY created_at DESC LIMIT 1`).get(cbProject.id);
|
|
143
|
-
// Tokens saved by caching = preamble size * 0.9 (Anthropic cached pricing is 10% of base)
|
|
144
|
-
const preambleTokens = Math.round(stablePreamble.length / 4);
|
|
145
|
-
if (preambleTokens > 0)
|
|
146
|
-
logSavings(rawDb, 'wyrm_context_build', 'cached_preamble', Math.round(preambleTokens * 0.9), sess?.id);
|
|
147
|
-
}
|
|
148
|
-
catch { /* best-effort */ }
|
|
149
|
-
const response = cachedResponse(text);
|
|
150
|
-
if (cacheKey)
|
|
151
|
-
cache.set(cacheKey, response, 10000);
|
|
152
|
-
return response;
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
name: "wyrm_truth_set",
|
|
157
|
-
description: "Use to lock in a confirmed fact or constraint as project ground truth - architecture, conventions, key decisions. Injected first into every brief; re-setting the same category+key supersedes (history kept).",
|
|
158
|
-
inputSchema: {
|
|
159
|
-
type: "object",
|
|
160
|
-
properties: {
|
|
161
|
-
projectPath: { type: "string" },
|
|
162
|
-
category: { type: "string", description: "e.g. architecture, conventions" },
|
|
163
|
-
key: { type: "string", description: "snake_case key" },
|
|
164
|
-
value: { type: "string", description: "The statement" },
|
|
165
|
-
rationale: { type: "string", description: "Why this is true" },
|
|
166
|
-
source: { type: "string", description: "'user'|'derived'|'observed'|'confirmed'" },
|
|
167
|
-
confidence: { type: "number", description: "0-1 (default 1.0)" },
|
|
168
|
-
ttl_days: { type: "number", description: "Stale after N days" },
|
|
169
|
-
},
|
|
170
|
-
required: ["projectPath", "category", "key", "value"],
|
|
171
|
-
},
|
|
172
|
-
annotations: TOOL_ANNOTATIONS["wyrm_truth_set"],
|
|
173
|
-
aliases: [],
|
|
174
|
-
handler: async (args, ctx) => {
|
|
175
|
-
const { db, groundTruths, presence } = ctx;
|
|
176
|
-
const { projectPath: tPath, category: tCat, key: tKey, value: tVal, rationale: tRat, source: tSrc, confidence: tConf, ttl_days: tTtl } = args;
|
|
177
|
-
// Truth categories are an OPEN vocabulary at the storage layer:
|
|
178
|
-
// ground_truths.category carries no schema CHECK, the wire schema
|
|
179
|
-
// documents free-form values ('conventions', 'decisions', ...), and
|
|
180
|
-
// the renderer passes unknown categories through with their own
|
|
181
|
-
// label. Closed-enum rejection here would invalidate legal stored
|
|
182
|
-
// data — so the boundary enforces presence/type only (there is no
|
|
183
|
-
// CHECK-violation path for this field in either write mode).
|
|
184
|
-
const tSafeCat = requireString('category', tCat, { maxLen: 200 });
|
|
185
|
-
const tProject = db.getProject(tPath);
|
|
186
|
-
if (!tProject)
|
|
187
|
-
return { content: [{ type: "text", text: `Project not found: ${tPath}` }], isError: true };
|
|
188
|
-
// v7 F2 (T012): canonical write seam — truth_set.
|
|
189
|
-
const tInput = {
|
|
190
|
-
category: tSafeCat,
|
|
191
|
-
key: tKey,
|
|
192
|
-
value: tVal,
|
|
193
|
-
rationale: tRat,
|
|
194
|
-
source: tSrc,
|
|
195
|
-
confidence: tConf ?? 1.0,
|
|
196
|
-
ttl_days: tTtl,
|
|
197
|
-
};
|
|
198
|
-
const truth = await daemonOr('truth_set', tProject.id, tInput, () => groundTruths.set(tProject.id, tInput));
|
|
199
|
-
cache.invalidate('wyrm_truth_get');
|
|
200
|
-
cache.invalidate('wyrm_context_build');
|
|
201
|
-
return { content: [{ type: "text", text: ` **Ground Truth Set** ✅\n\n- **Category:** ${tCat}\n- **Key:** ${tKey}\n- **Value:** ${tVal}${tRat ? `\n- **Rationale:** ${tRat}` : ''}${tTtl ? `\n- **TTL:** ${tTtl} days` : ''}\n- **ID:** ${truth.id}\n\nThis truth will be injected at the top of every \`wyrm_context_build\` response for this project.` }] };
|
|
202
|
-
},
|
|
203
|
-
},
|
|
204
|
-
{
|
|
205
|
-
name: "wyrm_truth_get",
|
|
206
|
-
description: "Use to read the established facts and ground rules for a codebase - validated truths the AI should treat as baseline, optionally filtered by category.",
|
|
207
|
-
inputSchema: {
|
|
208
|
-
type: "object",
|
|
209
|
-
properties: {
|
|
210
|
-
projectPath: { type: "string" },
|
|
211
|
-
category: { type: "string", description: "Category filter" },
|
|
212
|
-
},
|
|
213
|
-
required: ["projectPath"],
|
|
214
|
-
},
|
|
215
|
-
annotations: TOOL_ANNOTATIONS["wyrm_truth_get"],
|
|
216
|
-
aliases: [],
|
|
217
|
-
handler: async (args, ctx) => {
|
|
218
|
-
const { cachedResponse, db, groundTruths } = ctx;
|
|
219
|
-
const cacheKey = currentReadCacheKey();
|
|
220
|
-
const { projectPath: tgPath, category: tgCat } = args;
|
|
221
|
-
const tgProject = db.getProject(tgPath);
|
|
222
|
-
if (!tgProject)
|
|
223
|
-
return { content: [{ type: "text", text: `Project not found: ${tgPath}` }], isError: true };
|
|
224
|
-
const truths = groundTruths.getCurrent(tgProject.id, tgCat);
|
|
225
|
-
if (truths.length === 0) {
|
|
226
|
-
const msg = tgCat
|
|
227
|
-
? `No ground truths in category "${tgCat}" for this project.`
|
|
228
|
-
: `No ground truths stored yet. Use \`wyrm_truth_set\` to add validated facts.`;
|
|
229
|
-
return { content: [{ type: "text", text: ` **Ground Truths**\n\n${msg}` }] };
|
|
230
|
-
}
|
|
231
|
-
const byCategory = new Map();
|
|
232
|
-
for (const t of truths) {
|
|
233
|
-
if (!byCategory.has(t.category))
|
|
234
|
-
byCategory.set(t.category, []);
|
|
235
|
-
byCategory.get(t.category).push(t);
|
|
236
|
-
}
|
|
237
|
-
let text = ` **Ground Truths** — ${truths.length} active\n\n`;
|
|
238
|
-
for (const [cat, items] of byCategory) {
|
|
239
|
-
text += `### ${cat}\n`;
|
|
240
|
-
for (const t of items) {
|
|
241
|
-
const staleness = computeStaleness(t);
|
|
242
|
-
const stale = staleness !== null && staleness > 0.7;
|
|
243
|
-
const stalePrefix = stale ? '[⚠️ STALE] ' : '';
|
|
244
|
-
text += `- ${stalePrefix}**${t.key}:** ${t.value}`;
|
|
245
|
-
if (t.confidence < 1)
|
|
246
|
-
text += ` _(confidence: ${(t.confidence * 100).toFixed(0)}%)_`;
|
|
247
|
-
if (t.rationale)
|
|
248
|
-
text += `\n _${t.rationale}_`;
|
|
249
|
-
if (staleness !== null)
|
|
250
|
-
text += `\n _staleness: ${(staleness * 100).toFixed(0)}% (TTL: ${t.ttl_days}d)_`;
|
|
251
|
-
text += '\n';
|
|
252
|
-
}
|
|
253
|
-
text += '\n';
|
|
254
|
-
}
|
|
255
|
-
const response = cachedResponse(text);
|
|
256
|
-
if (cacheKey)
|
|
257
|
-
cache.set(cacheKey, response, 15000);
|
|
258
|
-
return response;
|
|
259
|
-
},
|
|
260
|
-
},
|
|
261
|
-
{
|
|
262
|
-
name: "wyrm_scaffold_get",
|
|
263
|
-
description: "Find the best reasoning scaffold for a task description. Returns the most relevant structured checklist, or null if no match meets the confidence threshold.",
|
|
264
|
-
inputSchema: {
|
|
265
|
-
type: "object",
|
|
266
|
-
properties: {
|
|
267
|
-
projectPath: { type: "string", description: "Project path (searches project + global)" },
|
|
268
|
-
task: { type: "string", description: "Task description to match against scaffolds" },
|
|
269
|
-
minConfidence: { type: "number", description: "Minimum match confidence 0-1 (default: 0.3)" },
|
|
270
|
-
},
|
|
271
|
-
required: ["task"],
|
|
272
|
-
},
|
|
273
|
-
annotations: TOOL_ANNOTATIONS["wyrm_scaffold_get"],
|
|
274
|
-
aliases: [],
|
|
275
|
-
handler: async (args, ctx) => {
|
|
276
|
-
const { cachedResponse, db, scaffoldLib } = ctx;
|
|
277
|
-
const cacheKey = currentReadCacheKey();
|
|
278
|
-
const { projectPath: sgPath, task: sgTask, minConfidence: sgConf } = args;
|
|
279
|
-
const sgProjectId = sgPath ? db.getProject(sgPath)?.id ?? undefined : undefined;
|
|
280
|
-
const match = scaffoldLib.findBest(sgTask, sgProjectId, sgConf);
|
|
281
|
-
if (!match) {
|
|
282
|
-
return { content: [{ type: "text", text: ` **Reasoning Scaffold**\n\nNo scaffold matched for: "${sgTask}"\n\nUse \`wyrm_scaffold_save\` to add scaffolds for this problem type.` }] };
|
|
283
|
-
}
|
|
284
|
-
const text = scaffoldLib.formatForContext(match);
|
|
285
|
-
const response = cachedResponse(` **Reasoning Scaffold Match** (${(match.matchScore * 100).toFixed(0)}% confidence)\n\n${text}`);
|
|
286
|
-
if (cacheKey)
|
|
287
|
-
cache.set(cacheKey, response, 15000);
|
|
288
|
-
return response;
|
|
289
|
-
},
|
|
290
|
-
},
|
|
291
|
-
{
|
|
292
|
-
name: "wyrm_sync",
|
|
293
|
-
description: "Sync database with .wyrm folders in all projects",
|
|
294
|
-
inputSchema: {
|
|
295
|
-
type: "object",
|
|
296
|
-
properties: {
|
|
297
|
-
projectPath: { type: "string", description: "Sync specific project, or all if not specified" },
|
|
298
|
-
direction: { type: "string", enum: ["import", "export", "both"], description: "Sync direction" },
|
|
299
|
-
},
|
|
300
|
-
},
|
|
301
|
-
annotations: TOOL_ANNOTATIONS["wyrm_sync"],
|
|
302
|
-
aliases: [],
|
|
303
|
-
handler: async (args, ctx) => {
|
|
304
|
-
const { db, sync } = ctx;
|
|
305
|
-
const { projectPath, direction } = args;
|
|
306
|
-
const dir = direction || 'both';
|
|
307
|
-
let count = 0;
|
|
308
|
-
if (projectPath) {
|
|
309
|
-
const project = db.getProject(projectPath);
|
|
310
|
-
if (project) {
|
|
311
|
-
if (dir === 'import' || dir === 'both')
|
|
312
|
-
sync.importFromFolder(projectPath);
|
|
313
|
-
if (dir === 'export' || dir === 'both')
|
|
314
|
-
sync.exportToFolder(projectPath);
|
|
315
|
-
count = 1;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
else {
|
|
319
|
-
const projects = db.getAllProjects(1000);
|
|
320
|
-
for (const p of projects) {
|
|
321
|
-
try {
|
|
322
|
-
if (dir === 'import' || dir === 'both')
|
|
323
|
-
sync.importFromFolder(p.path);
|
|
324
|
-
if (dir === 'export' || dir === 'both')
|
|
325
|
-
sync.exportToFolder(p.path);
|
|
326
|
-
count++;
|
|
327
|
-
}
|
|
328
|
-
catch {
|
|
329
|
-
// Skip failed syncs
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
return {
|
|
334
|
-
content: [{
|
|
335
|
-
type: "text",
|
|
336
|
-
text: ` Synced ${count} project(s)`
|
|
337
|
-
}]
|
|
338
|
-
};
|
|
339
|
-
},
|
|
340
|
-
},
|
|
341
|
-
{
|
|
342
|
-
name: "wyrm_stats",
|
|
343
|
-
description: "Use for a health snapshot of the memory database - row counts, vector index coverage, usage.",
|
|
344
|
-
inputSchema: {
|
|
345
|
-
type: "object",
|
|
346
|
-
properties: {
|
|
347
|
-
view: { type: "string", enum: ["failures"], description: "'failures' = prevented-repeat analytics" },
|
|
348
|
-
projectPath: { type: "string", description: "failures view scope" },
|
|
349
|
-
},
|
|
350
|
-
},
|
|
351
|
-
annotations: TOOL_ANNOTATIONS["wyrm_stats"],
|
|
352
|
-
aliases: [],
|
|
353
|
-
handler: async (args, ctx) => {
|
|
354
|
-
const { WRITE_TOOLS, analytics, cachedResponse, db, failures, getUsageStats, orchestrator, vectorStore } = ctx;
|
|
355
|
-
const a = args || {};
|
|
356
|
-
const cacheKey = currentReadCacheKey();
|
|
357
|
-
// v7 F2 (T017): prevented-repeat analytics view (spec FR-2). NOT
|
|
358
|
-
// cached on purpose: the failure_blocks ledger is written by
|
|
359
|
-
// wyrm_failure_check, which is not in WRITE_TOOLS, so the stats cache
|
|
360
|
-
// invalidation never fires for it — a 15s-stale "repeats blocked this
|
|
361
|
-
// run" would lie to an orchestrator polling mid-fleet, and the view is
|
|
362
|
-
// a handful of indexed COUNTs (cheap enough to stay fresh). "this run"
|
|
363
|
-
// resolves from the ambient actor envelope (failureStats → getActor()).
|
|
364
|
-
const { view: statsViewArg, projectPath: statsProjectPath } = args;
|
|
365
|
-
if (statsViewArg === "failures") {
|
|
366
|
-
const statsProject = statsProjectPath ? db.getProject(statsProjectPath) : null;
|
|
367
|
-
const failuresView = failures.failureStats({ projectId: statsProject?.id ?? null });
|
|
368
|
-
return {
|
|
369
|
-
content: [{ type: "text", text: renderFailureStats(failuresView) }],
|
|
370
|
-
// Pre-T019 dual-emit (the T011/T014 precedent): structuredContent
|
|
371
|
-
// carries the canonical view; outputSchema lands with F3 ToolSpec.
|
|
372
|
-
structuredContent: failuresView,
|
|
373
|
-
};
|
|
374
|
-
}
|
|
375
|
-
const stats = db.getStats();
|
|
376
|
-
const cacheStats = cache.stats();
|
|
377
|
-
const usage = getUsageStats();
|
|
378
|
-
// Vector coverage stats
|
|
379
|
-
let vectorStatsText = '';
|
|
380
|
-
if (vectorStore) {
|
|
381
|
-
const vectorStats = vectorStore.getStats();
|
|
382
|
-
const totalEmbeddable = stats.sessions + stats.quests + stats.dataPoints;
|
|
383
|
-
const vectorCoverage = totalEmbeddable > 0
|
|
384
|
-
? ((vectorStats.total / totalEmbeddable) * 100).toFixed(1)
|
|
385
|
-
: '0';
|
|
386
|
-
vectorStatsText =
|
|
387
|
-
`\n**Vectors:**\n` +
|
|
388
|
-
`- **Provider:** ${vectorStats.provider} (${vectorStats.model})\n` +
|
|
389
|
-
`- **Embeddings:** ${vectorStats.total} vectors\n` +
|
|
390
|
-
`- **Coverage:** ${vectorCoverage}% of embeddable content\n` +
|
|
391
|
-
`- **By Type:** ${Object.entries(vectorStats.byType).map(([t, c]) => `${t}: ${c}`).join(', ')}\n`;
|
|
392
|
-
}
|
|
393
|
-
const response = cachedResponse(` **Wyrm Statistics**\n\n` +
|
|
394
|
-
`- **Projects:** ${stats.projects}\n` +
|
|
395
|
-
`- **Sessions:** ${stats.sessions}\n` +
|
|
396
|
-
`- **Quests:** ${stats.quests}\n` +
|
|
397
|
-
`- **Data Points:** ${stats.dataPoints}\n` +
|
|
398
|
-
`- **Active Tokens:** ~${stats.totalTokens.toLocaleString()}\n` +
|
|
399
|
-
`- **Database Size:** ${stats.dbSize}\n` +
|
|
400
|
-
vectorStatsText +
|
|
401
|
-
`\n**Cache:** ${cacheStats.size} entries | Hit rate: ${usage.cacheHitRate}\n` +
|
|
402
|
-
`**Usage:** ${usage.totalCalls} calls | ~${usage.tokensSaved.toLocaleString()} tokens saved by cache`);
|
|
403
|
-
if (cacheKey)
|
|
404
|
-
cache.set(cacheKey, response, 15000);
|
|
405
|
-
return response;
|
|
406
|
-
},
|
|
407
|
-
},
|
|
408
|
-
{
|
|
409
|
-
name: "wyrm_vector_setup",
|
|
410
|
-
description: "Configure embedding provider and download models. Supports local, OpenAI, and Ollama.",
|
|
411
|
-
inputSchema: {
|
|
412
|
-
type: "object",
|
|
413
|
-
properties: {
|
|
414
|
-
provider: { type: "string", enum: ["auto", "local", "openai", "ollama", "none"], description: "Provider: auto-detect, local hash, OpenAI API, Ollama, or none" },
|
|
415
|
-
model: { type: "string", description: "Model name (e.g., 'text-embedding-3-small' for OpenAI, 'nomic-embed-text' for Ollama)" },
|
|
416
|
-
apiKey: { type: "string", description: "OpenAI API key (if using openai provider)" },
|
|
417
|
-
ollamaUrl: { type: "string", description: "Ollama URL (default: http://localhost:11434)" },
|
|
418
|
-
},
|
|
419
|
-
},
|
|
420
|
-
annotations: TOOL_ANNOTATIONS["wyrm_vector_setup"],
|
|
421
|
-
aliases: [],
|
|
422
|
-
handler: async (args, ctx) => {
|
|
423
|
-
const { db, indexingPipeline, memory, setVectorStore, vectorStore } = ctx;
|
|
424
|
-
const { provider: prov, model: mod, apiKey: key, ollamaUrl: url } = args;
|
|
425
|
-
// Build new provider config
|
|
426
|
-
const providerConfig = {
|
|
427
|
-
provider: (prov || 'auto'),
|
|
428
|
-
model: mod,
|
|
429
|
-
apiKey: key,
|
|
430
|
-
ollamaUrl: url,
|
|
431
|
-
};
|
|
432
|
-
try {
|
|
433
|
-
// Create new provider to test
|
|
434
|
-
const testProvider = createProvider(providerConfig);
|
|
435
|
-
const isReady = await testProvider.isReady();
|
|
436
|
-
if (!isReady && providerConfig.provider !== 'none') {
|
|
437
|
-
return {
|
|
438
|
-
content: [{
|
|
439
|
-
type: "text",
|
|
440
|
-
text: `❌ **Provider Not Ready**\n\nCouldn't connect to ${testProvider.name}. Check your configuration and try again.`
|
|
441
|
-
}],
|
|
442
|
-
isError: true
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
|
-
// Update global vector store
|
|
446
|
-
const __vs = createVectorStore(providerConfig, db.getDatabase());
|
|
447
|
-
setVectorStore(__vs);
|
|
448
|
-
memory.setVectorStore(__vs); // keep hybrid recall pointed at the live store
|
|
449
|
-
if (indexingPipeline)
|
|
450
|
-
indexingPipeline.updateStore(__vs);
|
|
451
|
-
const stats = __vs.getStats();
|
|
452
|
-
return {
|
|
453
|
-
content: [{
|
|
454
|
-
type: "text",
|
|
455
|
-
text: `✅ **Vector Setup Complete**\n\n` +
|
|
456
|
-
`- **Provider:** ${testProvider.name}\n` +
|
|
457
|
-
`- **Model:** ${testProvider.model}\n` +
|
|
458
|
-
`- **Dimensions:** ${testProvider.dimensions}\n` +
|
|
459
|
-
`- **Status:** ${isReady ? 'Ready' : 'Ready (will auto-connect)'}\n\n` +
|
|
460
|
-
`_Reindex your projects with wyrm_reindex to generate embeddings._`
|
|
461
|
-
}]
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
catch (err) {
|
|
465
|
-
return {
|
|
466
|
-
content: [{
|
|
467
|
-
type: "text",
|
|
468
|
-
text: `❌ **Vector Setup Failed**\n\n${String(err)}`
|
|
469
|
-
}],
|
|
470
|
-
isError: true
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
},
|
|
474
|
-
},
|
|
475
|
-
{
|
|
476
|
-
name: "wyrm_reindex",
|
|
477
|
-
description: "Rebuild embeddings for a project or all projects. Regenerates vectors for sessions, quests, data, and memory artifacts (the latter power hybrid recall).",
|
|
478
|
-
inputSchema: {
|
|
479
|
-
type: "object",
|
|
480
|
-
properties: {
|
|
481
|
-
projectPath: { type: "string", description: "Project to reindex, or leave empty for all projects" },
|
|
482
|
-
dryRun: { type: "boolean", description: "Show what would be indexed without making changes" },
|
|
483
|
-
},
|
|
484
|
-
},
|
|
485
|
-
annotations: TOOL_ANNOTATIONS["wyrm_reindex"],
|
|
486
|
-
aliases: [],
|
|
487
|
-
handler: async (args, ctx) => {
|
|
488
|
-
const { db, failures, vectorStore } = ctx;
|
|
489
|
-
const { projectPath: ppath, dryRun } = args;
|
|
490
|
-
if (!vectorStore) {
|
|
491
|
-
return {
|
|
492
|
-
content: [{
|
|
493
|
-
type: "text",
|
|
494
|
-
text: `❌ **Vector Indexing Disabled**\n\nRun wyrm_vector_setup first to enable vector search.`
|
|
495
|
-
}],
|
|
496
|
-
isError: true
|
|
497
|
-
};
|
|
498
|
-
}
|
|
499
|
-
let projectIds = [];
|
|
500
|
-
if (ppath) {
|
|
501
|
-
const proj = db.getProject(ppath);
|
|
502
|
-
if (!proj) {
|
|
503
|
-
return {
|
|
504
|
-
content: [{
|
|
505
|
-
type: "text",
|
|
506
|
-
text: `❌ **Project Not Found:** ${ppath}`
|
|
507
|
-
}],
|
|
508
|
-
isError: true
|
|
509
|
-
};
|
|
510
|
-
}
|
|
511
|
-
projectIds = [proj.id];
|
|
512
|
-
}
|
|
513
|
-
else {
|
|
514
|
-
const allProjs = db.getAllProjects(1000);
|
|
515
|
-
projectIds = allProjs.map(p => p.id);
|
|
516
|
-
}
|
|
517
|
-
// v7 F3 (T023): the project loop moved VERBATIM to src/reindex.ts
|
|
518
|
-
// (reindexProjects) so the new `wyrm index rebuild` CLI subcommand and
|
|
519
|
-
// this tool run the SAME code path. Per-row failures still log here.
|
|
520
|
-
const { indexed, skipped } = await reindexProjects(db.getDatabase(), vectorStore, projectIds, {
|
|
521
|
-
dryRun,
|
|
522
|
-
onError: (message, context) => logger.error(message, context),
|
|
523
|
-
});
|
|
524
|
-
return {
|
|
525
|
-
content: [{
|
|
526
|
-
type: "text",
|
|
527
|
-
text: ` **Reindexing ${dryRun ? '(Dry Run)' : 'Complete'}**\n\n` +
|
|
528
|
-
`- **Projects:** ${projectIds.length}\n` +
|
|
529
|
-
`- **Indexed:** ${indexed}\n` +
|
|
530
|
-
`- **Skipped:** ${skipped}\n\n` +
|
|
531
|
-
(dryRun ? '_Run without dryRun to actually index._' : '_Vectors are now up to date._')
|
|
532
|
-
}]
|
|
533
|
-
};
|
|
534
|
-
},
|
|
535
|
-
},
|
|
536
|
-
{
|
|
537
|
-
name: "wyrm_maintenance",
|
|
538
|
-
description: "Use for periodic upkeep of the memory store - archive old sessions, vacuum, prune stale events, sweep run quarantine, rebuild vector embeddings (reindex). Admin-gated, destructive; fleets run it between waves.",
|
|
539
|
-
inputSchema: {
|
|
540
|
-
type: "object",
|
|
541
|
-
properties: {
|
|
542
|
-
vacuum: { type: "boolean", description: "Reclaim space" },
|
|
543
|
-
archiveDays: { type: "number", description: "Archive sessions > N days" },
|
|
544
|
-
},
|
|
545
|
-
},
|
|
546
|
-
annotations: TOOL_ANNOTATIONS["wyrm_maintenance"],
|
|
547
|
-
aliases: [],
|
|
548
|
-
handler: async (args, ctx) => {
|
|
549
|
-
const { db, failures, presence, sessionSeen } = ctx;
|
|
550
|
-
const a = args || {};
|
|
551
|
-
const { vacuum, archiveDays } = args;
|
|
552
|
-
// v7 F3 (T023): the step sequence moved VERBATIM to
|
|
553
|
-
// src/maintenance.ts (runMaintenance) so the new `wyrm maintenance`
|
|
554
|
-
// CLI subcommand and this tool run the SAME code path. The 6.x text
|
|
555
|
-
// is reassembled byte-identically below (every line carried "- " and
|
|
556
|
-
// a trailing newline).
|
|
557
|
-
// v7 F3 (T029): + presence — stale-claim/presence eviction now rides
|
|
558
|
-
// maintenance (reap previously ran only inline at claim time).
|
|
559
|
-
const report = runMaintenance({ db, sessionSeen: sessionSeen(), failures, presence }, { vacuum, archiveDays });
|
|
560
|
-
const text = ' **Maintenance Complete**\n\n' +
|
|
561
|
-
report.lines.map((line) => `- ${line}\n`).join('') +
|
|
562
|
-
`\n**New Database Size:** ${report.dbSize}`;
|
|
563
|
-
return { content: [{ type: "text", text }] };
|
|
564
|
-
},
|
|
565
|
-
},
|
|
566
|
-
{
|
|
567
|
-
name: "wyrm_setup",
|
|
568
|
-
description: "Auto-detect installed AI clients (VS Code, Claude Desktop, Cursor, Windsurf, Zed) and configure Wyrm's MCP server in all of them. Run this to connect Wyrm to a new AI or after switching providers.",
|
|
569
|
-
inputSchema: {
|
|
570
|
-
type: "object",
|
|
571
|
-
properties: {
|
|
572
|
-
action: { type: "string", enum: ["configure", "check", "remove"], description: "Action: configure (default), check status, or remove from all" },
|
|
573
|
-
serverPath: { type: "string", description: "Override Wyrm server path (auto-detected if empty)" },
|
|
574
|
-
dbPath: { type: "string", description: "Override database path (default: ~/.wyrm/wyrm.db)" },
|
|
575
|
-
},
|
|
576
|
-
},
|
|
577
|
-
annotations: TOOL_ANNOTATIONS["wyrm_setup"],
|
|
578
|
-
aliases: [],
|
|
579
|
-
handler: async (args, ctx) => {
|
|
580
|
-
const { action, serverPath, dbPath } = args;
|
|
581
|
-
const setupAction = action || 'configure';
|
|
582
|
-
if (setupAction === 'check') {
|
|
583
|
-
return {
|
|
584
|
-
content: [{
|
|
585
|
-
type: "text",
|
|
586
|
-
text: getStatusSummary()
|
|
587
|
-
}]
|
|
588
|
-
};
|
|
589
|
-
}
|
|
590
|
-
if (setupAction === 'remove') {
|
|
591
|
-
const results = removeFromAll();
|
|
592
|
-
const removed = results.filter(r => r.action === 'configured');
|
|
593
|
-
const text = ` **Wyrm Removed**\n\n` +
|
|
594
|
-
`Removed from ${removed.length} AI client(s):\n` +
|
|
595
|
-
results.map(r => `- ${r.client.icon} ${r.client.name}: ${r.message}`).join('\n') +
|
|
596
|
-
`\n\nRun wyrm_setup again to reconnect.`;
|
|
597
|
-
return { content: [{ type: "text", text }] };
|
|
598
|
-
}
|
|
599
|
-
// Default: configure
|
|
600
|
-
const results = autoConfigureAll({
|
|
601
|
-
serverPath: serverPath || undefined,
|
|
602
|
-
dbPath: dbPath || undefined,
|
|
603
|
-
});
|
|
604
|
-
const configured = results.filter(r => r.action === 'configured' || r.action === 'updated');
|
|
605
|
-
const failed = results.filter(r => r.action === 'failed');
|
|
606
|
-
let text = ` **Wyrm Auto-Configure Complete**\n\n`;
|
|
607
|
-
text += `Connected to ${configured.length} AI client(s):\n`;
|
|
608
|
-
for (const r of results) {
|
|
609
|
-
const icon = r.action === 'configured' ? '✅' :
|
|
610
|
-
r.action === 'updated' ? '🔄' :
|
|
611
|
-
r.action === 'skipped' ? '○' : '❌';
|
|
612
|
-
text += `- ${icon} ${r.client.icon} ${r.client.name}: ${r.message}\n`;
|
|
613
|
-
}
|
|
614
|
-
if (failed.length > 0) {
|
|
615
|
-
text += `\n⚠️ ${failed.length} client(s) failed. Check errors above.`;
|
|
616
|
-
}
|
|
617
|
-
text += `\n\nServer: ${findWyrmServerPath()}\nDB: ${getDefaultDbPath()}`;
|
|
618
|
-
text += `\n\n_Switch AIs anytime — run wyrm_setup again to reconnect._`;
|
|
619
|
-
return { content: [{ type: "text", text }] };
|
|
620
|
-
},
|
|
621
|
-
},
|
|
622
|
-
{
|
|
623
|
-
name: "wyrm_usage",
|
|
624
|
-
description: "View token usage stats, cache hit rates, and estimated cost savings. Helps monitor and optimize AI credit consumption.",
|
|
625
|
-
inputSchema: {
|
|
626
|
-
type: "object",
|
|
627
|
-
properties: {
|
|
628
|
-
last: { type: "number", description: "Show stats for last N calls (default: all)" },
|
|
629
|
-
reset: { type: "boolean", description: "Reset usage counters" },
|
|
630
|
-
},
|
|
631
|
-
},
|
|
632
|
-
annotations: TOOL_ANNOTATIONS["wyrm_usage"],
|
|
633
|
-
aliases: [],
|
|
634
|
-
handler: async (args, ctx) => {
|
|
635
|
-
const { cachedResponse, getUsageStats, responseFingerprints, usageLog } = ctx;
|
|
636
|
-
const { last, reset } = args;
|
|
637
|
-
if (reset) {
|
|
638
|
-
usageLog.length = 0;
|
|
639
|
-
responseFingerprints.clear();
|
|
640
|
-
cache.invalidate();
|
|
641
|
-
return { content: [{ type: "text", text: " Usage counters reset, caches cleared." }] };
|
|
642
|
-
}
|
|
643
|
-
const usage = getUsageStats(last);
|
|
644
|
-
const cacheStats = cache.stats();
|
|
645
|
-
let text = ` **Wyrm Usage Report**\n\n`;
|
|
646
|
-
text += `## Overview${last ? ` (last ${last} calls)` : ''}\n`;
|
|
647
|
-
text += `- **Total Calls:** ${usage.totalCalls}\n`;
|
|
648
|
-
text += `- **Cache Hits:** ${usage.cachedCalls} (${usage.cacheHitRate})\n`;
|
|
649
|
-
text += `- **Tokens In:** ~${usage.totalTokensIn.toLocaleString()}\n`;
|
|
650
|
-
text += `- **Tokens Out:** ~${usage.totalTokensOut.toLocaleString()}\n`;
|
|
651
|
-
text += `- **Tokens Saved (cache):** ~${usage.tokensSaved.toLocaleString()}\n`;
|
|
652
|
-
text += `- **Avg Response:** ${usage.avgResponseMs}ms\n`;
|
|
653
|
-
text += `- **Active Cache Entries:** ${cacheStats.size}\n\n`;
|
|
654
|
-
if (usage.topTools.length > 0) {
|
|
655
|
-
text += `## Top Tools by Token Usage\n`;
|
|
656
|
-
for (const t of usage.topTools) {
|
|
657
|
-
text += `- **${t.tool}:** ${t.calls} calls, ~${t.tokens.toLocaleString()} tokens\n`;
|
|
658
|
-
}
|
|
659
|
-
text += '\n';
|
|
660
|
-
}
|
|
661
|
-
// Cost estimate (Claude Opus pricing: $15/M input, $75/M output)
|
|
662
|
-
const costInput = (usage.totalTokensIn / 1_000_000) * 15;
|
|
663
|
-
const costOutput = (usage.totalTokensOut / 1_000_000) * 75;
|
|
664
|
-
const costSaved = (usage.tokensSaved / 1_000_000) * 75;
|
|
665
|
-
text += `## Estimated Cost (Claude Opus rates)\n`;
|
|
666
|
-
text += `- **Input:** $${costInput.toFixed(4)}\n`;
|
|
667
|
-
text += `- **Output:** $${costOutput.toFixed(4)}\n`;
|
|
668
|
-
text += `- **Saved by cache:** $${costSaved.toFixed(4)}\n`;
|
|
669
|
-
text += `- **Net cost:** $${(costInput + costOutput - costSaved).toFixed(4)}`;
|
|
670
|
-
return cachedResponse(text, true); // ephemeral — don't cache the usage report itself
|
|
671
|
-
},
|
|
672
|
-
},
|
|
673
|
-
];
|
|
674
|
-
//# sourceMappingURL=intelligence.js.map
|
|
1
|
+
import{TOOL_ANNOTATIONS as b}from"../tool-annotations.js";import{currentReadCacheKey as T}from"./boundary.js";import{autoConfigureAll as B,findWyrmServerPath as U,getDefaultDbPath as N,getStatusSummary as V,removeFromAll as W}from"../autoconfig.js";import{daemonOr as K}from"../daemon-writer.js";import{renderFailureStats as q}from"../failure-patterns.js";import{computeStaleness as H}from"../intelligence.js";import{logger as z}from"../logger.js";import{runMaintenance as G}from"../maintenance.js";import{cache as v}from"../performance.js";import{createProvider as Y}from"../providers/embedding-provider.js";import{reindexProjects as Q}from"../reindex.js";import{requireString as Z}from"../validate.js";import{createVectorStore as J}from"../vectors.js";const ue=[{name:"wyrm_feedback",description:"Record whether a recalled knowledge artifact was useful. Adjusts confidence over time \u2014 successful reuse boosts it, failure lowers it. Call after applying a recalled pattern or lesson.",inputSchema:{type:"object",properties:{artifactId:{type:"number",description:"ID of the memory artifact (from wyrm_recall results)"},success:{type:"boolean",description:"Was this artifact helpful for the task?"}},required:["artifactId","success"]},annotations:b.wyrm_feedback,aliases:[],handler:async(u,f)=>{const{memory:d}=f,{artifactId:c,success:i}=u,a=d.get(c);if(!a)return{content:[{type:"text",text:`Artifact #${c} not found.`}],isError:!0};d.recordFeedback(c,i);const t=d.get(c);return{content:[{type:"text",text:`\u{F115D} Feedback recorded ${i?"\u2705":"\u274C"}
|
|
2
|
+
|
|
3
|
+
- Artifact #${c}: "${a.problem.slice(0,60)}"
|
|
4
|
+
- New confidence: ${(t.confidence*100).toFixed(0)}%
|
|
5
|
+
- Total uses: ${t.reuse_count} (\u2705 ${t.reuse_success_count} / \u274C ${t.reuse_failure_count})`}]}}},{name:"wyrm_context_build",description:"Use to build a compact context brief for the current task under a token budget - relevant patterns, lessons, and truths in one formatted block; max_tokens elides low-score items to recallable stubs.",inputSchema:{type:"object",properties:{projectPath:{type:"string"},task:{type:"string",description:"The current task"},maxItems:{type:"number",description:"Default 10, max 20"},kinds:{type:"array",items:{type:"string"},description:"Artifact kinds (see wyrm_recall)"},minConfidence:{type:"number",description:"Default 0.3"},max_tokens:{type:"number",description:"Token budget; low-score items elide to stubs"},session_id:{type:"number",description:"Already-seen dedup"},strict_budget:{type:"boolean",description:"Also elide truths over budget"}},required:["projectPath","task"]},annotations:b.wyrm_context_build,aliases:[],handler:async(u,f)=>{const{cachedResponse:d,db:c,groundTruths:i,memory:a,runBudgetedContextBuild:t,scaffoldLib:s}=f,o=u||{},r=T(),{projectPath:e,task:n,maxItems:m,kinds:l,minConfidence:p,max_tokens:y,session_id:g,strict_budget:j}=u,h=c.getProject(e);if(!h)return{content:[{type:"text",text:`Project not found: ${e}`}],isError:!0};if(y!=null&&!process.env.WYRM_DISABLE_TOKEN_BUDGET)return t({project:h,task:n,maxTokens:y,sessionId:g,strictBudget:j===!0,kinds:l,minConfidence:p});const x=[],w=i.formatForContext(h.id);w&&x.push(w.slice(0,1200));const _=s.findBest(n,h.id);if(_){const k=s.formatForContext(_);x.push(k.slice(0,1500))}const S=a.buildContextBrief(h.id,n,{maxItems:Math.min(m??10,20),kinds:l,minConfidence:p});if(S.sections.length>0){const k=S.text.slice(0,2e3);x.push(k)}if(x.length===0)return{content:[{type:"text",text:`\u{F115D} **Context Brief**
|
|
6
|
+
|
|
7
|
+
No relevant memory found for this task yet.
|
|
8
|
+
|
|
9
|
+
As you work, use \`wyrm_remember\` to store:
|
|
10
|
+
- \u2705 Patterns that work
|
|
11
|
+
- \u26A0\uFE0F Anti-patterns to avoid
|
|
12
|
+
- \u{1F4A1} Heuristics and shortcuts
|
|
13
|
+
- \u{1F9E0} Reasoning traces from complex problems
|
|
14
|
+
|
|
15
|
+
Future context briefs will become richer over time.`}]};const R=a.getStats(h.id),I=i.getStats(h.id),E=`\u{F115D} **Context Brief** \u2014 "${n}"
|
|
16
|
+
|
|
17
|
+
`,P=[];w&&P.push(w.slice(0,1200)),_&&P.push(s.formatForContext(_).slice(0,1500));const A=P.length>0?`<!-- cache-stable -->
|
|
18
|
+
${P.join(`
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
`)}
|
|
23
|
+
<!-- /cache-stable -->
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
`:"",M=S.sections.length>0?S.text.slice(0,2e3):"";let C=`${E}${A}${M}`;C+=`
|
|
28
|
+
|
|
29
|
+
_Brief: ${I.current} ground truth${I.current!==1?"s":""}, scaffold: ${_?_.scaffold.problem_type:"none"}, ${R.total} memory artifact${R.total!==1?"s":""}.`,S.sourceIds.length>0?C+=` Memory IDs: [${S.sourceIds.join(", ")}] \u2014 use \`wyrm_feedback\` to rate._`:C+="_";try{const{logSavings:k}=await import("../statusline.js"),O=c.getDatabase(),L=O.prepare("SELECT id FROM sessions WHERE project_id = ? ORDER BY created_at DESC LIMIT 1").get(h.id),D=Math.round(A.length/4);D>0&&k(O,"wyrm_context_build","cached_preamble",Math.round(D*.9),L?.id)}catch{}const F=d(C);return r&&v.set(r,F,1e4),F}},{name:"wyrm_truth_set",description:"Use to lock in a confirmed fact or constraint as project ground truth - architecture, conventions, key decisions. Injected first into every brief; re-setting the same category+key supersedes (history kept).",inputSchema:{type:"object",properties:{projectPath:{type:"string"},category:{type:"string",description:"e.g. architecture, conventions"},key:{type:"string",description:"snake_case key"},value:{type:"string",description:"The statement"},rationale:{type:"string",description:"Why this is true"},source:{type:"string",description:"'user'|'derived'|'observed'|'confirmed'"},confidence:{type:"number",description:"0-1 (default 1.0)"},ttl_days:{type:"number",description:"Stale after N days"}},required:["projectPath","category","key","value"]},annotations:b.wyrm_truth_set,aliases:[],handler:async(u,f)=>{const{db:d,groundTruths:c,presence:i}=f,{projectPath:a,category:t,key:s,value:o,rationale:r,source:e,confidence:n,ttl_days:m}=u,l=Z("category",t,{maxLen:200}),p=d.getProject(a);if(!p)return{content:[{type:"text",text:`Project not found: ${a}`}],isError:!0};const y={category:l,key:s,value:o,rationale:r,source:e,confidence:n??1,ttl_days:m},g=await K("truth_set",p.id,y,()=>c.set(p.id,y));return v.invalidate("wyrm_truth_get"),v.invalidate("wyrm_context_build"),{content:[{type:"text",text:`\u{F115D} **Ground Truth Set** \u2705
|
|
30
|
+
|
|
31
|
+
- **Category:** ${t}
|
|
32
|
+
- **Key:** ${s}
|
|
33
|
+
- **Value:** ${o}${r?`
|
|
34
|
+
- **Rationale:** ${r}`:""}${m?`
|
|
35
|
+
- **TTL:** ${m} days`:""}
|
|
36
|
+
- **ID:** ${g.id}
|
|
37
|
+
|
|
38
|
+
This truth will be injected at the top of every \`wyrm_context_build\` response for this project.`}]}}},{name:"wyrm_truth_get",description:"Use to read the established facts and ground rules for a codebase - validated truths the AI should treat as baseline, optionally filtered by category.",inputSchema:{type:"object",properties:{projectPath:{type:"string"},category:{type:"string",description:"Category filter"}},required:["projectPath"]},annotations:b.wyrm_truth_get,aliases:[],handler:async(u,f)=>{const{cachedResponse:d,db:c,groundTruths:i}=f,a=T(),{projectPath:t,category:s}=u,o=c.getProject(t);if(!o)return{content:[{type:"text",text:`Project not found: ${t}`}],isError:!0};const r=i.getCurrent(o.id,s);if(r.length===0)return{content:[{type:"text",text:`\u{F115D} **Ground Truths**
|
|
39
|
+
|
|
40
|
+
${s?`No ground truths in category "${s}" for this project.`:"No ground truths stored yet. Use `wyrm_truth_set` to add validated facts."}`}]};const e=new Map;for(const l of r)e.has(l.category)||e.set(l.category,[]),e.get(l.category).push(l);let n=`\u{F115D} **Ground Truths** \u2014 ${r.length} active
|
|
41
|
+
|
|
42
|
+
`;for(const[l,p]of e){n+=`### ${l}
|
|
43
|
+
`;for(const y of p){const g=H(y),h=g!==null&&g>.7?"[\u26A0\uFE0F STALE] ":"";n+=`- ${h}**${y.key}:** ${y.value}`,y.confidence<1&&(n+=` _(confidence: ${(y.confidence*100).toFixed(0)}%)_`),y.rationale&&(n+=`
|
|
44
|
+
_${y.rationale}_`),g!==null&&(n+=`
|
|
45
|
+
_staleness: ${(g*100).toFixed(0)}% (TTL: ${y.ttl_days}d)_`),n+=`
|
|
46
|
+
`}n+=`
|
|
47
|
+
`}const m=d(n);return a&&v.set(a,m,15e3),m}},{name:"wyrm_scaffold_get",description:"Find the best reasoning scaffold for a task description. Returns the most relevant structured checklist, or null if no match meets the confidence threshold.",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project path (searches project + global)"},task:{type:"string",description:"Task description to match against scaffolds"},minConfidence:{type:"number",description:"Minimum match confidence 0-1 (default: 0.3)"}},required:["task"]},annotations:b.wyrm_scaffold_get,aliases:[],handler:async(u,f)=>{const{cachedResponse:d,db:c,scaffoldLib:i}=f,a=T(),{projectPath:t,task:s,minConfidence:o}=u,r=t?c.getProject(t)?.id??void 0:void 0,e=i.findBest(s,r,o);if(!e)return{content:[{type:"text",text:`\u{F115D} **Reasoning Scaffold**
|
|
48
|
+
|
|
49
|
+
No scaffold matched for: "${s}"
|
|
50
|
+
|
|
51
|
+
Use \`wyrm_scaffold_save\` to add scaffolds for this problem type.`}]};const n=i.formatForContext(e),m=d(`\u{F115D} **Reasoning Scaffold Match** (${(e.matchScore*100).toFixed(0)}% confidence)
|
|
52
|
+
|
|
53
|
+
${n}`);return a&&v.set(a,m,15e3),m}},{name:"wyrm_sync",description:"Sync database with .wyrm folders in all projects",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Sync specific project, or all if not specified"},direction:{type:"string",enum:["import","export","both"],description:"Sync direction"}}},annotations:b.wyrm_sync,aliases:[],handler:async(u,f)=>{const{db:d,sync:c}=f,{projectPath:i,direction:a}=u,t=a||"both";let s=0;if(i)d.getProject(i)&&((t==="import"||t==="both")&&c.importFromFolder(i),(t==="export"||t==="both")&&c.exportToFolder(i),s=1);else{const o=d.getAllProjects(1e3);for(const r of o)try{(t==="import"||t==="both")&&c.importFromFolder(r.path),(t==="export"||t==="both")&&c.exportToFolder(r.path),s++}catch{}}return{content:[{type:"text",text:`\u{F115D} Synced ${s} project(s)`}]}}},{name:"wyrm_stats",description:"Use for a health snapshot of the memory database - row counts, vector index coverage, usage.",inputSchema:{type:"object",properties:{view:{type:"string",enum:["failures"],description:"'failures' = prevented-repeat analytics"},projectPath:{type:"string",description:"failures view scope"}}},annotations:b.wyrm_stats,aliases:[],handler:async(u,f)=>{const{WRITE_TOOLS:d,analytics:c,cachedResponse:i,db:a,failures:t,getUsageStats:s,orchestrator:o,vectorStore:r}=f,e=u||{},n=T(),{view:m,projectPath:l}=u;if(m==="failures"){const $=l?a.getProject(l):null,x=t.failureStats({projectId:$?.id??null});return{content:[{type:"text",text:q(x)}],structuredContent:x}}const p=a.getStats(),y=v.stats(),g=s();let j="";if(r){const $=r.getStats(),x=p.sessions+p.quests+p.dataPoints,w=x>0?($.total/x*100).toFixed(1):"0";j=`
|
|
54
|
+
**Vectors:**
|
|
55
|
+
- **Provider:** ${$.provider} (${$.model})
|
|
56
|
+
- **Embeddings:** ${$.total} vectors
|
|
57
|
+
- **Coverage:** ${w}% of embeddable content
|
|
58
|
+
- **By Type:** ${Object.entries($.byType).map(([_,S])=>`${_}: ${S}`).join(", ")}
|
|
59
|
+
`}const h=i(`\u{F115D} **Wyrm Statistics**
|
|
60
|
+
|
|
61
|
+
- **Projects:** ${p.projects}
|
|
62
|
+
- **Sessions:** ${p.sessions}
|
|
63
|
+
- **Quests:** ${p.quests}
|
|
64
|
+
- **Data Points:** ${p.dataPoints}
|
|
65
|
+
- **Active Tokens:** ~${p.totalTokens.toLocaleString()}
|
|
66
|
+
- **Database Size:** ${p.dbSize}
|
|
67
|
+
`+j+`
|
|
68
|
+
**Cache:** ${y.size} entries | Hit rate: ${g.cacheHitRate}
|
|
69
|
+
**Usage:** ${g.totalCalls} calls | ~${g.tokensSaved.toLocaleString()} tokens saved by cache`);return n&&v.set(n,h,15e3),h}},{name:"wyrm_vector_setup",description:"Configure embedding provider and download models. Supports local, OpenAI, and Ollama.",inputSchema:{type:"object",properties:{provider:{type:"string",enum:["auto","local","openai","ollama","none"],description:"Provider: auto-detect, local hash, OpenAI API, Ollama, or none"},model:{type:"string",description:"Model name (e.g., 'text-embedding-3-small' for OpenAI, 'nomic-embed-text' for Ollama)"},apiKey:{type:"string",description:"OpenAI API key (if using openai provider)"},ollamaUrl:{type:"string",description:"Ollama URL (default: http://localhost:11434)"}}},annotations:b.wyrm_vector_setup,aliases:[],handler:async(u,f)=>{const{db:d,indexingPipeline:c,memory:i,setVectorStore:a,vectorStore:t}=f,{provider:s,model:o,apiKey:r,ollamaUrl:e}=u,n={provider:s||"auto",model:o,apiKey:r,ollamaUrl:e};try{const m=Y(n),l=await m.isReady();if(!l&&n.provider!=="none")return{content:[{type:"text",text:`\u274C **Provider Not Ready**
|
|
70
|
+
|
|
71
|
+
Couldn't connect to ${m.name}. Check your configuration and try again.`}],isError:!0};const p=J(n,d.getDatabase());a(p),i.setVectorStore(p),c&&c.updateStore(p);const y=p.getStats();return{content:[{type:"text",text:`\u2705 **Vector Setup Complete**
|
|
72
|
+
|
|
73
|
+
- **Provider:** ${m.name}
|
|
74
|
+
- **Model:** ${m.model}
|
|
75
|
+
- **Dimensions:** ${m.dimensions}
|
|
76
|
+
- **Status:** ${l?"Ready":"Ready (will auto-connect)"}
|
|
77
|
+
|
|
78
|
+
_Reindex your projects with wyrm_reindex to generate embeddings._`}]}}catch(m){return{content:[{type:"text",text:`\u274C **Vector Setup Failed**
|
|
79
|
+
|
|
80
|
+
${String(m)}`}],isError:!0}}}},{name:"wyrm_reindex",description:"Rebuild embeddings for a project or all projects. Regenerates vectors for sessions, quests, data, and memory artifacts (the latter power hybrid recall).",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project to reindex, or leave empty for all projects"},dryRun:{type:"boolean",description:"Show what would be indexed without making changes"}}},annotations:b.wyrm_reindex,aliases:[],handler:async(u,f)=>{const{db:d,failures:c,vectorStore:i}=f,{projectPath:a,dryRun:t}=u;if(!i)return{content:[{type:"text",text:`\u274C **Vector Indexing Disabled**
|
|
81
|
+
|
|
82
|
+
Run wyrm_vector_setup first to enable vector search.`}],isError:!0};let s=[];if(a){const e=d.getProject(a);if(!e)return{content:[{type:"text",text:`\u274C **Project Not Found:** ${a}`}],isError:!0};s=[e.id]}else s=d.getAllProjects(1e3).map(n=>n.id);const{indexed:o,skipped:r}=await Q(d.getDatabase(),i,s,{dryRun:t,onError:(e,n)=>z.error(e,n)});return{content:[{type:"text",text:`\u{F115D} **Reindexing ${t?"(Dry Run)":"Complete"}**
|
|
83
|
+
|
|
84
|
+
- **Projects:** ${s.length}
|
|
85
|
+
- **Indexed:** ${o}
|
|
86
|
+
- **Skipped:** ${r}
|
|
87
|
+
|
|
88
|
+
`+(t?"_Run without dryRun to actually index._":"_Vectors are now up to date._")}]}}},{name:"wyrm_maintenance",description:"Use for periodic upkeep of the memory store - archive old sessions, vacuum, prune stale events, sweep run quarantine, rebuild vector embeddings (reindex). Admin-gated, destructive; fleets run it between waves.",inputSchema:{type:"object",properties:{vacuum:{type:"boolean",description:"Reclaim space"},archiveDays:{type:"number",description:"Archive sessions > N days"}}},annotations:b.wyrm_maintenance,aliases:[],handler:async(u,f)=>{const{db:d,failures:c,presence:i,sessionSeen:a}=f,t=u||{},{vacuum:s,archiveDays:o}=u,r=G({db:d,sessionSeen:a(),failures:c,presence:i},{vacuum:s,archiveDays:o});return{content:[{type:"text",text:`\u{F115D} **Maintenance Complete**
|
|
89
|
+
|
|
90
|
+
`+r.lines.map(n=>`- ${n}
|
|
91
|
+
`).join("")+`
|
|
92
|
+
**New Database Size:** ${r.dbSize}`}]}}},{name:"wyrm_setup",description:"Auto-detect installed AI clients (VS Code, Claude Desktop, Cursor, Windsurf, Zed) and configure Wyrm's MCP server in all of them. Run this to connect Wyrm to a new AI or after switching providers.",inputSchema:{type:"object",properties:{action:{type:"string",enum:["configure","check","remove"],description:"Action: configure (default), check status, or remove from all"},serverPath:{type:"string",description:"Override Wyrm server path (auto-detected if empty)"},dbPath:{type:"string",description:"Override database path (default: ~/.wyrm/wyrm.db)"}}},annotations:b.wyrm_setup,aliases:[],handler:async(u,f)=>{const{action:d,serverPath:c,dbPath:i}=u,a=d||"configure";if(a==="check")return{content:[{type:"text",text:V()}]};if(a==="remove"){const e=W();return{content:[{type:"text",text:`\u{F115D} **Wyrm Removed**
|
|
93
|
+
|
|
94
|
+
Removed from ${e.filter(l=>l.action==="configured").length} AI client(s):
|
|
95
|
+
`+e.map(l=>`- ${l.client.icon} ${l.client.name}: ${l.message}`).join(`
|
|
96
|
+
`)+`
|
|
97
|
+
|
|
98
|
+
Run wyrm_setup again to reconnect.`}]}}const t=B({serverPath:c||void 0,dbPath:i||void 0}),s=t.filter(e=>e.action==="configured"||e.action==="updated"),o=t.filter(e=>e.action==="failed");let r=`\u{F115D} **Wyrm Auto-Configure Complete**
|
|
99
|
+
|
|
100
|
+
`;r+=`Connected to ${s.length} AI client(s):
|
|
101
|
+
`;for(const e of t){const n=e.action==="configured"?"\u2705":e.action==="updated"?"\u{1F504}":e.action==="skipped"?"\u25CB":"\u274C";r+=`- ${n} ${e.client.icon} ${e.client.name}: ${e.message}
|
|
102
|
+
`}return o.length>0&&(r+=`
|
|
103
|
+
\u26A0\uFE0F ${o.length} client(s) failed. Check errors above.`),r+=`
|
|
104
|
+
|
|
105
|
+
Server: ${U()}
|
|
106
|
+
DB: ${N()}`,r+=`
|
|
107
|
+
|
|
108
|
+
_Switch AIs anytime \u2014 run wyrm_setup again to reconnect._`,{content:[{type:"text",text:r}]}}},{name:"wyrm_usage",description:"View token usage stats, cache hit rates, and estimated cost savings. Helps monitor and optimize AI credit consumption.",inputSchema:{type:"object",properties:{last:{type:"number",description:"Show stats for last N calls (default: all)"},reset:{type:"boolean",description:"Reset usage counters"}}},annotations:b.wyrm_usage,aliases:[],handler:async(u,f)=>{const{cachedResponse:d,getUsageStats:c,responseFingerprints:i,usageLog:a}=f,{last:t,reset:s}=u;if(s)return a.length=0,i.clear(),v.invalidate(),{content:[{type:"text",text:"\u{F115D} Usage counters reset, caches cleared."}]};const o=c(t),r=v.stats();let e=`\u{F115D} **Wyrm Usage Report**
|
|
109
|
+
|
|
110
|
+
`;if(e+=`## Overview${t?` (last ${t} calls)`:""}
|
|
111
|
+
`,e+=`- **Total Calls:** ${o.totalCalls}
|
|
112
|
+
`,e+=`- **Cache Hits:** ${o.cachedCalls} (${o.cacheHitRate})
|
|
113
|
+
`,e+=`- **Tokens In:** ~${o.totalTokensIn.toLocaleString()}
|
|
114
|
+
`,e+=`- **Tokens Out:** ~${o.totalTokensOut.toLocaleString()}
|
|
115
|
+
`,e+=`- **Tokens Saved (cache):** ~${o.tokensSaved.toLocaleString()}
|
|
116
|
+
`,e+=`- **Avg Response:** ${o.avgResponseMs}ms
|
|
117
|
+
`,e+=`- **Active Cache Entries:** ${r.size}
|
|
118
|
+
|
|
119
|
+
`,o.topTools.length>0){e+=`## Top Tools by Token Usage
|
|
120
|
+
`;for(const p of o.topTools)e+=`- **${p.tool}:** ${p.calls} calls, ~${p.tokens.toLocaleString()} tokens
|
|
121
|
+
`;e+=`
|
|
122
|
+
`}const n=o.totalTokensIn/1e6*15,m=o.totalTokensOut/1e6*75,l=o.tokensSaved/1e6*75;return e+=`## Estimated Cost (Claude Opus rates)
|
|
123
|
+
`,e+=`- **Input:** $${n.toFixed(4)}
|
|
124
|
+
`,e+=`- **Output:** $${m.toFixed(4)}
|
|
125
|
+
`,e+=`- **Saved by cache:** $${l.toFixed(4)}
|
|
126
|
+
`,e+=`- **Net cost:** $${(n+m-l).toFixed(4)}`,d(e,!0)}}];export{ue as intelligenceToolSpecs};
|