wyrm-mcp 7.2.1 → 7.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/LICENSE +26 -667
  2. package/NOTICE +14 -33
  3. package/dist/activation.js +1 -60
  4. package/dist/agent-daemon.js +4 -281
  5. package/dist/agent-loop.js +7 -332
  6. package/dist/analytics.js +13 -236
  7. package/dist/attribution.js +1 -49
  8. package/dist/audit.js +2 -457
  9. package/dist/auto-capture.js +3 -138
  10. package/dist/auto-orchestrator.js +1 -325
  11. package/dist/autoconfig.js +39 -840
  12. package/dist/buddy-runner.js +1 -109
  13. package/dist/buddy.js +14 -564
  14. package/dist/build-flags.js +1 -17
  15. package/dist/capabilities.js +3 -183
  16. package/dist/capture.js +1 -56
  17. package/dist/causality.js +6 -107
  18. package/dist/cli.js +20 -281
  19. package/dist/cloud/cli.js +5 -541
  20. package/dist/cloud/client.js +1 -221
  21. package/dist/cloud/crypto.js +1 -85
  22. package/dist/cloud/machine-id.js +2 -113
  23. package/dist/cloud/recovery.js +1 -60
  24. package/dist/cloud/sync-engine.js +7 -543
  25. package/dist/cloud-backup.js +5 -579
  26. package/dist/cloud-profile.js +1 -138
  27. package/dist/cloud-sync-entrypoint.js +1 -47
  28. package/dist/cloud-sync.js +2 -309
  29. package/dist/constellation.js +12 -168
  30. package/dist/context-build-budgeted.js +4 -144
  31. package/dist/context-ranking.js +1 -69
  32. package/dist/crypto.js +1 -179
  33. package/dist/daemon-write-endpoint.js +1 -290
  34. package/dist/daemon-writer.js +2 -406
  35. package/dist/database.js +43 -1110
  36. package/dist/deprecations.js +2 -162
  37. package/dist/design.js +13 -141
  38. package/dist/event-replication.js +1 -112
  39. package/dist/events-sse.js +7 -43
  40. package/dist/events.js +6 -238
  41. package/dist/failure-patterns.js +42 -659
  42. package/dist/federation.js +12 -236
  43. package/dist/goals.js +13 -101
  44. package/dist/golden.js +3 -355
  45. package/dist/handlers/agent.js +4 -165
  46. package/dist/handlers/alias-adapters.js +1 -129
  47. package/dist/handlers/aliases.js +1 -171
  48. package/dist/handlers/audit.js +1 -87
  49. package/dist/handlers/boundary.js +1 -221
  50. package/dist/handlers/capture.js +73 -1109
  51. package/dist/handlers/causality.js +7 -114
  52. package/dist/handlers/cloud.js +85 -382
  53. package/dist/handlers/companion.js +28 -459
  54. package/dist/handlers/datalake.js +7 -187
  55. package/dist/handlers/dispatch-context.js +0 -22
  56. package/dist/handlers/entity.js +25 -256
  57. package/dist/handlers/events.js +16 -335
  58. package/dist/handlers/failure.js +13 -340
  59. package/dist/handlers/goals.js +4 -296
  60. package/dist/handlers/intelligence.js +126 -674
  61. package/dist/handlers/invoicing.js +1 -70
  62. package/dist/handlers/mcpclient.js +6 -137
  63. package/dist/handlers/orchestration.js +40 -125
  64. package/dist/handlers/output-schemas.js +1 -24
  65. package/dist/handlers/presence.js +3 -99
  66. package/dist/handlers/project.js +28 -182
  67. package/dist/handlers/prompts.js +6 -157
  68. package/dist/handlers/quest.js +4 -224
  69. package/dist/handlers/recall.js +11 -218
  70. package/dist/handlers/registry.js +1 -167
  71. package/dist/handlers/resources.js +1 -288
  72. package/dist/handlers/review.js +11 -74
  73. package/dist/handlers/run.js +17 -487
  74. package/dist/handlers/search.js +15 -326
  75. package/dist/handlers/session.js +28 -615
  76. package/dist/handlers/share.js +8 -184
  77. package/dist/handlers/shims.js +1 -464
  78. package/dist/handlers/skill.js +67 -449
  79. package/dist/handlers/survivors.js +1 -120
  80. package/dist/handlers/symbols.js +8 -109
  81. package/dist/handlers/syncops.js +4 -302
  82. package/dist/handlers/types.js +1 -27
  83. package/dist/harvest.js +5 -191
  84. package/dist/hours.js +7 -156
  85. package/dist/http-auth.js +3 -321
  86. package/dist/http-fast.js +21 -1137
  87. package/dist/icons.js +1 -47
  88. package/dist/index.js +2 -924
  89. package/dist/indexer.js +4 -145
  90. package/dist/intelligence.js +31 -261
  91. package/dist/internal-dispatch.js +3 -212
  92. package/dist/keyset.js +1 -110
  93. package/dist/knowledge-graph.js +12 -176
  94. package/dist/license.js +2 -441
  95. package/dist/logger.js +2 -199
  96. package/dist/maintenance.js +2 -148
  97. package/dist/mcp-client.js +6 -262
  98. package/dist/memory-artifacts.js +30 -449
  99. package/dist/migrate-prompt.js +2 -124
  100. package/dist/migrations.js +40 -655
  101. package/dist/performance.js +1 -228
  102. package/dist/presence.js +11 -140
  103. package/dist/priority-embed.js +5 -164
  104. package/dist/providers/embedding-provider.js +1 -196
  105. package/dist/readonly-gate.js +1 -29
  106. package/dist/rehydration.js +9 -157
  107. package/dist/reindex.js +1 -88
  108. package/dist/render-target.js +21 -514
  109. package/dist/render.js +4 -280
  110. package/dist/repl-guard.js +1 -173
  111. package/dist/replication-daemon-entrypoint.js +1 -31
  112. package/dist/replication-daemon.js +2 -262
  113. package/dist/resilience.js +1 -591
  114. package/dist/reverse-bridge.js +5 -360
  115. package/dist/security.js +1 -244
  116. package/dist/session-seen.js +3 -51
  117. package/dist/setup.js +1 -260
  118. package/dist/skill-author.js +5 -168
  119. package/dist/spec-kit.js +1 -191
  120. package/dist/sqlite-busy.js +1 -154
  121. package/dist/statusline.js +11 -315
  122. package/dist/sub-agent.js +13 -262
  123. package/dist/summarizer.js +13 -139
  124. package/dist/symbols.js +7 -283
  125. package/dist/sync.js +5 -359
  126. package/dist/tasks-dispatch.js +1 -84
  127. package/dist/tasks.js +1 -282
  128. package/dist/token-budget.js +1 -143
  129. package/dist/tool-analytics.js +7 -129
  130. package/dist/tool-annotations.js +1 -365
  131. package/dist/tool-manifest-v2.json +1 -1
  132. package/dist/tool-manifest.json +1 -1
  133. package/dist/tool-profiles.js +1 -75
  134. package/dist/trace-harvest.js +6 -244
  135. package/dist/types.js +1 -30
  136. package/dist/ui-dashboard.js +41 -50
  137. package/dist/ulid.js +1 -81
  138. package/dist/validate.js +1 -129
  139. package/dist/vault.js +1 -534
  140. package/dist/vectors.js +3 -184
  141. package/dist/version-check.js +4 -136
  142. package/dist/visibility.js +19 -155
  143. package/dist/wyrm-cli.js +98 -2464
  144. package/dist/wyrm-guard.js +14 -424
  145. package/dist/wyrm-loop.js +3 -150
  146. package/dist/wyrm-manifest.json +1 -1
  147. package/dist/wyrm-statusline-daemon.js +1 -11
  148. package/dist/wyrm-statusline.js +4 -56
  149. package/dist/wyrm-ui.js +9 -77
  150. 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};