wyrm-mcp 9.2.2 → 9.2.3
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/dist/activation.js +1 -1
- package/dist/agent-loop.js +2 -2
- package/dist/audit.js +2 -2
- package/dist/autoconfig.js +6 -6
- package/dist/backup-cli.js +6 -0
- package/dist/backup.js +1 -0
- package/dist/bridge.js +1 -1
- package/dist/buddy-runner.js +1 -1
- package/dist/buddy.js +4 -4
- package/dist/capabilities.js +1 -1
- package/dist/causality.js +3 -3
- package/dist/cloud/cli.js +3 -3
- package/dist/cloud/sync-daemon-entrypoint.js +1 -1
- package/dist/cloud/sync-engine.js +8 -7
- package/dist/cloud-backup.js +4 -4
- package/dist/cloud-sync-entrypoint.js +1 -1
- package/dist/cloud-sync.js +1 -1
- package/dist/connectors/ingest.js +2 -2
- package/dist/constellation.js +1 -1
- package/dist/context-build-budgeted.js +4 -4
- package/dist/context-ranking.js +1 -1
- package/dist/daemon-writer.js +1 -1
- package/dist/database.js +41 -23
- package/dist/design.js +1 -1
- package/dist/erasure-cli.js +3 -0
- package/dist/erasure-log.js +8 -0
- package/dist/erasure.js +1 -0
- package/dist/event-replication.js +1 -1
- package/dist/events.js +11 -11
- package/dist/failure-patterns.js +67 -44
- package/dist/federation.js +11 -10
- package/dist/governance.js +1 -0
- package/dist/handlers/agent.js +4 -4
- package/dist/handlers/alias-adapters.js +1 -1
- package/dist/handlers/audit.js +1 -1
- package/dist/handlers/boundary.js +1 -1
- package/dist/handlers/capture.js +54 -52
- package/dist/handlers/causality.js +2 -2
- package/dist/handlers/cloud.js +9 -9
- package/dist/handlers/companion.js +4 -4
- package/dist/handlers/entity.js +6 -6
- package/dist/handlers/events.js +4 -4
- package/dist/handlers/failure.js +15 -12
- package/dist/handlers/goals.js +2 -2
- package/dist/handlers/intelligence.js +81 -74
- package/dist/handlers/mcpclient.js +3 -3
- package/dist/handlers/presence.js +1 -1
- package/dist/handlers/prompts.js +2 -2
- package/dist/handlers/quest.js +11 -4
- package/dist/handlers/recall.js +10 -10
- package/dist/handlers/resources.js +1 -1
- package/dist/handlers/review.js +3 -3
- package/dist/handlers/run.js +3 -3
- package/dist/handlers/search.js +1 -1
- package/dist/handlers/session.js +27 -25
- package/dist/handlers/share.js +3 -3
- package/dist/handlers/skill.js +2 -2
- package/dist/handlers/symbols.js +5 -5
- package/dist/handlers/syncops.js +2 -2
- package/dist/harvest.js +5 -5
- package/dist/hours.js +1 -1
- package/dist/http-auth.js +2 -2
- package/dist/http-fast.js +17 -17
- package/dist/http-projects.js +4 -0
- package/dist/http-rate-limit.js +1 -0
- package/dist/index-health.js +52 -0
- package/dist/index.js +2 -2
- package/dist/indexer.js +4 -4
- package/dist/intelligence.js +36 -29
- package/dist/lane-provenance.js +39 -0
- package/dist/license.js +1 -1
- package/dist/lineage.js +15 -0
- package/dist/live-embedding-state.js +2 -2
- package/dist/maintenance.js +2 -2
- package/dist/mcp-integrity.js +2 -2
- package/dist/memory-artifacts.js +39 -37
- package/dist/metabolize.js +9 -7
- package/dist/metrics.js +1 -1
- package/dist/migrate-prompt.js +1 -1
- package/dist/migrations.js +236 -45
- package/dist/priority-embed.js +1 -1
- package/dist/providers/embedding-provider.js +1 -1
- package/dist/readonly-gate.js +1 -1
- package/dist/redact-backfill.js +1 -0
- package/dist/rehydration.js +30 -20
- package/dist/reindex.js +1 -1
- package/dist/render-target.js +6 -6
- package/dist/repl-guard.js +1 -1
- package/dist/replication-daemon-entrypoint.js +1 -1
- package/dist/replication-daemon.js +1 -1
- package/dist/retention.js +1 -0
- package/dist/reverse-bridge.js +6 -6
- package/dist/review-sample.js +31 -0
- package/dist/security.js +1 -1
- package/dist/self-maintenance.js +4 -4
- package/dist/served-records.js +8 -2
- package/dist/setup-wizard.js +1 -1
- package/dist/setup.js +1 -1
- package/dist/skill-author.js +2 -2
- package/dist/store-open.js +1 -0
- package/dist/store-readonly.js +1 -0
- package/dist/sub-agent.js +2 -2
- package/dist/tool-manifest-v2.json +1 -1
- package/dist/tool-manifest.json +1 -1
- package/dist/truth-freshness.js +8 -0
- package/dist/ui-dashboard.js +4 -4
- package/dist/vault.js +1 -1
- package/dist/vector-init.js +1 -1
- package/dist/vectors.js +4 -3
- package/dist/visibility.js +12 -12
- package/dist/write-guard.js +1 -0
- package/dist/wyrm-cli.js +108 -92
- package/dist/wyrm-guard.js +13 -12
- package/dist/wyrm-loop.js +3 -3
- package/dist/wyrm-manifest.json +1 -1
- package/dist/wyrm-memo.js +5 -5
- package/package.json +1 -1
- package/scripts/hooks/wyrm-session-capture.mjs +3 -0
package/dist/handlers/capture.js
CHANGED
|
@@ -1,63 +1,65 @@
|
|
|
1
|
-
import{join as
|
|
1
|
+
import{join as ie}from"path";import{createHash as J}from"crypto";import{TOOL_ANNOTATIONS as L}from"../tool-annotations.js";import{renderResult as C,withGlyph as R,validationErrorResponse as A}from"../render.js";import{ValidationError as Q,asEnum as se,asTags as z}from"../validate.js";import{daemonOr as W}from"../daemon-writer.js";import{classifyCapture as oe}from"../capture.js";import{recordWrite as D}from"../receipts.js";import{extractCandidates as V,candidateToArtifact as X,escapeLikePattern as O}from"../auto-capture.js";import{parseTrace as ne,segmentsToText as ae}from"../trace-harvest.js";import{getActor as de,runWithActor as ce}from"./boundary.js";import{harvestProjects as pe}from"../harvest.js";import{readFileSync as G,readdirSync as ue,statSync as N,lstatSync as le}from"fs";import{join as U}from"path";import{homedir as Z}from"os";import{resolveUserPath as H,userPathRoots as B,SecurityError as ee}from"../security.js";import{autoEnrollProject as me}from"../project-resolve.js";import{readSpecDir as ye,specTaskSignature as fe}from"../spec-kit.js";function K(y,f){return(y?.needs_review??f)===1}const te=["auto","quest","truth","memory","classify","direct","extract","trace","session","artifacts","import","spec","checklist","feedback"],M=(y,f)=>f.map(b=>`${y}:${b}`),he=["auto","classify","quest","truth","memory"];function re(y){return new Set(y.toLowerCase().split(/[^a-z0-9]+/).filter(f=>f.length>2))}function ge(y,f,b){if(y.value.trim()===b.trim())return!1;if(y.key.trim().toLowerCase()===f.trim().toLowerCase())return!0;const w=re(y.value),p=re(b);if(w.size===0||p.size===0)return!1;let h=0;for(const n of w)p.has(n)&&h++;return h/(w.size+p.size-h)>=.6}const Le=[{name:"wyrm_capture",description:"Use to save anything worth keeping - the single write funnel. Auto-classifies notes, decisions, and lessons into quest, ground truth, or memory; risky writes queue for wyrm_review. mode=direct (store a proven pattern or lesson learned so future agents reuse it), extract (pull memories from a conversation transcript or freeform text), trace (harvest a session JSONL / tool-call log into run-tagged review candidates), session (close out a work session into reusable knowledge), artifacts (harvest a repo's READMEs and recent commits into durable facts), import source=git|pr|rules, spec (turn a tasks.md into tracked work items), checklist, feedback (artifactId + success: mark what you recalled as useful, or not). Keywords: remember this, jot down, write down, future reference.",inputSchema:{type:"object",properties:{content:{type:"string",description:"classify/rules"},project_id:{type:"number",description:"classify/import"},tags:{type:"array",items:{type:"string"}},mode:{type:"string",enum:[...te],description:"quest|truth|memory force the type"},source:{type:"string",enum:["git","pr","rules"]},projectPath:{type:"string"},kind:{type:"string",enum:["reasoning_trace","lesson","pattern","anti_pattern","heuristic"]},problem:{type:"string",description:"direct"},validatedFix:{type:"string",description:"direct"},text:{type:"string",description:"extract"},sessionId:{type:"string",description:"session"},candidates:{type:"array",items:{type:"object"},description:"session"},dryRun:{type:"boolean",description:"artifacts"},commits:{type:"array",items:{type:"object"},description:"import git"},title:{type:"string",description:"import pr"},body:{type:"string",description:"import pr"},specDir:{type:"string",description:"spec"},problemType:{type:"string",description:"checklist"},whenToUse:{type:"string",description:"checklist"},steps:{type:"array",items:{type:"string"},description:"checklist"}},required:[]},outputSchema:{type:"object",properties:{status:{type:"string",enum:["captured","queued_for_review","skipped"]},mode:{type:"string"},refs:{type:"array",items:{type:"string"}},type:{type:"string"},subtype:{type:"string"},confidence:{type:"number"},reasoning:{type:"string"},id:{type:"integer"},ref:{type:"string"},needs_review:{type:"boolean"},reason:{type:"string"},artifact_id:{type:"integer"},conflicts_with:{type:"array"},advisory_conflicts:{type:"array"}},required:["status","mode","refs"]},annotations:L.wyrm_capture,aliases:[],handler:async(y,{store:f,raw:b,memory:w,truths:p,cache:h})=>{const{content:n,project_id:g,projectPath:d,mode:a}=y,_=z("tags",y.tags);if(a!==void 0&&!he.includes(a))throw new Q("mode",`must be one of: ${te.join(", ")}`);if(typeof n!="string"||!n.trim()){const u=y;return u.problem||u.validated_fix||u.validatedFix||u.kind?A("wyrm_capture","mode","you passed pattern/lesson fields (kind/problem/validated_fix); set mode:'direct' to store them as a distilled artifact."):A("wyrm_capture","content","content is required for auto capture (or use mode:'direct' with problem + validated_fix).")}let v=oe(n);a&&a!=="auto"&&a!=="classify"&&(v={type:a,subtype:{quest:"quest",truth:"decision",memory:"pattern"}[a]??a,confidence:100,reasoning:`Mode override: ${a}`});const{type:$,subtype:s,confidence:o,reasoning:j}=v,i=f,e=g??(d?f.getProject(d)?.id??me(i,d)?.id:void 0)??null;let r=0,P="",I=!1,t,k=!1,c="";if($==="quest"){if(e===null)return A("wyrm_capture","projectPath","is required for quest capture");r=(await W("quest_add",e,{title:n.slice(0,200),description:"",priority:"medium",tags:_?.join(",")},()=>f.addQuest(e,n.slice(0,200),"","medium",_?.join(",")))).id,P="quest",h.invalidate("wyrm_all_quests"),h.invalidate("wyrm_stats")}else if($==="truth"){if(e===null)return A("wyrm_capture","projectPath","is required for truth capture");if(a!=="truth"&&o>=80){const u=s??"other",m=p.getCurrent(e).filter(q=>q.category===u&&ge(q,n.slice(0,60),n));if(m.length>0){const q={kind:"pattern",problem:`Potential conflict with ${m.length} existing truth(s) in category "${u}" (same key or near-identical statement)`,validatedFix:n,whyItWorked:"Pending review: possible supersession",tags:_??[],confidence:o/100,needsReview:1},x=await W("artifact_add",e,q,()=>w.add(e,q)),E=K(x,1),F=D(b(),{tool:"wyrm_capture",outcome:E?"queued":"stored",refTable:"mem",refId:x.id,reason:E?`conflict_check: possible supersession of ${m.length} truth(s); review to activate`:`conflict_check: possible supersession of ${m.length} truth(s); auto-approved, live in recall`,projectId:e});return C({status:E?"queued_for_review":"captured",mode:a??"auto",refs:M("mem",[x.id]),reason:"conflict_check",artifact_id:x.id,conflicts_with:m.map(Y=>({id:Y.id,content:Y.value.slice(0,80)})),receipt:F})}}if(a!=="truth"&&o<100){const u={kind:"pattern",problem:n,validatedFix:"",whyItWorked:"",tags:_??[],confidence:o/100,needsReview:1},l=await W("artifact_add",e,u,()=>w.add(e,u));r=l.id,P="mem",I=K(l,1)}else{const u={category:"decision",key:n.slice(0,60),value:n},{crossCategoryKeys:l,crossCategoryWarning:m}=await import("../truth-freshness.js"),q=b().prepare("SELECT id FROM ground_truths WHERE project_id = ? AND category = 'decision' AND key = ? AND is_current = 1 ORDER BY id DESC LIMIT 1").get(e,u.key.trim());c=m(u.key,l(b(),e,"decision",u.key));const x=await W("truth_set",e,u,()=>p.set(e,u));k=q!==void 0&&q.id===x.id,r=x.id,P="truth",h.invalidate("wyrm_truth_get"),h.invalidate("wyrm_context_build")}}else{if(e===null)return A("wyrm_capture","projectPath","is required for memory capture");const u=o>=75,l={kind:s,problem:n,validatedFix:"",whyItWorked:"",tags:_??[],confidence:o/100,needsReview:u?0:1},m=await W("artifact_add",e,l,()=>w.add(e,l));if(r=m.id,P="mem",I=K(m,u?0:1),s==="heuristic"&&o>=75&&e!==null)try{const q=n.replace(/['"*]/g," ").trim().split(/\s+/).slice(0,5).join(" "),x=b().prepare(`
|
|
2
2
|
SELECT a.id, a.problem FROM memory_artifacts a
|
|
3
3
|
JOIN memory_artifacts_fts ON a.id = memory_artifacts_fts.rowid
|
|
4
4
|
WHERE memory_artifacts_fts MATCH ? AND a.project_id = ? AND a.kind = 'heuristic' AND a.needs_review = 0
|
|
5
5
|
LIMIT 3
|
|
6
|
-
`).all(
|
|
7
|
-
Confidence: ${
|
|
8
|
-
ID: ${
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
`+
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
`,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
`),
|
|
23
|
-
|
|
24
|
-
${
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
${S}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
`),
|
|
38
|
-
|
|
39
|
-
${
|
|
6
|
+
`).all(q,e);x.length>0&&(t=x.map(E=>({id:E.id,content:E.problem.slice(0,80)})))}catch{}}const S=D(b(),{tool:"wyrm_capture",outcome:I?"queued":"stored",refTable:P,refId:r,reason:I?`needs_review (confidence ${o}%); review to activate`:[k?"verified: unchanged value re-asserted, last_verified_at stamped, no new version":"",c?`warning: ${c}`:""].filter(Boolean).join("; ")||void 0,projectId:e??void 0}),T={status:"captured",mode:a==="classify"||a===void 0?"auto":a,refs:[`${P}:${r}`],type:$,subtype:s,confidence:o,reasoning:j,id:r,ref:`${P}:${r}`,needs_review:I,receipt:S,...t&&t.length>0?{advisory_conflicts:t}:{},...k?{verified:!0}:{},...c?{warning:c}:{}};return C(T,(u,l)=>{let m=`Captured as ${u.type}: ${u.subtype}
|
|
7
|
+
Confidence: ${u.confidence}% | ${u.reasoning}
|
|
8
|
+
ID: ${u.ref}`;u.verified&&(m+=`
|
|
9
|
+
Verified in place: the same value was already current, so no new version was written.`);const q=u.warning;q&&(m+=`
|
|
10
|
+
`+R(l.warn,q)),u.needs_review&&(m+=`
|
|
11
|
+
`+R(l.warn,"Stored for review -- run `wyrm_review` to activate"));const x=u.advisory_conflicts;return x&&x.length>0&&(m+=`
|
|
12
|
+
|
|
13
|
+
`+R(l.warn,`Advisory: ${x.length} similar heuristic(s) found:`)+`
|
|
14
|
+
`+x.map(E=>` ${l.bullet} [${E.id}] ${E.content}`).join(`
|
|
15
|
+
`)),m})}},{name:"wyrm_remember",description:"Store a distilled knowledge artifact: proven patterns, lessons learned, anti-patterns, and reasoning traces. These are recalled automatically by wyrm_context_build to help AI models apply past knowledge to new tasks. Example: wyrm_remember({ projectPath: '/home/user/api', kind: 'anti_pattern', problem: 'Jest suite hung after adding the SSE server', validatedFix: 'close() the server in afterAll; its keep-alive timer held the event loop open', whyItWorked: 'open handles block jest exit', outcome: 'positive', tags: ['jest', 'sse'] })",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project this knowledge belongs to"},kind:{type:"string",enum:["reasoning_trace","lesson","pattern","anti_pattern","heuristic"],description:"reasoning_trace: a solved problem with steps; lesson: a general insight; pattern: a proven approach; anti_pattern: what NOT to do; heuristic: a rule of thumb"},problem:{type:"string",description:"What was being solved or observed (be specific and searchable)"},constraints:{type:"string",description:"What conditions or constraints applied"},validatedFix:{type:"string",description:"What actually worked: the validated solution or approach"},whyItWorked:{type:"string",description:"The insight behind WHY this worked (key for transfer learning)"},outcome:{type:"string",enum:["positive","negative","neutral"],description:"Was this approach successful?"},tags:{type:"array",items:{type:"string"},description:"Searchable tags (e.g. ['auth', 'rate-limit', 'typescript'])"},confidence:{type:"number",description:"Confidence level 0.0\u20131.0 (default: 1.0)"},sourceSessionId:{type:"number",description:"Session ID where this was discovered (optional)"}},required:["projectPath","kind","problem"]},outputSchema:{type:"object",properties:{id:{type:"integer"},kind:{type:"string",enum:["reasoning_trace","lesson","pattern","anti_pattern","heuristic"]},problem:{type:"string"},validated_fix:{type:["string","null"]},why_it_worked:{type:["string","null"]},confidence:{type:"number"},tags:{type:["string","null"]}},required:["id","kind","problem","validated_fix","why_it_worked","confidence","tags"]},annotations:L.wyrm_remember,aliases:[],handler:async(y,{store:f,memory:b,raw:w})=>{const{projectPath:p,kind:h,problem:n,constraints:g,outcome:d,confidence:a,sourceSessionId:_}=y,v=z("tags",y.tags),$=y,s=$.validatedFix??$.validated_fix,o=$.whyItWorked??$.why_it_worked,j=se("kind",h,["reasoning_trace","lesson","pattern","anti_pattern","heuristic"]);if(j===void 0)throw new Q("kind","is required");const i=f.getProject(p);if(!i)return A("wyrm_remember","projectPath","project not found");const e={kind:j,problem:n,constraints:g,validatedFix:s,whyItWorked:o,outcome:d,tags:v,confidence:a,sourceSessionId:_},r=await W("artifact_add",i.id,e,()=>b.add(i.id,e)),P=D(w(),{tool:"wyrm_remember",outcome:r.needs_review===1?"queued":"stored",refTable:"mem",refId:r.id,reason:r.needs_review===1?"needs_review: hidden until approved":void 0,source:r.created_by,projectId:i.id}),I={status:r.needs_review===1?"queued_for_review":"captured",mode:"direct",refs:M("mem",[r.id]),id:r.id,kind:r.kind,problem:r.problem,validated_fix:r.validated_fix??null,why_it_worked:r.why_it_worked??null,confidence:r.confidence,tags:r.tags??null,receipt:P};return C(I,(t,k)=>{let c=R(k.brand,"**Memory Stored**")+`
|
|
16
|
+
|
|
17
|
+
`;return c+=`${k.bullet} **Kind:** ${t.kind}
|
|
18
|
+
`,c+=`${k.bullet} **ID:** ${t.id}
|
|
19
|
+
`,c+=`${k.bullet} **Problem:** ${t.problem}
|
|
20
|
+
`,t.validated_fix&&(c+=`${k.bullet} **Solution:** ${t.validated_fix}
|
|
21
|
+
`),t.why_it_worked&&(c+=`${k.bullet} **Why it worked:** ${t.why_it_worked}
|
|
22
|
+
`),c+=`${k.bullet} **Confidence:** ${(t.confidence*100).toFixed(0)}%
|
|
23
|
+
`,t.tags&&(c+=`${k.bullet} **Tags:** ${t.tags}
|
|
24
|
+
`),c+="\n_Use `wyrm_recall` to retrieve similar memories, or `wyrm_context_build` to assemble a task brief._",c})}},{name:"wyrm_auto_capture",description:"Auto-extract durable memories from freeform text (a session transcript, notes, a chat) into the REVIEW QUEUE. A LOCAL extractor (a configurable Ollama model via WYRM_EXTRACT_MODEL or the `model` arg if set, else a deterministic heuristic) pulls out truths, failures, decisions, patterns, and lessons as needs_review candidates; approve/reject with wyrm_review (nothing auto-trusted). Closes the 'just talk, it remembers' gap without a cloud LLM. Deduped + idempotent. Example: wyrm_auto_capture({ projectPath: '/home/user/api', text: 'Deploy failed twice because wrangler.toml pointed at the old KV id; switched to the new binding and decided to pin binding names in CI.' })",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project to file the extracted candidates under"},text:{type:"string",description:"Freeform text to extract durable memories from"},model:{type:"string",description:"Optional Ollama model for extraction (overrides WYRM_EXTRACT_MODEL); omit for deterministic extraction"}},required:["projectPath","text"]},outputSchema:{type:"object",properties:{method:{type:"string"},model:{type:["string","null"]},added:{type:"integer"},skipped:{type:"integer"},candidates:{type:"array",items:{type:"object",properties:{kind:{type:"string"},text:{type:"string"}},required:["kind","text"]}}},required:["method","model","added","skipped","candidates"]},annotations:L.wyrm_auto_capture,aliases:[],handler:async(y,{store:f,raw:b,memory:w,cache:p})=>{const{projectPath:h,text:n,model:g}=y,d=f.getProject(h);if(!d)return A("wyrm_auto_capture","projectPath","project not found");if(!n||n.trim().length<20)return A("wyrm_auto_capture","text","must be at least 20 characters");const{candidates:a,method:_,model:v}=await V(n,{model:g}),$=b();let s=0,o=0;const j=[];for(const e of a){const r=X(e),P=r.tags[r.tags.length-1];if($.prepare("SELECT 1 FROM memory_artifacts WHERE project_id = ? AND tags LIKE ? ESCAPE '\\' LIMIT 1").get(d.id,"%"+O(P)+"%")){o++,D($,{tool:"wyrm_auto_capture",outcome:"dropped",reason:"dedup: already captured",source:"auto-extract",projectId:d.id});continue}const I=w.add(d.id,{kind:r.kind,problem:r.problem,tags:r.tags,confidence:r.confidence,needsReview:1,createdBy:"auto-extract"});j.push(I.id),D($,{tool:"wyrm_auto_capture",outcome:"queued",refTable:"mem",refId:I.id,reason:"needs_review: auto-extracted; review to activate",source:"auto-extract",projectId:d.id}),s++}s>0&&p.invalidate();const i={method:_,model:v??null,added:s,skipped:o,status:s>0?"queued_for_review":"skipped",mode:"extract",refs:M("mem",j),receipt:{outcome:s>0?"queued":"dropped",reason:`${s} queued for review, ${o} dropped as duplicates`},candidates:a.slice(0,8).map(e=>({kind:e.kind,text:e.text.slice(0,80)}))};return C(i,(e,r)=>{const P=e.candidates.map(I=>` ${r.bullet} [${I.kind}] ${I.text}`).join(`
|
|
25
|
+
`);return R(r.brand,`Auto-capture (${e.method}${e.model?` - ${e.model}`:""}) -- ${e.added} candidate(s) -> review queue, ${e.skipped} already present.`)+`
|
|
26
|
+
${P}
|
|
27
|
+
|
|
28
|
+
Approve/reject with wyrm_review.`})}},{name:"wyrm_capture_trace",description:"Harvest a harness's working trace into run-tagged review-queue candidates (OFFLINE). Ingests a Claude Code session transcript (JSONL), a ~/.dragon/traces file/dir, or WYRM_TRACE_TOOL_CALLS event output. The SAME local extractor as auto-capture (Ollama WYRM_EXTRACT_MODEL if set, else deterministic; never a cloud LLM) pulls durable truths/failures/decisions/patterns/lessons; secrets (API keys, tokens, passwords) are REDACTED before anything is stored; candidates land needs_review=1 stamped with the run_id (arg or ambient envelope). Approve/reject with wyrm_review; deduped + idempotent. Pass `trace` (inline text) OR `path` (a trace file or the ~/.dragon/traces dir). Reached via wyrm_capture({ mode: 'trace' }).",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project to file the candidates under"},trace:{type:"string",description:"Inline trace text (JSONL transcript / dragon records / tool-call events)"},path:{type:"string",description:"Path to a trace file, or the ~/.dragon/traces directory (newest file is read); omit ~ for absolute"},format:{type:"string",enum:["auto","claude-jsonl","dragon","tool-calls"],description:"Trace format (default auto-detect)"},run_id:{type:"string",description:"Tag candidates with this run (default: ambient envelope run_id)"},model:{type:"string",description:"Optional Ollama model for extraction (overrides WYRM_EXTRACT_MODEL); omit for deterministic"}},required:["projectPath"]},outputSchema:{type:"object",properties:{method:{type:"string"},model:{type:["string","null"]},format:{type:"string"},source:{type:"string"},run_id:{type:["string","null"]},segments:{type:"integer"},added:{type:"integer"},skipped:{type:"integer"},candidates:{type:"array",items:{type:"object",properties:{kind:{type:"string"},text:{type:"string"}},required:["kind","text"]}}},required:["method","model","format","source","run_id","segments","added","skipped","candidates"]},annotations:L.wyrm_capture_trace,aliases:[],handler:async(y,{store:f,raw:b,memory:w,cache:p})=>{const{projectPath:h,trace:n,path:g,format:d,run_id:a,model:_}=y,v=f.getProject(h);if(!v)return A("wyrm_capture_trace","projectPath","project not found");const $=de(),s=typeof a=="string"&&a.trim()?a.trim().slice(0,64):$.run_id;let o="",j="inline";if(typeof n=="string"&&n.trim())o=n,j="inline";else if(typeof g=="string"&&g.trim()){let l;try{l=H(g,B([U(Z(),".dragon","traces")]),"wyrm_capture_trace.path")}catch(m){if(m instanceof ee)return A("wyrm_capture_trace","path",m.message);throw m}try{if(N(l).isDirectory()){const q=B([U(Z(),".dragon","traces")]),x=ue(l).map(E=>U(l,E)).filter(E=>{try{return le(E).isFile()?(H(E,q,"wyrm_capture_trace.path"),!0):!1}catch{return!1}}).sort((E,F)=>N(F).mtimeMs-N(E).mtimeMs);if(x.length===0)return A("wyrm_capture_trace","path","no trace files in directory");o=G(x[0],"utf-8"),j=x[0]}else o=G(l,"utf-8"),j=l}catch(m){return A("wyrm_capture_trace","path",`could not read trace path: ${m.message}`)}}else return A("wyrm_capture_trace","trace","provide inline trace or path to trace file");const{segments:i,format:e}=ne(o,d??"auto"),r=ae(i);if(r.trim().length<20){const l={method:"deterministic",model:null,format:e,source:j,run_id:s,segments:i.length,added:0,skipped:0,status:"skipped",mode:"trace",refs:[],candidates:[]};return C(l,(m,q)=>R(q.brand,`Trace harvest (${m.format}) -- no durable content extracted from ${m.segments} segment(s).`))}const{candidates:P,method:I,model:t}=await V(r,{model:_}),k=b();let c=0,S=0;const T=[];for(const l of P){const m=X(l),q=m.tags[m.tags.length-1];if(k.prepare("SELECT 1 FROM memory_artifacts WHERE project_id = ? AND tags LIKE ? ESCAPE '\\' LIMIT 1").get(v.id,"%"+O(q)+"%")){S++;continue}const x=s?[...m.tags,"trace",`run:${s}`]:[...m.tags,"trace"],E=ce({agent_id:$.agent_id,run_id:s,source:s?"param":$.source},()=>w.add(v.id,{kind:m.kind,problem:m.problem,tags:x,confidence:m.confidence,needsReview:1,createdBy:"trace-harvest"}));T.push(E.id),c++}c>0&&p.invalidate();const u={method:I,model:t??null,format:e,source:j,run_id:s,segments:i.length,added:c,skipped:S,status:c>0?"queued_for_review":"skipped",mode:"trace",refs:M("mem",T),candidates:P.slice(0,8).map(l=>({kind:l.kind,text:l.text.slice(0,80)}))};return C(u,(l,m)=>{const q=l.candidates.map(x=>` ${m.bullet} [${x.kind}] ${x.text}`).join(`
|
|
29
|
+
`);return R(m.brand,`Trace harvest (${l.format}, ${l.method}${l.model?` - ${l.model}`:""}) -- ${l.segments} segment(s) -> ${l.added} candidate(s) to review queue, ${l.skipped} already present${l.run_id?` [run:${l.run_id}]`:""}.`)+(q?`
|
|
30
|
+
${q}`:"")+`
|
|
31
|
+
|
|
32
|
+
Approve/reject with wyrm_review.`})}},{name:"wyrm_distill",description:"Distill a work session into candidate memory artifacts for review. Parses the session's objectives, decisions, and outcomes into structured artifacts (lessons, patterns, anti-patterns) and queues them for approval via wyrm_review. Use at session end to extract institutional knowledge.",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project root path"},sessionId:{type:"string",description:"Session ID to distill"},candidates:{type:"array",description:"Pre-parsed candidate artifacts to store for review",items:{type:"object",properties:{kind:{type:"string",enum:["lesson","pattern","anti_pattern","heuristic","reasoning_trace"]},title:{type:"string"},content:{type:"string"},tags:{type:"array",items:{type:"string"}},confidence:{type:"number"}},required:["kind","title","content"]}}},required:["projectPath","candidates"]},outputSchema:{type:"object",properties:{count:{type:"integer"},artifacts:{type:"array",items:{type:"object",properties:{id:{type:"integer"},kind:{type:"string"},title:{type:"string"}},required:["id","kind","title"]}}},required:["count","artifacts"]},annotations:L.wyrm_distill,aliases:[],handler:(y,{store:f,memory:b})=>{const{projectPath:w,candidates:p}=y,h=f.getProject(w);if(!h)return{content:[{type:"text",text:`Project not found: ${w}`}],isError:!0};if(!p||p.length===0)return{content:[{type:"text",text:`**Distill**
|
|
33
|
+
|
|
34
|
+
No candidates provided. Provide an array of candidate artifacts to distill.`}],isError:!0};const n=[];for(const d of p){const a=b.add(h.id,{kind:d.kind,problem:d.title,validatedFix:d.content,tags:d.tags??[],confidence:d.confidence??.7,needsReview:1});n.push({id:a.id,kind:d.kind,title:d.title})}const g={status:"queued_for_review",mode:"session",refs:M("mem",n.map(d=>d.id)),count:n.length,artifacts:n};return C(g,(d,a)=>{let _=R(a.brand,`**Distillation Complete** -- ${d.count} artifact${d.count!==1?"s":""} queued for review`)+`
|
|
35
|
+
|
|
36
|
+
`;return _+=d.artifacts.map(v=>`${a.bullet} [#${v.id}] **${v.kind}**: ${v.title}`).join(`
|
|
37
|
+
`),_+="\n\nUse `wyrm_review` with each ID to approve or reject.",_})}},{name:"wyrm_harvest",description:"Auto-populate memory from artifacts you already produce. Walks a project (or ALL registered projects) and harvests durable facts from its docs (README/CLAUDE/AGENTS/ARCHITECTURE) plus recent commit subjects (git log) into the REVIEW QUEUE; approve/reject with wyrm_review, nothing is auto-trusted. Idempotent (re-runs skip what's already harvested). Use dryRun to preview. The fix for a thin corpus.",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Harvest one project; omit to harvest ALL registered projects"},dryRun:{type:"boolean",description:"Preview what would be harvested without writing anything"},gitLimit:{type:"number",description:"How many recent commits to scan per project (default 30, max 200)"},includeCode:{type:"boolean",description:"Also harvest CODE: tech-stack facts from manifests (package.json/Cargo.toml/\u2026) + TODO/FIXME markers into the review queue, AND index code symbols (functions/classes/types) for search. Heavier: scope to one project for the full code pass."}}},outputSchema:{type:"object",properties:{dry_run:{type:"boolean"},projects_scanned:{type:"integer"},added:{type:"integer"},skipped:{type:"integer"},top:{type:"array",items:{type:"object",properties:{project:{type:"string"},added:{type:"integer"},skipped:{type:"integer"}},required:["project","added","skipped"]}},sample:{type:"array",items:{type:"string"}},code_symbols:{type:["object","null"],properties:{symbols:{type:"integer"},projects:{type:"integer"},files:{type:"integer"}},required:["symbols","projects","files"]}},required:["dry_run","projects_scanned","added","skipped","top","sample","code_symbols"]},annotations:L.wyrm_harvest,aliases:[],handler:(y,{store:f,raw:b,memory:w,symbols:p,cache:h})=>{const{projectPath:n,dryRun:g,gitLimit:d,includeCode:a}=y,_=b(),v=[],$={existsBySig:(t,k)=>!!_.prepare("SELECT 1 FROM memory_artifacts WHERE project_id = ? AND tags LIKE ? ESCAPE '\\' LIMIT 1").get(t,"%"+O(k)+"%"),addCandidate:(t,k)=>{const c=w.add(t,{kind:k.kind,problem:k.text,tags:[...k.tags,k.sig],confidence:k.confidence,needsReview:1,createdBy:"harvest"});v.push(c.id),D(_,{tool:"wyrm_harvest",outcome:"queued",refTable:"mem",refId:c.id,reason:"needs_review: harvested; review to activate",source:"harvest",projectId:t})},onSkip:t=>{D(_,{tool:"wyrm_harvest",outcome:"dropped",reason:"dedup: signature already harvested",source:"harvest",projectId:t})}};let s;if(n){const t=f.getProject(n);if(!t)return{content:[{type:"text",text:"Project not found"}],isError:!0};s=[{id:t.id,name:t.name,path:t.path}]}else s=f.getAllProjects(500).map(t=>({id:t.id,name:t.name,path:t.path}));const{reports:o,totalAdded:j,totalSkipped:i}=pe($,s,{dryRun:g,gitLimit:d,includeCode:a});!g&&j>0&&h.invalidate();let e=null;if(a&&!g){let t=0,k=0,c=0;for(const S of s)try{const T=p.indexProject(S.id,S.path);t+=T.files,k+=T.symbols,c++}catch{}e={symbols:k,projects:c,files:t}}const r=o.filter(t=>t.added>0).sort((t,k)=>k.added-t.added).slice(0,15).map(t=>({project:t.project,added:t.added,skipped:t.skipped})),P=o.flatMap(t=>t.sample??[]).slice(0,6),I={status:!g&&j>0?"queued_for_review":"skipped",mode:"artifacts",refs:M("mem",v),dry_run:!!g,projects_scanned:s.length,added:j,skipped:i,top:r,sample:P,code_symbols:e};return C(I,(t,k)=>{const c=t.code_symbols?`
|
|
38
|
+
Indexed ${t.code_symbols.symbols} code symbols across ${t.code_symbols.projects} project(s) (${t.code_symbols.files} files).`:"",S=t.top.map(u=>` +${String(u.added).padStart(3)} (skip ${u.skipped}) ${u.project}`).join(`
|
|
39
|
+
`),T=t.sample.map(u=>` ${k.bullet} ${u}`).join(`
|
|
40
|
+
`);return R(k.brand,`Harvest ${t.dry_run?"(dry run) ":""}-- ${t.added} candidate(s) -> review queue across ${t.projects_scanned} project(s); ${t.skipped} already present.`)+`${c}
|
|
41
|
+
${S}`+(T?`
|
|
40
42
|
|
|
41
43
|
Sample:
|
|
42
|
-
${
|
|
44
|
+
${T}`:"")+`
|
|
43
45
|
|
|
44
|
-
Approve/reject with wyrm_review.`})}},{name:"wyrm_import_git",description:"Import a list of commits into the Wyrm memory review queue. Useful for onboarding a project's history or capturing recent work.",inputSchema:{type:"object",properties:{project_id:{type:"number",description:"Project ID"},commits:{type:"array",description:"Array of commit objects",items:{type:"object",properties:{message:{type:"string"},diff_summary:{type:"string"},author:{type:"string"},date:{type:"string"}},required:["message"]}},auto_approve:{type:"boolean",description:"Skip review queue and activate immediately (default: false)"}},required:["project_id","commits"]},outputSchema:{type:"object",properties:{captured:{type:"integer"},skipped:{type:"integer"},auto_approved:{type:"boolean"}},required:["captured","skipped","auto_approved"]},annotations:
|
|
45
|
-
${s.diff_summary??""}`).digest("hex").slice(0,16);if(
|
|
46
|
+
Approve/reject with wyrm_review.`})}},{name:"wyrm_import_git",description:"Import a list of commits into the Wyrm memory review queue. Useful for onboarding a project's history or capturing recent work.",inputSchema:{type:"object",properties:{project_id:{type:"number",description:"Project ID"},commits:{type:"array",description:"Array of commit objects",items:{type:"object",properties:{message:{type:"string"},diff_summary:{type:"string"},author:{type:"string"},date:{type:"string"}},required:["message"]}},auto_approve:{type:"boolean",description:"Skip review queue and activate immediately (default: false)"}},required:["project_id","commits"]},outputSchema:{type:"object",properties:{captured:{type:"integer"},skipped:{type:"integer"},auto_approved:{type:"boolean"}},required:["captured","skipped","auto_approved"]},annotations:L.wyrm_import_git,aliases:[],handler:(y,{store:f,memory:b,raw:w})=>{const{project_id:p,commits:h,auto_approve:n}=y;if(!f.getProjectById(p))return{content:[{type:"text",text:`Project ID ${p} not found.`}],isError:!0};let d=0,a=0;const _=[],v=w();for(const s of h){const o=s.message??"";if(/^Merge /i.test(o)){a++;continue}if(/^(chore|bump|release|version)/i.test(o)){a++;continue}const j="gi:"+J("sha1").update(`${o}
|
|
47
|
+
${s.diff_summary??""}`).digest("hex").slice(0,16);if(v.prepare("SELECT 1 FROM memory_artifacts WHERE project_id = ? AND tags LIKE ? ESCAPE '\\' LIMIT 1").get(p,"%"+O(j)+"%")){a++,D(v,{tool:"wyrm_import_git",outcome:"dropped",reason:"dedup: this commit was already imported (gi: signature match)",source:"import:git",projectId:p});continue}let i="pattern";/^fix(\(.+\))?:/i.test(o)?i="lesson":/^feat(\(.+\))?:/i.test(o)?i="pattern":/^refactor(\(.+\))?:/i.test(o)&&(i="heuristic");const e=o.split(":")[0]??"commit",r=b.add(p,{kind:i,problem:o,validatedFix:s.diff_summary??"",whyItWorked:`Committed by ${s.author??"unknown"} on ${s.date??"unknown"}`,tags:["git","commit",e.toLowerCase(),j],confidence:.6,needsReview:n?0:1});_.push(r.id),D(v,{tool:"wyrm_import_git",outcome:n?"stored":"queued",refTable:"mem",refId:r.id,reason:n?void 0:"needs_review: imported; review to activate",source:"import:git",projectId:p}),d++}const $={status:d===0?"skipped":n?"captured":"queued_for_review",mode:"import",refs:M("mem",_),captured:d,skipped:a,auto_approved:!!n};return C($,(s,o)=>R(o.brand,"**Git Import Complete**")+`
|
|
46
48
|
|
|
47
49
|
${o.bullet} **Captured:** ${s.captured}
|
|
48
50
|
${o.bullet} **Skipped:** ${s.skipped} (merges/chores)
|
|
49
|
-
${o.bullet} **Review needed:** ${s.auto_approved?"No (auto-approved)":`Yes -- run \`wyrm_review\` to activate ${s.captured} artifact${s.captured!==1?"s":""}`}`)}},{name:"wyrm_import_pr",description:"Import a pull request (title, body, review comments) into the Wyrm memory review queue to extract patterns and heuristics.",inputSchema:{type:"object",properties:{project_id:{type:"number",description:"Project ID"},title:{type:"string",description:"PR title"},body:{type:"string",description:"PR description / body"},review_comments:{type:"array",items:{type:"string"},description:"Review comment strings"},auto_approve:{type:"boolean",description:"Skip review queue (default: false)"}},required:["project_id","title","body"]},outputSchema:{type:"object",properties:{created:{type:"integer"},auto_approved:{type:"boolean"}},required:["created","auto_approved"]},annotations:
|
|
51
|
+
${o.bullet} **Review needed:** ${s.auto_approved?"No (auto-approved)":`Yes -- run \`wyrm_review\` to activate ${s.captured} artifact${s.captured!==1?"s":""}`}`)}},{name:"wyrm_import_pr",description:"Import a pull request (title, body, review comments) into the Wyrm memory review queue to extract patterns and heuristics.",inputSchema:{type:"object",properties:{project_id:{type:"number",description:"Project ID"},title:{type:"string",description:"PR title"},body:{type:"string",description:"PR description / body"},review_comments:{type:"array",items:{type:"string"},description:"Review comment strings"},auto_approve:{type:"boolean",description:"Skip review queue (default: false)"}},required:["project_id","title","body"]},outputSchema:{type:"object",properties:{created:{type:"integer"},auto_approved:{type:"boolean"}},required:["created","auto_approved"]},annotations:L.wyrm_import_pr,aliases:[],handler:(y,{store:f,memory:b,raw:w})=>{const{project_id:p,title:h,body:n,review_comments:g,auto_approve:d}=y;if(!f.getProjectById(p))return{content:[{type:"text",text:`Project ID ${p} not found.`}],isError:!0};let _=0,v=0;const $=[],s=d?0:1,o=w(),j=(e,r,P)=>{const I="pr:"+J("sha1").update(r).digest("hex").slice(0,16);if(o.prepare("SELECT 1 FROM memory_artifacts WHERE project_id = ? AND tags LIKE ? ESCAPE '\\' LIMIT 1").get(p,"%"+O(I)+"%")){v++,D(o,{tool:"wyrm_import_pr",outcome:"dropped",reason:"dedup: already imported (pr: signature)",source:"import:pr",projectId:p});return}const t=b.add(p,{kind:e,problem:r,validatedFix:"",whyItWorked:"",tags:[...P,I],confidence:.65,needsReview:s,createdBy:"import:pr"});$.push(t.id),D(o,{tool:"wyrm_import_pr",outcome:d?"stored":"queued",refTable:"mem",refId:t.id,reason:d?void 0:"needs_review: imported; review to activate",source:"import:pr",projectId:p}),_++};j("pattern",`${h}
|
|
50
52
|
|
|
51
|
-
${n}`,["git","pr"]);for(const
|
|
53
|
+
${n}`,["git","pr"]);for(const e of g??[])/\b(should|must|always|never|prefer|avoid|use|don't)\b/i.test(e)&&j("heuristic",e,["git","pr","review"]);const i={status:_===0?"skipped":d?"captured":"queued_for_review",mode:"import",refs:M("mem",$),created:_,skipped:v,auto_approved:!!d};return C(i,(e,r)=>R(r.brand,"**PR Import Complete**")+`
|
|
52
54
|
|
|
53
|
-
${r.bullet} **Artifacts created:** ${
|
|
54
|
-
${r.bullet} **Review needed:** ${
|
|
55
|
+
${r.bullet} **Artifacts created:** ${e.created}
|
|
56
|
+
${r.bullet} **Review needed:** ${e.auto_approved?"No (auto-approved)":"Yes -- run `wyrm_review` to activate"}`)}},{name:"wyrm_import_rules",description:"Import a .cursorrules / copilot-instructions file into Wyrm. Parses by section and creates ground truths (for constraint/rule language) or memory artifacts (for general guidelines).",inputSchema:{type:"object",properties:{project_id:{type:"number",description:"Project ID"},content:{type:"string",description:"Full text of the rules file"},format:{type:"string",enum:["cursorrules","copilot","plain"],description:"Source format (default: plain)"},source_file:{type:"string",description:"Filename for provenance (optional)"}},required:["project_id","content"]},outputSchema:{type:"object",properties:{format:{type:"string"},source:{type:"string"},truths_created:{type:"integer"},artifacts_created:{type:"integer"}},required:["format","source","truths_created","artifacts_created"]},annotations:L.wyrm_import_rules,aliases:[],handler:async(y,{store:f,memory:b,truths:w,raw:p})=>{const{project_id:h,content:n,format:g,source_file:d}=y;if(!f.getProjectById(h))return{content:[{type:"text",text:`Project ID ${h} not found.`}],isError:!0};const _=g??"plain",v=d??"rules",$=["imported",_,v],s=n.split(/\n(?=#)/),o=n.split(/\n\n+/),j=(s.length>=o.length?s:o).map(c=>c.trim()).filter(c=>c.length>=15);let i=0,e=0,r=0;const P=[],{isConfigShapedTruth:I}=await import("../metabolize.js"),t=p();for(const c of j){const S=c.slice(0,50).replace(/\n/g," ");if(/\b(always|never|must|use|don't|avoid|prefer)\b/i.test(c)&&!I(S,c)){const T=w.set(h,{category:"constraint",key:S,value:c,source:v});D(t,{tool:"wyrm_import_rules",outcome:"stored",refTable:"truth",refId:T.id,source:"import:rules",projectId:h}),P.push(`truth:${T.id}`),i++}else{const T=b.add(h,{kind:"heuristic",problem:c,validatedFix:"",whyItWorked:"",tags:$,confidence:.7,needsReview:1,createdBy:"import:rules"});P.push(`mem:${T.id}`),I(S,c)&&r++,D(t,{tool:"wyrm_import_rules",outcome:"queued",refTable:"mem",refId:T.id,reason:I(S,c)?"truth-shape lint: config shape routed to review":"needs_review: imported; review to activate",source:"import:rules",projectId:h}),e++}}const k={status:e>0?"queued_for_review":i>0?"captured":"skipped",mode:"import",refs:P,format:_,source:v,truths_created:i,artifacts_created:e,linted:r};return C(k,(c,S)=>R(S.brand,`**Rules Import Complete** (${c.format})`)+`
|
|
55
57
|
|
|
56
|
-
${
|
|
57
|
-
${
|
|
58
|
-
${
|
|
58
|
+
${S.bullet} **Ground truths created:** ${c.truths_created} (active immediately)
|
|
59
|
+
${S.bullet} **Memory artifacts created:** ${c.artifacts_created} (pending review)
|
|
60
|
+
${S.bullet} **Source:** ${c.source}
|
|
59
61
|
|
|
60
|
-
Run \`wyrm_review\` to approve the memory artifacts.`)}},{name:"wyrm_spec_register",description:"Make spec-kit Wyrm-native. Reads a spec directory (spec.md title/summary + tasks.md task list) and creates one Wyrm quest per task, linked to a project, with a stored spec\u2192project link. Idempotent
|
|
62
|
+
Run \`wyrm_review\` to approve the memory artifacts.`)}},{name:"wyrm_spec_register",description:"Make spec-kit Wyrm-native. Reads a spec directory (spec.md title/summary + tasks.md task list) and creates one Wyrm quest per task, linked to a project, with a stored spec\u2192project link. Idempotent: re-running updates the same quests (deduped by a per-spec-task signature) instead of duplicating.",inputSchema:{type:"object",properties:{specDir:{type:"string",description:"Absolute path to the spec directory (e.g. '.../dragon-platform/specs/001-ghostmesh')"},projectPath:{type:"string",description:"Path of the Wyrm project to attach the spec + quests to (must already be registered)"},priority:{type:"string",enum:["critical","high","medium","low"],description:"Priority for the generated quests (default 'medium')"}},required:["specDir","projectPath"]},outputSchema:{type:"object",properties:{spec_dir:{type:"string"},title:{type:["string","null"]},project:{type:"string"},tasks_parsed:{type:"integer"},quests_created:{type:"integer"},quests_updated:{type:"integer"},quest_ids:{type:"array",items:{type:"integer"}}},required:["spec_dir","title","project","tasks_parsed","quests_created","quests_updated","quest_ids"]},annotations:L.wyrm_spec_register,aliases:[],handler:(y,{store:f,indexing:b})=>{const{specDir:w,projectPath:p,priority:h}=y,n=["specDir","projectPath"].filter(i=>{const e=y[i];return!e&&e!==0&&e!==!1});if(n.length>0)return{content:[{type:"text",text:`Missing required arguments: ${n.join(", ")}`}],isError:!0};const g=f.getProject(p);if(!g)return{content:[{type:"text",text:`**Spec Register**: Project not found at "${p}". Register it first (wyrm_project action=scan, or wyrm_session action=start).`}],isError:!0};let d;try{const i=typeof g.path=="string"&&g.path?[g.path]:[];d=H(w,B(i),"wyrm_spec_register.specDir")}catch(i){if(i instanceof ee)return A("wyrm_spec_register","specDir",i.message);throw i}const a=ye(d);if(a.tasks.length===0){const i={status:"skipped",mode:"spec",refs:[],spec_dir:w,title:a.title??null,project:g.name,tasks_parsed:0,quests_created:0,quests_updated:0,quest_ids:[]};return C(i,(e,r)=>R(r.brand,`**Spec Register**: No tasks found in ${ie(e.spec_dir,"tasks.md")}. Nothing to create.`))}const _=h||"medium";let v=0,$=0;const s=[];for(const i of a.tasks){const e=fe(w,i.id),r=`${i.id}: ${i.title}`,P=e,I=f.findQuestBySpecSignature(g.id,e);if(I)f.updateQuestFields(I.id,{title:r,tags:P}),s.push(I.id),$++;else{const t=f.addQuest(g.id,r,`spec-kit task from ${w}`,_,P);b()?.enqueue("quest",t.id,g.id),s.push(t.id),v++}}const o=f.upsertSpec(g.id,w,a.title,a.summary,a.tasks.length),j={status:"captured",mode:"spec",refs:M("quest",s),spec_dir:o.spec_dir,title:o.title??null,project:g.name,tasks_parsed:a.tasks.length,quests_created:v,quests_updated:$,quest_ids:s};return C(j,(i,e)=>{let r=R(e.brand,"**Spec Registered**")+`
|
|
61
63
|
|
|
62
64
|
`;return r+=`Spec: ${i.title||"(untitled)"}
|
|
63
65
|
`,o.summary&&(r+=`Summary: ${o.summary}
|
|
@@ -68,11 +70,11 @@ Run \`wyrm_review\` to approve the memory artifacts.`)}},{name:"wyrm_spec_regist
|
|
|
68
70
|
`,r+=`
|
|
69
71
|
\`\`\`json
|
|
70
72
|
${JSON.stringify({spec:i.spec_dir,title:i.title,project:i.project,tasksParsed:i.tasks_parsed,questsCreated:i.quests_created,questsUpdated:i.quests_updated,questIds:i.quest_ids},null,2)}
|
|
71
|
-
\`\`\``,r})}},{name:"wyrm_scaffold_save",description:"Save a reasoning scaffold
|
|
73
|
+
\`\`\``,r})}},{name:"wyrm_scaffold_save",description:"Save a reasoning scaffold: a structured checklist that guides step-by-step thinking for a specific problem type. Scaffolds are automatically surfaced by wyrm_context_build when the task matches, helping AI models apply proven reasoning patterns.",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project path (optional, null for global scaffolds)"},problemType:{type:"string",description:"Short slug for the problem type (e.g. 'api-design', 'security-review', 'db-migration')"},description:{type:"string",description:"What kind of task this scaffold helps with"},whenToUse:{type:"string",description:"Conditions that indicate this scaffold should be applied"},steps:{type:"array",items:{type:"string"},description:"Ordered checklist steps to follow"},verificationSteps:{type:"array",items:{type:"string"},description:"Steps to verify the work is correct"},doNotApplyIf:{type:"string",description:"Conditions where this scaffold does NOT apply"},tags:{type:"array",items:{type:"string"},description:"Searchable tags"}},required:["problemType","description","whenToUse","steps"]},outputSchema:{type:"object",properties:{id:{type:"integer"},problem_type:{type:"string"},description:{type:"string"},steps:{type:"integer"}},required:["id","problem_type","description","steps"]},annotations:L.wyrm_scaffold_save,aliases:[],handler:(y,{store:f,scaffolds:b,cache:w})=>{const{projectPath:p,problemType:h,description:n,whenToUse:g,steps:d,verificationSteps:a,doNotApplyIf:_,tags:v}=y,$=p?f.getProject(p)?.id??void 0:void 0,s=b.save({projectId:$,problemType:h,description:n,whenToUse:g,steps:d,verificationSteps:a??void 0,doNotApplyIf:_?[_]:void 0,tags:v??[]});w.invalidate("wyrm_scaffold_get"),w.invalidate("wyrm_context_build");const o={status:"captured",mode:"checklist",refs:M("scaffold",[s.id]),id:s.id,problem_type:h,description:n,steps:d.length};return C(o,(j,i)=>R(i.brand,"**Reasoning Scaffold Saved**")+`
|
|
72
74
|
|
|
73
|
-
${i.bullet} **Problem Type:** ${
|
|
74
|
-
${i.bullet} **Description:** ${
|
|
75
|
-
${i.bullet} **Steps:** ${
|
|
76
|
-
${i.bullet} **ID:** ${
|
|
75
|
+
${i.bullet} **Problem Type:** ${j.problem_type}
|
|
76
|
+
${i.bullet} **Description:** ${j.description}
|
|
77
|
+
${i.bullet} **Steps:** ${j.steps} checklist items
|
|
78
|
+
${i.bullet} **ID:** ${j.id}
|
|
77
79
|
|
|
78
|
-
This scaffold will be surfaced by \`wyrm_context_build\` when tasks match "${
|
|
80
|
+
This scaffold will be surfaced by \`wyrm_context_build\` when tasks match "${j.problem_type}".`)}}];export{Le as captureToolSpecs,ge as truthMayConflict};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{TOOL_ANNOTATIONS as u}from"../tool-annotations.js";import{staleFoundationWarning as k}from"../causality.js";import{refreshThread as b}from"../thread-assemble.js";import{ValidationError as w,asEnum as p,asNumber as x,asString as v,requireInt as _}from"../validate.js";import{requireProjectFromArg as j}from"./project-arg.js";const y=["truth","artifact","quest","session","data"],q=["because_of","supersedes","refutes","verifies"];function h(r,s){if(s===void 0)throw new w(r,"is required");return s}const P=[{name:"wyrm_decided_because",description:"Use to link a choice or decision to the assumption or evidence it was based on - records a causal edge (truth/quest/artifact/session) so stale foundations invalidate downstream.",inputSchema:{type:"object",properties:{projectPath:{type:"string"},from_kind:{type:"string",enum:["truth","artifact","quest","session","data"]},from_id:{type:"number"},to_kind:{type:"string",enum:["truth","artifact","quest","session","data"]},to_id:{type:"number"},relation:{type:"string",enum:["because_of","supersedes","refutes","verifies"],description:"Default because_of"},rationale:{type:"string",description:"Why"},confidence:{type:"number",description:"0-1, default 1.0"}},required:["projectPath","from_kind","from_id","to_kind","to_id"]},annotations:u.wyrm_decided_because,aliases:[],handler:async(r,s)=>{const{causality:d,db:o}=s,t=r,n=h("from_kind",p("from_kind",t.from_kind,y)),a=_("from_id",t.from_id,{min:1}),e=h("to_kind",p("to_kind",t.to_kind,y)),c=_("to_id",t.to_id,{min:1}),i=p("relation",t.relation,q),$=v("rationale",t.rationale),g=x("confidence",t.confidence,{min:0,max:1}),m=j(o,o.getDatabase(),t.projectPath);if(!m)return{content:[{type:"text",text:`Project not found: ${String(t.projectPath)}`}],isError:!0};const{edge:f,created:l}=d.linkOnce({project_id:m.id,from_kind:n,from_id:a,to_kind:e,to_id:c,relation:i,rationale:$,confidence:g});return l&&b(o.getDatabase(),m.id),{content:[{type:"text",text:`\u{F115D} Edge #${f.id}
|
|
1
|
+
import{TOOL_ANNOTATIONS as u}from"../tool-annotations.js";import{staleFoundationWarning as k}from"../causality.js";import{refreshThread as b}from"../thread-assemble.js";import{ValidationError as w,asEnum as p,asNumber as x,asString as v,requireInt as _}from"../validate.js";import{requireProjectFromArg as j}from"./project-arg.js";const y=["truth","artifact","quest","session","data"],q=["because_of","supersedes","refutes","verifies"];function h(r,s){if(s===void 0)throw new w(r,"is required");return s}const P=[{name:"wyrm_decided_because",description:"Use to link a choice or decision to the assumption or evidence it was based on - records a causal edge (truth/quest/artifact/session) so stale foundations invalidate downstream.",inputSchema:{type:"object",properties:{projectPath:{type:"string"},from_kind:{type:"string",enum:["truth","artifact","quest","session","data"]},from_id:{type:"number"},to_kind:{type:"string",enum:["truth","artifact","quest","session","data"]},to_id:{type:"number"},relation:{type:"string",enum:["because_of","supersedes","refutes","verifies"],description:"Default because_of"},rationale:{type:"string",description:"Why"},confidence:{type:"number",description:"0-1, default 1.0"}},required:["projectPath","from_kind","from_id","to_kind","to_id"]},annotations:u.wyrm_decided_because,aliases:[],handler:async(r,s)=>{const{causality:d,db:o}=s,t=r,n=h("from_kind",p("from_kind",t.from_kind,y)),a=_("from_id",t.from_id,{min:1}),e=h("to_kind",p("to_kind",t.to_kind,y)),c=_("to_id",t.to_id,{min:1}),i=p("relation",t.relation,q),$=v("rationale",t.rationale),g=x("confidence",t.confidence,{min:0,max:1}),m=j(o,o.getDatabase(),t.projectPath);if(!m)return{content:[{type:"text",text:`Project not found: ${String(t.projectPath)}`}],isError:!0};const{edge:f,created:l}=d.linkOnce({project_id:m.id,from_kind:n,from_id:a,to_kind:e,to_id:c,relation:i,rationale:$,confidence:g});return l&&b(o.getDatabase(),m.id),{content:[{type:"text",text:`\u{F115D} Edge #${f.id}: ${n}#${a} ${f.relation} ${e}#${c}${l?"":" (already recorded)"}`}]}}},{name:"wyrm_decision_downstream",description:"List all decisions that depend on the given source. Use before invalidating a truth to see the blast radius.",inputSchema:{type:"object",properties:{kind:{type:"string",enum:["truth","artifact","quest","session","data"]},id:{type:"number"}},required:["kind","id"]},annotations:u.wyrm_decision_downstream,aliases:[],handler:async(r,s)=>{const{causality:d}=s,{kind:o,id:t}=r,n=d.downstreamOf(o,t);if(n.length===0)return{content:[{type:"text",text:`\u{F115D} No active dependents on ${o}#${t}.`}]};const a=n.map(e=>`\u2022 ${e.from_kind}#${e.from_id} (${e.relation}, conf ${e.confidence})${e.rationale?`: ${e.rationale}`:""}`).join(`
|
|
2
2
|
`);return{content:[{type:"text",text:`\u{F115D} ${n.length} dependent(s):
|
|
3
3
|
${a}`}]}}},{name:"wyrm_decision_upstream",description:"List all sources a decision was based on (incoming edges). Walks the 'why did we choose this?' chain.",inputSchema:{type:"object",properties:{kind:{type:"string",enum:["truth","artifact","quest","session","data"]},id:{type:"number"}},required:["kind","id"]},annotations:u.wyrm_decision_upstream,aliases:[],handler:async(r,s)=>{const{causality:d}=s,{kind:o,id:t}=r,n=d.upstreamOf(o,t),a=k(d.staleFoundations(o,t)),e=a?`${a}
|
|
4
4
|
|
|
5
|
-
`:"";if(n.length===0)return{content:[{type:"text",text:`${e}\u{F115D} No recorded basis for ${o}#${t}.`}]};const c=n.map(i=>`\u2022 ${i.to_kind}#${i.to_id} (${i.relation}, conf ${i.confidence})${i.rationale
|
|
5
|
+
`:"";if(n.length===0)return{content:[{type:"text",text:`${e}\u{F115D} No recorded basis for ${o}#${t}.`}]};const c=n.map(i=>`\u2022 ${i.to_kind}#${i.to_id} (${i.relation}, conf ${i.confidence})${i.rationale?`: ${i.rationale}`:""}`).join(`
|
|
6
6
|
`);return{content:[{type:"text",text:`${e}\u{F115D} ${n.length} basis edge(s):
|
|
7
7
|
${c}`}]}}},{name:"wyrm_decision_invalidate",description:"Cascade-invalidate all decisions downstream of a source (recursive). Use when a foundational truth turns out to be wrong. Returns the set of affected (kind, id) pairs.",inputSchema:{type:"object",properties:{kind:{type:"string",enum:["truth","artifact","quest","session","data"]},id:{type:"number"},reason:{type:"string",description:"Why the source was invalidated"}},required:["kind","id"]},annotations:u.wyrm_decision_invalidate,aliases:[],handler:async(r,s)=>{const{causality:d}=s,o=r||{},{kind:t,id:n,reason:a}=r,e=d.invalidateDownstream(t,n,a);if(e.invalidated===0)return{content:[{type:"text",text:`\u{F115D} No dependents to invalidate from ${t}#${n}.`}]};const c=e.affected.map(i=>`\u2022 ${i.kind}#${i.id} (edge #${i.edge_id})`).join(`
|
|
8
8
|
`);return{content:[{type:"text",text:`\u{F115D} Invalidated ${e.invalidated} downstream decision(s):
|
package/dist/handlers/cloud.js
CHANGED
|
@@ -35,7 +35,7 @@ ${[r?.stdout,r?.stderr].filter(Boolean).join(`
|
|
|
35
35
|
- **Tier:** ${r.tier}
|
|
36
36
|
- **Features:** ${r.features.join(", ")}
|
|
37
37
|
|
|
38
|
-
Restart Wyrm to apply all features.`}]}:{content:[{type:"text",text:`\u{F115D} \u2717 License activation failed: ${r.error||"unknown error"}`}],isError:!0}}catch{return{content:[{type:"text",text:"\u{F115D} \u2717 Invalid license format. Please verify your license key."}],isError:!0}}}},{name:"wyrm_analytics_dashboard",description:"View usage analytics dashboard
|
|
38
|
+
Restart Wyrm to apply all features.`}]}:{content:[{type:"text",text:`\u{F115D} \u2717 License activation failed: ${r.error||"unknown error"}`}],isError:!0}}catch{return{content:[{type:"text",text:"\u{F115D} \u2717 Invalid license format. Please verify your license key."}],isError:!0}}}},{name:"wyrm_analytics_dashboard",description:"View usage analytics dashboard: tool calls, tokens, costs, cache efficiency (Pro+)",inputSchema:{type:"object",properties:{days:{type:"number",description:"Number of days to analyze (default: 30)"}}},annotations:d.wyrm_analytics_dashboard,aliases:[],handler:async(a,l)=>{const{analytics:i}=l,r=a||{};if(!y("analytics"))return{content:[{type:"text",text:`\u{F115D} Analytics requires a Pro license or higher.
|
|
39
39
|
Visit https://ghosts.lk/wyrm to upgrade.`}]};const c=a?.days||30,n=i.dashboard(c);let t=`\u{F115D} **Usage Analytics** (last ${c} days)
|
|
40
40
|
|
|
41
41
|
`;if(t+=`### Overview
|
|
@@ -47,19 +47,19 @@ Visit https://ghosts.lk/wyrm to upgrade.`}]};const c=a?.days||30,n=i.dashboard(c
|
|
|
47
47
|
`,t+=`- **Estimated Cost:** $${n.summary.estimated_cost_usd.toFixed(4)}
|
|
48
48
|
|
|
49
49
|
`,n.top_tools.length>0){t+=`### Top Tools
|
|
50
|
-
`;for(const e of n.top_tools.slice(0,10))t+=`- **${e.tool}
|
|
50
|
+
`;for(const e of n.top_tools.slice(0,10))t+=`- **${e.tool}**: ${e.calls} calls, ${e.tokens.toLocaleString()} tokens
|
|
51
51
|
`;t+=`
|
|
52
52
|
`}if(n.daily.length>0){t+=`### Recent Activity
|
|
53
53
|
`;for(const e of n.daily.slice(-7))t+=`- ${e.date}: ${e.calls} calls, ${e.tokens.toLocaleString()} tokens
|
|
54
54
|
`}return{content:[{type:"text",text:t}]}}},{name:"wyrm_cost_report",description:"View estimated API cost report for current billing period (Pro+)",inputSchema:{type:"object",properties:{period:{type:"string",description:"Period in YYYY-MM format (default: current month)"}}},annotations:d.wyrm_cost_report,aliases:[],handler:async(a,l)=>{const{analytics:i}=l,r=a||{};if(!y("analytics"))return{content:[{type:"text",text:`\u{F115D} Cost reports require a Pro license or higher.
|
|
55
|
-
Visit https://ghosts.lk/wyrm to upgrade.`}]};const c=a?.period,n=i.costReport(c);let t=`\u{F115D} **Cost Report
|
|
55
|
+
Visit https://ghosts.lk/wyrm to upgrade.`}]};const c=a?.period,n=i.costReport(c);let t=`\u{F115D} **Cost Report**: ${n.period}
|
|
56
56
|
|
|
57
57
|
`;if(t+=`- **Total Cost:** $${n.total_cost_usd.toFixed(4)}
|
|
58
58
|
`,t+=`- **Projected Monthly:** $${n.projected_monthly_usd.toFixed(2)}
|
|
59
59
|
|
|
60
60
|
`,n.tools.length>0){t+=`### By Tool
|
|
61
|
-
`;for(const e of n.tools.slice(0,10))t+=`- **${e.tool}
|
|
62
|
-
`}return{content:[{type:"text",text:t}]}}},{name:"wyrm_cloud_backup",description:"Legacy whole-DB snapshot backup to encrypted cloud storage (Pro+): backup, restore, list, prune. This is a full-database snapshot (last-write-wins on restore), NOT the sovereign per-row sync
|
|
61
|
+
`;for(const e of n.tools.slice(0,10))t+=`- **${e.tool}**: ${e.calls} calls, $${e.cost_usd.toFixed(4)}
|
|
62
|
+
`}return{content:[{type:"text",text:t}]}}},{name:"wyrm_cloud_backup",description:"Legacy whole-DB snapshot backup to encrypted cloud storage (Pro+): backup, restore, list, prune. This is a full-database snapshot (last-write-wins on restore), NOT the sovereign per-row sync; for multi-device memory use wyrm_cloud_sync action=sovereign. Keep this for simple point-in-time backups.",inputSchema:{type:"object",properties:{action:{type:"string",description:"Action: backup, restore, list, prune",enum:["backup","restore","list","prune"]},backupKey:{type:"string",description:"Backup key for restore (from list)"},keepCount:{type:"number",description:"Number of backups to keep when pruning (default: 10)"}},required:["action"]},annotations:d.wyrm_cloud_backup,aliases:[],handler:async(a,l)=>{const{cloudBackup:i,db:r}=l,c=a||{};if(!y("cloud_backup"))return{content:[{type:"text",text:`\u{F115D} Cloud backup requires a Pro license or higher.
|
|
63
63
|
Visit https://ghosts.lk/wyrm to upgrade.`}]};const n=a?.action;switch(n){case"backup":{const t=await i.backup(r.getDatabasePath()),e=t.metadata;return{content:[{type:"text",text:`\u{F115D} \u2713 Backup complete!
|
|
64
64
|
|
|
65
65
|
- **Key:** ${t.key}
|
|
@@ -73,15 +73,15 @@ Visit https://ghosts.lk/wyrm to upgrade.`}]};const n=a?.action;switch(n){case"ba
|
|
|
73
73
|
|
|
74
74
|
\u26A0\uFE0F Restart Wyrm to use the restored database.`}]}}case"list":{const t=await i.listBackups();if(t.length===0)return{content:[{type:"text",text:"\u{F115D} No backups found."}]};let e=`\u{F115D} **Cloud Backups** (${t.length} found)
|
|
75
75
|
|
|
76
|
-
`;for(const o of t)e+=`- **${o.timestamp}
|
|
77
|
-
`;return{content:[{type:"text",text:e}]}}case"prune":{const t=a?.keepCount||10;return{content:[{type:"text",text:`\u{F115D} \u2713 Pruned ${(await i.pruneBackups(t)).deleted} old backup(s). Kept ${t} most recent.`}]}}default:return{content:[{type:"text",text:`Unknown backup action: ${n}. Use: backup, restore, list, prune`}],isError:!0}}}},{name:"wyrm_cloud_sync",description:"Cloud sync. The SOVEREIGN path is the promoted one: zero-knowledge, per-row AES-256-GCM E2E-encrypted delta sync with tombstone propagation
|
|
78
|
-
Visit https://ghosts.lk/wyrm to upgrade. (The sovereign path
|
|
76
|
+
`;for(const o of t)e+=`- **${o.timestamp}**: ${(o.db_size/1024).toFixed(1)} KB, encrypted: ${o.encrypted}, machine: ${o.machine_id.slice(0,8)}...
|
|
77
|
+
`;return{content:[{type:"text",text:e}]}}case"prune":{const t=a?.keepCount||10;return{content:[{type:"text",text:`\u{F115D} \u2713 Pruned ${(await i.pruneBackups(t)).deleted} old backup(s). Kept ${t} most recent.`}]}}default:return{content:[{type:"text",text:`Unknown backup action: ${n}. Use: backup, restore, list, prune`}],isError:!0}}}},{name:"wyrm_cloud_sync",description:"Cloud sync. The SOVEREIGN path is the promoted one: zero-knowledge, per-row AES-256-GCM E2E-encrypted delta sync with tombstone propagation; the cloud never sees plaintext. Use action 'sovereign' to sync (push+pull), 'sovereign-preview' for a dry run, 'sovereign-status' to show device + sync state. For hands-off periodic sync, 'sovereign-daemon-start' / 'sovereign-daemon-stop' / 'sovereign-daemon-status' run a background daemon. (First-time login is an interactive device flow: run `wyrm cloud login` in a terminal once.) The start/stop/restart/status/force-sync actions drive the LEGACY whole-DB snapshot daemon (last-write-wins by mtime, weaker), retained only for simple full-DB backup; it is NOT the sovereign path. Prefer sovereign for multi-device memory.",inputSchema:{type:"object",properties:{action:{type:"string",description:"Sovereign (preferred): sovereign, sovereign-preview, sovereign-status, sovereign-daemon-start, sovereign-daemon-stop, sovereign-daemon-status. Legacy snapshot daemon: start, stop, restart, status, force-sync.",enum:["sovereign","sovereign-preview","sovereign-status","sovereign-daemon-start","sovereign-daemon-stop","sovereign-daemon-status","start","stop","restart","status","force-sync"]},all:{type:"boolean",description:"Sovereign only: sync every semantic table, ignoring visibility flags (default: false)"},interval_minutes:{type:"number",description:"Legacy snapshot only: sync interval (default: 10)"},keep_count:{type:"number",description:"Legacy snapshot only: snapshots to retain after pruning (default: 20)"}},required:["action"]},annotations:d.wyrm_cloud_sync,aliases:[],handler:async(a,l)=>{const{cloudBackup:i,cloudSyncManager:r,db:c,sync:n}=l,t=a||{},e=a?.action,o={sovereign:a?.all?["sync","--all"]:["sync"],"sovereign-preview":["sync","--dry-run"],"sovereign-status":["status"],"sovereign-daemon-start":["daemon","start"],"sovereign-daemon-stop":["daemon","stop"],"sovereign-daemon-status":["daemon","status"]};if(e in o){const s=await T(o[e]);return{content:[{type:"text",text:s.text}],isError:!s.ok}}if(!y("cloud_backup"))return{content:[{type:"text",text:`\u{F115D} The legacy snapshot daemon requires a Pro license or higher.
|
|
78
|
+
Visit https://ghosts.lk/wyrm to upgrade. (The sovereign path, action=sovereign, is the preferred sync.)`}]};const p=a?.interval_minutes,m=a?.keep_count;switch(e){case"start":{const s=r.start({interval_minutes:p,keep_count:m});return s.ok?{content:[{type:"text",text:`\u{F115D} \u2713 cloud-sync daemon started (pid ${s.pid}). Logs: ~/.wyrm/wyrm-cloud-sync.log`}]}:{content:[{type:"text",text:`\u{F115D} cloud-sync: ${s.reason??"failed to start"}${s.pid?` (pid ${s.pid})`:""}`}],isError:!s.pid}}case"stop":{const s=r.stop();return{content:[{type:"text",text:s.ok?"\u{F115D} \u2713 cloud-sync daemon stopped":`\u{F115D} cloud-sync: ${s.reason??"failed to stop"}`}],isError:!s.ok}}case"restart":{r.stop(),await new Promise(u=>{setTimeout(u,500)});const s=r.start({interval_minutes:p,keep_count:m});return{content:[{type:"text",text:s.ok?`\u{F115D} \u2713 cloud-sync daemon restarted (pid ${s.pid})`:`\u{F115D} cloud-sync restart failed: ${s.reason??"unknown"}`}],isError:!s.ok}}case"status":{const s=r.status(),u=["\u{F115D} **Legacy Snapshot Daemon Status** (not the sovereign path; see action=sovereign-status)"];return u.push(`- Running: ${s.running?"\u2713 yes":"\u2717 no"}`),s.pid&&u.push(`- PID: ${s.pid}`),s.state&&(s.state.last_uploaded_ts&&u.push(`- Last upload: ${new Date(s.state.last_uploaded_ts).toISOString()}${s.state.last_uploaded_key?` (${s.state.last_uploaded_key})`:""}`),s.state.last_restored_key&&u.push(`- Last restore: ${s.state.last_restored_key}`),s.state.last_check_ts&&u.push(`- Last check: ${new Date(s.state.last_check_ts).toISOString()}`),u.push(`- Machine: ${s.state.machine}`)),{content:[{type:"text",text:u.join(`
|
|
79
79
|
`)}]}}case"force-sync":{const u=await new h(i,c.getDatabasePath()).tick();return{content:[{type:"text",text:`\u{F115D} force-sync: ${JSON.stringify(u,null,2)}`}]}}default:return{content:[{type:"text",text:`Unknown cloud-sync action: ${e}. Use: start, stop, restart, status, force-sync`}],isError:!0}}}},{name:"wyrm_encrypt_setup",description:"Set up or check encryption status for sensitive data (Pro+)",inputSchema:{type:"object",properties:{action:{type:"string",description:"Action: status, enable, test",enum:["status","enable","test"]},password:{type:"string",description:"Encryption password (min 8 chars, required for enable)"}},required:["action"]},annotations:d.wyrm_encrypt_setup,aliases:[],handler:async(a,l)=>{const i=a||{};if(!y("encryption"))return{content:[{type:"text",text:`\u{F115D} Encryption setup requires a Pro license or higher.
|
|
80
80
|
Visit https://ghosts.lk/wyrm to upgrade.`}]};const r=a?.action,c=f();switch(r){case"status":{const n=c.isEnabled();let t=`\u{F115D} **Encryption Status**
|
|
81
81
|
|
|
82
82
|
`;return t+=`- **Enabled:** ${n?"\u2713 Yes":"\u2717 No"}
|
|
83
83
|
`,t+=`- **Algorithm:** AES-256-GCM
|
|
84
|
-
`,n?t+=`- **Status:** Active
|
|
84
|
+
`,n?t+=`- **Status:** Active (new data is encrypted at rest)
|
|
85
85
|
`:t+='\nTo enable, run: `wyrm_encrypt_setup` with action "enable" and a strong password.\n',{content:[{type:"text",text:t}]}}case"enable":{const n=a?.password;return!n||n.length<8?{content:[{type:"text",text:"\u{F115D} Password must be at least 8 characters."}],isError:!0}:(g(n),{content:[{type:"text",text:`\u{F115D} \u2713 Encryption enabled!
|
|
86
86
|
|
|
87
87
|
- **Algorithm:** AES-256-GCM
|
|
@@ -10,10 +10,10 @@ ${t.injected.map(r=>`- ${r}`).join(`
|
|
|
10
10
|
${t.errors.map(r=>`- ${r}`).join(`
|
|
11
11
|
`)}
|
|
12
12
|
|
|
13
|
-
`),t.injected.length>0&&(e+="AI models in this project will now call `wyrm_session_prime` at the start of each conversation."),{content:[{type:"text",text:e}]}}},{name:"buddy",description:"Use when another buddy-compatible MCP server (or the operator) asks Wyrm for a brief, data-grounded status reply - the Buddy Protocol v1.0 well-known entry point. Companion params switch to the full companion reply; bare calls stay protocol-pure. Cycle-protected.",inputSchema:{type:"object",properties:{from_buddy:{type:"string",description:"Caller identity"},project_hint:{type:"string",description:"Scope the reply"},mood_hint:{type:"string",enum:["normal","celebratory","stuck","quiet"]},size:{type:"string",enum:["full","compact","mini"]},format:{type:"string",enum:["markdown","json"],description:"Peer mode only."},project_path:{type:"string",description:"Companion mode: project root (default cwd)"},persona:{type:"string",enum:["wyrm","drogo","berlin","sumair","custom"],description:"Companion voice."},persona_name:{type:"string",description:"For persona=custom"},mood:{type:"string",description:"Companion override (mood_hint values)"},federate:{type:"boolean",description:"Fold in other buddy MCPs"}}},annotations:l.buddy,aliases:[],handler:async(i,c)=>{const{WYRM_PACKAGE_VERSION:s,db:a,runCompanionBuddy:t}=c,e=i||{};if(m("size",e.size,["full","compact","mini"]),m("mood_hint",e.mood_hint,["normal","celebratory","stuck","quiet"]),m("format",e.format,["markdown","json"]),m("persona",e.persona,["wyrm","drogo","berlin","sumair","custom"]),_(i??{}))return await t(i??{});const r=i,o=f(a.getDatabase(),r,s);return r.format==="json"?{content:[{type:"text",text:JSON.stringify(o.json,null,2)}]}:{content:[{type:"text",text:o.markdown}]}}},{name:"wyrm_buddy",description:"Friendly, data-grounded coding companion. Returns a brief greeting + project state + suggested next step sourced entirely from Wyrm's real data. Every claim cites a real DB row ID
|
|
14
|
-
`)}]}}return{content:[{type:"text",text:o}]}}},{name:"wyrm_token_delete",description:"Remove a design token from a project.",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project root path."},category:{type:"string",enum:["color","type","spacing","motion","shadow","radius","breakpoint","custom"]},key:{type:"string",description:"Token key to remove."}},required:["projectPath","category","key"]},annotations:l.wyrm_token_delete,aliases:[],handler:async(i,c)=>{const{db:s,designSystem:a}=c,{projectPath:t,category:e,key:r}=i,o=s.getProject(t);return o?{content:[{type:"text",text:a().deleteToken(o.id,e,r)?`\u{1F5D1} removed \`${e}/${r}\``:`not found: \`${e}/${r}\``}]}:{content:[{type:"text",text:`Project not found: ${t}`}],isError:!0}}},{name:"wyrm_reference_add",description:"Clip a design reference (URL, image path, palette, or inline snippet) for inspiration. Stored per-project (or globally if no project path). Tag aggressively
|
|
15
|
-
`)}]}}},{name:"wyrm_reference_search",description:"Full-text search across design references' title / notes / tags. Returns matching references sorted by recency.",inputSchema:{type:"object",properties:{query:{type:"string",description:"FTS5 query
|
|
16
|
-
`)}]}}},{name:"wyrm_capabilities",description:"Use to learn what your memory system can actually do - feature inventory, why each matters, runtime state, and which tool to reach for. Call at session start instead of re-discovering the surface.",inputSchema:{type:"object",properties:{format:{type:"string",enum:["json","markdown"]}}},annotations:l.wyrm_capabilities,aliases:[],handler:async(i,c)=>{const{WYRM_TOOL_COUNT:s,agentDaemon:a,db:t,toolCounts:e}=c,r=m("format",i.format,["json","markdown"])??"markdown",o=e?.(),p=w(t.getDatabase(),o?.active??s,{hasEncryption:()=>!!process.env.WYRM_ENCRYPTION_KEY,isFederationEnabled:()=>!0,isAgentRunning:()=>{try{return a.status().running}catch{return null}}},o?.full);return r==="json"?{content:[{type:"text",text:JSON.stringify(p,null,2)}]}:{content:[{type:"text",text:j(p)}]}}},{name:"wyrm_intro",description:"Return a plain-English explanation of what Wyrm is and what it does. Aimed at HUMANS who don't yet understand Wyrm's value
|
|
13
|
+
`),t.injected.length>0&&(e+="AI models in this project will now call `wyrm_session_prime` at the start of each conversation."),{content:[{type:"text",text:e}]}}},{name:"buddy",description:"Use when another buddy-compatible MCP server (or the operator) asks Wyrm for a brief, data-grounded status reply - the Buddy Protocol v1.0 well-known entry point. Companion params switch to the full companion reply; bare calls stay protocol-pure. Cycle-protected.",inputSchema:{type:"object",properties:{from_buddy:{type:"string",description:"Caller identity"},project_hint:{type:"string",description:"Scope the reply"},mood_hint:{type:"string",enum:["normal","celebratory","stuck","quiet"]},size:{type:"string",enum:["full","compact","mini"]},format:{type:"string",enum:["markdown","json"],description:"Peer mode only."},project_path:{type:"string",description:"Companion mode: project root (default cwd)"},persona:{type:"string",enum:["wyrm","drogo","berlin","sumair","custom"],description:"Companion voice."},persona_name:{type:"string",description:"For persona=custom"},mood:{type:"string",description:"Companion override (mood_hint values)"},federate:{type:"boolean",description:"Fold in other buddy MCPs"}}},annotations:l.buddy,aliases:[],handler:async(i,c)=>{const{WYRM_PACKAGE_VERSION:s,db:a,runCompanionBuddy:t}=c,e=i||{};if(m("size",e.size,["full","compact","mini"]),m("mood_hint",e.mood_hint,["normal","celebratory","stuck","quiet"]),m("format",e.format,["markdown","json"]),m("persona",e.persona,["wyrm","drogo","berlin","sumair","custom"]),_(i??{}))return await t(i??{});const r=i,o=f(a.getDatabase(),r,s);return r.format==="json"?{content:[{type:"text",text:JSON.stringify(o.json,null,2)}]}:{content:[{type:"text",text:o.markdown}]}}},{name:"wyrm_buddy",description:"Friendly, data-grounded coding companion. Returns a brief greeting + project state + suggested next step sourced entirely from Wyrm's real data. Every claim cites a real DB row ID; no hallucinated encouragement. Personas: wyrm (default, dragon-themed), drogo (gruff), berlin (precise), sumair (warm), custom. Moods: normal / celebratory / stuck / quiet (auto-detected). Sizes: full (10-line PhantomDragon-style mascot), compact (5-line workspace-friendly), mini (single-line inline prefix). Federates with other registered MCPs that expose `buddy`, `*_buddy`, or `buddy_*` tools; their replies fold into a 'From other buddies' section.",inputSchema:{type:"object",properties:{project_path:{type:"string",description:"Project root. Default: current cwd if registered."},mood:{type:"string",enum:["normal","celebratory","stuck","quiet"],description:"Override the auto-detected mood."},persona:{type:"string",enum:["wyrm","drogo","berlin","sumair","custom"],description:"Voice. Default: WYRM_BUDDY_DEFAULT_PERSONA env or 'wyrm'."},persona_name:{type:"string",description:"Required when persona === 'custom'."},size:{type:"string",enum:["full","compact","mini"],description:"Mascot size. 'full' = 10-line head+shoulder banner (default), 'compact' = 5-line workspace-friendly, 'mini' = single-line inline prefix on the greeting."},federate:{type:"boolean",description:"Query other registered MCPs with *_buddy tools and fold their replies in. Default: true if any are registered."}}},annotations:l.wyrm_buddy,aliases:[],handler:async(i,c)=>{const{runCompanionBuddy:s}=c;return await s(i??{})}},{name:"wyrm_token_set",description:"Set a design token for a project. Tokens are colour / type / spacing / motion / shadow / radius / breakpoint / custom: the design system primitives the project is built on. Stored first-class so AI helpers working on creative tasks don't have to re-derive the system from scattered CSS each time. Upserts on (project_id, category, key).",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project root path."},category:{type:"string",enum:["color","type","spacing","motion","shadow","radius","breakpoint","custom"],description:"Token category."},key:{type:"string",description:"Short slug, e.g. 'primary' / 'h1' / 'page-padding-md'."},value:{type:"string",description:"Actual value: '#00B89F' / 'Inter 900 2.5rem' / '1.25rem' / etc."},notes:{type:"string",description:"Optional why / when-to-use guidance."},source:{type:"string",enum:["user","derived","imported","observed"],description:"How the token was determined. Default: 'user'."}},required:["projectPath","category","key","value"]},annotations:l.wyrm_token_set,aliases:[],handler:async(i,c)=>{const{db:s,designSystem:a}=c,{projectPath:t,category:e,key:r,value:o,notes:p,source:u}=i,n=s.getProject(t);if(!n)return{content:[{type:"text",text:`Project not found: ${t}`}],isError:!0};const d=a().setToken({projectId:n.id,category:e,key:r,value:o,notes:p,source:u});return{content:[{type:"text",text:`\u{1F3A8} token set: \`${d.category}/${d.key}\` = \`${d.value}\``+(d.notes?` (${d.notes})`:"")}]}}},{name:"wyrm_token_get",description:"Retrieve all design tokens for a project, optionally filtered by category. Returns the design system primitives the project is built on.",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project root path."},category:{type:"string",enum:["color","type","spacing","motion","shadow","radius","breakpoint","custom"],description:"Filter by category. Omit for all tokens."}},required:["projectPath"]},annotations:l.wyrm_token_get,aliases:[],handler:async(i,c)=>{const{db:s,designSystem:a}=c,{projectPath:t,category:e}=i,r=s.getProject(t);if(!r)return{content:[{type:"text",text:`Project not found: ${t}`}],isError:!0};const o=a().formatForContext(r.id);if(!o)return{content:[{type:"text",text:`_(no design tokens for ${r.name} yet; use \`wyrm_token_set\` to start the design system)_`}]};if(e){const p=a().listTokens(r.id,e);if(p.length===0)return{content:[{type:"text",text:`_(no ${e} tokens for ${r.name})_`}]};const u=[`### \u{1F3A8} ${e} tokens for ${r.name}`,""];for(const n of p)u.push(`- \`${n.key}\` = \`${n.value}\`${n.notes?` (${n.notes})`:""}`);return{content:[{type:"text",text:u.join(`
|
|
14
|
+
`)}]}}return{content:[{type:"text",text:o}]}}},{name:"wyrm_token_delete",description:"Remove a design token from a project.",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project root path."},category:{type:"string",enum:["color","type","spacing","motion","shadow","radius","breakpoint","custom"]},key:{type:"string",description:"Token key to remove."}},required:["projectPath","category","key"]},annotations:l.wyrm_token_delete,aliases:[],handler:async(i,c)=>{const{db:s,designSystem:a}=c,{projectPath:t,category:e,key:r}=i,o=s.getProject(t);return o?{content:[{type:"text",text:a().deleteToken(o.id,e,r)?`\u{1F5D1} removed \`${e}/${r}\``:`not found: \`${e}/${r}\``}]}:{content:[{type:"text",text:`Project not found: ${t}`}],isError:!0}}},{name:"wyrm_reference_add",description:"Clip a design reference (URL, image path, palette, or inline snippet) for inspiration. Stored per-project (or globally if no project path). Tag aggressively: tags drive future search and recall when building a new project in the same vein.",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project root path. Omit to attach as a global reference."},kind:{type:"string",enum:["url","image","palette","snippet"],description:"Reference type."},location:{type:"string",description:"URL, file path, or inline payload (e.g. palette like '#00B89F,#ff5a55,#0a0c0e' or a code snippet)."},title:{type:"string",description:"Short title: what is this reference?"},notes:{type:"string",description:"Why you saved it / what to remember about it."},tags:{type:"string",description:"Comma-separated tags, e.g. 'hero,scroll-tied,framer-motion'."}},required:["kind","location"]},annotations:l.wyrm_reference_add,aliases:[],handler:async(i,c)=>{const{db:s,designSystem:a}=c,{projectPath:t,kind:e,location:r,title:o,notes:p,tags:u}=i;let n=null;if(t){const y=s.getProject(t);if(!y)return{content:[{type:"text",text:`Project not found: ${t}`}],isError:!0};n=y.id}const d=a().addReference({projectId:n,kind:e,location:r,title:o,notes:p,tags:u}),h=n?` (project ${n})`:" (global)";return{content:[{type:"text",text:`\u{1F4CC} reference #${d.id} saved${h}: \`${d.kind}\` ${d.title??d.location.slice(0,60)}${d.tags?` \xB7 tags: ${d.tags}`:""}`}]}}},{name:"wyrm_reference_list",description:"Browse saved design references. Filter by project or tag. Default: 50 most recent across all projects.",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Limit to a specific project."},tag:{type:"string",description:"Filter to references containing this tag."},limit:{type:"number",description:"Max results (default 50, max 200)."}}},annotations:l.wyrm_reference_list,aliases:[],handler:async(i,c)=>{const{db:s,designSystem:a}=c,{projectPath:t,tag:e,limit:r}=i;let o;if(t){const n=s.getProject(t);if(!n)return{content:[{type:"text",text:`Project not found: ${t}`}],isError:!0};o=n.id}const p=a().listReferences({projectId:o,tag:e,limit:r});if(p.length===0)return{content:[{type:"text",text:"_(no references match)_"}]};const u=[`\u{1F4CC} **${p.length} reference${p.length===1?"":"s"}**`];for(const n of p){const d=n.project_id?`p${n.project_id}`:"global";u.push(`- [${n.id}/${d}] \`${n.kind}\` ${n.title??n.location.slice(0,60)}${n.tags?` \xB7 ${n.tags}`:""}`)}return{content:[{type:"text",text:u.join(`
|
|
15
|
+
`)}]}}},{name:"wyrm_reference_search",description:"Full-text search across design references' title / notes / tags. Returns matching references sorted by recency.",inputSchema:{type:"object",properties:{query:{type:"string",description:"FTS5 query: keywords, phrases in quotes."},projectPath:{type:"string",description:"Limit to a specific project."},limit:{type:"number",description:"Max results (default 20, max 100)."}},required:["query"]},annotations:l.wyrm_reference_search,aliases:[],handler:async(i,c)=>{const{db:s,designSystem:a}=c,{query:t,projectPath:e,limit:r}=i;let o;if(e){const n=s.getProject(e);if(!n)return{content:[{type:"text",text:`Project not found: ${e}`}],isError:!0};o=n.id}const p=a().searchReferences(t,{projectId:o,limit:r});if(p.length===0)return{content:[{type:"text",text:`_(no references match "${t}")_`}]};const u=[`\u{1F50D} **${p.length} reference${p.length===1?"":"s"}** matching "${t}"`];for(const n of p){const d=n.project_id?`p${n.project_id}`:"global";u.push(`- [${n.id}/${d}] \`${n.kind}\` ${n.title??n.location.slice(0,60)}${n.tags?` \xB7 ${n.tags}`:""}`)}return{content:[{type:"text",text:u.join(`
|
|
16
|
+
`)}]}}},{name:"wyrm_capabilities",description:"Use to learn what your memory system can actually do - feature inventory, why each matters, runtime state, and which tool to reach for. Call at session start instead of re-discovering the surface.",inputSchema:{type:"object",properties:{format:{type:"string",enum:["json","markdown"]}}},annotations:l.wyrm_capabilities,aliases:[],handler:async(i,c)=>{const{WYRM_TOOL_COUNT:s,agentDaemon:a,db:t,toolCounts:e}=c,r=m("format",i.format,["json","markdown"])??"markdown",o=e?.(),p=w(t.getDatabase(),o?.active??s,{hasEncryption:()=>!!process.env.WYRM_ENCRYPTION_KEY,isFederationEnabled:()=>!0,isAgentRunning:()=>{try{return a.status().running}catch{return null}}},o?.full);return r==="json"?{content:[{type:"text",text:JSON.stringify(p,null,2)}]}:{content:[{type:"text",text:j(p)}]}}},{name:"wyrm_intro",description:"Return a plain-English explanation of what Wyrm is and what it does. Aimed at HUMANS who don't yet understand Wyrm's value: co-founders, operators, designers, anyone who didn't read the docs. Call this when a human user asks 'what does Wyrm even do?' or seems unaware Wyrm is involved in their AI assistant's behavior. NOT for AI-agent self-orientation (use wyrm_capabilities for that).",inputSchema:{type:"object",properties:{}},annotations:l.wyrm_intro,aliases:[],handler:async(i,c)=>{const{WYRM_PACKAGE_VERSION:s}=c,{renderIntro:a}=await import("../visibility.js");return{content:[{type:"text",text:a(s)}]}}},{name:"wyrm_migrate_prompt",description:"Rewrite the Wyrm-managed block in this project's AI-client instruction files (.cursor/rules, .github/copilot-instructions.md, CLAUDE.md, etc.) to the current canonical version. Operator-authored content outside the <!-- wyrm:start --> / <!-- wyrm:end --> markers is preserved verbatim. Dry-run by default; pass apply:true to write changes.",inputSchema:{type:"object",properties:{project_path:{type:"string",description:"Project root containing the client config files."},apply:{type:"boolean",description:"Default false (dry-run). Set true to actually write changes."}},required:["project_path"]},annotations:l.wyrm_migrate_prompt,aliases:[],handler:async(i,c)=>{const{migrateProject:s,renderMigrationReport:a}=await import("../migrate-prompt.js"),{WYRM_INJECT_BLOCK:t}=await import("../autoconfig.js"),{project_path:e,apply:r=!1}=i;if(!e)return{content:[{type:"text",text:"project_path required"}],isError:!0};const o=s({projectPath:e,newBlock:t,apply:r});return{content:[{type:"text",text:a(o,r)}]}}},{name:"wyrm_digest",description:"Render a plain-English summary of what Wyrm has done for the operator over a period. Shows counts (sessions tracked, quests completed, repeated failures blocked, ground truths set, references clipped, hours logged) and highlights so a non-technical operator can see Wyrm's actual contribution. Use this when the user asks 'what did Wyrm do?', 'is Wyrm doing anything?', or to demonstrate value to skeptics.",inputSchema:{type:"object",properties:{period:{type:"string",enum:["today","week","month","quarter","year","all"],description:"Reporting window (default: week)."}}},annotations:l.wyrm_digest,aliases:[],handler:async(i,c)=>{const{db:s}=c,{gatherDigest:a,renderDigest:t}=await import("../visibility.js"),{period:e="week"}=i,r=a(s.getDatabase(),e);return{content:[{type:"text",text:t(r)}]}}},{name:"wyrm_check_update",description:"Check whether a newer wyrm-mcp is available on npm. Cached 24h by default; pass force:true to bypass cache.",inputSchema:{type:"object",properties:{force:{type:"boolean",description:"Bypass the 24-hour cache and re-query the registry."}}},annotations:l.wyrm_check_update,aliases:[],handler:async(i,c)=>{const{WYRM_PACKAGE_VERSION:s,db:a}=c,{force:t=!1}=i,e=await b(a.getDatabase(),s,{force:t}),r=e.latest?e.lookupFailed?`${e.latest} (last known; registry unreachable now)`:e.latest:e.lookupFailed?"unknown (npm registry unreachable)":"unknown (version check disabled)",o=e.updateAvailable?"yes":e.lookupFailed&&!e.latest?"unknown (lookup failed)":"no";let p=`\u{F115D} **Update check**
|
|
17
17
|
|
|
18
18
|
- Current: ${e.current}
|
|
19
19
|
- Latest: ${r}
|