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/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(
|
|
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};
|
package/dist/version-check.js
CHANGED
|
@@ -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(),
|
|
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};
|
package/dist/visibility.js
CHANGED
|
@@ -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 >= ${
|
|
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', '-${
|
|
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
|
-
|
|
94
|
-
|
|
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
|
|
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
|
|
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"*
|
|
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?"*
|
|
179
|
-
- *"What have we learned about [topic]?"*
|
|
180
|
-
- *"Save this as a ground truth: [fact]"*
|
|
181
|
-
- *"What did Wyrm do for me this week?"*
|
|
182
|
-
- *"Show me everything Wyrm can do"*
|
|
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**
|
|
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};
|