wyrm-mcp 7.2.0 → 7.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/LICENSE +26 -667
  2. package/NOTICE +14 -33
  3. package/dist/activation.d.ts.map +1 -1
  4. package/dist/activation.js +1 -44
  5. package/dist/activation.js.map +1 -1
  6. package/dist/agent-daemon.js +4 -281
  7. package/dist/agent-loop.js +7 -332
  8. package/dist/analytics.js +13 -236
  9. package/dist/attribution.js +1 -49
  10. package/dist/audit.js +2 -457
  11. package/dist/auto-capture.js +3 -138
  12. package/dist/auto-orchestrator.js +1 -325
  13. package/dist/autoconfig.js +39 -840
  14. package/dist/buddy-runner.js +1 -109
  15. package/dist/buddy.js +14 -564
  16. package/dist/build-flags.js +1 -17
  17. package/dist/capabilities.js +3 -183
  18. package/dist/capture.js +1 -56
  19. package/dist/causality.js +6 -107
  20. package/dist/cli.js +20 -281
  21. package/dist/cloud/cli.js +5 -541
  22. package/dist/cloud/client.js +1 -221
  23. package/dist/cloud/crypto.js +1 -85
  24. package/dist/cloud/machine-id.js +2 -113
  25. package/dist/cloud/recovery.js +1 -60
  26. package/dist/cloud/sync-engine.js +7 -543
  27. package/dist/cloud-backup.js +5 -579
  28. package/dist/cloud-profile.js +1 -138
  29. package/dist/cloud-sync-entrypoint.js +1 -47
  30. package/dist/cloud-sync.js +2 -309
  31. package/dist/constellation.js +12 -168
  32. package/dist/context-build-budgeted.js +4 -144
  33. package/dist/context-ranking.js +1 -69
  34. package/dist/crypto.js +1 -179
  35. package/dist/daemon-write-endpoint.js +1 -290
  36. package/dist/daemon-writer.js +2 -406
  37. package/dist/database.js +43 -1110
  38. package/dist/deprecations.js +2 -162
  39. package/dist/design.js +13 -141
  40. package/dist/event-replication.js +1 -112
  41. package/dist/events-sse.js +7 -43
  42. package/dist/events.js +6 -238
  43. package/dist/failure-patterns.js +42 -659
  44. package/dist/federation.js +12 -236
  45. package/dist/goals.js +13 -101
  46. package/dist/golden.js +3 -355
  47. package/dist/handlers/agent.js +4 -165
  48. package/dist/handlers/alias-adapters.js +1 -129
  49. package/dist/handlers/aliases.js +1 -171
  50. package/dist/handlers/audit.js +1 -87
  51. package/dist/handlers/boundary.js +1 -221
  52. package/dist/handlers/capture.js +73 -1109
  53. package/dist/handlers/causality.js +7 -114
  54. package/dist/handlers/cloud.js +85 -382
  55. package/dist/handlers/companion.js +28 -459
  56. package/dist/handlers/datalake.js +7 -187
  57. package/dist/handlers/dispatch-context.js +0 -22
  58. package/dist/handlers/entity.js +25 -256
  59. package/dist/handlers/events.js +16 -335
  60. package/dist/handlers/failure.js +13 -340
  61. package/dist/handlers/goals.js +4 -296
  62. package/dist/handlers/intelligence.js +126 -674
  63. package/dist/handlers/invoicing.js +1 -70
  64. package/dist/handlers/mcpclient.js +6 -137
  65. package/dist/handlers/orchestration.js +40 -125
  66. package/dist/handlers/output-schemas.js +1 -24
  67. package/dist/handlers/presence.js +3 -99
  68. package/dist/handlers/project.js +28 -182
  69. package/dist/handlers/prompts.js +6 -157
  70. package/dist/handlers/quest.js +4 -224
  71. package/dist/handlers/recall.js +11 -218
  72. package/dist/handlers/registry.js +1 -167
  73. package/dist/handlers/resources.js +1 -288
  74. package/dist/handlers/review.js +11 -74
  75. package/dist/handlers/run.js +17 -487
  76. package/dist/handlers/search.js +15 -326
  77. package/dist/handlers/session.js +28 -615
  78. package/dist/handlers/share.js +8 -184
  79. package/dist/handlers/shims.js +1 -464
  80. package/dist/handlers/skill.js +67 -449
  81. package/dist/handlers/survivors.js +1 -120
  82. package/dist/handlers/symbols.js +8 -109
  83. package/dist/handlers/syncops.js +4 -302
  84. package/dist/handlers/types.js +1 -27
  85. package/dist/harvest.js +5 -191
  86. package/dist/hours.js +7 -156
  87. package/dist/http-auth.js +3 -321
  88. package/dist/http-fast.js +21 -1137
  89. package/dist/icons.js +1 -47
  90. package/dist/index.js +2 -924
  91. package/dist/indexer.js +4 -145
  92. package/dist/intelligence.js +31 -261
  93. package/dist/internal-dispatch.js +3 -212
  94. package/dist/keyset.js +1 -110
  95. package/dist/knowledge-graph.js +12 -176
  96. package/dist/license.d.ts +11 -0
  97. package/dist/license.d.ts.map +1 -1
  98. package/dist/license.js +2 -414
  99. package/dist/license.js.map +1 -1
  100. package/dist/logger.js +2 -199
  101. package/dist/maintenance.js +2 -148
  102. package/dist/mcp-client.js +6 -262
  103. package/dist/memory-artifacts.js +30 -449
  104. package/dist/migrate-prompt.js +2 -124
  105. package/dist/migrations.js +40 -655
  106. package/dist/performance.js +1 -228
  107. package/dist/presence.js +11 -140
  108. package/dist/priority-embed.js +5 -164
  109. package/dist/providers/embedding-provider.js +1 -196
  110. package/dist/readonly-gate.js +1 -29
  111. package/dist/rehydration.js +9 -157
  112. package/dist/reindex.js +1 -88
  113. package/dist/render-target.js +21 -514
  114. package/dist/render.js +4 -280
  115. package/dist/repl-guard.js +1 -173
  116. package/dist/replication-daemon-entrypoint.js +1 -31
  117. package/dist/replication-daemon.js +2 -262
  118. package/dist/resilience.js +1 -591
  119. package/dist/reverse-bridge.js +5 -360
  120. package/dist/security.js +1 -244
  121. package/dist/session-seen.js +3 -51
  122. package/dist/setup.js +1 -260
  123. package/dist/skill-author.js +5 -168
  124. package/dist/spec-kit.js +1 -191
  125. package/dist/sqlite-busy.js +1 -154
  126. package/dist/statusline.js +11 -315
  127. package/dist/sub-agent.js +13 -262
  128. package/dist/summarizer.js +13 -139
  129. package/dist/symbols.js +7 -283
  130. package/dist/sync.js +5 -359
  131. package/dist/tasks-dispatch.js +1 -84
  132. package/dist/tasks.js +1 -282
  133. package/dist/token-budget.js +1 -143
  134. package/dist/tool-analytics.js +7 -129
  135. package/dist/tool-annotations.js +1 -365
  136. package/dist/tool-manifest-v2.json +1 -1
  137. package/dist/tool-manifest.json +1 -1
  138. package/dist/tool-profiles.js +1 -75
  139. package/dist/trace-harvest.js +6 -244
  140. package/dist/types.js +1 -30
  141. package/dist/ui-dashboard.js +41 -50
  142. package/dist/ulid.js +1 -81
  143. package/dist/validate.js +1 -129
  144. package/dist/vault.js +1 -534
  145. package/dist/vectors.js +3 -184
  146. package/dist/version-check.js +4 -136
  147. package/dist/visibility.js +19 -155
  148. package/dist/wyrm-cli.js +98 -2451
  149. package/dist/wyrm-cli.js.map +1 -1
  150. package/dist/wyrm-guard.js +14 -424
  151. package/dist/wyrm-loop.js +3 -150
  152. package/dist/wyrm-manifest.json +1 -1
  153. package/dist/wyrm-statusline-daemon.js +1 -11
  154. package/dist/wyrm-statusline.js +4 -56
  155. package/dist/wyrm-ui.js +9 -77
  156. package/package.json +4 -2
