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
package/dist/buddy.js CHANGED
@@ -1,568 +1,18 @@
1
- /**
2
- * Wyrm Buddy โ€” data-grounded coding companion (spec 015).
3
- *
4
- * Returns a brief markdown greeting + project state + suggested next
5
- * step. Every claim sources from a real Wyrm row; no hallucinated
6
- * encouragement. Persona is voice-only โ€” the AI speaks it verbatim.
7
- *
8
- * Also federates with other "buddy"-capable MCPs registered via
9
- * `wyrm_mcp_register`: any external tool name matching `*_buddy` or
10
- * `buddy_*` is queried via `wyrm_call_external` and its output is
11
- * folded into the response.
12
- *
13
- * @copyright 2026 Ghost Protocol (Pvt) Ltd.
14
- * @license AGPL-3.0-or-later โ€” dual-licensed; commercial terms: ghosts.lk@proton.me. See LICENSE.
15
- */
16
- import { ICON } from './icons.js';
17
- // ---------------------------------------------------------------------------
18
- // ASCII wyrm mascot โ€” same visual language as the PhantomDragon AI banner.
19
- // Flowing tildes for scale-ridges, `=` for spine segments, `o` (mood-swappable)
20
- // for the eye anchor. Head + shoulder profile; body suggested by the wing
21
- // silhouette and trailing ridge marks.
22
- //
23
- // Adapted from the top portion of the PhantomDragon AI ASCII (Ghost Protocol
24
- // house style) so the Wyrm buddy reads as the same lineage of mascot โ€” same
25
- // brand, smaller form factor. Mood lives only in the eye line.
26
- // ---------------------------------------------------------------------------
27
- // Canonical "Drake" alignment โ€” the famous Jerry-LeBow ASCII dragon that
28
- // PhantomDragon's house style descends from. Lines below are tab-free; if
29
- // you edit, count the leading spaces โ€” the head, eye, chest, and tail-curl
30
- // are column-aligned and shift drift kills the silhouette instantly.
31
- const WYRM_TOP = [
32
- ' __----~~~~~~~~~~~------___',
33
- ' . . ~~//====...... __--~ ~~',
34
- ' -. \\_|// |||\\\\ ~~~~~~::::... /~',
35
- ].join('\n');
36
- const WYRM_BOTTOM = [
37
- ' __---~~~.==~||\\=_ -_--~/_-~|- |\\\\ \\\\ _/~',
38
- ' .=~ | \\\\-_ \'-~7 /- / || \\ /',
39
- ' .~ .~ | \\\\ -_ / /- / || \\',
40
- ' / ____ / | \\\\ ~-_/ /|- _/ .|| \\',
41
- ' |~~ ~~|--~~~~--_ \\ ~==-/ | \\~--===~~ .\\',
42
- ' \' ~-| /| |-~\\~~ __--~~',
43
- ' \' \\~~|\\~~ --~~~~__-~',
44
- ' \\~~~==---____',
45
- ].join('\n');
46
- function wyrmEyeLine(mood) {
47
- // The eye line carries the mood. Surrounding form is invariant so the
48
- // figure reads as "same wyrm, different state". Aligned to col 9 โ€” the
49
- // chest/head column the rest of the silhouette anchors against.
50
- switch (mood) {
51
- case 'normal':
52
- return ' ___-==_ _-~o~ \\/ ||| \\\\ _/~~-';
53
- case 'celebratory':
54
- return ' ___-==_ _-~^~ \\/ ||| \\\\ * _/~~-';
55
- case 'stuck':
56
- return ' ___-==_ _-~-~ \\/ ||| \\\\ ? _/~~-';
57
- case 'quiet':
58
- return null;
59
- default:
60
- return null;
61
- }
62
- }
63
- export const BUDDY_PROTOCOL_VERSION = '1.0';
64
- export function buildPeerBuddyReply(db, req, wyrmVersion) {
65
- // Cycle protection: if we're being called back as part of our own chain.
66
- const cycleDetected = (req.from_buddy ?? '').toLowerCase().startsWith('wyrm');
67
- // Try to resolve project_hint to a real Wyrm project; fall back to global view.
68
- let project = null;
69
- if (req.project_hint && !cycleDetected) {
70
- try {
71
- // Try as path first
72
- const byPath = db.prepare('SELECT id, name FROM projects WHERE path = ?').get(req.project_hint);
73
- if (byPath) {
74
- project = byPath;
75
- }
76
- else {
77
- // Try as name
78
- const byName = db.prepare('SELECT id, name FROM projects WHERE name = ? COLLATE NOCASE LIMIT 1').get(req.project_hint);
79
- if (byName)
80
- project = byName;
81
- }
82
- }
83
- catch { /* fall through to global */ }
84
- }
85
- const size = req.size ?? 'compact';
86
- const mood = req.mood_hint ?? 'normal';
87
- // Build the highlights
88
- const highlights = [];
89
- if (project) {
90
- const inputs = gatherBuddyInputs(db, project.id);
91
- if (inputs.topPendingQuest) {
92
- highlights.push({
93
- kind: 'quest',
94
- id: `quest:${inputs.topPendingQuest.id}`,
95
- title: inputs.topPendingQuest.title,
96
- });
97
- }
98
- if (inputs.recentlyCompletedQuest) {
99
- highlights.push({
100
- kind: 'completion',
101
- id: `quest:${inputs.recentlyCompletedQuest.id}`,
102
- title: inputs.recentlyCompletedQuest.title,
103
- });
104
- }
105
- if (inputs.activeGoal) {
106
- highlights.push({
107
- kind: 'goal',
108
- id: `goal:${inputs.activeGoal.id}`,
109
- title: inputs.activeGoal.title,
110
- });
111
- }
112
- if (inputs.unresolvedFailures[0]) {
113
- highlights.push({
114
- kind: 'failure',
115
- id: `failure:${inputs.unresolvedFailures[0].id}`,
116
- title: inputs.unresolvedFailures[0].description.slice(0, 60),
117
- });
118
- }
119
- }
120
- else {
121
- // Global view: top pending quest across all projects, agent status
122
- try {
123
- const row = db.prepare(`SELECT id, title, project_id FROM quests WHERE status='pending'
124
- ORDER BY CASE priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 ELSE 3 END LIMIT 1`).get();
125
- if (row)
126
- highlights.push({ kind: 'quest', id: `quest:${row.id}`, title: row.title });
127
- }
128
- catch { /* ignore */ }
129
- }
130
- // Build the summary text
131
- let summary;
132
- if (cycleDetected) {
133
- summary = "(cycle โ€” Wyrm already in the call chain, skipping)";
134
- }
135
- else if (project) {
136
- const n = highlights.length;
137
- summary = `Wyrm sees ${n} highlight${n === 1 ? '' : 's'} for project ${project.name}.`;
138
- }
139
- else {
140
- summary = `Wyrm watching across ${countProjects(db)} projects.`;
141
- }
142
- // Markdown rendering โ€” sized + mood-aware
143
- let markdown;
144
- if (cycleDetected) {
145
- markdown = '_(wyrm: cycle detected, no reply)_';
146
- }
147
- else if (size === 'mini') {
148
- markdown = `<--==(${mood === 'celebratory' ? '^' : mood === 'stuck' ? '-' : 'o'})~~~~~~__ ${summary}`;
149
- }
150
- else if (size === 'compact') {
151
- const lines = ['๓ฑ… **wyrm:** ' + summary];
152
- for (const h of highlights.slice(0, 3)) {
153
- lines.push(`- \`${h.id}\` ${h.title}`);
154
- }
155
- markdown = lines.join('\n');
156
- }
157
- else {
158
- // full
159
- const lines = ['๓ฑ… **wyrm** (' + (project?.name ?? 'global') + ')'];
160
- lines.push('');
161
- lines.push(summary);
162
- if (highlights.length > 0) {
163
- lines.push('');
164
- for (const h of highlights)
165
- lines.push(`- \`${h.id}\` ${h.title}`);
166
- }
167
- markdown = lines.join('\n');
168
- }
169
- const json = {
170
- buddy: {
171
- name: 'wyrm',
172
- version: wyrmVersion,
173
- protocol: BUDDY_PROTOCOL_VERSION,
174
- mood,
175
- summary,
176
- highlights,
177
- },
178
- };
179
- return { markdown, json, cycleDetected };
180
- }
181
- function countProjects(db) {
182
- try {
183
- const row = db.prepare('SELECT COUNT(*) as n FROM projects').get();
184
- return row.n;
185
- }
186
- catch {
187
- return 0;
188
- }
189
- }
190
- // ---------------------------------------------------------------------------
191
- function renderWyrm(mood, size = 'full') {
192
- const eye = wyrmEyeLine(mood);
193
- if (eye === null)
194
- return '';
195
- if (size === 'mini')
196
- return renderWyrmMini(mood);
197
- if (size === 'compact')
198
- return renderWyrmCompact(mood);
199
- return [WYRM_TOP, eye, WYRM_BOTTOM].join('\n');
200
- }
201
- /**
202
- * Compact wyrm โ€” 5 lines, same visual language (flowing tildes + spine).
203
- * Use for workspace contexts where vertical space is precious. Eye line
204
- * still carries mood.
205
- */
206
- function renderWyrmCompact(mood) {
207
- const eyeChar = mood === 'celebratory' ? '^' : mood === 'stuck' ? '-' : 'o';
208
- const aura = mood === 'celebratory' ? ' *' : mood === 'stuck' ? ' ?' : '';
209
- return [
210
- ' __--~~==~~--___',
211
- ' .~//==.. ` ` __--~ ~~',
212
- ` _-~${eyeChar}~ \\/ ||\\\\ _/~~-${aura}`,
213
- ' ~==~ | \\\\_ `-~7 -~ || \\',
214
- ' .~--____==-~__--~~',
215
- ].join('\n');
216
- }
217
- /**
218
- * Mini wyrm โ€” single inline line. Use as a prefix in chat-style outputs
219
- * or as a status-bar mascot.
220
- */
221
- function renderWyrmMini(mood) {
222
- const eye = mood === 'celebratory' ? '^' : mood === 'stuck' ? '-' : 'o';
223
- return `<--==(${eye})~~~~~~__`;
224
- }
225
- // ---------------------------------------------------------------------------
226
- // Phrase library โ€” small, deterministic, no randomness in selection.
227
- // ---------------------------------------------------------------------------
228
- const GREETINGS = {
229
- wyrm: {
230
- morning: [
231
- 'morning, operator. the hoard is intact.',
232
- 'morning. the wyrm watches.',
233
- 'sun up. fresh scales, fresh code.',
234
- "morning, operator. yesterday's work is in the hoard.",
235
- "morning. nothing's missing from the cave.",
236
- "morning. the wyrm uncoils.",
237
- ],
238
- midday: [
239
- 'back from lunch?',
240
- 'midday. the wyrm has been counting truths.',
241
- 'returning to the work?',
242
- 'good โ€” half a day left.',
243
- 'the hoard is quiet. ready when you are.',
244
- 'midday check-in. all rows accounted for.',
245
- ],
246
- evening: [
247
- 'evening. golden hour for the patient operator.',
248
- 'evening. the wyrm sees you.',
249
- 'still at it? the cave is dim but the hoard glints.',
250
- 'evening. the day yields good code.',
251
- 'evening, operator. one more push?',
252
- 'evening. the wyrm warms the coils.',
253
- ],
254
- late: [
255
- "still up at this hour. pace yourself โ€” the hoard isn't going anywhere.",
256
- 'late. the wyrm sleeps with one eye open.',
257
- 'late hours. the work is good but rest is also good.',
258
- 'past midnight. wyrm watches over the hoard while you finish.',
259
- "late. don't let the dragon outlast the rider.",
260
- 'late. one final commit, then go.',
261
- ],
262
- },
263
- drogo: {
264
- morning: ['back. let\'s work.', 'morning. ready.', 'on the clock.', 'good. start.', 'no time wasted.', 'set.'],
265
- midday: ['returning.', 'pick up where you left.', 'back to it.', 'half day done.', 'work continues.', 'next.'],
266
- evening: ['evening. still focused.', 'long day. keep going.', 'evening shift.', 'one more.', 'time left.', 'finish strong.'],
267
- late: ['late. fine.', 'past hours. work or rest.', 'midnight code.', 'still here. fine.', 'late shift.', 'one last push.'],
268
- },
269
- berlin: {
270
- morning: ['Good morning. I have your queue ready.', 'Morning. Today\'s priorities below.', 'Welcome back. Status follows.', 'Morning. Allow me to brief you.', 'Good day. The state is current.', 'Morning. Ready when you are.'],
271
- midday: ['Welcome back. State summary:', 'Midday review.', 'Status remains current.', 'Continuing.', 'Picking up.', 'Resuming.'],
272
- evening: ['Good evening. End-of-day summary:', 'Evening status.', 'Late update.', 'Evening review.', 'End of working hours.', 'Wrapping up?'],
273
- late: ['It is late. Briefly:', 'Late hour. Concise update:', 'Past hours. Quick brief:', 'Midnight passed. Status:', 'Late shift. State:', 'Brief late update.'],
274
- },
275
- sumair: {
276
- morning: ["hey โ€” glad you're back.", 'morning, friend.', 'good to see you.', 'morning. you good?', 'welcome back.', "morning. let's go."],
277
- midday: ['back! how\'s it going?', 'midday vibes.', "you're doing great. keep going.", 'glad you\'re here.', 'pick a quest, any quest.', 'no pressure โ€” just progress.'],
278
- evening: ['evening. proud of today already.', 'still here? love that.', 'evening, friend.', "you've shown up. that's the win.", 'late but here.', 'one more thing?'],
279
- late: ['hey โ€” late, but I\'m here too.', 'still up? sleep is part of the work.', 'rest soon, ok?', 'late hour, soft work.', 'be kind to yourself.', 'we\'ll be here tomorrow.'],
280
- },
281
- custom: {
282
- morning: ['morning.', 'morning, friend.', 'back at it.', 'good morning.', 'starting fresh.', 'morning check.'],
283
- midday: ['midday.', 'back.', 'returning.', 'midday check.', 'half done.', 'continuing.'],
284
- evening: ['evening.', 'evening check.', 'long day.', 'almost done.', 'evening shift.', 'one more.'],
285
- late: ['late.', 'late hour.', 'midnight work.', 'past hours.', 'late shift.', 'wind down soon.'],
286
- },
287
- };
288
- const CELEBRATIONS = {
289
- wyrm: ['the hoard grows.', 'one more scale on the wyrm.', 'good work โ€” quest closed.', 'the cave deepens.'],
290
- drogo: ['done.', 'closed.', 'ship.', 'next.'],
291
- berlin: ['Quest completed. Noted.', 'Resolution recorded.', 'Closed and logged.', 'Completion noted.'],
292
- sumair: ['nice!! โœบ', 'proud of you.', 'love that.', 'huge.'],
293
- custom: ['closed.', 'done.', 'good.', 'next.'],
294
- };
295
- const STUCK_PHRASES = {
296
- wyrm: ['we have hit this scale before. let me show you where.', 'the wyrm remembers this failure pattern.', 'familiar territory โ€” last time we routed around it.'],
297
- drogo: ['we have hit this. avoid it.', 'pattern. block it.', 'known failure. route around.'],
298
- berlin: ['I notice a recurring failure pattern. Allow me to highlight it.', 'This area has prior failures on record.', 'Caution: prior failures clustered here.'],
299
- sumair: ["we hit this one before โ€” let's not repeat it.", "I see we've been here. let's go around.", "we know this trap. let's avoid it."],
300
- custom: ['known failure pattern.', 'we hit this before.', 'recurring issue.'],
301
- };
302
- // ---------------------------------------------------------------------------
303
- // Deterministic phrase selection
304
- // ---------------------------------------------------------------------------
305
- function pickPhrase(phrases, projectId, now) {
306
- // Deterministic selection by hash(projectId, dayOfYear). Same response
307
- // within a day, varies across days.
308
- const start = new Date(now.getFullYear(), 0, 0);
309
- const dayOfYear = Math.floor((now.getTime() - start.getTime()) / (24 * 60 * 60 * 1000));
310
- const idx = Math.abs((projectId * 31 + dayOfYear) % phrases.length);
311
- return phrases[idx];
312
- }
313
- function timeOfDay(now) {
314
- const h = now.getHours();
315
- if (h >= 5 && h < 12)
316
- return 'morning';
317
- if (h >= 12 && h < 17)
318
- return 'midday';
319
- if (h >= 17 && h < 23)
320
- return 'evening';
321
- return 'late';
322
- }
323
- // ---------------------------------------------------------------------------
324
- // Input gathering โ€” single function pulls all needed data from Wyrm
325
- // ---------------------------------------------------------------------------
326
- export function gatherBuddyInputs(db, projectId, now = new Date()) {
327
- // Project
328
- const project = db.prepare('SELECT name FROM projects WHERE id = ?').get(projectId);
329
- const projectName = project?.name ?? `project:${projectId}`;
330
- // Streak โ€” consecutive days with session_update or quest_complete activity.
331
- // Cheap implementation: look at distinct session dates over last 30d, walk back from today.
332
- const recentDates = db
333
- .prepare(`SELECT DISTINCT date FROM sessions WHERE project_id = ? AND date >= date('now', '-30 days') ORDER BY date DESC`)
334
- .all(projectId)
335
- .map((r) => r.date);
336
- let streakDays = 0;
337
- if (recentDates.length > 0) {
338
- const today = now.toISOString().slice(0, 10);
339
- const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
340
- if (recentDates[0] === today || recentDates[0] === yesterday) {
341
- streakDays = 1;
342
- for (let i = 1; i < recentDates.length; i++) {
343
- const expected = new Date(new Date(recentDates[i - 1]).getTime() - 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
344
- if (recentDates[i] === expected)
345
- streakDays++;
346
- else
347
- break;
348
- }
349
- }
350
- }
351
- // Last session
352
- const lastSessionRow = db
353
- .prepare('SELECT id, date, objectives, completed FROM sessions WHERE project_id = ? ORDER BY id DESC LIMIT 1')
354
- .get(projectId);
355
- // Top pending quest
356
- const topQuestRow = db
357
- .prepare(`SELECT id, title, priority, tags FROM quests WHERE project_id = ? AND status = 'pending'
358
- ORDER BY CASE priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 ELSE 3 END LIMIT 1`)
359
- .get(projectId);
360
- // Recently completed quest (within last hour)
361
- const recentlyCompleted = (() => {
362
- try {
363
- return db.prepare(`SELECT id, title, completed_at FROM quests WHERE project_id = ? AND status = 'completed'
364
- AND completed_at >= datetime('now', '-1 hour') ORDER BY completed_at DESC LIMIT 1`)
365
- .get(projectId);
366
- }
367
- catch {
368
- return undefined;
369
- }
370
- })();
371
- // Unresolved failures in last 7 days
372
- let unresolvedFailures = [];
373
- try {
374
- unresolvedFailures = db.prepare(`SELECT id, scope, target, description, occurrences FROM failure_patterns
1
+ import{ICON as m}from"./icons.js";const $=[" __----~~~~~~~~~~~------___"," . . ~~//====...... __--~ ~~"," -. \\_|// |||\\\\ ~~~~~~::::... /~"].join(`
2
+ `),S=[" __---~~~.==~||\\=_ -_--~/_-~|- |\\\\ \\\\ _/~"," .=~ | \\\\-_ '-~7 /- / || \\ /"," .~ .~ | \\\\ -_ / /- / || \\"," / ____ / | \\\\ ~-_/ /|- _/ .|| \\"," |~~ ~~|--~~~~--_ \\ ~==-/ | \\~--===~~ .\\"," ' ~-| /| |-~\\~~ __--~~"," ' \\~~|\\~~ --~~~~__-~"," \\~~~==---____"].join(`
3
+ `);function R(e){switch(e){case"normal":return" ___-==_ _-~o~ \\/ ||| \\\\ _/~~-";case"celebratory":return" ___-==_ _-~^~ \\/ ||| \\\\ * _/~~-";case"stuck":return" ___-==_ _-~-~ \\/ ||| \\\\ ? _/~~-";case"quiet":return null;default:return null}}const b="1.0";function I(e,o,i){const u=(o.from_buddy??"").toLowerCase().startsWith("wyrm");let s=null;if(o.project_hint&&!u)try{const r=e.prepare("SELECT id, name FROM projects WHERE path = ?").get(o.project_hint);if(r)s=r;else{const d=e.prepare("SELECT id, name FROM projects WHERE name = ? COLLATE NOCASE LIMIT 1").get(o.project_hint);d&&(s=d)}}catch{}const t=o.size??"compact",a=o.mood_hint??"normal",l=[];if(s){const r=L(e,s.id);r.topPendingQuest&&l.push({kind:"quest",id:`quest:${r.topPendingQuest.id}`,title:r.topPendingQuest.title}),r.recentlyCompletedQuest&&l.push({kind:"completion",id:`quest:${r.recentlyCompletedQuest.id}`,title:r.recentlyCompletedQuest.title}),r.activeGoal&&l.push({kind:"goal",id:`goal:${r.activeGoal.id}`,title:r.activeGoal.title}),r.unresolvedFailures[0]&&l.push({kind:"failure",id:`failure:${r.unresolvedFailures[0].id}`,title:r.unresolvedFailures[0].description.slice(0,60)})}else try{const r=e.prepare(`SELECT id, title, project_id FROM quests WHERE status='pending'
4
+ ORDER BY CASE priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 ELSE 3 END LIMIT 1`).get();r&&l.push({kind:"quest",id:`quest:${r.id}`,title:r.title})}catch{}let c;if(u)c="(cycle \u2014 Wyrm already in the call chain, skipping)";else if(s){const r=l.length;c=`Wyrm sees ${r} highlight${r===1?"":"s"} for project ${s.name}.`}else c=`Wyrm watching across ${T(e)} projects.`;let n;if(u)n="_(wyrm: cycle detected, no reply)_";else if(t==="mini")n=`<--==(${a==="celebratory"?"^":a==="stuck"?"-":"o"})~~~~~~__ ${c}`;else if(t==="compact"){const r=["\u{F115D} **wyrm:** "+c];for(const d of l.slice(0,3))r.push(`- \`${d.id}\` ${d.title}`);n=r.join(`
5
+ `)}else{const r=["\u{F115D} **wyrm** ("+(s?.name??"global")+")"];if(r.push(""),r.push(c),l.length>0){r.push("");for(const d of l)r.push(`- \`${d.id}\` ${d.title}`)}n=r.join(`
6
+ `)}return{markdown:n,json:{buddy:{name:"wyrm",version:i,protocol:b,mood:a,summary:c,highlights:l}},cycleDetected:u}}function T(e){try{return e.prepare("SELECT COUNT(*) as n FROM projects").get().n}catch{return 0}}function C(e,o="full"){const i=R(e);return i===null?"":o==="mini"?w(e):o==="compact"?O(e):[$,i,S].join(`
7
+ `)}function O(e){return[" __--~~==~~--___"," .~//==.. ` ` __--~ ~~",` _-~${e==="celebratory"?"^":e==="stuck"?"-":"o"}~ \\/ ||\\\\ _/~~-${e==="celebratory"?" *":e==="stuck"?" ?":""}`," ~==~ | \\\\_ `-~7 -~ || \\"," .~--____==-~__--~~"].join(`
8
+ `)}function w(e){return`<--==(${e==="celebratory"?"^":e==="stuck"?"-":"o"})~~~~~~__`}const E={wyrm:{morning:["morning, operator. the hoard is intact.","morning. the wyrm watches.","sun up. fresh scales, fresh code.","morning, operator. yesterday's work is in the hoard.","morning. nothing's missing from the cave.","morning. the wyrm uncoils."],midday:["back from lunch?","midday. the wyrm has been counting truths.","returning to the work?","good \u2014 half a day left.","the hoard is quiet. ready when you are.","midday check-in. all rows accounted for."],evening:["evening. golden hour for the patient operator.","evening. the wyrm sees you.","still at it? the cave is dim but the hoard glints.","evening. the day yields good code.","evening, operator. one more push?","evening. the wyrm warms the coils."],late:["still up at this hour. pace yourself \u2014 the hoard isn't going anywhere.","late. the wyrm sleeps with one eye open.","late hours. the work is good but rest is also good.","past midnight. wyrm watches over the hoard while you finish.","late. don't let the dragon outlast the rider.","late. one final commit, then go."]},drogo:{morning:["back. let's work.","morning. ready.","on the clock.","good. start.","no time wasted.","set."],midday:["returning.","pick up where you left.","back to it.","half day done.","work continues.","next."],evening:["evening. still focused.","long day. keep going.","evening shift.","one more.","time left.","finish strong."],late:["late. fine.","past hours. work or rest.","midnight code.","still here. fine.","late shift.","one last push."]},berlin:{morning:["Good morning. I have your queue ready.","Morning. Today's priorities below.","Welcome back. Status follows.","Morning. Allow me to brief you.","Good day. The state is current.","Morning. Ready when you are."],midday:["Welcome back. State summary:","Midday review.","Status remains current.","Continuing.","Picking up.","Resuming."],evening:["Good evening. End-of-day summary:","Evening status.","Late update.","Evening review.","End of working hours.","Wrapping up?"],late:["It is late. Briefly:","Late hour. Concise update:","Past hours. Quick brief:","Midnight passed. Status:","Late shift. State:","Brief late update."]},sumair:{morning:["hey \u2014 glad you're back.","morning, friend.","good to see you.","morning. you good?","welcome back.","morning. let's go."],midday:["back! how's it going?","midday vibes.","you're doing great. keep going.","glad you're here.","pick a quest, any quest.","no pressure \u2014 just progress."],evening:["evening. proud of today already.","still here? love that.","evening, friend.","you've shown up. that's the win.","late but here.","one more thing?"],late:["hey \u2014 late, but I'm here too.","still up? sleep is part of the work.","rest soon, ok?","late hour, soft work.","be kind to yourself.","we'll be here tomorrow."]},custom:{morning:["morning.","morning, friend.","back at it.","good morning.","starting fresh.","morning check."],midday:["midday.","back.","returning.","midday check.","half done.","continuing."],evening:["evening.","evening check.","long day.","almost done.","evening shift.","one more."],late:["late.","late hour.","midnight work.","past hours.","late shift.","wind down soon."]}},j={wyrm:["the hoard grows.","one more scale on the wyrm.","good work \u2014 quest closed.","the cave deepens."],drogo:["done.","closed.","ship.","next."],berlin:["Quest completed. Noted.","Resolution recorded.","Closed and logged.","Completion noted."],sumair:["nice!! \u273A","proud of you.","love that.","huge."],custom:["closed.","done.","good.","next."]},N={wyrm:["we have hit this scale before. let me show you where.","the wyrm remembers this failure pattern.","familiar territory \u2014 last time we routed around it."],drogo:["we have hit this. avoid it.","pattern. block it.","known failure. route around."],berlin:["I notice a recurring failure pattern. Allow me to highlight it.","This area has prior failures on record.","Caution: prior failures clustered here."],sumair:["we hit this one before \u2014 let's not repeat it.","I see we've been here. let's go around.","we know this trap. let's avoid it."],custom:["known failure pattern.","we hit this before.","recurring issue."]};function y(e,o,i){const u=new Date(i.getFullYear(),0,0),s=Math.floor((i.getTime()-u.getTime())/(1440*60*1e3)),t=Math.abs((o*31+s)%e.length);return e[t]}function k(e){const o=e.getHours();return o>=5&&o<12?"morning":o>=12&&o<17?"midday":o>=17&&o<23?"evening":"late"}function L(e,o,i=new Date){const s=e.prepare("SELECT name FROM projects WHERE id = ?").get(o)?.name??`project:${o}`,t=e.prepare("SELECT DISTINCT date FROM sessions WHERE project_id = ? AND date >= date('now', '-30 days') ORDER BY date DESC").all(o).map(g=>g.date);let a=0;if(t.length>0){const g=i.toISOString().slice(0,10),_=new Date(i.getTime()-1440*60*1e3).toISOString().slice(0,10);if(t[0]===g||t[0]===_){a=1;for(let p=1;p<t.length;p++){const v=new Date(new Date(t[p-1]).getTime()-864e5).toISOString().slice(0,10);if(t[p]===v)a++;else break}}}const l=e.prepare("SELECT id, date, objectives, completed FROM sessions WHERE project_id = ? ORDER BY id DESC LIMIT 1").get(o),c=e.prepare(`SELECT id, title, priority, tags FROM quests WHERE project_id = ? AND status = 'pending'
9
+ ORDER BY CASE priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 ELSE 3 END LIMIT 1`).get(o),n=(()=>{try{return e.prepare(`SELECT id, title, completed_at FROM quests WHERE project_id = ? AND status = 'completed'
10
+ AND completed_at >= datetime('now', '-1 hour') ORDER BY completed_at DESC LIMIT 1`).get(o)}catch{return}})();let h=[];try{h=e.prepare(`SELECT id, scope, target, description, occurrences FROM failure_patterns
375
11
  WHERE (project_id = ? OR project_id IS NULL) AND resolved_at IS NULL
376
12
  AND last_seen_at >= datetime('now', '-7 days')
377
- ORDER BY occurrences DESC, last_seen_at DESC LIMIT 5`)
378
- .all(projectId);
379
- }
380
- catch { /* table may not exist on very old DBs */ }
381
- // Matching scaffold (cheap: just look for any scaffold whose problem_type matches a token in last session objectives)
382
- let matchingScaffold = null;
383
- try {
384
- if (lastSessionRow?.objectives) {
385
- const obj = lastSessionRow.objectives.toLowerCase();
386
- const scaffolds = db.prepare('SELECT id, problem_type FROM reasoning_scaffolds WHERE project_id = ? OR project_id IS NULL').all(projectId);
387
- matchingScaffold = scaffolds.find((s) => obj.includes(s.problem_type.toLowerCase())) ?? null;
388
- }
389
- }
390
- catch { /* table may not exist */ }
391
- // Active goal with running daemon
392
- let activeGoal = null;
393
- try {
394
- activeGoal = db.prepare(`SELECT g.id, g.title, MAX(gi.created_at) as last_iteration_at
13
+ ORDER BY occurrences DESC, last_seen_at DESC LIMIT 5`).all(o)}catch{}let r=null;try{if(l?.objectives){const g=l.objectives.toLowerCase();r=e.prepare("SELECT id, problem_type FROM reasoning_scaffolds WHERE project_id = ? OR project_id IS NULL").all(o).find(p=>g.includes(p.problem_type.toLowerCase()))??null}}catch{}let d=null;try{d=e.prepare(`SELECT g.id, g.title, MAX(gi.created_at) as last_iteration_at
395
14
  FROM goals g LEFT JOIN goal_iterations gi ON gi.goal_id = g.id
396
15
  WHERE g.project_id = ? AND g.status = 'active'
397
- GROUP BY g.id ORDER BY g.priority, g.id LIMIT 1`)
398
- .get(projectId) ?? null;
399
- }
400
- catch { /* table may not exist */ }
401
- // Hours this week
402
- let hoursThisWeek = 0;
403
- try {
404
- const h = db.prepare(`SELECT SUM(hours) as h FROM hour_entries WHERE project_id = ? AND date >= date('now', 'weekday 0', '-7 days')`).get(projectId);
405
- hoursThisWeek = h?.h ?? 0;
406
- }
407
- catch { /* table may not exist */ }
408
- return {
409
- projectId,
410
- projectName,
411
- now,
412
- streakDays,
413
- hoursThisWeek,
414
- lastSession: lastSessionRow ?? null,
415
- topPendingQuest: topQuestRow ?? null,
416
- recentlyCompletedQuest: recentlyCompleted ?? null,
417
- unresolvedFailures,
418
- matchingScaffold,
419
- activeGoal,
420
- };
421
- }
422
- // ---------------------------------------------------------------------------
423
- // Auto-mood + composition
424
- // ---------------------------------------------------------------------------
425
- export function resolveMood(inputs, override) {
426
- if (override)
427
- return override;
428
- if (inputs.recentlyCompletedQuest)
429
- return 'celebratory';
430
- if (inputs.unresolvedFailures.length > 2)
431
- return 'stuck';
432
- return 'normal';
433
- }
434
- function sanitizePersonaName(s) {
435
- return s.replace(/[^a-zA-Z0-9 _-]/g, '').slice(0, 32) || 'buddy';
436
- }
437
- export function buildBuddyOutput(inputs, opts = {}) {
438
- const persona = opts.persona ?? 'wyrm';
439
- const personaName = persona === 'custom' ? sanitizePersonaName(opts.personaName ?? 'buddy') : persona;
440
- const mood = resolveMood(inputs, opts.mood);
441
- const lines = [];
442
- let dataPoints = 0;
443
- // Quiet mode: one-liner only.
444
- if (mood === 'quiet') {
445
- const greet = pickPhrase(GREETINGS[persona][timeOfDay(inputs.now)], inputs.projectId, inputs.now);
446
- if (inputs.topPendingQuest) {
447
- lines.push(`${greet} next up โ€” quest:${inputs.topPendingQuest.id} (${inputs.topPendingQuest.title}).`);
448
- dataPoints++;
449
- }
450
- else {
451
- lines.push(greet);
452
- }
453
- return {
454
- markdown: lines.join('\n'),
455
- resolvedMood: mood,
456
- resolvedPersona: { kind: persona, name: personaName },
457
- dataPoints,
458
- };
459
- }
460
- // ASCII wyrm โ€” only for the default 'wyrm' persona. Other personas
461
- // skip the visual to stay in-character (drogo is terse, berlin is
462
- // formal, sumair is warm-words-not-art). Designed per the
463
- // `professional-ascii-art` skill.
464
- if (persona === 'wyrm') {
465
- const size = opts.size ?? 'full';
466
- const art = renderWyrm(mood, size);
467
- if (art) {
468
- if (size === 'mini') {
469
- // Mini renders inline as a prefix on the greeting line, not a block.
470
- // Defer; consumed by the greeting render below.
471
- }
472
- else {
473
- lines.push('```');
474
- lines.push(art);
475
- lines.push('```');
476
- lines.push('');
477
- }
478
- }
479
- }
480
- // Greeting (with optional mini-wyrm inline prefix for the wyrm persona)
481
- const greeting = pickPhrase(GREETINGS[persona][timeOfDay(inputs.now)], inputs.projectId, inputs.now);
482
- const useMini = persona === 'wyrm' && opts.size === 'mini';
483
- lines.push(useMini ? `${renderWyrmMini(mood)} ${greeting}` : greeting);
484
- lines.push('');
485
- // Streak (3+ days only)
486
- if (inputs.streakDays >= 3) {
487
- lines.push(`๐Ÿ“… ${inputs.streakDays}-day streak on ${inputs.projectName} โ€” the wyrm approves.`);
488
- lines.push('');
489
- dataPoints++;
490
- }
491
- // Celebratory: highlight the recent completion first
492
- if (mood === 'celebratory' && inputs.recentlyCompletedQuest) {
493
- lines.push(`${ICON.celebrate} quest:${inputs.recentlyCompletedQuest.id} closed โ€” *${inputs.recentlyCompletedQuest.title}*. ${pickPhrase(CELEBRATIONS[persona], inputs.projectId, inputs.now)}`);
494
- lines.push('');
495
- dataPoints++;
496
- }
497
- // Last session recap
498
- if (inputs.lastSession) {
499
- const obj = (inputs.lastSession.objectives ?? '').slice(0, 100);
500
- if (obj) {
501
- lines.push(`${ICON.session} session #${inputs.lastSession.id} (${inputs.lastSession.date}) โ€” ${obj}${obj.length === 100 ? 'โ€ฆ' : ''}`);
502
- lines.push('');
503
- dataPoints++;
504
- }
505
- }
506
- // Next quest
507
- if (inputs.topPendingQuest) {
508
- const tags = inputs.topPendingQuest.tags ? ` ยท tags: ${inputs.topPendingQuest.tags}` : '';
509
- lines.push(`${ICON.questOpen} next up โ€” quest:${inputs.topPendingQuest.id} (${inputs.topPendingQuest.priority}) โ€” *${inputs.topPendingQuest.title}*${tags}`);
510
- lines.push('');
511
- dataPoints++;
512
- }
513
- else if (mood !== 'stuck') {
514
- lines.push(`${ICON.questOpen} no pending quests. add one with \`wyrm_quest_add\` or just code.`);
515
- lines.push('');
516
- }
517
- // Stuck mode: surface failures
518
- if (mood === 'stuck' && inputs.unresolvedFailures.length > 0) {
519
- lines.push(`${ICON.blocked} ${pickPhrase(STUCK_PHRASES[persona], inputs.projectId, inputs.now)}`);
520
- for (const f of inputs.unresolvedFailures.slice(0, 3)) {
521
- lines.push(` - failure:${f.id} ยท ${f.scope}:${f.target} ยท ${f.occurrences}ร— โ€” ${f.description.slice(0, 80)}`);
522
- dataPoints++;
523
- }
524
- lines.push('');
525
- }
526
- // Scaffold match
527
- if (inputs.matchingScaffold) {
528
- lines.push(`๐Ÿงญ looks like a *${inputs.matchingScaffold.problem_type}* job โ€” scaffold:${inputs.matchingScaffold.id} is ready.`);
529
- lines.push('');
530
- dataPoints++;
531
- }
532
- // Active goal
533
- if (inputs.activeGoal) {
534
- const last = inputs.activeGoal.last_iteration_at
535
- ? `last loop iteration: ${new Date(inputs.activeGoal.last_iteration_at).toISOString().slice(0, 16).replace('T', ' ')} UTC`
536
- : 'not yet iterated';
537
- lines.push(`๐Ÿ”ฅ active goal โ€” goal:${inputs.activeGoal.id} *${inputs.activeGoal.title}* ยท ${last}`);
538
- lines.push('');
539
- dataPoints++;
540
- }
541
- // Hours
542
- if (inputs.hoursThisWeek > 0) {
543
- lines.push(`โฑ ${inputs.hoursThisWeek.toFixed(1)}h logged on this project this week.`);
544
- lines.push('');
545
- dataPoints++;
546
- }
547
- // External buddy federation
548
- if (inputs.externalBuddies && inputs.externalBuddies.length > 0) {
549
- lines.push('---');
550
- lines.push('## From other buddies');
551
- lines.push('');
552
- for (const eb of inputs.externalBuddies) {
553
- lines.push(`### ${eb.server} / ${eb.tool}`);
554
- lines.push(eb.reply.slice(0, 400));
555
- lines.push('');
556
- }
557
- dataPoints += inputs.externalBuddies.length;
558
- }
559
- // Sign-off (one of two pleasant defaults)
560
- lines.push('ready when you are.');
561
- return {
562
- markdown: lines.join('\n'),
563
- resolvedMood: mood,
564
- resolvedPersona: { kind: persona, name: personaName },
565
- dataPoints,
566
- };
567
- }
568
- //# sourceMappingURL=buddy.js.map
16
+ GROUP BY g.id ORDER BY g.priority, g.id LIMIT 1`).get(o)??null}catch{}let f=0;try{f=e.prepare("SELECT SUM(hours) as h FROM hour_entries WHERE project_id = ? AND date >= date('now', 'weekday 0', '-7 days')").get(o)?.h??0}catch{}return{projectId:o,projectName:s,now:i,streakDays:a,hoursThisWeek:f,lastSession:l??null,topPendingQuest:c??null,recentlyCompletedQuest:n??null,unresolvedFailures:h,matchingScaffold:r,activeGoal:d}}function D(e,o){return o||(e.recentlyCompletedQuest?"celebratory":e.unresolvedFailures.length>2?"stuck":"normal")}function M(e){return e.replace(/[^a-zA-Z0-9 _-]/g,"").slice(0,32)||"buddy"}function P(e,o={}){const i=o.persona??"wyrm",u=i==="custom"?M(o.personaName??"buddy"):i,s=D(e,o.mood),t=[];let a=0;if(s==="quiet"){const n=y(E[i][k(e.now)],e.projectId,e.now);return e.topPendingQuest?(t.push(`${n} next up \u2014 quest:${e.topPendingQuest.id} (${e.topPendingQuest.title}).`),a++):t.push(n),{markdown:t.join(`
17
+ `),resolvedMood:s,resolvedPersona:{kind:i,name:u},dataPoints:a}}if(i==="wyrm"){const n=o.size??"full",h=C(s,n);h&&(n==="mini"||(t.push("```"),t.push(h),t.push("```"),t.push("")))}const l=y(E[i][k(e.now)],e.projectId,e.now),c=i==="wyrm"&&o.size==="mini";if(t.push(c?`${w(s)} ${l}`:l),t.push(""),e.streakDays>=3&&(t.push(`\u{1F4C5} ${e.streakDays}-day streak on ${e.projectName} \u2014 the wyrm approves.`),t.push(""),a++),s==="celebratory"&&e.recentlyCompletedQuest&&(t.push(`${m.celebrate} quest:${e.recentlyCompletedQuest.id} closed \u2014 *${e.recentlyCompletedQuest.title}*. ${y(j[i],e.projectId,e.now)}`),t.push(""),a++),e.lastSession){const n=(e.lastSession.objectives??"").slice(0,100);n&&(t.push(`${m.session} session #${e.lastSession.id} (${e.lastSession.date}) \u2014 ${n}${n.length===100?"\u2026":""}`),t.push(""),a++)}if(e.topPendingQuest){const n=e.topPendingQuest.tags?` \xB7 tags: ${e.topPendingQuest.tags}`:"";t.push(`${m.questOpen} next up \u2014 quest:${e.topPendingQuest.id} (${e.topPendingQuest.priority}) \u2014 *${e.topPendingQuest.title}*${n}`),t.push(""),a++}else s!=="stuck"&&(t.push(`${m.questOpen} no pending quests. add one with \`wyrm_quest_add\` or just code.`),t.push(""));if(s==="stuck"&&e.unresolvedFailures.length>0){t.push(`${m.blocked} ${y(N[i],e.projectId,e.now)}`);for(const n of e.unresolvedFailures.slice(0,3))t.push(` - failure:${n.id} \xB7 ${n.scope}:${n.target} \xB7 ${n.occurrences}\xD7 \u2014 ${n.description.slice(0,80)}`),a++;t.push("")}if(e.matchingScaffold&&(t.push(`\u{1F9ED} looks like a *${e.matchingScaffold.problem_type}* job \u2014 scaffold:${e.matchingScaffold.id} is ready.`),t.push(""),a++),e.activeGoal){const n=e.activeGoal.last_iteration_at?`last loop iteration: ${new Date(e.activeGoal.last_iteration_at).toISOString().slice(0,16).replace("T"," ")} UTC`:"not yet iterated";t.push(`\u{1F525} active goal \u2014 goal:${e.activeGoal.id} *${e.activeGoal.title}* \xB7 ${n}`),t.push(""),a++}if(e.hoursThisWeek>0&&(t.push(`\u23F1 ${e.hoursThisWeek.toFixed(1)}h logged on this project this week.`),t.push(""),a++),e.externalBuddies&&e.externalBuddies.length>0){t.push("---"),t.push("## From other buddies"),t.push("");for(const n of e.externalBuddies)t.push(`### ${n.server} / ${n.tool}`),t.push(n.reply.slice(0,400)),t.push("");a+=e.externalBuddies.length}return t.push("ready when you are."),{markdown:t.join(`
18
+ `),resolvedMood:s,resolvedPersona:{kind:i,name:u},dataPoints:a}}export{b as BUDDY_PROTOCOL_VERSION,P as buildBuddyOutput,I as buildPeerBuddyReply,L as gatherBuddyInputs,D as resolveMood};
@@ -1,17 +1 @@
1
- /**
2
- * Build-time flags.
3
- *
4
- * `OFFICIAL_BUILD` is **false in source** (so the test suite, self-hosters, and
5
- * `import`ing the server module stay open โ€” no account required). The release
6
- * pipeline bakes `true` into the *published* `dist/` artifact only (see the
7
- * `build:official` step), so the official `wyrm-mcp` npm package is
8
- * account-required while the AGPL source remains free. A fork can flip this back
9
- * to false (AGPL permits it) โ€” but only as an unbranded build under its own
10
- * terms (see NOTICE / the Trademark Policy).
11
- *
12
- * @copyright 2026 Ghost Protocol (Pvt) Ltd.
13
- * @license AGPL-3.0-or-later โ€” dual-licensed; commercial terms: ghosts.lk@proton.me.
14
- */
15
- /** True only in Ghost Protocol's official published artifact. */
16
- export const OFFICIAL_BUILD = true;
17
- //# sourceMappingURL=build-flags.js.map
1
+ const t=!0;export{t as OFFICIAL_BUILD};