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/vectors.js CHANGED
@@ -1,34 +1,4 @@
1
- /**
2
- * Wyrm Vector Embeddings — Semantic Search Support
3
- *
4
- * @copyright 2026 Ghost Protocol (Pvt) Ltd.
5
- * @license AGPL-3.0-or-later — dual-licensed; commercial terms: ghosts.lk@proton.me. See LICENSE.
6
- *
7
- * Features:
8
- * - Provider-agnostic embeddings (local, OpenAI, Ollama)
9
- * - SQLite-backed vector storage (no external DB needed)
10
- * - Cosine similarity search with content-hash deduplication
11
- * - Graceful degradation when no provider is configured
12
- */
13
- import { createProvider, NoneProvider, } from './providers/embedding-provider.js';
14
- /**
15
- * SQLite-backed vector store with pluggable embedding providers.
16
- */
17
- export class VectorStore {
18
- provider;
19
- db;
20
- constructor(provider, db) {
21
- this.provider = provider;
22
- this.db = db || null;
23
- if (this.db) {
24
- this.ensureTable();
25
- }
26
- }
27
- /** Create the vectors table if it doesn't exist */
28
- ensureTable() {
29
- if (!this.db)
30
- return;
31
- this.db.exec(`
1
+ import{createProvider as u,NoneProvider as l}from"./providers/embedding-provider.js";class _{provider;db;constructor(e,t){this.provider=e,this.db=t||null,this.db&&this.ensureTable()}ensureTable(){this.db&&this.db.exec(`
32
2
  CREATE TABLE IF NOT EXISTS vectors (
33
3
  id INTEGER PRIMARY KEY AUTOINCREMENT,
34
4
  project_id INTEGER,
@@ -44,162 +14,11 @@ export class VectorStore {
44
14
  CREATE INDEX IF NOT EXISTS idx_vectors_project ON vectors(project_id);
45
15
  CREATE INDEX IF NOT EXISTS idx_vectors_content ON vectors(content_type, content_id);
46
16
  CREATE INDEX IF NOT EXISTS idx_vectors_hash ON vectors(content_hash);
47
- `);
48
- }
49
- /**
50
- * Hash content for deduplication — same hash means same content, skip re-embedding.
51
- */
52
- hashContent(content) {
53
- let hash = 0;
54
- for (let i = 0; i < content.length; i++) {
55
- const char = content.charCodeAt(i);
56
- hash = ((hash << 5) - hash) + char;
57
- hash = hash & hash;
58
- }
59
- return Math.abs(hash).toString(16);
60
- }
61
- /**
62
- * Add or update a vector entry.
63
- * Returns the content hash, or null if the provider is disabled.
64
- */
65
- async addVector(content, contentType, contentId, projectId) {
66
- if (this.provider instanceof NoneProvider)
67
- return null;
68
- const contentHash = this.hashContent(content);
69
- // Skip if content unchanged (same hash + same model already stored)
70
- if (this.db) {
71
- const existing = this.db.prepare('SELECT id FROM vectors WHERE content_type = ? AND content_id = ? AND model = ? AND content_hash = ?').get(contentType, contentId, this.provider.model, contentHash);
72
- if (existing)
73
- return contentHash;
74
- }
75
- let embedding;
76
- try {
77
- embedding = await this.provider.embed(content);
78
- }
79
- catch {
80
- return null;
81
- }
82
- if (embedding.length === 0)
83
- return null;
84
- if (this.db) {
85
- // Upsert: replace if same content_type + content_id + model
86
- this.db.prepare(`
17
+ `)}hashContent(e){let t=0;for(let n=0;n<e.length;n++){const o=e.charCodeAt(n);t=(t<<5)-t+o,t=t&t}return Math.abs(t).toString(16)}async addVector(e,t,n,o){if(this.provider instanceof l)return null;const i=this.hashContent(e);if(this.db&&this.db.prepare("SELECT id FROM vectors WHERE content_type = ? AND content_id = ? AND model = ? AND content_hash = ?").get(t,n,this.provider.model,i))return i;let r;try{r=await this.provider.embed(e)}catch{return null}return r.length===0?null:(this.db&&this.db.prepare(`
87
18
  INSERT INTO vectors (project_id, content_type, content_id, content_hash, embedding, model, dimensions)
88
19
  VALUES (?, ?, ?, ?, ?, ?, ?)
89
20
  ON CONFLICT(content_type, content_id, model) DO UPDATE SET
90
21
  content_hash = excluded.content_hash,
91
22
  embedding = excluded.embedding,
92
23
  created_at = datetime('now')
93
- `).run(projectId, contentType, contentId, contentHash, Buffer.from(embedding.buffer), this.provider.model, this.provider.dimensions);
94
- }
95
- return contentHash;
96
- }
97
- /**
98
- * Cosine similarity between two vectors.
99
- */
100
- cosineSimilarity(a, b) {
101
- if (a.length !== b.length)
102
- return 0;
103
- let dotProduct = 0;
104
- let magnitudeA = 0;
105
- let magnitudeB = 0;
106
- for (let i = 0; i < a.length; i++) {
107
- dotProduct += a[i] * b[i];
108
- magnitudeA += a[i] * a[i];
109
- magnitudeB += b[i] * b[i];
110
- }
111
- const magnitude = Math.sqrt(magnitudeA) * Math.sqrt(magnitudeB);
112
- return magnitude > 0 ? dotProduct / magnitude : 0;
113
- }
114
- /**
115
- * Search for similar content by embedding the query and comparing.
116
- */
117
- async search(query, limit = 10, projectId, contentTypes) {
118
- if (this.provider instanceof NoneProvider || !this.db)
119
- return [];
120
- let queryEmbedding;
121
- try {
122
- queryEmbedding = await this.provider.embed(query);
123
- }
124
- catch {
125
- return [];
126
- }
127
- if (queryEmbedding.length === 0)
128
- return [];
129
- // Load all matching vectors from SQLite
130
- let sql = 'SELECT * FROM vectors WHERE model = ?';
131
- const params = [this.provider.model];
132
- if (projectId !== undefined) {
133
- sql += ' AND project_id = ?';
134
- params.push(projectId);
135
- }
136
- if (contentTypes && contentTypes.length > 0) {
137
- sql += ` AND content_type IN (${contentTypes.map(() => '?').join(',')})`;
138
- params.push(...contentTypes);
139
- }
140
- const rows = this.db.prepare(sql).all(...params);
141
- const results = [];
142
- for (const row of rows) {
143
- const storedEmbedding = new Float32Array(row.embedding.buffer, row.embedding.byteOffset, row.dimensions);
144
- const similarity = this.cosineSimilarity(queryEmbedding, storedEmbedding);
145
- results.push({
146
- content_type: row.content_type,
147
- content_id: row.content_id,
148
- project_id: row.project_id,
149
- similarity,
150
- content_hash: row.content_hash,
151
- });
152
- }
153
- results.sort((a, b) => b.similarity - a.similarity);
154
- return results.slice(0, limit);
155
- }
156
- /**
157
- * Remove vector by content hash.
158
- */
159
- removeVector(contentHash) {
160
- if (!this.db)
161
- return false;
162
- const result = this.db.prepare('DELETE FROM vectors WHERE content_hash = ?').run(contentHash);
163
- return result.changes > 0;
164
- }
165
- /**
166
- * Clear all vectors for a project.
167
- */
168
- clearProjectVectors(projectId) {
169
- if (!this.db)
170
- return 0;
171
- const result = this.db.prepare('DELETE FROM vectors WHERE project_id = ?').run(projectId);
172
- return result.changes;
173
- }
174
- /**
175
- * Get stats about the vector store.
176
- */
177
- getStats() {
178
- const byType = {};
179
- const byProject = {};
180
- let total = 0;
181
- if (this.db) {
182
- const typeRows = this.db.prepare('SELECT content_type, COUNT(*) as cnt FROM vectors GROUP BY content_type').all();
183
- for (const r of typeRows) {
184
- byType[r.content_type] = r.cnt;
185
- total += r.cnt;
186
- }
187
- const projRows = this.db.prepare('SELECT project_id, COUNT(*) as cnt FROM vectors GROUP BY project_id').all();
188
- for (const r of projRows) {
189
- byProject[r.project_id] = r.cnt;
190
- }
191
- }
192
- return { total, byType, byProject, provider: this.provider.name, model: this.provider.model };
193
- }
194
- /** Get the active embedding provider */
195
- getProvider() { return this.provider; }
196
- }
197
- /**
198
- * Create a vector store instance with config.
199
- * Accepts an optional SQLite database for persistent storage.
200
- */
201
- export function createVectorStore(config, db) {
202
- const provider = createProvider(config);
203
- return new VectorStore(provider, db);
204
- }
205
- //# sourceMappingURL=vectors.js.map
24
+ `).run(o,t,n,i,Buffer.from(r.buffer),this.provider.model,this.provider.dimensions),i)}cosineSimilarity(e,t){if(e.length!==t.length)return 0;let n=0,o=0,i=0;for(let s=0;s<e.length;s++)n+=e[s]*t[s],o+=e[s]*e[s],i+=t[s]*t[s];const r=Math.sqrt(o)*Math.sqrt(i);return r>0?n/r:0}async search(e,t=10,n,o){if(this.provider instanceof l||!this.db)return[];let i;try{i=await this.provider.embed(e)}catch{return[]}if(i.length===0)return[];let r="SELECT * FROM vectors WHERE model = ?";const s=[this.provider.model];n!==void 0&&(r+=" AND project_id = ?",s.push(n)),o&&o.length>0&&(r+=` AND content_type IN (${o.map(()=>"?").join(",")})`,s.push(...o));const E=this.db.prepare(r).all(...s),c=[];for(const d of E){const h=new Float32Array(d.embedding.buffer,d.embedding.byteOffset,d.dimensions),p=this.cosineSimilarity(i,h);c.push({content_type:d.content_type,content_id:d.content_id,project_id:d.project_id,similarity:p,content_hash:d.content_hash})}return c.sort((d,h)=>h.similarity-d.similarity),c.slice(0,t)}removeVector(e){return this.db?this.db.prepare("DELETE FROM vectors WHERE content_hash = ?").run(e).changes>0:!1}clearProjectVectors(e){return this.db?this.db.prepare("DELETE FROM vectors WHERE project_id = ?").run(e).changes:0}getStats(){const e={},t={};let n=0;if(this.db){const o=this.db.prepare("SELECT content_type, COUNT(*) as cnt FROM vectors GROUP BY content_type").all();for(const r of o)e[r.content_type]=r.cnt,n+=r.cnt;const i=this.db.prepare("SELECT project_id, COUNT(*) as cnt FROM vectors GROUP BY project_id").all();for(const r of i)t[r.project_id]=r.cnt}return{total:n,byType:e,byProject:t,provider:this.provider.name,model:this.provider.model}}getProvider(){return this.provider}}function m(a,e){const t=u(a);return new _(t,e)}export{_ as VectorStore,m as createVectorStore};
@@ -1,148 +1,16 @@
1
- /**
2
- * Version check — quietly polls the npm registry once per day to see if a
3
- * newer wyrm-mcp is available, caches the result in the Wyrm database, and
4
- * exposes it to both the MCP startup banner and the AI (via the
5
- * `wyrm_check_update` tool).
6
- *
7
- * Design notes:
8
- * - Cache TTL: 24 h. We don't want to ping registry every MCP server start
9
- * (which can be per-session-restart for some clients).
10
- * - Cache lives in a single-row `update_check` table so it survives restarts.
11
- * - Network errors are non-fatal — we just log debug and keep going.
12
- * - `fetch` is the Node 22+ global; no extra deps.
13
- *
14
- * @copyright 2026 Ghost Protocol (Pvt) Ltd.
15
- * @license AGPL-3.0-or-later — dual-licensed; commercial terms: ghosts.lk@proton.me. See LICENSE.
16
- */
17
- import { logger } from './logger.js';
18
- const REGISTRY_URL = 'https://registry.npmjs.org/wyrm-mcp/latest';
19
- const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
20
- const FETCH_TIMEOUT_MS = 10_000; // generous for mobile / slow networks; startup is async anyway
21
- function ensureSchema(db) {
22
- db.exec(`
1
+ import{logger as h}from"./logger.js";const p="https://registry.npmjs.org/wyrm-mcp/latest",T=1440*60*1e3,_=1e4;function u(e){e.exec(`
23
2
  CREATE TABLE IF NOT EXISTS update_check (
24
3
  id INTEGER PRIMARY KEY CHECK (id = 1),
25
4
  checked_at TEXT NOT NULL,
26
5
  latest_version TEXT,
27
6
  check_succeeded INTEGER NOT NULL DEFAULT 0
28
7
  );
29
- `);
30
- }
31
- function readCache(db) {
32
- ensureSchema(db);
33
- const row = db
34
- .prepare('SELECT checked_at, latest_version, check_succeeded FROM update_check WHERE id = 1')
35
- .get();
36
- if (!row)
37
- return null;
38
- return {
39
- checkedAt: new Date(row.checked_at).getTime(),
40
- latest: row.latest_version,
41
- ok: row.check_succeeded === 1,
42
- };
43
- }
44
- function writeCache(db, latest, ok) {
45
- ensureSchema(db);
46
- db.prepare(`
8
+ `)}function m(e){u(e);const t=e.prepare("SELECT checked_at, latest_version, check_succeeded FROM update_check WHERE id = 1").get();return t?{checkedAt:new Date(t.checked_at).getTime(),latest:t.latest_version,ok:t.check_succeeded===1}:null}function E(e,t,c){u(e),e.prepare(`
47
9
  INSERT INTO update_check (id, checked_at, latest_version, check_succeeded)
48
10
  VALUES (1, ?, ?, ?)
49
11
  ON CONFLICT(id) DO UPDATE SET
50
12
  checked_at = excluded.checked_at,
51
13
  latest_version = excluded.latest_version,
52
14
  check_succeeded = excluded.check_succeeded
53
- `).run(new Date().toISOString(), latest, ok ? 1 : 0);
54
- }
55
- /**
56
- * Semver comparison — only handles the standard X.Y.Z (no prerelease tags).
57
- * Returns: -1 if a<b, 0 if a==b, +1 if a>b. Anything unparseable returns 0.
58
- */
59
- function semverCompare(a, b) {
60
- const parse = (s) => s.split('.').map((p) => parseInt(p, 10));
61
- const [a1, a2, a3] = parse(a);
62
- const [b1, b2, b3] = parse(b);
63
- if ([a1, a2, a3, b1, b2, b3].some((n) => Number.isNaN(n)))
64
- return 0;
65
- if (a1 !== b1)
66
- return a1 < b1 ? -1 : 1;
67
- if (a2 !== b2)
68
- return a2 < b2 ? -1 : 1;
69
- if (a3 !== b3)
70
- return a3 < b3 ? -1 : 1;
71
- return 0;
72
- }
73
- async function fetchLatestFromRegistry() {
74
- if (typeof fetch !== 'function')
75
- return null; // very old Node
76
- const controller = new AbortController();
77
- const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
78
- try {
79
- const res = await fetch(REGISTRY_URL, {
80
- headers: { Accept: 'application/json' },
81
- signal: controller.signal,
82
- });
83
- if (!res.ok)
84
- return null;
85
- const data = (await res.json());
86
- return typeof data.version === 'string' ? data.version : null;
87
- }
88
- catch {
89
- return null;
90
- }
91
- finally {
92
- clearTimeout(timer);
93
- }
94
- }
95
- /**
96
- * Returns the current update status. By default uses the cache when fresh.
97
- * Pass `force: true` to bypass the cache (used by `wyrm_check_update`).
98
- */
99
- export async function getUpdateStatus(db, current, opts = {}) {
100
- ensureSchema(db);
101
- const force = opts.force === true;
102
- // Use cache if fresh and not forced.
103
- if (!force) {
104
- const cached = readCache(db);
105
- if (cached && Date.now() - cached.checkedAt < CACHE_TTL_MS) {
106
- const latest = cached.latest;
107
- return {
108
- current,
109
- latest,
110
- updateAvailable: latest != null && semverCompare(current, latest) < 0,
111
- checkedAt: new Date(cached.checkedAt).toISOString(),
112
- source: 'cache',
113
- };
114
- }
115
- }
116
- const latest = await fetchLatestFromRegistry();
117
- const ok = latest !== null;
118
- writeCache(db, latest, ok);
119
- if (!ok) {
120
- logger.debug('version-check: registry fetch failed (offline or rate-limited)');
121
- }
122
- return {
123
- current,
124
- latest,
125
- updateAvailable: latest != null && semverCompare(current, latest) < 0,
126
- checkedAt: new Date().toISOString(),
127
- source: ok ? 'live' : 'offline',
128
- };
129
- }
130
- /**
131
- * Startup banner — log a single line to stderr if a newer version is out.
132
- * Stderr (not stdout) so it never pollutes the MCP stdio protocol.
133
- */
134
- export async function emitStartupVersionBanner(db, current) {
135
- try {
136
- const status = await getUpdateStatus(db, current);
137
- if (status.updateAvailable) {
138
- // T032 findings pass: T023 hard-exiled wyrm_self_update off MCP (its
139
- // alias returns a WYRM_CLI_EXILE redirect, never updates) — the banner
140
- // must name the real path, the `wyrm update` CLI replacement.
141
- process.stderr.write(`󱅝 wyrm-mcp ${status.latest} is available (you have ${current}). Run "wyrm update" from a shell, or "npm install -g wyrm-mcp@latest" manually.\n`);
142
- }
143
- }
144
- catch {
145
- // Never crash startup on a version check.
146
- }
147
- }
148
- //# sourceMappingURL=version-check.js.map
15
+ `).run(new Date().toISOString(),t,c?1:0)}function d(e,t){const c=l=>l.split(".").map(f=>parseInt(f,10)),[r,n,s]=c(e),[a,o,i]=c(t);return[r,n,s,a,o,i].some(l=>Number.isNaN(l))?0:r!==a?r<a?-1:1:n!==o?n<o?-1:1:s!==i?s<i?-1:1:0}async function k(){if(typeof fetch!="function")return null;const e=new AbortController,t=setTimeout(()=>e.abort(),_);try{const c=await fetch(p,{headers:{Accept:"application/json"},signal:e.signal});if(!c.ok)return null;const r=await c.json();return typeof r.version=="string"?r.version:null}catch{return null}finally{clearTimeout(t)}}async function S(e,t,c={}){if(u(e),!(c.force===!0)){const a=m(e);if(a&&Date.now()-a.checkedAt<T){const o=a.latest;return{current:t,latest:o,updateAvailable:o!=null&&d(t,o)<0,checkedAt:new Date(a.checkedAt).toISOString(),source:"cache"}}}const n=await k(),s=n!==null;return E(e,n,s),s||h.debug("version-check: registry fetch failed (offline or rate-limited)"),{current:t,latest:n,updateAvailable:n!=null&&d(t,n)<0,checkedAt:new Date().toISOString(),source:s?"live":"offline"}}async function w(e,t){try{const c=await S(e,t);c.updateAvailable&&process.stderr.write(`\u{F115D} wyrm-mcp ${c.latest} is available (you have ${t}). Run "wyrm update" from a shell, or "npm install -g wyrm-mcp@latest" manually.
16
+ `)}catch{}}export{w as emitStartupVersionBanner,S as getUpdateStatus};
@@ -1,163 +1,29 @@
1
- /**
2
- * Visibility tools (5.9.0).
3
- *
4
- * Wyrm's value tends to be invisible — users don't realise the AI is
5
- * remembering things, blocking failed patterns, injecting truths, citing
6
- * past sessions. This module surfaces what Wyrm is actually doing so the
7
- * operator can see the work product, and AI helpers can attribute their
8
- * answers to Wyrm sources rather than silently using them.
9
- *
10
- * Two tools:
11
- * - wyrm_intro: plain-English explanation of what Wyrm is + can do.
12
- * Aimed at humans new to Wyrm, not at AI agents.
13
- * - wyrm_digest: "what Wyrm did for you this period" — counts and
14
- * highlights from the operator's actual usage data.
15
- *
16
- * @copyright 2026 Ghost Protocol (Pvt) Ltd.
17
- * @license AGPL-3.0-or-later — dual-licensed; commercial terms: ghosts.lk@proton.me. See LICENSE.
18
- */
19
- import { ICON } from './icons.js';
20
- export const DIGEST_PRESETS = {
21
- today: { days: 1, label: 'today' },
22
- week: { days: 7, label: 'this week' },
23
- month: { days: 30, label: 'this month' },
24
- quarter: { days: 90, label: 'this quarter' },
25
- year: { days: 365, label: 'this year' },
26
- all: { days: 36500, label: 'all time' },
27
- };
28
- export function gatherDigest(db, periodKey = 'week') {
29
- // Use hasOwn() to defeat prototype-pollution probes (`__proto__`,
30
- // `toString`, `constructor`, etc. would otherwise resolve to
31
- // Object.prototype, bypass the `??` fallback, and produce `undefined`
32
- // when we access `.days`.)
33
- const period = Object.hasOwn(DIGEST_PRESETS, periodKey)
34
- ? DIGEST_PRESETS[periodKey]
35
- : DIGEST_PRESETS.week;
36
- const since = `datetime('now', '-${period.days} days')`;
37
- const num = (sql) => {
38
- try {
39
- const row = db.prepare(sql).get();
40
- return row?.n ?? 0;
41
- }
42
- catch {
43
- return 0;
44
- }
45
- };
46
- return {
47
- period,
48
- toolCalls: num(`SELECT COUNT(*) AS n FROM tool_call_log WHERE called_at >= ${since}`),
49
- topTools: (() => {
50
- try {
51
- const rows = db.prepare(`
1
+ import{ICON as r}from"./icons.js";const m={today:{days:1,label:"today"},week:{days:7,label:"this week"},month:{days:30,label:"this month"},quarter:{days:90,label:"this quarter"},year:{days:365,label:"this year"},all:{days:36500,label:"all time"}};function W(a,i="week"){const o=Object.hasOwn(m,i)?m[i]:m.week,s=`datetime('now', '-${o.days} days')`,t=n=>{try{return a.prepare(n).get()?.n??0}catch{return 0}};return{period:o,toolCalls:t(`SELECT COUNT(*) AS n FROM tool_call_log WHERE called_at >= ${s}`),topTools:(()=>{try{return a.prepare(`
52
2
  SELECT tool_name AS name, COUNT(*) AS count
53
3
  FROM tool_call_log
54
- WHERE called_at >= ${since}
4
+ WHERE called_at >= ${s}
55
5
  GROUP BY tool_name
56
6
  ORDER BY count DESC
57
7
  LIMIT 5
58
- `).all();
59
- return rows;
60
- }
61
- catch {
62
- return [];
63
- }
64
- })(),
65
- sessions: num(`SELECT COUNT(*) AS n FROM sessions WHERE date >= date('now', '-${period.days} days')`),
66
- questsCompleted: num(`SELECT COUNT(*) AS n FROM quests WHERE status='completed' AND completed_at >= ${since}`),
67
- questsAdded: num(`SELECT COUNT(*) AS n FROM quests WHERE created_at >= ${since}`),
68
- failuresBlocked: num(`SELECT COALESCE(SUM(occurrences), 0) AS n FROM failure_patterns WHERE last_seen_at >= ${since}`),
69
- truthsSet: num(`SELECT COUNT(*) AS n FROM ground_truths WHERE created_at >= ${since} OR last_verified_at >= ${since}`),
70
- referencesAdded: num(`SELECT COUNT(*) AS n FROM design_references WHERE captured_at >= ${since}`),
71
- memoryArtifactsAdded: num(`SELECT COUNT(*) AS n FROM memory_artifacts WHERE created_at >= ${since}`),
72
- hoursLogged: (() => {
73
- try {
74
- const row = db.prepare(`SELECT COALESCE(SUM(hours), 0) AS n FROM hour_entries WHERE date >= date('now', '-${period.days} days')`).get();
75
- return row.n;
76
- }
77
- catch {
78
- return 0;
79
- }
80
- })(),
81
- projectsActive: (() => {
82
- try {
83
- return db.prepare(`
8
+ `).all()}catch{return[]}})(),sessions:t(`SELECT COUNT(*) AS n FROM sessions WHERE date >= date('now', '-${o.days} days')`),questsCompleted:t(`SELECT COUNT(*) AS n FROM quests WHERE status='completed' AND completed_at >= ${s}`),questsAdded:t(`SELECT COUNT(*) AS n FROM quests WHERE created_at >= ${s}`),failuresBlocked:t(`SELECT COALESCE(SUM(occurrences), 0) AS n FROM failure_patterns WHERE last_seen_at >= ${s}`),truthsSet:t(`SELECT COUNT(*) AS n FROM ground_truths WHERE created_at >= ${s} OR last_verified_at >= ${s}`),referencesAdded:t(`SELECT COUNT(*) AS n FROM design_references WHERE captured_at >= ${s}`),memoryArtifactsAdded:t(`SELECT COUNT(*) AS n FROM memory_artifacts WHERE created_at >= ${s}`),hoursLogged:(()=>{try{return a.prepare(`SELECT COALESCE(SUM(hours), 0) AS n FROM hour_entries WHERE date >= date('now', '-${o.days} days')`).get().n}catch{return 0}})(),projectsActive:(()=>{try{return a.prepare(`
84
9
  SELECT p.id, p.name
85
10
  FROM projects p
86
11
  JOIN sessions s ON s.project_id = p.id
87
- WHERE s.date >= date('now', '-${period.days} days')
12
+ WHERE s.date >= date('now', '-${o.days} days')
88
13
  GROUP BY p.id, p.name
89
14
  ORDER BY MAX(s.date) DESC
90
15
  LIMIT 10
91
- `).all();
92
- }
93
- catch {
94
- return [];
95
- }
96
- })(),
97
- };
98
- }
99
- export function renderDigest(data) {
100
- const { period, toolCalls, topTools, sessions, questsCompleted, questsAdded, failuresBlocked, truthsSet, referencesAdded, memoryArtifactsAdded, hoursLogged, projectsActive } = data;
101
- const lines = [];
102
- lines.push(`# ${ICON.brand} What Wyrm did ${period.label}`);
103
- lines.push('');
104
- if (toolCalls === 0 && sessions === 0) {
105
- lines.push('_Wyrm was idle — no recorded activity in this period._');
106
- lines.push('');
107
- lines.push('If your AI helper is connected to Wyrm but you see nothing here, it means it hasn\'t been calling Wyrm\'s tools. Try asking it: *"summarize my current project using wyrm_session_prime"* or *"what failures has Wyrm recorded on this project?"* — that\'ll get the loop going.');
108
- return lines.join('\n');
109
- }
110
- lines.push('## The numbers');
111
- lines.push('');
112
- if (sessions > 0)
113
- lines.push(`- ${ICON.session} **${sessions}** session${sessions === 1 ? '' : 's'} tracked`);
114
- if (toolCalls > 0)
115
- lines.push(`- ${ICON.toolCall} **${toolCalls}** Wyrm tool call${toolCalls === 1 ? '' : 's'} (your AI reached for Wyrm this many times)`);
116
- if (questsCompleted > 0)
117
- lines.push(`- ${ICON.questDone} **${questsCompleted}** quest${questsCompleted === 1 ? '' : 's'} completed`);
118
- if (questsAdded > 0)
119
- lines.push(`- ${ICON.questOpen} **${questsAdded}** quest${questsAdded === 1 ? '' : 's'} added`);
120
- if (failuresBlocked > 0)
121
- lines.push(`- ${ICON.blocked} **${failuresBlocked}** repeated-failure block${failuresBlocked === 1 ? '' : 's'} — Wyrm refused to suggest something that didn't work before`);
122
- if (truthsSet > 0)
123
- lines.push(`- ${ICON.truth} **${truthsSet}** ground truth${truthsSet === 1 ? '' : 's'} set or re-verified`);
124
- if (memoryArtifactsAdded > 0)
125
- lines.push(`- ${ICON.memory} **${memoryArtifactsAdded}** memory artifact${memoryArtifactsAdded === 1 ? '' : 's'} stored (patterns, lessons, anti-patterns)`);
126
- if (referencesAdded > 0)
127
- lines.push(`- ${ICON.reference} **${referencesAdded}** design reference${referencesAdded === 1 ? '' : 's'} clipped`);
128
- if (hoursLogged > 0)
129
- lines.push(`- ${ICON.bullet} **${hoursLogged.toFixed(1)}h** logged across sessions`);
130
- if (topTools.length > 0) {
131
- lines.push('');
132
- lines.push('## Wyrm tools your AI reached for most');
133
- lines.push('');
134
- for (const t of topTools) {
135
- lines.push(`- \`${t.name}\` — ${t.count}×`);
136
- }
137
- }
138
- if (projectsActive.length > 0) {
139
- lines.push('');
140
- lines.push('## Projects Wyrm tracked');
141
- lines.push('');
142
- for (const p of projectsActive) {
143
- lines.push(`- ${p.name}`);
144
- }
145
- }
146
- lines.push('');
147
- lines.push('---');
148
- lines.push('_If this is the first time you\'re seeing what Wyrm does — that\'s the point. It works in the background. Every quest you completed, every failed approach your AI didn\'t repeat, every ground truth it cited — Wyrm made that happen. Without Wyrm your AI would have re-explored the same ground every session._');
149
- return lines.join('\n');
150
- }
151
- export function renderIntro(version) {
152
- return `# ${ICON.brand} What is Wyrm?
153
-
154
- You're running **Wyrm ${version}** — a memory system that makes your AI assistant *much* better at working on your stuff.
16
+ `).all()}catch{return[]}})()}}function w(a){const{period:i,toolCalls:o,topTools:s,sessions:t,questsCompleted:n,questsAdded:u,failuresBlocked:l,truthsSet:h,referencesAdded:y,memoryArtifactsAdded:c,hoursLogged:p,projectsActive:f}=a,e=[];if(e.push(`# ${r.brand} What Wyrm did ${i.label}`),e.push(""),o===0&&t===0)return e.push("_Wyrm was idle \u2014 no recorded activity in this period._"),e.push(""),e.push(`If your AI helper is connected to Wyrm but you see nothing here, it means it hasn't been calling Wyrm's tools. Try asking it: *"summarize my current project using wyrm_session_prime"* or *"what failures has Wyrm recorded on this project?"* \u2014 that'll get the loop going.`),e.join(`
17
+ `);if(e.push("## The numbers"),e.push(""),t>0&&e.push(`- ${r.session} **${t}** session${t===1?"":"s"} tracked`),o>0&&e.push(`- ${r.toolCall} **${o}** Wyrm tool call${o===1?"":"s"} (your AI reached for Wyrm this many times)`),n>0&&e.push(`- ${r.questDone} **${n}** quest${n===1?"":"s"} completed`),u>0&&e.push(`- ${r.questOpen} **${u}** quest${u===1?"":"s"} added`),l>0&&e.push(`- ${r.blocked} **${l}** repeated-failure block${l===1?"":"s"} \u2014 Wyrm refused to suggest something that didn't work before`),h>0&&e.push(`- ${r.truth} **${h}** ground truth${h===1?"":"s"} set or re-verified`),c>0&&e.push(`- ${r.memory} **${c}** memory artifact${c===1?"":"s"} stored (patterns, lessons, anti-patterns)`),y>0&&e.push(`- ${r.reference} **${y}** design reference${y===1?"":"s"} clipped`),p>0&&e.push(`- ${r.bullet} **${p.toFixed(1)}h** logged across sessions`),s.length>0){e.push(""),e.push("## Wyrm tools your AI reached for most"),e.push("");for(const d of s)e.push(`- \`${d.name}\` \u2014 ${d.count}\xD7`)}if(f.length>0){e.push(""),e.push("## Projects Wyrm tracked"),e.push("");for(const d of f)e.push(`- ${d.name}`)}return e.push(""),e.push("---"),e.push("_If this is the first time you're seeing what Wyrm does \u2014 that's the point. It works in the background. Every quest you completed, every failed approach your AI didn't repeat, every ground truth it cited \u2014 Wyrm made that happen. Without Wyrm your AI would have re-explored the same ground every session._"),e.join(`
18
+ `)}function E(a){return`# ${r.brand} What is Wyrm?
19
+
20
+ You're running **Wyrm ${a}** \u2014 a memory system that makes your AI assistant *much* better at working on your stuff.
155
21
 
156
22
  ## The simplest explanation
157
23
 
158
24
  Without Wyrm: every time you start a new conversation with Claude / Copilot / Cursor / Windsurf, the AI starts from nothing. It doesn't remember what you worked on yesterday, what decisions you made, what didn't work last time.
159
25
 
160
- With Wyrm: your AI **remembers**. Across every session, every tool, every project. You don't have to re-explain. The AI picks up where you left off *with all the context*.
26
+ With Wyrm: your AI **remembers**. Across every session, every tool, every project. You don't have to re-explain. The AI picks up where you left off \u2014 *with all the context*.
161
27
 
162
28
  ## What Wyrm specifically does
163
29
 
@@ -165,27 +31,25 @@ It's not just "memory" in the file-write sense. Wyrm solves four specific proble
165
31
 
166
32
  **1. Amnesia.** Wyrm assembles a complete briefing at every session start. Your AI inherits the prior state instead of starting from zero.
167
33
 
168
- **2. Repeated mistakes.** When an approach fails, Wyrm records it. Next time, your AI is *blocked* from suggesting the same failed thing again. Other memory tools learn only from success Wyrm learns from failure too.
34
+ **2. Repeated mistakes.** When an approach fails, Wyrm records it. Next time, your AI is *blocked* from suggesting the same failed thing again. Other memory tools learn only from success \u2014 Wyrm learns from failure too.
169
35
 
170
36
  **3. Lost rationale.** When you make a decision ("we use Postgres, not Mongo"), Wyrm stores it as a *ground truth* linked to the reasoning. When something downstream changes, Wyrm cascades the impact so you don't accidentally violate decisions you forgot you made.
171
37
 
172
- **4. Tool-call sprawl.** The agent loop turns goals into autonomous tool sequences. Say *"keep my dependencies up to date weekly"* Wyrm's daemon does it.
38
+ **4. Tool-call sprawl.** The agent loop turns goals into autonomous tool sequences. Say *"keep my dependencies up to date weekly"* \u2014 Wyrm's daemon does it.
173
39
 
174
40
  ## What to try first
175
41
 
176
42
  If you've never used Wyrm directly, ask your AI:
177
43
 
178
- - *"What's the state of this project?"* your AI will call \`wyrm_session_prime\` and brief you.
179
- - *"What have we learned about [topic]?"* your AI will search Wyrm's memory.
180
- - *"Save this as a ground truth: [fact]"* your AI will store it for future sessions.
181
- - *"What did Wyrm do for me this week?"* run \`wyrm_digest\` to see the work product.
182
- - *"Show me everything Wyrm can do"* call \`wyrm_capabilities\` to get the full tool catalogue.
44
+ - *"What's the state of this project?"* \u2014 your AI will call \`wyrm_session_prime\` and brief you.
45
+ - *"What have we learned about [topic]?"* \u2014 your AI will search Wyrm's memory.
46
+ - *"Save this as a ground truth: [fact]"* \u2014 your AI will store it for future sessions.
47
+ - *"What did Wyrm do for me this week?"* \u2014 run \`wyrm_digest\` to see the work product.
48
+ - *"Show me everything Wyrm can do"* \u2014 call \`wyrm_capabilities\` to get the full tool catalogue.
183
49
 
184
50
  ## The 30-second pitch for skeptics
185
51
 
186
52
  You know that feeling when your AI says *"Could you remind me what we decided about X?"* on session three of the same project? Wyrm exists so that never happens. It's the layer between you and AI amnesia.
187
53
 
188
- If you've been using Claude Code / Cursor / Windsurf with Wyrm installed, **Wyrm has already been working** silently, in the background, every session. \`wyrm_digest\` shows you the receipts.
189
- `;
190
- }
191
- //# sourceMappingURL=visibility.js.map
54
+ If you've been using Claude Code / Cursor / Windsurf with Wyrm installed, **Wyrm has already been working** \u2014 silently, in the background, every session. \`wyrm_digest\` shows you the receipts.
55
+ `}export{m as DIGEST_PRESETS,W as gatherDigest,w as renderDigest,E as renderIntro};