@@ -1,459 +1,28 @@
1
- /**
2
- * @copyright 2026 Ghost Protocol (Pvt) Ltd.
3
- * @license AGPL-3.0-or-later
4
- */
5
- import { TOOL_ANNOTATIONS } from "../tool-annotations.js";
6
- import { injectSystemPrompt } from "../autoconfig.js";
7
- import { buildPeerBuddyReply } from "../buddy.js";
8
- import { getCapabilities, renderCapabilityBriefing } from "../capabilities.js";
9
- import { getUpdateStatus } from "../version-check.js";
10
- import { isCompanionBuddyCall } from "./alias-adapters.js";
11
- import { spawn as cpSpawn } from "child_process";
12
- export const companionToolSpecs = [
13
- {
14
- name: "wyrm_inject_prompt",
15
- description: "Opt-in: inject a Wyrm session-prime instruction block into .github/copilot-instructions.md and/or .cursor/rules for a project. Uses managed markers so re-runs are safe.",
16
- inputSchema: {
17
- type: "object",
18
- properties: {
19
- project_path: { type: "string", description: "Absolute path to the project root" },
20
- clients: { type: "array", items: { type: "string" }, description: "Clients to inject into: 'copilot', 'cursor' (default: both detected)" },
21
- },
22
- required: ["project_path"],
23
- },
24
- annotations: TOOL_ANNOTATIONS["wyrm_inject_prompt"],
25
- aliases: [],
26
- handler: async (args, ctx) => {
27
- const { project_path: injPath, clients: injClients } = args;
28
- const injectResult = injectSystemPrompt(injPath, injClients ?? []);
29
- let injText = `󱅝 **System Prompt Injection**\n\n`;
30
- if (injectResult.injected.length > 0) {
31
- injText += `✅ Injected into:\n${injectResult.injected.map((f) => `- ${f}`).join('\n')}\n\n`;
32
- }
33
- if (injectResult.skipped.length > 0) {
34
- injText += `○ Skipped (unknown client): ${injectResult.skipped.join(', ')}\n\n`;
35
- }
36
- if (injectResult.errors.length > 0) {
37
- injText += `❌ Errors:\n${injectResult.errors.map((e) => `- ${e}`).join('\n')}\n\n`;
38
- }
39
- if (injectResult.injected.length > 0) {
40
- injText += `AI models in this project will now call \`wyrm_session_prime\` at the start of each conversation.`;
41
- }
42
- return { content: [{ type: "text", text: injText }] };
43
- },
44
- },
45
- {
46
- name: "buddy",
47
- description: "Use when another buddy-compatible MCP server (or the operator) asks Wyrm for a brief, data-grounded status reply - the Buddy Protocol v1.0 well-known entry point. Companion params switch to the full companion reply; bare calls stay protocol-pure. Cycle-protected.",
48
- inputSchema: {
49
- type: "object",
50
- properties: {
51
- // v7 F3 (T026): property prose compressed to fund the hot-path
52
- // outputSchemas under the 8K default-surface pin (T022/T025 trade).
53
- from_buddy: { type: "string", description: "Caller identity" },
54
- project_hint: { type: "string", description: "Scope the reply" },
55
- mood_hint: { type: "string", enum: ["normal", "celebratory", "stuck", "quiet"] },
56
- size: { type: "string", enum: ["full", "compact", "mini"] },
57
- format: { type: "string", enum: ["markdown", "json"], description: "Peer mode only." },
58
- project_path: { type: "string", description: "Companion mode: project root (default cwd)" },
59
- persona: { type: "string", enum: ["wyrm", "drogo", "berlin", "sumair", "custom"], description: "Companion voice." },
60
- persona_name: { type: "string", description: "For persona=custom" },
61
- mood: { type: "string", description: "Companion override (mood_hint values)" },
62
- federate: { type: "boolean", description: "Fold in other buddy MCPs" },
63
- },
64
- },
65
- annotations: TOOL_ANNOTATIONS["buddy"],
66
- aliases: [],
67
- handler: async (args, ctx) => {
68
- const { WYRM_PACKAGE_VERSION, db, runCompanionBuddy } = ctx;
69
- const a = args || {};
70
- // v7 F3 (T021) buddy fold: wyrm_buddy's companion params live here as
71
- // ADDITIVE optional args. Any of them present → the SAME companion
72
- // code path wyrm_buddy runs (runCompanionBuddy — one implementation,
73
- // not a duplicate). Absent → Buddy Protocol v1.0 peer behavior,
74
- // byte-identical to 6.x ("protocol behavior unchanged when absent").
75
- if (isCompanionBuddyCall(args ?? {})) {
76
- return await runCompanionBuddy(args ?? {});
77
- }
78
- // Buddy Protocol v1.0 — peer entry point. Other MCP servers' buddies
79
- // call this. Project-agnostic by default; project_hint scopes it.
80
- const peerReq = args;
81
- const reply = buildPeerBuddyReply(db.getDatabase(), peerReq, WYRM_PACKAGE_VERSION);
82
- if (peerReq.format === 'json') {
83
- return { content: [{ type: 'text', text: JSON.stringify(reply.json, null, 2) }] };
84
- }
85
- return { content: [{ type: 'text', text: reply.markdown }] };
86
- },
87
- },
88
- {
89
- name: "wyrm_buddy",
90
- description: "Friendly, data-grounded coding companion. Returns a brief greeting + project state + suggested next step sourced entirely from Wyrm's real data. Every claim cites a real DB row ID — no hallucinated encouragement. Personas: wyrm (default, dragon-themed), drogo (gruff), berlin (precise), sumair (warm), custom. Moods: normal / celebratory / stuck / quiet — auto-detected. Sizes: full (10-line PhantomDragon-style mascot), compact (5-line workspace-friendly), mini (single-line inline prefix). Federates with other registered MCPs that expose `buddy`, `*_buddy`, or `buddy_*` tools — their replies fold into a 'From other buddies' section.",
91
- inputSchema: {
92
- type: "object",
93
- properties: {
94
- project_path: { type: "string", description: "Project root. Default: current cwd if registered." },
95
- mood: { type: "string", enum: ["normal", "celebratory", "stuck", "quiet"], description: "Override the auto-detected mood." },
96
- persona: { type: "string", enum: ["wyrm", "drogo", "berlin", "sumair", "custom"], description: "Voice. Default: WYRM_BUDDY_DEFAULT_PERSONA env or 'wyrm'." },
97
- persona_name: { type: "string", description: "Required when persona === 'custom'." },
98
- size: { type: "string", enum: ["full", "compact", "mini"], description: "Mascot size. 'full' = 10-line head+shoulder banner (default), 'compact' = 5-line workspace-friendly, 'mini' = single-line inline prefix on the greeting." },
99
- federate: { type: "boolean", description: "Query other registered MCPs with *_buddy tools and fold their replies in. Default: true if any are registered." },
100
- },
101
- },
102
- annotations: TOOL_ANNOTATIONS["wyrm_buddy"],
103
- aliases: [],
104
- handler: async (args, ctx) => {
105
- const { runCompanionBuddy } = ctx;
106
- // v7 F3 (T021): alias of the well-known `buddy` tool (the buddy fold).
107
- // Routes to the EXACT code path buddy's companion mode runs; the alias
108
- // spine (handlers/aliases.ts) carries the argument adapter and the
109
- // golden replay CI proves equivalence on the live wire.
110
- return await runCompanionBuddy(args ?? {});
111
- },
112
- },
113
- {
114
- name: "wyrm_token_set",
115
- description: "Set a design token for a project. Tokens are colour / type / spacing / motion / shadow / radius / breakpoint / custom — the design system primitives the project is built on. Stored first-class so AI helpers working on creative tasks don't have to re-derive the system from scattered CSS each time. Upserts on (project_id, category, key).",
116
- inputSchema: {
117
- type: "object",
118
- properties: {
119
- projectPath: { type: "string", description: "Project root path." },
120
- category: { type: "string", enum: ["color", "type", "spacing", "motion", "shadow", "radius", "breakpoint", "custom"], description: "Token category." },
121
- key: { type: "string", description: "Short slug, e.g. 'primary' / 'h1' / 'page-padding-md'." },
122
- value: { type: "string", description: "Actual value: '#00B89F' / 'Inter 900 2.5rem' / '1.25rem' / etc." },
123
- notes: { type: "string", description: "Optional why / when-to-use guidance." },
124
- source: { type: "string", enum: ["user", "derived", "imported", "observed"], description: "How the token was determined. Default: 'user'." },
125
- },
126
- required: ["projectPath", "category", "key", "value"],
127
- },
128
- annotations: TOOL_ANNOTATIONS["wyrm_token_set"],
129
- aliases: [],
130
- handler: async (args, ctx) => {
131
- const { db, designSystem } = ctx;
132
- const { projectPath: tsPath, category: tsCat, key: tsKey, value: tsVal, notes: tsNotes, source: tsSrc } = args;
133
- const proj = db.getProject(tsPath);
134
- if (!proj)
135
- return { content: [{ type: 'text', text: `Project not found: ${tsPath}` }], isError: true };
136
- const token = designSystem().setToken({ projectId: proj.id, category: tsCat, key: tsKey, value: tsVal, notes: tsNotes, source: tsSrc });
137
- return { content: [{ type: 'text', text: `🎨 token set: \`${token.category}/${token.key}\` = \`${token.value}\`` + (token.notes ? ` — ${token.notes}` : '') }] };
138
- },
139
- },
140
- {
141
- name: "wyrm_token_get",
142
- description: "Retrieve all design tokens for a project, optionally filtered by category. Returns the design system primitives the project is built on.",
143
- inputSchema: {
144
- type: "object",
145
- properties: {
146
- projectPath: { type: "string", description: "Project root path." },
147
- category: { type: "string", enum: ["color", "type", "spacing", "motion", "shadow", "radius", "breakpoint", "custom"], description: "Filter by category. Omit for all tokens." },
148
- },
149
- required: ["projectPath"],
150
- },
151
- annotations: TOOL_ANNOTATIONS["wyrm_token_get"],
152
- aliases: [],
153
- handler: async (args, ctx) => {
154
- const { db, designSystem } = ctx;
155
- const { projectPath: tgPath, category: tgCat } = args;
156
- const proj = db.getProject(tgPath);
157
- if (!proj)
158
- return { content: [{ type: 'text', text: `Project not found: ${tgPath}` }], isError: true };
159
- const md = designSystem().formatForContext(proj.id);
160
- if (!md)
161
- return { content: [{ type: 'text', text: `_(no design tokens for ${proj.name} yet — use \`wyrm_token_set\` to start the design system)_` }] };
162
- // If category filter, slice the markdown
163
- if (tgCat) {
164
- const tokens = designSystem().listTokens(proj.id, tgCat);
165
- if (tokens.length === 0)
166
- return { content: [{ type: 'text', text: `_(no ${tgCat} tokens for ${proj.name})_` }] };
167
- const lines = [`### 🎨 ${tgCat} tokens for ${proj.name}`, ''];
168
- for (const t of tokens)
169
- lines.push(`- \`${t.key}\` = \`${t.value}\`${t.notes ? ` — ${t.notes}` : ''}`);
170
- return { content: [{ type: 'text', text: lines.join('\n') }] };
171
- }
172
- return { content: [{ type: 'text', text: md }] };
173
- },
174
- },
175
- {
176
- name: "wyrm_token_delete",
177
- description: "Remove a design token from a project.",
178
- inputSchema: {
179
- type: "object",
180
- properties: {
181
- projectPath: { type: "string", description: "Project root path." },
182
- category: { type: "string", enum: ["color", "type", "spacing", "motion", "shadow", "radius", "breakpoint", "custom"] },
183
- key: { type: "string", description: "Token key to remove." },
184
- },
185
- required: ["projectPath", "category", "key"],
186
- },
187
- annotations: TOOL_ANNOTATIONS["wyrm_token_delete"],
188
- aliases: [],
189
- handler: async (args, ctx) => {
190
- const { db, designSystem } = ctx;
191
- const { projectPath: tdPath, category: tdCat, key: tdKey } = args;
192
- const proj = db.getProject(tdPath);
193
- if (!proj)
194
- return { content: [{ type: 'text', text: `Project not found: ${tdPath}` }], isError: true };
195
- const ok = designSystem().deleteToken(proj.id, tdCat, tdKey);
196
- return { content: [{ type: 'text', text: ok ? `🗑 removed \`${tdCat}/${tdKey}\`` : `not found: \`${tdCat}/${tdKey}\`` }] };
197
- },
198
- },
199
- {
200
- name: "wyrm_reference_add",
201
- description: "Clip a design reference (URL, image path, palette, or inline snippet) for inspiration. Stored per-project (or globally if no project path). Tag aggressively — tags drive future search and recall when building a new project in the same vein.",
202
- inputSchema: {
203
- type: "object",
204
- properties: {
205
- projectPath: { type: "string", description: "Project root path. Omit to attach as a global reference." },
206
- kind: { type: "string", enum: ["url", "image", "palette", "snippet"], description: "Reference type." },
207
- location: { type: "string", description: "URL, file path, or inline payload (e.g. palette like '#00B89F,#ff5a55,#0a0c0e' or a code snippet)." },
208
- title: { type: "string", description: "Short title — what is this reference?" },
209
- notes: { type: "string", description: "Why you saved it / what to remember about it." },
210
- tags: { type: "string", description: "Comma-separated tags — 'hero,scroll-tied,framer-motion'." },
211
- },
212
- required: ["kind", "location"],
213
- },
214
- annotations: TOOL_ANNOTATIONS["wyrm_reference_add"],
215
- aliases: [],
216
- handler: async (args, ctx) => {
217
- const { db, designSystem } = ctx;
218
- const { projectPath: raPath, kind: raKind, location: raLoc, title: raTitle, notes: raNotes, tags: raTags } = args;
219
- let projectId = null;
220
- if (raPath) {
221
- const proj = db.getProject(raPath);
222
- if (!proj)
223
- return { content: [{ type: 'text', text: `Project not found: ${raPath}` }], isError: true };
224
- projectId = proj.id;
225
- }
226
- const ref = designSystem().addReference({ projectId, kind: raKind, location: raLoc, title: raTitle, notes: raNotes, tags: raTags });
227
- const scope = projectId ? ` (project ${projectId})` : ' (global)';
228
- return { content: [{ type: 'text', text: `📌 reference #${ref.id} saved${scope}: \`${ref.kind}\` ${ref.title ?? ref.location.slice(0, 60)}${ref.tags ? ` · tags: ${ref.tags}` : ''}` }] };
229
- },
230
- },
231
- {
232
- name: "wyrm_reference_list",
233
- description: "Browse saved design references. Filter by project or tag. Default: 50 most recent across all projects.",
234
- inputSchema: {
235
- type: "object",
236
- properties: {
237
- projectPath: { type: "string", description: "Limit to a specific project." },
238
- tag: { type: "string", description: "Filter to references containing this tag." },
239
- limit: { type: "number", description: "Max results (default 50, max 200)." },
240
- },
241
- },
242
- annotations: TOOL_ANNOTATIONS["wyrm_reference_list"],
243
- aliases: [],
244
- handler: async (args, ctx) => {
245
- const { db, designSystem } = ctx;
246
- const { projectPath: rlPath, tag: rlTag, limit: rlLimit } = args;
247
- let projectId;
248
- if (rlPath) {
249
- const proj = db.getProject(rlPath);
250
- if (!proj)
251
- return { content: [{ type: 'text', text: `Project not found: ${rlPath}` }], isError: true };
252
- projectId = proj.id;
253
- }
254
- const refs = designSystem().listReferences({ projectId, tag: rlTag, limit: rlLimit });
255
- if (refs.length === 0)
256
- return { content: [{ type: 'text', text: '_(no references match)_' }] };
257
- const lines = [`📌 **${refs.length} reference${refs.length === 1 ? '' : 's'}**`];
258
- for (const r of refs) {
259
- const scope = r.project_id ? `p${r.project_id}` : 'global';
260
- lines.push(`- [${r.id}/${scope}] \`${r.kind}\` ${r.title ?? r.location.slice(0, 60)}${r.tags ? ` · ${r.tags}` : ''}`);
261
- }
262
- return { content: [{ type: 'text', text: lines.join('\n') }] };
263
- },
264
- },
265
- {
266
- name: "wyrm_reference_search",
267
- description: "Full-text search across design references' title / notes / tags. Returns matching references sorted by recency.",
268
- inputSchema: {
269
- type: "object",
270
- properties: {
271
- query: { type: "string", description: "FTS5 query — keywords, phrases in quotes." },
272
- projectPath: { type: "string", description: "Limit to a specific project." },
273
- limit: { type: "number", description: "Max results (default 20, max 100)." },
274
- },
275
- required: ["query"],
276
- },
277
- annotations: TOOL_ANNOTATIONS["wyrm_reference_search"],
278
- aliases: [],
279
- handler: async (args, ctx) => {
280
- const { db, designSystem } = ctx;
281
- const { query: rsQuery, projectPath: rsPath, limit: rsLimit } = args;
282
- let projectId;
283
- if (rsPath) {
284
- const proj = db.getProject(rsPath);
285
- if (!proj)
286
- return { content: [{ type: 'text', text: `Project not found: ${rsPath}` }], isError: true };
287
- projectId = proj.id;
288
- }
289
- const refs = designSystem().searchReferences(rsQuery, { projectId, limit: rsLimit });
290
- if (refs.length === 0)
291
- return { content: [{ type: 'text', text: `_(no references match "${rsQuery}")_` }] };
292
- const lines = [`🔍 **${refs.length} reference${refs.length === 1 ? '' : 's'}** matching "${rsQuery}"`];
293
- for (const r of refs) {
294
- const scope = r.project_id ? `p${r.project_id}` : 'global';
295
- lines.push(`- [${r.id}/${scope}] \`${r.kind}\` ${r.title ?? r.location.slice(0, 60)}${r.tags ? ` · ${r.tags}` : ''}`);
296
- }
297
- return { content: [{ type: 'text', text: lines.join('\n') }] };
298
- },
299
- },
300
- {
301
- name: "wyrm_capabilities",
302
- description: "Use to learn what your memory system can actually do - feature inventory, why each matters, runtime state, and which tool to reach for. Call at session start instead of re-discovering the surface.",
303
- inputSchema: {
304
- type: "object",
305
- properties: {
306
- format: { type: "string", enum: ["json", "markdown"] },
307
- },
308
- },
309
- annotations: TOOL_ANNOTATIONS["wyrm_capabilities"],
310
- aliases: [],
311
- handler: async (args, ctx) => {
312
- const { WYRM_TOOL_COUNT, agentDaemon, db } = ctx;
313
- const { format: capFmt = "markdown" } = args;
314
- const report = getCapabilities(db.getDatabase(), WYRM_TOOL_COUNT, {
315
- detectVectorProvider: () => {
316
- if (process.env.WYRM_VECTOR_PROVIDER === 'openai')
317
- return 'openai';
318
- return 'ollama';
319
- },
320
- hasEncryption: () => !!process.env.WYRM_ENCRYPTION_KEY,
321
- isFederationEnabled: () => true,
322
- isAgentRunning: () => {
323
- try {
324
- return agentDaemon.status().running;
325
- }
326
- catch {
327
- return null;
328
- }
329
- },
330
- });
331
- if (capFmt === 'json') {
332
- return { content: [{ type: 'text', text: JSON.stringify(report, null, 2) }] };
333
- }
334
- return { content: [{ type: 'text', text: renderCapabilityBriefing(report) }] };
335
- },
336
- },
337
- {
338
- name: "wyrm_intro",
339
- description: "Return a plain-English explanation of what Wyrm is and what it does. Aimed at HUMANS who don't yet understand Wyrm's value — co-founders, operators, designers, anyone who didn't read the docs. Call this when a human user asks 'what does Wyrm even do?' or seems unaware Wyrm is involved in their AI assistant's behavior. NOT for AI-agent self-orientation (use wyrm_capabilities for that).",
340
- inputSchema: {
341
- type: "object",
342
- properties: {},
343
- },
344
- annotations: TOOL_ANNOTATIONS["wyrm_intro"],
345
- aliases: [],
346
- handler: async (args, ctx) => {
347
- const { WYRM_PACKAGE_VERSION } = ctx;
348
- const { renderIntro } = await import('../visibility.js');
349
- return { content: [{ type: 'text', text: renderIntro(WYRM_PACKAGE_VERSION) }] };
350
- },
351
- },
352
- {
353
- name: "wyrm_migrate_prompt",
354
- description: "Rewrite the Wyrm-managed block in this project's AI-client instruction files (.cursor/rules, .github/copilot-instructions.md, CLAUDE.md, etc.) to the current canonical version. Operator-authored content outside the <!-- wyrm:start --> / <!-- wyrm:end --> markers is preserved verbatim. Dry-run by default; pass apply:true to write changes.",
355
- inputSchema: {
356
- type: "object",
357
- properties: {
358
- project_path: { type: "string", description: "Project root containing the client config files." },
359
- apply: { type: "boolean", description: "Default false (dry-run). Set true to actually write changes." },
360
- },
361
- required: ["project_path"],
362
- },
363
- annotations: TOOL_ANNOTATIONS["wyrm_migrate_prompt"],
364
- aliases: [],
365
- handler: async (args, ctx) => {
366
- const { migrateProject, renderMigrationReport } = await import('../migrate-prompt.js');
367
- const { WYRM_INJECT_BLOCK } = await import('../autoconfig.js');
368
- const { project_path: mpPath, apply: mpApply = false } = args;
369
- if (!mpPath) {
370
- return { content: [{ type: 'text', text: 'project_path required' }], isError: true };
371
- }
372
- const results = migrateProject({ projectPath: mpPath, newBlock: WYRM_INJECT_BLOCK, apply: mpApply });
373
- return { content: [{ type: 'text', text: renderMigrationReport(results, mpApply) }] };
374
- },
375
- },
376
- {
377
- name: "wyrm_digest",
378
- description: "Render a plain-English summary of what Wyrm has done for the operator over a period. Shows counts (sessions tracked, quests completed, repeated failures blocked, ground truths set, references clipped, hours logged) and highlights so a non-technical operator can see Wyrm's actual contribution. Use this when the user asks 'what did Wyrm do?', 'is Wyrm doing anything?', or to demonstrate value to skeptics.",
379
- inputSchema: {
380
- type: "object",
381
- properties: {
382
- period: { type: "string", enum: ["today", "week", "month", "quarter", "year", "all"], description: "Reporting window (default: week)." },
383
- },
384
- },
385
- annotations: TOOL_ANNOTATIONS["wyrm_digest"],
386
- aliases: [],
387
- handler: async (args, ctx) => {
388
- const { db } = ctx;
389
- const { gatherDigest, renderDigest } = await import('../visibility.js');
390
- const { period = 'week' } = args;
391
- const data = gatherDigest(db.getDatabase(), period);
392
- return { content: [{ type: 'text', text: renderDigest(data) }] };
393
- },
394
- },
395
- {
396
- name: "wyrm_check_update",
397
- description: "Check whether a newer wyrm-mcp is available on npm. Cached 24h by default; pass force:true to bypass cache.",
398
- inputSchema: {
399
- type: "object",
400
- properties: {
401
- force: { type: "boolean", description: "Bypass the 24-hour cache and re-query the registry." },
402
- },
403
- },
404
- annotations: TOOL_ANNOTATIONS["wyrm_check_update"],
405
- aliases: [],
406
- handler: async (args, ctx) => {
407
- const { WYRM_PACKAGE_VERSION, db } = ctx;
408
- const { force = false } = args;
409
- const status = await getUpdateStatus(db.getDatabase(), WYRM_PACKAGE_VERSION, { force });
410
- let text = `󱅝 **Update check**\n\n- Current: ${status.current}\n- Latest: ${status.latest ?? 'unknown (offline?)'}\n- Update available: ${status.updateAvailable ? 'yes' : 'no'}\n- Checked: ${status.checkedAt} (${status.source})`;
411
- if (status.updateAvailable) {
412
- text += `\n\nRun \`wyrm_self_update\` with \`confirm: true\` to upgrade, or \`npm install -g wyrm-mcp@latest\` from your shell.`;
413
- }
414
- return { content: [{ type: 'text', text }] };
415
- },
416
- },
417
- {
418
- name: "wyrm_self_update",
419
- description: "Run `npm install -g wyrm-mcp@latest` to upgrade. Returns the install transcript. Requires the operator to have write access to the global npm prefix.",
420
- inputSchema: {
421
- type: "object",
422
- properties: {
423
- confirm: { type: "boolean", description: "Must be true to actually run the upgrade (safety guard)." },
424
- },
425
- required: ["confirm"],
426
- },
427
- annotations: TOOL_ANNOTATIONS["wyrm_self_update"],
428
- aliases: [],
429
- handler: async (args, ctx) => {
430
- const { confirm: selfConfirm = false } = args;
431
- if (!selfConfirm) {
432
- return {
433
- content: [{
434
- type: 'text',
435
- text: '󱅝 **Self-update declined**: call again with `confirm: true` to run `npm install -g wyrm-mcp@latest`. Note: requires write access to the global npm prefix.',
436
- }],
437
- };
438
- }
439
- const transcript = await new Promise((resolve) => {
440
- const proc = cpSpawn('npm', ['install', '-g', 'wyrm-mcp@latest'], { stdio: ['ignore', 'pipe', 'pipe'] });
441
- const chunks = [];
442
- proc.stdout.on('data', (b) => chunks.push(b.toString()));
443
- proc.stderr.on('data', (b) => chunks.push(b.toString()));
444
- proc.on('close', (code) => {
445
- chunks.push(`\n[exit ${code ?? 'unknown'}]`);
446
- resolve(chunks.join(''));
447
- });
448
- proc.on('error', (err) => resolve(`spawn failed: ${err.message}`));
449
- });
450
- return {
451
- content: [{
452
- type: 'text',
453
- text: `󱅝 **Self-update transcript**\n\n\`\`\`\n${transcript}\n\`\`\`\n\nRestart your MCP client to pick up the new binary.`,
454
- }],
455
- };
456
- },
457
- },
458
- ];
459
- //# sourceMappingURL=companion.js.map
1
+ import{TOOL_ANNOTATIONS as l}from"../tool-annotations.js";import{injectSystemPrompt as h}from"../autoconfig.js";import{buildPeerBuddyReply as g}from"../buddy.js";import{getCapabilities as f,renderCapabilityBriefing as w}from"../capabilities.js";import{getUpdateStatus as j}from"../version-check.js";import{isCompanionBuddyCall as b}from"./alias-adapters.js";import{spawn as _}from"child_process";const R=[{name:"wyrm_inject_prompt",description:"Opt-in: inject a Wyrm session-prime instruction block into .github/copilot-instructions.md and/or .cursor/rules for a project. Uses managed markers so re-runs are safe.",inputSchema:{type:"object",properties:{project_path:{type:"string",description:"Absolute path to the project root"},clients:{type:"array",items:{type:"string"},description:"Clients to inject into: 'copilot', 'cursor' (default: both detected)"}},required:["project_path"]},annotations:l.wyrm_inject_prompt,aliases:[],handler:async(o,c)=>{const{project_path:i,clients:a}=o,t=h(i,a??[]);let e=`\u{F115D} **System Prompt Injection**
2
+
3
+ `;return t.injected.length>0&&(e+=`\u2705 Injected into:
4
+ ${t.injected.map(r=>`- ${r}`).join(`
5
+ `)}
6
+
7
+ `),t.skipped.length>0&&(e+=`\u25CB Skipped (unknown client): ${t.skipped.join(", ")}
8
+
9
+ `),t.errors.length>0&&(e+=`\u274C Errors:
10
+ ${t.errors.map(r=>`- ${r}`).join(`
11
+ `)}
12
+
13
+ `),t.injected.length>0&&(e+="AI models in this project will now call `wyrm_session_prime` at the start of each conversation."),{content:[{type:"text",text:e}]}}},{name:"buddy",description:"Use when another buddy-compatible MCP server (or the operator) asks Wyrm for a brief, data-grounded status reply - the Buddy Protocol v1.0 well-known entry point. Companion params switch to the full companion reply; bare calls stay protocol-pure. Cycle-protected.",inputSchema:{type:"object",properties:{from_buddy:{type:"string",description:"Caller identity"},project_hint:{type:"string",description:"Scope the reply"},mood_hint:{type:"string",enum:["normal","celebratory","stuck","quiet"]},size:{type:"string",enum:["full","compact","mini"]},format:{type:"string",enum:["markdown","json"],description:"Peer mode only."},project_path:{type:"string",description:"Companion mode: project root (default cwd)"},persona:{type:"string",enum:["wyrm","drogo","berlin","sumair","custom"],description:"Companion voice."},persona_name:{type:"string",description:"For persona=custom"},mood:{type:"string",description:"Companion override (mood_hint values)"},federate:{type:"boolean",description:"Fold in other buddy MCPs"}}},annotations:l.buddy,aliases:[],handler:async(o,c)=>{const{WYRM_PACKAGE_VERSION:i,db:a,runCompanionBuddy:t}=c,e=o||{};if(b(o??{}))return await t(o??{});const r=o,s=g(a.getDatabase(),r,i);return r.format==="json"?{content:[{type:"text",text:JSON.stringify(s.json,null,2)}]}:{content:[{type:"text",text:s.markdown}]}}},{name:"wyrm_buddy",description:"Friendly, data-grounded coding companion. Returns a brief greeting + project state + suggested next step sourced entirely from Wyrm's real data. Every claim cites a real DB row ID \u2014 no hallucinated encouragement. Personas: wyrm (default, dragon-themed), drogo (gruff), berlin (precise), sumair (warm), custom. Moods: normal / celebratory / stuck / quiet \u2014 auto-detected. Sizes: full (10-line PhantomDragon-style mascot), compact (5-line workspace-friendly), mini (single-line inline prefix). Federates with other registered MCPs that expose `buddy`, `*_buddy`, or `buddy_*` tools \u2014 their replies fold into a 'From other buddies' section.",inputSchema:{type:"object",properties:{project_path:{type:"string",description:"Project root. Default: current cwd if registered."},mood:{type:"string",enum:["normal","celebratory","stuck","quiet"],description:"Override the auto-detected mood."},persona:{type:"string",enum:["wyrm","drogo","berlin","sumair","custom"],description:"Voice. Default: WYRM_BUDDY_DEFAULT_PERSONA env or 'wyrm'."},persona_name:{type:"string",description:"Required when persona === 'custom'."},size:{type:"string",enum:["full","compact","mini"],description:"Mascot size. 'full' = 10-line head+shoulder banner (default), 'compact' = 5-line workspace-friendly, 'mini' = single-line inline prefix on the greeting."},federate:{type:"boolean",description:"Query other registered MCPs with *_buddy tools and fold their replies in. Default: true if any are registered."}}},annotations:l.wyrm_buddy,aliases:[],handler:async(o,c)=>{const{runCompanionBuddy:i}=c;return await i(o??{})}},{name:"wyrm_token_set",description:"Set a design token for a project. Tokens are colour / type / spacing / motion / shadow / radius / breakpoint / custom \u2014 the design system primitives the project is built on. Stored first-class so AI helpers working on creative tasks don't have to re-derive the system from scattered CSS each time. Upserts on (project_id, category, key).",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project root path."},category:{type:"string",enum:["color","type","spacing","motion","shadow","radius","breakpoint","custom"],description:"Token category."},key:{type:"string",description:"Short slug, e.g. 'primary' / 'h1' / 'page-padding-md'."},value:{type:"string",description:"Actual value: '#00B89F' / 'Inter 900 2.5rem' / '1.25rem' / etc."},notes:{type:"string",description:"Optional why / when-to-use guidance."},source:{type:"string",enum:["user","derived","imported","observed"],description:"How the token was determined. Default: 'user'."}},required:["projectPath","category","key","value"]},annotations:l.wyrm_token_set,aliases:[],handler:async(o,c)=>{const{db:i,designSystem:a}=c,{projectPath:t,category:e,key:r,value:s,notes:p,source:u}=o,n=i.getProject(t);if(!n)return{content:[{type:"text",text:`Project not found: ${t}`}],isError:!0};const d=a().setToken({projectId:n.id,category:e,key:r,value:s,notes:p,source:u});return{content:[{type:"text",text:`\u{1F3A8} token set: \`${d.category}/${d.key}\` = \`${d.value}\``+(d.notes?` \u2014 ${d.notes}`:"")}]}}},{name:"wyrm_token_get",description:"Retrieve all design tokens for a project, optionally filtered by category. Returns the design system primitives the project is built on.",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project root path."},category:{type:"string",enum:["color","type","spacing","motion","shadow","radius","breakpoint","custom"],description:"Filter by category. Omit for all tokens."}},required:["projectPath"]},annotations:l.wyrm_token_get,aliases:[],handler:async(o,c)=>{const{db:i,designSystem:a}=c,{projectPath:t,category:e}=o,r=i.getProject(t);if(!r)return{content:[{type:"text",text:`Project not found: ${t}`}],isError:!0};const s=a().formatForContext(r.id);if(!s)return{content:[{type:"text",text:`_(no design tokens for ${r.name} yet \u2014 use \`wyrm_token_set\` to start the design system)_`}]};if(e){const p=a().listTokens(r.id,e);if(p.length===0)return{content:[{type:"text",text:`_(no ${e} tokens for ${r.name})_`}]};const u=[`### \u{1F3A8} ${e} tokens for ${r.name}`,""];for(const n of p)u.push(`- \`${n.key}\` = \`${n.value}\`${n.notes?` \u2014 ${n.notes}`:""}`);return{content:[{type:"text",text:u.join(`
14
+ `)}]}}return{content:[{type:"text",text:s}]}}},{name:"wyrm_token_delete",description:"Remove a design token from a project.",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project root path."},category:{type:"string",enum:["color","type","spacing","motion","shadow","radius","breakpoint","custom"]},key:{type:"string",description:"Token key to remove."}},required:["projectPath","category","key"]},annotations:l.wyrm_token_delete,aliases:[],handler:async(o,c)=>{const{db:i,designSystem:a}=c,{projectPath:t,category:e,key:r}=o,s=i.getProject(t);return s?{content:[{type:"text",text:a().deleteToken(s.id,e,r)?`\u{1F5D1} removed \`${e}/${r}\``:`not found: \`${e}/${r}\``}]}:{content:[{type:"text",text:`Project not found: ${t}`}],isError:!0}}},{name:"wyrm_reference_add",description:"Clip a design reference (URL, image path, palette, or inline snippet) for inspiration. Stored per-project (or globally if no project path). Tag aggressively \u2014 tags drive future search and recall when building a new project in the same vein.",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project root path. Omit to attach as a global reference."},kind:{type:"string",enum:["url","image","palette","snippet"],description:"Reference type."},location:{type:"string",description:"URL, file path, or inline payload (e.g. palette like '#00B89F,#ff5a55,#0a0c0e' or a code snippet)."},title:{type:"string",description:"Short title \u2014 what is this reference?"},notes:{type:"string",description:"Why you saved it / what to remember about it."},tags:{type:"string",description:"Comma-separated tags \u2014 'hero,scroll-tied,framer-motion'."}},required:["kind","location"]},annotations:l.wyrm_reference_add,aliases:[],handler:async(o,c)=>{const{db:i,designSystem:a}=c,{projectPath:t,kind:e,location:r,title:s,notes:p,tags:u}=o;let n=null;if(t){const m=i.getProject(t);if(!m)return{content:[{type:"text",text:`Project not found: ${t}`}],isError:!0};n=m.id}const d=a().addReference({projectId:n,kind:e,location:r,title:s,notes:p,tags:u}),y=n?` (project ${n})`:" (global)";return{content:[{type:"text",text:`\u{1F4CC} reference #${d.id} saved${y}: \`${d.kind}\` ${d.title??d.location.slice(0,60)}${d.tags?` \xB7 tags: ${d.tags}`:""}`}]}}},{name:"wyrm_reference_list",description:"Browse saved design references. Filter by project or tag. Default: 50 most recent across all projects.",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Limit to a specific project."},tag:{type:"string",description:"Filter to references containing this tag."},limit:{type:"number",description:"Max results (default 50, max 200)."}}},annotations:l.wyrm_reference_list,aliases:[],handler:async(o,c)=>{const{db:i,designSystem:a}=c,{projectPath:t,tag:e,limit:r}=o;let s;if(t){const n=i.getProject(t);if(!n)return{content:[{type:"text",text:`Project not found: ${t}`}],isError:!0};s=n.id}const p=a().listReferences({projectId:s,tag:e,limit:r});if(p.length===0)return{content:[{type:"text",text:"_(no references match)_"}]};const u=[`\u{1F4CC} **${p.length} reference${p.length===1?"":"s"}**`];for(const n of p){const d=n.project_id?`p${n.project_id}`:"global";u.push(`- [${n.id}/${d}] \`${n.kind}\` ${n.title??n.location.slice(0,60)}${n.tags?` \xB7 ${n.tags}`:""}`)}return{content:[{type:"text",text:u.join(`
15
+ `)}]}}},{name:"wyrm_reference_search",description:"Full-text search across design references' title / notes / tags. Returns matching references sorted by recency.",inputSchema:{type:"object",properties:{query:{type:"string",description:"FTS5 query \u2014 keywords, phrases in quotes."},projectPath:{type:"string",description:"Limit to a specific project."},limit:{type:"number",description:"Max results (default 20, max 100)."}},required:["query"]},annotations:l.wyrm_reference_search,aliases:[],handler:async(o,c)=>{const{db:i,designSystem:a}=c,{query:t,projectPath:e,limit:r}=o;let s;if(e){const n=i.getProject(e);if(!n)return{content:[{type:"text",text:`Project not found: ${e}`}],isError:!0};s=n.id}const p=a().searchReferences(t,{projectId:s,limit:r});if(p.length===0)return{content:[{type:"text",text:`_(no references match "${t}")_`}]};const u=[`\u{1F50D} **${p.length} reference${p.length===1?"":"s"}** matching "${t}"`];for(const n of p){const d=n.project_id?`p${n.project_id}`:"global";u.push(`- [${n.id}/${d}] \`${n.kind}\` ${n.title??n.location.slice(0,60)}${n.tags?` \xB7 ${n.tags}`:""}`)}return{content:[{type:"text",text:u.join(`
16
+ `)}]}}},{name:"wyrm_capabilities",description:"Use to learn what your memory system can actually do - feature inventory, why each matters, runtime state, and which tool to reach for. Call at session start instead of re-discovering the surface.",inputSchema:{type:"object",properties:{format:{type:"string",enum:["json","markdown"]}}},annotations:l.wyrm_capabilities,aliases:[],handler:async(o,c)=>{const{WYRM_TOOL_COUNT:i,agentDaemon:a,db:t}=c,{format:e="markdown"}=o,r=f(t.getDatabase(),i,{detectVectorProvider:()=>process.env.WYRM_VECTOR_PROVIDER==="openai"?"openai":"ollama",hasEncryption:()=>!!process.env.WYRM_ENCRYPTION_KEY,isFederationEnabled:()=>!0,isAgentRunning:()=>{try{return a.status().running}catch{return null}}});return e==="json"?{content:[{type:"text",text:JSON.stringify(r,null,2)}]}:{content:[{type:"text",text:w(r)}]}}},{name:"wyrm_intro",description:"Return a plain-English explanation of what Wyrm is and what it does. Aimed at HUMANS who don't yet understand Wyrm's value \u2014 co-founders, operators, designers, anyone who didn't read the docs. Call this when a human user asks 'what does Wyrm even do?' or seems unaware Wyrm is involved in their AI assistant's behavior. NOT for AI-agent self-orientation (use wyrm_capabilities for that).",inputSchema:{type:"object",properties:{}},annotations:l.wyrm_intro,aliases:[],handler:async(o,c)=>{const{WYRM_PACKAGE_VERSION:i}=c,{renderIntro:a}=await import("../visibility.js");return{content:[{type:"text",text:a(i)}]}}},{name:"wyrm_migrate_prompt",description:"Rewrite the Wyrm-managed block in this project's AI-client instruction files (.cursor/rules, .github/copilot-instructions.md, CLAUDE.md, etc.) to the current canonical version. Operator-authored content outside the <!-- wyrm:start --> / <!-- wyrm:end --> markers is preserved verbatim. Dry-run by default; pass apply:true to write changes.",inputSchema:{type:"object",properties:{project_path:{type:"string",description:"Project root containing the client config files."},apply:{type:"boolean",description:"Default false (dry-run). Set true to actually write changes."}},required:["project_path"]},annotations:l.wyrm_migrate_prompt,aliases:[],handler:async(o,c)=>{const{migrateProject:i,renderMigrationReport:a}=await import("../migrate-prompt.js"),{WYRM_INJECT_BLOCK:t}=await import("../autoconfig.js"),{project_path:e,apply:r=!1}=o;if(!e)return{content:[{type:"text",text:"project_path required"}],isError:!0};const s=i({projectPath:e,newBlock:t,apply:r});return{content:[{type:"text",text:a(s,r)}]}}},{name:"wyrm_digest",description:"Render a plain-English summary of what Wyrm has done for the operator over a period. Shows counts (sessions tracked, quests completed, repeated failures blocked, ground truths set, references clipped, hours logged) and highlights so a non-technical operator can see Wyrm's actual contribution. Use this when the user asks 'what did Wyrm do?', 'is Wyrm doing anything?', or to demonstrate value to skeptics.",inputSchema:{type:"object",properties:{period:{type:"string",enum:["today","week","month","quarter","year","all"],description:"Reporting window (default: week)."}}},annotations:l.wyrm_digest,aliases:[],handler:async(o,c)=>{const{db:i}=c,{gatherDigest:a,renderDigest:t}=await import("../visibility.js"),{period:e="week"}=o,r=a(i.getDatabase(),e);return{content:[{type:"text",text:t(r)}]}}},{name:"wyrm_check_update",description:"Check whether a newer wyrm-mcp is available on npm. Cached 24h by default; pass force:true to bypass cache.",inputSchema:{type:"object",properties:{force:{type:"boolean",description:"Bypass the 24-hour cache and re-query the registry."}}},annotations:l.wyrm_check_update,aliases:[],handler:async(o,c)=>{const{WYRM_PACKAGE_VERSION:i,db:a}=c,{force:t=!1}=o,e=await j(a.getDatabase(),i,{force:t});let r=`\u{F115D} **Update check**
17
+
18
+ - Current: ${e.current}
19
+ - Latest: ${e.latest??"unknown (offline?)"}
20
+ - Update available: ${e.updateAvailable?"yes":"no"}
21
+ - Checked: ${e.checkedAt} (${e.source})`;return e.updateAvailable&&(r+="\n\nRun `wyrm_self_update` with `confirm: true` to upgrade, or `npm install -g wyrm-mcp@latest` from your shell."),{content:[{type:"text",text:r}]}}},{name:"wyrm_self_update",description:"Run `npm install -g wyrm-mcp@latest` to upgrade. Returns the install transcript. Requires the operator to have write access to the global npm prefix.",inputSchema:{type:"object",properties:{confirm:{type:"boolean",description:"Must be true to actually run the upgrade (safety guard)."}},required:["confirm"]},annotations:l.wyrm_self_update,aliases:[],handler:async(o,c)=>{const{confirm:i=!1}=o;return i?{content:[{type:"text",text:`\u{F115D} **Self-update transcript**
22
+
23
+ \`\`\`
24
+ ${await new Promise(t=>{const e=_("npm",["install","-g","wyrm-mcp@latest"],{stdio:["ignore","pipe","pipe"]}),r=[];e.stdout.on("data",s=>r.push(s.toString())),e.stderr.on("data",s=>r.push(s.toString())),e.on("close",s=>{r.push(`
25
+ [exit ${s??"unknown"}]`),t(r.join(""))}),e.on("error",s=>t(`spawn failed: ${s.message}`))})}
26
+ \`\`\`
27
+
28
+ Restart your MCP client to pick up the new binary.`}]}:{content:[{type:"text",text:"\u{F115D} **Self-update declined**: call again with `confirm: true` to run `npm install -g wyrm-mcp@latest`. Note: requires write access to the global npm prefix."}]}}}];export{R as companionToolSpecs};