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