wyrm-mcp 7.2.4 → 7.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/autoconfig.d.ts.map +1 -1
  2. package/dist/autoconfig.js +17 -17
  3. package/dist/autoconfig.js.map +1 -1
  4. package/dist/causality.d.ts +21 -0
  5. package/dist/causality.d.ts.map +1 -1
  6. package/dist/causality.js +12 -6
  7. package/dist/causality.js.map +1 -1
  8. package/dist/content-signature.d.ts +33 -0
  9. package/dist/content-signature.d.ts.map +1 -0
  10. package/dist/content-signature.js +1 -0
  11. package/dist/content-signature.js.map +1 -0
  12. package/dist/failure-patterns.d.ts +15 -0
  13. package/dist/failure-patterns.d.ts.map +1 -1
  14. package/dist/failure-patterns.js +26 -20
  15. package/dist/failure-patterns.js.map +1 -1
  16. package/dist/handlers/causality.d.ts.map +1 -1
  17. package/dist/handlers/causality.js +7 -5
  18. package/dist/handlers/causality.js.map +1 -1
  19. package/dist/handlers/recall.d.ts.map +1 -1
  20. package/dist/handlers/recall.js +11 -9
  21. package/dist/handlers/recall.js.map +1 -1
  22. package/dist/handlers/run.d.ts +44 -0
  23. package/dist/handlers/run.d.ts.map +1 -1
  24. package/dist/handlers/run.js +10 -12
  25. package/dist/handlers/run.js.map +1 -1
  26. package/dist/importers.d.ts +77 -0
  27. package/dist/importers.d.ts.map +1 -0
  28. package/dist/importers.js +1 -0
  29. package/dist/importers.js.map +1 -0
  30. package/dist/migrations.d.ts.map +1 -1
  31. package/dist/migrations.js +1 -1
  32. package/dist/migrations.js.map +1 -1
  33. package/dist/receipt.d.ts +45 -0
  34. package/dist/receipt.d.ts.map +1 -0
  35. package/dist/receipt.js +1 -0
  36. package/dist/receipt.js.map +1 -0
  37. package/dist/render-target.d.ts +15 -1
  38. package/dist/render-target.d.ts.map +1 -1
  39. package/dist/render-target.js +11 -11
  40. package/dist/render-target.js.map +1 -1
  41. package/dist/tool-manifest-v2.json +1 -1
  42. package/dist/tool-manifest.json +1 -1
  43. package/dist/wyrm-cli.js +45 -42
  44. package/dist/wyrm-cli.js.map +1 -1
  45. package/dist/wyrm-guard.d.ts.map +1 -1
  46. package/dist/wyrm-guard.js +13 -13
  47. package/dist/wyrm-guard.js.map +1 -1
  48. package/dist/wyrm-manifest.json +1 -1
  49. package/package.json +1 -1
package/dist/wyrm-cli.js CHANGED
@@ -1,79 +1,79 @@
1
1
  #!/usr/bin/env node
2
- import{join as oe,dirname as ae}from"path";import{homedir as Ee}from"os";import{existsSync as ce,readFileSync as V}from"fs";import{fileURLToPath as le}from"url";import{spawnSync as B}from"child_process";import{createInterface as K}from"readline";import{WyrmDB as xe}from"./database.js";import{MemoryArtifacts as G}from"./memory-artifacts.js";import{GroundTruths as de}from"./intelligence.js";import{Rehydration as Ce}from"./rehydration.js";import{makeRenderDeps as pe,buildRenderPlan as se,renderToDisk as Re,renderForClient as ue}from"./render-target.js";import{classifyCapture as Pe}from"./capture.js";import{c as d,colors as J,formatTable as M,printSection as j,printSuccess as v,printError as p,icons as L}from"./cli.js";import{readActor as Te}from"./attribution.js";function O(){try{const l=ae(le(import.meta.url));return JSON.parse(V(oe(l,"..","package.json"),"utf-8"))}catch{return{}}}function R(l,i){const e=typeof l=="string"?parseInt(l,10):NaN;return Number.isFinite(e)?e:i}function z(l,i){const e=typeof l=="string"?parseFloat(l):NaN;return Number.isFinite(e)?e:i}function De(){return process.env.WYRM_DB_PATH??oe(Ee(),".wyrm","wyrm.db")}function E(){return new xe(De())}function x(l){const i=[],e={};let s=0;for(;s<l.length;){const r=l[s];if(r.startsWith("--")){const t=r.slice(2),n=l[s+1];n&&!n.startsWith("--")?(e[t]=n,s+=2):(e[t]=!0,s++)}else i.push(r),s++}return{positional:i,flags:e}}function P(l,i){return i?l.getDatabase().prepare("SELECT * FROM projects WHERE LOWER(name) LIKE LOWER(?) LIMIT 1").get(`%${i}%`)??null:null}async function Me(l){const{positional:i,flags:e}=x(l),s=i[0];s||(p("Usage: wyrm search <query> [--project <name>] [--type all|memories|truths|quests|data]"),process.exit(1));const r=E(),t=r.getDatabase(),n=e.type??"all",a=e.project,u=(a?P(r,a):null)?.id;j(`Search: "${s}"`);const c=[];if(n==="all"||n==="memories")try{const m=u?`AND m.project_id = ${u}`:"",y=t.prepare(`
2
+ import{join as B,dirname as ce}from"path";import{homedir as xe}from"os";import{existsSync as K,readFileSync as q}from"fs";import{fileURLToPath as le}from"url";import{spawnSync as G}from"child_process";import{createInterface as J}from"readline";import{WyrmDB as Ce}from"./database.js";import{MemoryArtifacts as H}from"./memory-artifacts.js";import{GroundTruths as de}from"./intelligence.js";import{Rehydration as Pe}from"./rehydration.js";import{makeRenderDeps as pe,buildRenderPlan as se,renderToDisk as De,renderForClient as ue,detectClients as Ie,ALL_RENDER_CLIENTS as re,ALL_CLIENT_MARKERS as Te}from"./render-target.js";import{classifyCapture as Me}from"./capture.js";import{importFrom as Ne,IMPORT_SOURCES as me}from"./importers.js";import{c as d,colors as z,formatTable as T,printSection as S,printSuccess as v,printError as u,icons as O}from"./cli.js";import{readActor as Ae}from"./attribution.js";function L(){try{const l=ce(le(import.meta.url));return JSON.parse(q(B(l,"..","package.json"),"utf-8"))}catch{return{}}}function D(l,a){const e=typeof l=="string"?parseInt(l,10):NaN;return Number.isFinite(e)?e:a}function Q(l,a){const e=typeof l=="string"?parseFloat(l):NaN;return Number.isFinite(e)?e:a}function Oe(){return process.env.WYRM_DB_PATH??B(xe(),".wyrm","wyrm.db")}function _(){return new Ce(Oe())}function R(l){const a=[],e={};let s=0;for(;s<l.length;){const n=l[s];if(n.startsWith("--")){const t=n.slice(2),r=l[s+1];r&&!r.startsWith("--")?(e[t]=r,s+=2):(e[t]=!0,s++)}else a.push(n),s++}return{positional:a,flags:e}}function P(l,a){return a?l.getDatabase().prepare("SELECT * FROM projects WHERE LOWER(name) LIKE LOWER(?) LIMIT 1").get(`%${a}%`)??null:null}async function Le(l){const{positional:a,flags:e}=R(l),s=a[0];s||(u("Usage: wyrm search <query> [--project <name>] [--type all|memories|truths|quests|data]"),process.exit(1));const n=_(),t=n.getDatabase(),r=e.type??"all",i=e.project,f=(i?P(n,i):null)?.id;S(`Search: "${s}"`);const c=[];if(r==="all"||r==="memories")try{const m=f?`AND m.project_id = ${f}`:"",y=t.prepare(`
3
3
  SELECT m.id, m.kind, m.problem, m.project_id FROM memory_artifacts m
4
4
  JOIN memory_artifacts_fts fts ON m.id = fts.rowid
5
5
  WHERE memory_artifacts_fts MATCH ? ${m}
6
6
  LIMIT 20
7
- `).all(s);for(const f of y)c.push([`mem:${f.id}`,"memory",f.kind,f.problem.slice(0,80)])}catch{}if(n==="all"||n==="sessions")try{const m=u?`AND s.project_id = ${u}`:"",y=t.prepare(`
7
+ `).all(s);for(const p of y)c.push([`mem:${p.id}`,"memory",p.kind,p.problem.slice(0,80)])}catch{}if(r==="all"||r==="sessions")try{const m=f?`AND s.project_id = ${f}`:"",y=t.prepare(`
8
8
  SELECT s.id, s.objectives, s.project_id FROM sessions s
9
9
  JOIN sessions_fts fts ON s.id = fts.rowid
10
10
  WHERE sessions_fts MATCH ? ${m}
11
11
  LIMIT 10
12
- `).all(s);for(const f of y)c.push([`session:${f.id}`,"session","",f.objectives.slice(0,80)])}catch{}if(n==="all"||n==="quests")try{const m=u?`AND q.project_id = ${u}`:"",y=t.prepare(`
12
+ `).all(s);for(const p of y)c.push([`session:${p.id}`,"session","",p.objectives.slice(0,80)])}catch{}if(r==="all"||r==="quests")try{const m=f?`AND q.project_id = ${f}`:"",y=t.prepare(`
13
13
  SELECT q.id, q.title, q.priority, q.project_id FROM quests q
14
14
  JOIN quests_fts fts ON q.id = fts.rowid
15
15
  WHERE quests_fts MATCH ? ${m}
16
16
  LIMIT 10
17
- `).all(s);for(const f of y)c.push([`quest:${f.id}`,"quest",f.priority,f.title.slice(0,80)])}catch{}if(n==="all"||n==="data")try{const m=u?`AND d.project_id = ${u}`:"",y=t.prepare(`
17
+ `).all(s);for(const p of y)c.push([`quest:${p.id}`,"quest",p.priority,p.title.slice(0,80)])}catch{}if(r==="all"||r==="data")try{const m=f?`AND d.project_id = ${f}`:"",y=t.prepare(`
18
18
  SELECT d.id, d.category, d.key, d.value FROM data_lake d
19
19
  JOIN data_lake_fts fts ON d.id = fts.rowid
20
20
  WHERE data_lake_fts MATCH ? ${m}
21
21
  LIMIT 10
22
- `).all(s);for(const f of y)c.push([`data:${f.id}`,"data",f.category,f.value.slice(0,80)])}catch{}if(r.close(),c.length===0){console.log(d.dim(` No results found for "${s}"`));return}console.log(M(["ID","Type","Subtype","Preview"],c)),console.log(d.dim(`
23
- ${c.length} result${c.length!==1?"s":""}`))}async function Ie(l){const{flags:i}=x(l),e=i.type??"all",s=R(i.limit,20),r=i.project,t=E(),n=t.getDatabase(),o=(r?P(t,r):null)?.id,u=o?`WHERE project_id = ${o}`:"",c=o?`AND project_id = ${o}`:"";if(j("Wyrm Memory"),e==="all"||e==="memories"){j("Memories");const m=n.prepare(`
22
+ `).all(s);for(const p of y)c.push([`data:${p.id}`,"data",p.category,p.value.slice(0,80)])}catch{}if(n.close(),c.length===0){console.log(d.dim(` No results found for "${s}"`));return}console.log(T(["ID","Type","Subtype","Preview"],c)),console.log(d.dim(`
23
+ ${c.length} result${c.length!==1?"s":""}`))}async function We(l){const{flags:a}=R(l),e=a.type??"all",s=D(a.limit,20),n=a.project,t=_(),r=t.getDatabase(),o=(n?P(t,n):null)?.id,f=o?`WHERE project_id = ${o}`:"",c=o?`AND project_id = ${o}`:"";if(S("Wyrm Memory"),e==="all"||e==="memories"){S("Memories");const m=r.prepare(`
24
24
  SELECT id, kind, confidence, problem, tags FROM memory_artifacts
25
- ${u}
25
+ ${f}
26
26
  ORDER BY created_at DESC LIMIT ?
27
- `).all(s);if(m.length>0){const y=m.map(f=>[`mem:${f.id}`,f.kind,`${Math.round(f.confidence*100)}%`,f.problem.slice(0,60),f.tags?.slice(0,30)??""]);console.log(M(["ID","Kind","Conf","Preview","Tags"],y))}else console.log(d.dim(" No memories yet."))}if(e==="all"||e==="truths"){j("Ground Truths");const m=n.prepare(`
27
+ `).all(s);if(m.length>0){const y=m.map(p=>[`mem:${p.id}`,p.kind,`${Math.round(p.confidence*100)}%`,p.problem.slice(0,60),p.tags?.slice(0,30)??""]);console.log(T(["ID","Kind","Conf","Preview","Tags"],y))}else console.log(d.dim(" No memories yet."))}if(e==="all"||e==="truths"){S("Ground Truths");const m=r.prepare(`
28
28
  SELECT id, category, key, value FROM ground_truths
29
29
  WHERE is_current = 1 ${c}
30
30
  ORDER BY created_at DESC LIMIT ?
31
- `).all(s);if(m.length>0){const y=m.map(f=>[`truth:${f.id}`,f.category,f.key.slice(0,30),f.value.slice(0,60)]);console.log(M(["ID","Category","Key","Value"],y))}else console.log(d.dim(" No ground truths yet."))}if(e==="all"||e==="quests"){j("Quests");const m=n.prepare(`
31
+ `).all(s);if(m.length>0){const y=m.map(p=>[`truth:${p.id}`,p.category,p.key.slice(0,30),p.value.slice(0,60)]);console.log(T(["ID","Category","Key","Value"],y))}else console.log(d.dim(" No ground truths yet."))}if(e==="all"||e==="quests"){S("Quests");const m=r.prepare(`
32
32
  SELECT id, priority, title, status FROM quests
33
- ${u}
33
+ ${f}
34
34
  ORDER BY created_at DESC LIMIT ?
35
- `).all(s);if(m.length>0){const y=m.map(f=>[`quest:${f.id}`,f.priority,f.title.slice(0,60),f.status]);console.log(M(["ID","Priority","Title","Status"],y))}else console.log(d.dim(" No quests yet."))}t.close()}async function Ae(l){const{positional:i}=x(l),e=i[0];e||(p("Usage: wyrm show <typed-id> (e.g. mem:41, quest:12, truth:7, data:5, session:3)"),process.exit(1));const[s,r]=e.split(":"),t=parseInt(r??"",10);(!s||isNaN(t))&&(p(`Invalid typed ID: ${e}. Format: type:number (e.g. mem:41)`),process.exit(1));const n=E(),a=n.getDatabase();switch(j(`${e}`),s){case"mem":{const o=a.prepare("SELECT * FROM memory_artifacts WHERE id = ?").get(t);if(!o){p(`Memory artifact ${t} not found`);break}console.log(d.bold("Kind: ")+o.kind),console.log(d.bold("Confidence:")+` ${Math.round(o.confidence*100)}%`),console.log(d.bold("Problem: ")+`
35
+ `).all(s);if(m.length>0){const y=m.map(p=>[`quest:${p.id}`,p.priority,p.title.slice(0,60),p.status]);console.log(T(["ID","Priority","Title","Status"],y))}else console.log(d.dim(" No quests yet."))}t.close()}async function Ye(l){const{positional:a}=R(l),e=a[0];e||(u("Usage: wyrm show <typed-id> (e.g. mem:41, quest:12, truth:7, data:5, session:3)"),process.exit(1));const[s,n]=e.split(":"),t=parseInt(n??"",10);(!s||isNaN(t))&&(u(`Invalid typed ID: ${e}. Format: type:number (e.g. mem:41)`),process.exit(1));const r=_(),i=r.getDatabase();switch(S(`${e}`),s){case"mem":{const o=i.prepare("SELECT * FROM memory_artifacts WHERE id = ?").get(t);if(!o){u(`Memory artifact ${t} not found`);break}console.log(d.bold("Kind: ")+o.kind),console.log(d.bold("Confidence:")+` ${Math.round(o.confidence*100)}%`),console.log(d.bold("Problem: ")+`
36
36
  `+o.problem),o.validated_fix&&console.log(d.bold("Solution: ")+`
37
37
  `+o.validated_fix),o.why_it_worked&&console.log(d.bold("Why: ")+`
38
- `+o.why_it_worked),o.tags&&console.log(d.bold("Tags: ")+o.tags),console.log(d.bold("Created: ")+o.created_at);break}case"quest":{const o=a.prepare("SELECT * FROM quests WHERE id = ?").get(t);if(!o){p(`Quest ${t} not found`);break}console.log(d.bold("Title: ")+o.title),console.log(d.bold("Priority: ")+o.priority),console.log(d.bold("Status: ")+o.status),o.description&&console.log(d.bold("Desc: ")+`
39
- `+o.description),o.tags&&console.log(d.bold("Tags: ")+o.tags),console.log(d.bold("Created: ")+o.created_at);break}case"truth":{const o=a.prepare("SELECT * FROM ground_truths WHERE id = ?").get(t);if(!o){p(`Ground truth ${t} not found`);break}console.log(d.bold("Category: ")+o.category),console.log(d.bold("Key: ")+o.key),console.log(d.bold("Value: ")+`
38
+ `+o.why_it_worked),o.tags&&console.log(d.bold("Tags: ")+o.tags),console.log(d.bold("Created: ")+o.created_at);break}case"quest":{const o=i.prepare("SELECT * FROM quests WHERE id = ?").get(t);if(!o){u(`Quest ${t} not found`);break}console.log(d.bold("Title: ")+o.title),console.log(d.bold("Priority: ")+o.priority),console.log(d.bold("Status: ")+o.status),o.description&&console.log(d.bold("Desc: ")+`
39
+ `+o.description),o.tags&&console.log(d.bold("Tags: ")+o.tags),console.log(d.bold("Created: ")+o.created_at);break}case"truth":{const o=i.prepare("SELECT * FROM ground_truths WHERE id = ?").get(t);if(!o){u(`Ground truth ${t} not found`);break}console.log(d.bold("Category: ")+o.category),console.log(d.bold("Key: ")+o.key),console.log(d.bold("Value: ")+`
40
40
  `+o.value),o.rationale&&console.log(d.bold("Rationale:")+`
41
- `+o.rationale),console.log(d.bold("Active: ")+(o.is_current?"Yes":"No (superseded)")),console.log(d.bold("Created: ")+o.created_at);break}case"data":{const o=a.prepare("SELECT * FROM data_lake WHERE id = ?").get(t);if(!o){p(`Data point ${t} not found`);break}console.log(d.bold("Category: ")+o.category),console.log(d.bold("Key: ")+o.key),console.log(d.bold("Value: ")+`
42
- `+String(o.value).slice(0,500)),console.log(d.bold("Created: ")+o.created_at);break}case"session":{const o=a.prepare("SELECT * FROM sessions WHERE id = ?").get(t);if(!o){p(`Session ${t} not found`);break}console.log(d.bold("Date: ")+o.date),console.log(d.bold("Objectives: ")+`
41
+ `+o.rationale),console.log(d.bold("Active: ")+(o.is_current?"Yes":"No (superseded)")),console.log(d.bold("Created: ")+o.created_at);break}case"data":{const o=i.prepare("SELECT * FROM data_lake WHERE id = ?").get(t);if(!o){u(`Data point ${t} not found`);break}console.log(d.bold("Category: ")+o.category),console.log(d.bold("Key: ")+o.key),console.log(d.bold("Value: ")+`
42
+ `+String(o.value).slice(0,500)),console.log(d.bold("Created: ")+o.created_at);break}case"session":{const o=i.prepare("SELECT * FROM sessions WHERE id = ?").get(t);if(!o){u(`Session ${t} not found`);break}console.log(d.bold("Date: ")+o.date),console.log(d.bold("Objectives: ")+`
43
43
  `+o.objectives),o.completed&&console.log(d.bold("Completed: ")+`
44
44
  `+o.completed),o.notes&&console.log(d.bold("Notes: ")+`
45
- `+o.notes);break}default:p(`Unknown type prefix: ${s}. Use mem|quest|truth|data|session`)}n.close()}async function Ne(l){const{positional:i,flags:e}=x(l),s=i[0];s||(p('Usage: wyrm capture "<content>" [--project <name>] [--mode auto|quest|truth|memory]'),process.exit(1));const r=e.project,t=e.mode,n=E(),a=n.getDatabase();let o=null;if(r){const S=P(n,r);S||(p(`Project not found: ${r}`),n.close(),process.exit(1)),o=S.id}let u=Pe(s);t&&t!=="auto"&&(u={type:t,subtype:{quest:"quest",truth:"decision",memory:"pattern"}[t]??t,confidence:100,reasoning:`Mode override: ${t}`});const{type:c,subtype:m,confidence:y,reasoning:f}=u;(c==="quest"||c==="truth"||c==="memory")&&o===null&&(p('A --project is required to capture. Use: wyrm capture "<text>" --project <name>'),n.close(),process.exit(1));let g=0,w="",h=!1;const b=new G(a),_=new de(a);if(c==="quest")g=n.addQuest(o,s.slice(0,200),"","medium").id,w="quest";else if(c==="truth")t!=="truth"&&y<100?(g=b.add(o,{kind:"pattern",problem:s,confidence:y/100,needsReview:1}).id,w="mem",h=!0):(g=_.set(o,{category:"decision",key:s.slice(0,60),value:s}).id,w="truth");else{const S=y>=75;g=b.add(o,{kind:m,problem:s,confidence:y/100,needsReview:S?0:1}).id,w="mem",S||(h=!0)}n.close(),v(`Captured as ${c}: ${m}`),console.log(`${d.dim("Confidence:")} ${y}% | ${d.dim(f)}`),console.log(`${d.dim("ID:")} ${w}:${g}`),h&&console.log(`${L.warning} Stored for review \u2014 run ${d.cyan("wyrm review")} to activate`)}async function Le(l){const{flags:i}=x(l),e=R(i.session,0),s=i.path,r=i.project,t=R(i["max-chars"],6e3),n=i.quiet===!0,a=console.log;console.log=()=>{};const o=E();try{const u=new Ce(o.getDatabase());let c=e;if(c<=0){let f=s?o.getProject(s):void 0;if(!f&&r&&(f=P(o,r)??void 0),f||(f=o.getProject(process.cwd())),!f){n||process.stderr.write(`wyrm rehydrate: no Wyrm project for this directory, nothing to restore.
46
- `);return}const g=o.getRecentSessions(f.id,1);if(g.length===0){n||process.stderr.write(`wyrm rehydrate: project "${f.name}" has no prior sessions yet.
47
- `);return}c=g[0].id}const m=u.rehydrate(c);if(!m){n||process.stderr.write(`wyrm rehydrate: session ${c} not found.
45
+ `+o.notes);break}default:u(`Unknown type prefix: ${s}. Use mem|quest|truth|data|session`)}r.close()}async function Ue(l){const{positional:a,flags:e}=R(l),s=a[0];s||(u('Usage: wyrm capture "<content>" [--project <name>] [--mode auto|quest|truth|memory]'),process.exit(1));const n=e.project,t=e.mode,r=_(),i=r.getDatabase();let o=null;if(n){const b=P(r,n);b||(u(`Project not found: ${n}`),r.close(),process.exit(1)),o=b.id}let f=Me(s);t&&t!=="auto"&&(f={type:t,subtype:{quest:"quest",truth:"decision",memory:"pattern"}[t]??t,confidence:100,reasoning:`Mode override: ${t}`});const{type:c,subtype:m,confidence:y,reasoning:p}=f;(c==="quest"||c==="truth"||c==="memory")&&o===null&&(u('A --project is required to capture. Use: wyrm capture "<text>" --project <name>'),r.close(),process.exit(1));let h=0,g="",w=!1;const $=new H(i),E=new de(i);if(c==="quest")h=r.addQuest(o,s.slice(0,200),"","medium").id,g="quest";else if(c==="truth")t!=="truth"&&y<100?(h=$.add(o,{kind:"pattern",problem:s,confidence:y/100,needsReview:1}).id,g="mem",w=!0):(h=E.set(o,{category:"decision",key:s.slice(0,60),value:s}).id,g="truth");else{const b=y>=75;h=$.add(o,{kind:m,problem:s,confidence:y/100,needsReview:b?0:1}).id,g="mem",b||(w=!0)}r.close(),v(`Captured as ${c}: ${m}`),console.log(`${d.dim("Confidence:")} ${y}% | ${d.dim(p)}`),console.log(`${d.dim("ID:")} ${g}:${h}`),w&&console.log(`${O.warning} Stored for review \u2014 run ${d.cyan("wyrm review")} to activate`)}async function Fe(l){const{flags:a}=R(l),e=D(a.session,0),s=a.path,n=a.project,t=D(a["max-chars"],6e3),r=a.quiet===!0,i=console.log;console.log=()=>{};const o=_();try{const f=new Pe(o.getDatabase());let c=e;if(c<=0){let p=s?o.getProject(s):void 0;if(!p&&n&&(p=P(o,n)??void 0),p||(p=o.getProject(process.cwd())),!p){r||process.stderr.write(`wyrm rehydrate: no Wyrm project for this directory, nothing to restore.
46
+ `);return}const h=o.getRecentSessions(p.id,1);if(h.length===0){r||process.stderr.write(`wyrm rehydrate: project "${p.name}" has no prior sessions yet.
47
+ `);return}c=h[0].id}const m=f.rehydrate(c);if(!m){r||process.stderr.write(`wyrm rehydrate: session ${c} not found.
48
48
  `);return}let y=m.briefing_markdown;t>0&&y.length>t&&(y=y.slice(0,t)+`
49
49
 
50
50
  _... brief truncated at ${t} chars, run \`wyrm show session:${m.session_id}\` for the full record._`),process.stdout.write(y+`
51
- `)}finally{o.close(),console.log=a}}async function Oe(l){const{flags:i}=x(l),e=i.path,s=i.project,r=i.out,t=i.brief===!0,n=i.force===!0,a=i.quiet===!0,o=(typeof i.client=="string"?i.client:"").split(",").map(g=>g.trim().toLowerCase()).filter(Boolean),u=["claude","cursor","copilot","agents"],c=o.filter(g=>u.includes(g)),m=o.filter(g=>!u.includes(g));m.length>0&&(p(`Unknown --client value(s): ${m.join(", ")} (valid: ${u.join("|")})`),process.exit(1));const y=console.log;t&&(console.log=()=>{});const f=E();try{let g=e?f.getProject(e):void 0;!g&&s&&(g=P(f,s)??void 0),g||(g=f.getProject(process.cwd())),g||(console.log=y,p("wyrm render: no Wyrm project for this directory (use --path or --project)."),process.exit(1));const w=pe(f.getDatabase()),h={wyrm_version:O().version??"unknown",compiled_at:new Date().toISOString()};if(t){const $=se(w,g,h);console.log=y,process.stdout.write($.sessionBrief+`
52
- `);return}const b=r??g.path;{const $=await import("./reverse-bridge.js"),T=se(w,g,h),Y={"MEMORY.md":T.memoryMd};for(const q of c){const A=ue(q,T.model,h);Y[A.relPath]=A.block}try{const q=$.makeBridgeDeps(f.getDatabase()),A=await $.sweepProject(q,{id:g.id,path:g.path},Y,{rootDir:b});A.added>0&&!a&&console.log(` ${L.warning} harvested ${A.added} human edit(s) \u2192 review queue before overwrite`)}catch{}}const{plan:_,writes:S}=Re(w,g,h,{rootDir:b,clients:c,force:n});if(!a){v(`Rendered ${g.name} memory (${_.model.truths.length} truths, ${_.model.failures.length} failures, ${_.model.quests.length} quests, ${_.model.artifacts.length} patterns) to ${b}`);for(const $ of S){const T=$.action==="created"?L.success:$.action==="updated"?L.info:L.warning;console.log(` ${T} ${$.action.padEnd(7)} ${$.path}${$.reason?` (${$.reason})`:""}`)}}}finally{f.close(),console.log=y}}async function We(l){const{flags:i}=x(l),e=i.path,s=i.project,r=i.root,t=i["dry-run"]===!0||i.dry===!0,n=(typeof i.client=="string"?i.client:"").split(",").map(m=>m.trim().toLowerCase()).filter(Boolean),a=["claude","cursor","copilot","agents"],o=n.filter(m=>a.includes(m)),u=await import("./reverse-bridge.js"),c=E();try{let m=e?c.getProject(e):void 0;if(!m&&s&&(m=P(c,s)??void 0),m||(m=c.getProject(process.cwd())),!m){p("wyrm reverse-bridge: no Wyrm project for this directory (use --path or --project)."),process.exitCode=1;return}const y=pe(c.getDatabase()),f={wyrm_version:O().version??"unknown",compiled_at:new Date().toISOString()},g=se(y,m,f),w={"MEMORY.md":g.memoryMd};for(const _ of o){const S=ue(_,g.model,f);w[S.relPath]=S.block}const h=u.makeBridgeDeps(c.getDatabase()),b=await u.sweepProject(h,{id:m.id,path:m.path},w,{dryRun:t,rootDir:r});j(`Reverse bridge ${t?"(dry run) ":""}\u2014 ${b.added} candidate(s) queued, ${b.skipped} already present (${b.filesWithEdits}/${b.filesScanned} file(s) with edits)`);for(const _ of b.sample)console.log(` ${L.bullet} ${_}`);!t&&b.added>0&&v("Review with: wyrm review")}finally{c.close()}}async function Ye(l){const{positional:i,flags:e}=x(l.slice(1)),s=l[0],r=e.project;if(s==="git"){const t=R(e.last,20),n=E(),a=n.getDatabase();let o=null;if(r){const g=P(n,r);g||(p(`Project not found: ${r}`),n.close(),process.exit(1)),o=g.id}else{const g=a.prepare("SELECT p.* FROM projects p JOIN sessions s ON s.project_id = p.id ORDER BY s.created_at DESC LIMIT 1").get();g&&(o=g.id)}o||(p("No project found. Use --project <name>"),n.close(),process.exit(1));const u=B("git",["log","--pretty=format:%H%x1f%s%x1f%an%x1f%ai",`-${t}`],{cwd:process.cwd(),encoding:"utf-8",timeout:1e4,shell:!1});(u.error||u.status!==0)&&(p("git log failed. Make sure you are in a git repository."),n.close(),process.exit(1));const c=u.stdout.split(`
53
- `).filter(g=>g.trim()),m=new G(a);let y=0,f=0;for(const g of c){const[,w,h,b]=g.split(""),_=w??"";if(/^Merge /i.test(_)||/^(chore|bump|release|version)/i.test(_)){f++;continue}let S="pattern";/^fix(\(.+\))?:/i.test(_)?S="lesson":/^refactor(\(.+\))?:/i.test(_)&&(S="heuristic");const $=_.split(":")[0]??"commit";m.add(o,{kind:S,problem:_,whyItWorked:`Committed by ${h??"unknown"} on ${b??"unknown"}`,tags:["git","commit",$.toLowerCase()],confidence:.6,needsReview:1}),y++}n.close(),v(`Imported ${y} commits (${f} skipped). Run ${d.cyan("wyrm review")} to activate.`)}else if(s==="rules"){const t=i[0],n=e.format??"plain";t||(p("Usage: wyrm import rules <path> [--project <name>] [--format cursorrules|copilot|plain]"),process.exit(1)),ce(t)||(p(`File not found: ${t}`),process.exit(1));const a=V(t,"utf-8"),o=E(),u=o.getDatabase();let c=null;if(r){const $=P(o,r);$||(p(`Project not found: ${r}`),o.close(),process.exit(1)),c=$.id}else{const $=u.prepare("SELECT p.* FROM projects p JOIN sessions s ON s.project_id = p.id ORDER BY s.created_at DESC LIMIT 1").get();$&&(c=$.id)}c||(p("No project found. Use --project <name>"),o.close(),process.exit(1));const m=t.split("/").pop()??"rules",y=["imported",n,m],f=a.split(/\n(?=#)/),g=a.split(/\n\n+/),w=(f.length>=g.length?f:g).map($=>$.trim()).filter($=>$.length>=15),h=new G(u),b=new de(u);let _=0,S=0;for(const $ of w)/\b(always|never|must|use|don't|avoid|prefer)\b/i.test($)?(b.set(c,{category:"constraint",key:$.slice(0,50).replace(/\n/g," "),value:$,source:m}),_++):(h.add(c,{kind:"heuristic",problem:$,tags:y,confidence:.7,needsReview:1}),S++);o.close(),v(`Imported ${_} ground truths + ${S} artifacts (pending review).`)}else p("Usage: wyrm import git [--project <name>] [--last N]"),p(" wyrm import rules <path> [--project <name>] [--format cursorrules|copilot|plain]"),process.exit(1)}async function qe(l){const{flags:i}=x(l),e=i.project,s=E(),r=s.getDatabase();if(j("Wyrm Statistics"),e){const t=P(s,e);t||(p(`Project not found: ${e}`),s.close(),process.exit(1));const n=s.getProjectStats(t.id),a=[["Sessions",String(n.sessions)],["Quests (pending)",String(n.quests.pending)],["Quests (completed)",String(n.quests.completed)],["Data Points",String(n.dataPoints)]];console.log(M(["Metric","Value"],a))}else{const t=s.getStats(),n=[["Projects",String(t.projects)],["Sessions",String(t.sessions)],["Quests",String(t.quests)],["Data Points",String(t.dataPoints)],["DB Size",t.dbSize]],a=r.prepare("SELECT COUNT(*) as n FROM memory_artifacts").get().n,o=r.prepare("SELECT COUNT(*) as n FROM ground_truths WHERE is_current = 1").get().n;n.push(["Memories",String(a)],["Ground Truths",String(o)]),console.log(M(["Metric","Value"],n))}s.close()}async function Ue(l){const{flags:i}=x(l),e=i.project,s=E(),r=s.getDatabase();let t=null;if(e){const c=P(s,e);c||(p(`Project not found: ${e}`),s.close(),process.exit(1)),t=c.id}const n=t?`AND project_id = ${t}`:"",a=r.prepare(`
51
+ `)}finally{o.close(),console.log=i}}async function qe(l){const{flags:a}=R(l),e=a.path,s=a.project,n=a.out,t=a.brief===!0,r=a.force===!0,i=a.quiet===!0,o=(typeof a.client=="string"?a.client:"").split(",").map(p=>p.trim().toLowerCase()).filter(Boolean);let f=o.filter(p=>re.includes(p));const c=o.filter(p=>!re.includes(p));c.length>0&&(u(`Unknown --client value(s): ${c.join(", ")} (valid: ${re.join("|")})`),process.exit(1));const m=console.log;t&&(console.log=()=>{});const y=_();try{let p=e?y.getProject(e):void 0;!p&&s&&(p=P(y,s)??void 0),p||(p=y.getProject(process.cwd())),p||(console.log=m,u("wyrm render: no Wyrm project for this directory (use --path or --project)."),process.exit(1));const h=pe(y.getDatabase()),g={wyrm_version:L().version??"unknown",compiled_at:new Date().toISOString()};if(t){const b=se(h,p,g);console.log=m,process.stdout.write(b.sessionBrief+`
52
+ `);return}const w=n??p.path;f.length===0&&o.length===0&&(f=Ie(Te.filter(b=>K(B(w,b)))));{const b=await import("./reverse-bridge.js"),x=se(h,p,g),j={"MEMORY.md":x.memoryMd};for(const W of f){const N=ue(W,x.model,g);j[N.relPath]=N.block}try{const W=b.makeBridgeDeps(y.getDatabase()),N=await b.sweepProject(W,{id:p.id,path:p.path},j,{rootDir:w});N.added>0&&!i&&console.log(` ${O.warning} harvested ${N.added} human edit(s) \u2192 review queue before overwrite`)}catch{}}const{plan:$,writes:E}=De(h,p,g,{rootDir:w,clients:f,force:r});if(!i){v(`Rendered ${p.name} memory (${$.model.truths.length} truths, ${$.model.failures.length} failures, ${$.model.quests.length} quests, ${$.model.artifacts.length} patterns) to ${w}`);for(const b of E){const x=b.action==="created"?O.success:b.action==="updated"?O.info:O.warning;console.log(` ${x} ${b.action.padEnd(7)} ${b.path}${b.reason?` (${b.reason})`:""}`)}}}finally{y.close(),console.log=m}}async function He(l){const{flags:a}=R(l),e=a.path,s=a.project,n=a.root,t=a["dry-run"]===!0||a.dry===!0,r=(typeof a.client=="string"?a.client:"").split(",").map(m=>m.trim().toLowerCase()).filter(Boolean),i=["claude","cursor","copilot","agents"],o=r.filter(m=>i.includes(m)),f=await import("./reverse-bridge.js"),c=_();try{let m=e?c.getProject(e):void 0;if(!m&&s&&(m=P(c,s)??void 0),m||(m=c.getProject(process.cwd())),!m){u("wyrm reverse-bridge: no Wyrm project for this directory (use --path or --project)."),process.exitCode=1;return}const y=pe(c.getDatabase()),p={wyrm_version:L().version??"unknown",compiled_at:new Date().toISOString()},h=se(y,m,p),g={"MEMORY.md":h.memoryMd};for(const E of o){const b=ue(E,h.model,p);g[b.relPath]=b.block}const w=f.makeBridgeDeps(c.getDatabase()),$=await f.sweepProject(w,{id:m.id,path:m.path},g,{dryRun:t,rootDir:n});S(`Reverse bridge ${t?"(dry run) ":""}\u2014 ${$.added} candidate(s) queued, ${$.skipped} already present (${$.filesWithEdits}/${$.filesScanned} file(s) with edits)`);for(const E of $.sample)console.log(` ${O.bullet} ${E}`);!t&&$.added>0&&v("Review with: wyrm review")}finally{c.close()}}async function Ve(l){const a=R(l);if(typeof a.flags.from=="string"){await Be(a);return}const{positional:e,flags:s}=R(l.slice(1)),n=l[0],t=s.project;if(n==="git"){const r=D(s.last,20),i=_(),o=i.getDatabase();let f=null;if(t){const g=P(i,t);g||(u(`Project not found: ${t}`),i.close(),process.exit(1)),f=g.id}else{const g=o.prepare("SELECT p.* FROM projects p JOIN sessions s ON s.project_id = p.id ORDER BY s.created_at DESC LIMIT 1").get();g&&(f=g.id)}f||(u("No project found. Use --project <name>"),i.close(),process.exit(1));const c=G("git",["log","--pretty=format:%H%x1f%s%x1f%an%x1f%ai",`-${r}`],{cwd:process.cwd(),encoding:"utf-8",timeout:1e4,shell:!1});(c.error||c.status!==0)&&(u("git log failed. Make sure you are in a git repository."),i.close(),process.exit(1));const m=c.stdout.split(`
53
+ `).filter(g=>g.trim()),y=new H(o);let p=0,h=0;for(const g of m){const[,w,$,E]=g.split(""),b=w??"";if(/^Merge /i.test(b)||/^(chore|bump|release|version)/i.test(b)){h++;continue}let x="pattern";/^fix(\(.+\))?:/i.test(b)?x="lesson":/^refactor(\(.+\))?:/i.test(b)&&(x="heuristic");const j=b.split(":")[0]??"commit";y.add(f,{kind:x,problem:b,whyItWorked:`Committed by ${$??"unknown"} on ${E??"unknown"}`,tags:["git","commit",j.toLowerCase()],confidence:.6,needsReview:1}),p++}i.close(),v(`Imported ${p} commits (${h} skipped). Run ${d.cyan("wyrm review")} to activate.`)}else if(n==="rules"){const r=e[0],i=s.format??"plain";r||(u("Usage: wyrm import rules <path> [--project <name>] [--format cursorrules|copilot|plain]"),process.exit(1)),K(r)||(u(`File not found: ${r}`),process.exit(1));const o=q(r,"utf-8"),f=_(),c=f.getDatabase();let m=null;if(t){const j=P(f,t);j||(u(`Project not found: ${t}`),f.close(),process.exit(1)),m=j.id}else{const j=c.prepare("SELECT p.* FROM projects p JOIN sessions s ON s.project_id = p.id ORDER BY s.created_at DESC LIMIT 1").get();j&&(m=j.id)}m||(u("No project found. Use --project <name>"),f.close(),process.exit(1));const y=r.split("/").pop()??"rules",p=["imported",i,y],h=o.split(/\n(?=#)/),g=o.split(/\n\n+/),w=(h.length>=g.length?h:g).map(j=>j.trim()).filter(j=>j.length>=15),$=new H(c),E=new de(c);let b=0,x=0;for(const j of w)/\b(always|never|must|use|don't|avoid|prefer)\b/i.test(j)?(E.set(m,{category:"constraint",key:j.slice(0,50).replace(/\n/g," "),value:j,source:y}),b++):($.add(m,{kind:"heuristic",problem:j,tags:p,confidence:.7,needsReview:1}),x++);f.close(),v(`Imported ${b} ground truths + ${x} artifacts (pending review).`)}else u("Usage: wyrm import git [--project <name>] [--last N]"),u(" wyrm import rules <path> [--project <name>] [--format cursorrules|copilot|plain]"),u(` wyrm import --from ${me.join("|")} <file.json> [--project <name>]`),process.exit(1)}async function Be(l){const a=l.flags.from,e=l.positional[0],s=l.flags.project,n=`Usage: wyrm import --from ${me.join("|")} <file.json> [--project <name>]`;e||(u(n),process.exit(1)),K(e)||(u(`File not found: ${e}`),process.exit(1));let t;try{t=JSON.parse(q(e,"utf-8"))}catch(o){u(`Could not parse JSON from ${e}: ${o.message}`),process.exit(1)}let r;try{r=Ne(a,t)}catch(o){u(o.message),process.exit(1)}const i=_();try{let o=s?P(i,s):null;if(o||(o=i.getProject(process.cwd())??null),!o){u(s?`Project not found: ${s}`:"wyrm import: no Wyrm project for this directory (pass --project <name>)."),process.exitCode=1;return}if(r.length===0){u(`No importable memories found in ${e} for source "${a}".`);return}const f=i.getDatabase(),c=new H(f);try{const{createVectorStore:y}=await import("./vectors.js"),p=process.env.WYRM_VECTOR_PROVIDER??"auto";c.setVectorStore(y({provider:p},f))}catch{}let m=0;for(const y of r)c.add(o.id,{kind:"lesson",problem:y.text,tags:y.tags,constraints:y.metadata?JSON.stringify(y.metadata):void 0,whyItWorked:`Imported from ${y.source}`,outcome:"neutral",confidence:.6,needsReview:1}),m++;v(`Imported ${m} ${m===1?"memory":"memories"} from ${a} into ${d.cyan(o.name)} review queue. Run ${d.cyan("wyrm review")} to vet (tagged ${d.dim(`imported_from:${r[0]?.source??a}`)}).`)}finally{i.close()}}async function Ke(l){const{flags:a}=R(l),e=a.project,s=_(),n=s.getDatabase();if(S("Wyrm Statistics"),e){const t=P(s,e);t||(u(`Project not found: ${e}`),s.close(),process.exit(1));const r=s.getProjectStats(t.id),i=[["Sessions",String(r.sessions)],["Quests (pending)",String(r.quests.pending)],["Quests (completed)",String(r.quests.completed)],["Data Points",String(r.dataPoints)]];console.log(T(["Metric","Value"],i))}else{const t=s.getStats(),r=[["Projects",String(t.projects)],["Sessions",String(t.sessions)],["Quests",String(t.quests)],["Data Points",String(t.dataPoints)],["DB Size",t.dbSize]],i=n.prepare("SELECT COUNT(*) as n FROM memory_artifacts").get().n,o=n.prepare("SELECT COUNT(*) as n FROM ground_truths WHERE is_current = 1").get().n;r.push(["Memories",String(i)],["Ground Truths",String(o)]),console.log(T(["Metric","Value"],r))}s.close()}async function Ge(l){const{flags:a}=R(l),e=a.project,s=_(),n=s.getDatabase();let t=null;if(e){const c=P(s,e);c||(u(`Project not found: ${e}`),s.close(),process.exit(1)),t=c.id}const r=t?`AND project_id = ${t}`:"",i=n.prepare(`
54
54
  SELECT id, kind, problem FROM memory_artifacts
55
- WHERE needs_review = 1 ${n}
55
+ WHERE needs_review = 1 ${r}
56
56
  ORDER BY created_at ASC
57
- `).all();if(a.length===0){console.log(d.dim(` No artifacts pending review${e?` for ${e}`:""}.`)),s.close();return}j(`Review Queue (${a.length} items)`);const o=K({input:process.stdin,output:process.stdout}),u=c=>new Promise(m=>{o.question(c,m)});for(const c of a){console.log(`
58
- ${d.bold(`[${c.kind}] #${c.id}`)}`),console.log(d.dim("\u2500".repeat(60))),console.log(c.problem.slice(0,300)),console.log(d.dim("\u2500".repeat(60)));const y=(await u(`${d.cyan("[a]")}pprove / ${d.red("[r]")}eject / ${d.yellow("[s]")}kip? `)).trim().toLowerCase();y==="a"?(r.prepare("UPDATE memory_artifacts SET needs_review = 0, updated_at = datetime('now') WHERE id = ?").run(c.id),v(`Approved #${c.id}`)):y==="r"?(r.prepare("DELETE FROM memory_artifacts WHERE id = ?").run(c.id),console.log(`${L.cross} Rejected #${c.id}`)):console.log(d.dim(` Skipped #${c.id}`))}o.close(),s.close(),console.log(`
59
- Review complete.`)}async function Fe(l){const{positional:i,flags:e}=x(l),s=i[0];(!s||!["export","import","preview"].includes(s))&&(p("Usage: wyrm sync export --out <path> | wyrm sync import --from <path> | wyrm sync preview --from <path>"),process.exit(1));const{randomBytes:r,pbkdf2Sync:t,createCipheriv:n,createDecipheriv:a}=await import("crypto"),{readFileSync:o,writeFileSync:u,copyFileSync:c,unlinkSync:m,existsSync:y,chmodSync:f}=await import("fs"),{homedir:g}=await import("os"),{join:w}=await import("path"),h=(await import("better-sqlite3")).default;let b=process.env.WYRM_SYNC_PASSPHRASE??"";if(!b){const C=K({input:process.stdin,output:process.stdout});b=await new Promise(D=>{process.stdout.write("Passphrase: "),process.stdin.isTTY&&process.stdin.setRawMode?.(!0),C.question("",N=>{process.stdin.isTTY&&process.stdin.setRawMode?.(!1),console.log(""),C.close(),D(N)})})}b||(p("Passphrase is required. Set WYRM_SYNC_PASSPHRASE or enter interactively."),process.exit(1));const _=w(g(),".wyrm"),S=E();if(s==="export"){const C=e.out;C||(p("--out <path> is required"),process.exit(1));const D=w(_,"wyrm_cli_export_temp.db");try{const N=S.getDatabase();y(D)&&m(D),N.prepare("VACUUM INTO ?").run(D);const F=o(D),W=r(32),H=r(16),$e=t(b,W,6e5,32,"sha256"),te=n("aes-256-gcm",$e,H),ke=Buffer.concat([te.update(F),te.final()]),Se=te.getAuthTag(),je=Buffer.from("WYRM"),ne=Buffer.alloc(1);ne.writeUInt8(1,0);const ie=Buffer.concat([je,ne,W,H,Se,ke]);u(C,ie);try{f(C,384)}catch{}try{m(D)}catch{}const _e=(ie.length/(1024*1024)).toFixed(2);v(`Exported to ${C} (${_e} MB)`)}catch(N){try{y(D)&&m(D)}catch{}p(`Export failed: ${N}`)}S.close();return}const $=e.from;$||(p("--from <path> is required"),process.exit(1));const T=o($);T.subarray(0,4).toString("ascii")!=="WYRM"&&(p("Invalid Wyrm snapshot file."),process.exit(1));const Y=T.readUInt8(4);Y!==1&&(p(`Unsupported snapshot version: ${Y}`),process.exit(1));const q=T.subarray(5,37),A=T.subarray(37,53),ge=T.subarray(53,69),he=T.subarray(69),we=t(b,q,6e5,32,"sha256"),Q=a("aes-256-gcm",we,A);Q.setAuthTag(ge);let X;try{X=Buffer.concat([Q.update(he),Q.final()])}catch{p("Decryption failed \u2014 wrong passphrase or corrupted file."),process.exit(1)}if(s==="preview"){const C=w(_,"wyrm_cli_preview_temp.db");y(C)&&m(C),u(C,X);try{const D=new h(C,{readonly:!0}),N=["projects","sessions","ground_truths","memory_artifacts","quests"];j("Snapshot Preview");const F=[];for(const W of N)try{const H=D.prepare(`SELECT COUNT(*) as n FROM ${W}`).get();F.push([W,String(H.n)])}catch{F.push([W,"?"])}console.log(M(["Table","Count"],F)),D.close()}catch(D){p(`Preview failed: ${D}`)}try{y(C)&&m(C)}catch{}S.close();return}const U=S.getDatabasePath(),re=K({input:process.stdin,output:process.stdout}),be=await new Promise(C=>{re.question("This will REPLACE your current database. Type CONFIRM to proceed: ",C)});if(re.close(),be.trim()!=="CONFIRM"){console.log(d.dim("Aborted.")),S.close();return}const ve=new Date().toISOString().replace(/[:.]/g,"-"),Z=`${U}.backup.${ve}`;c(U,Z),v(`Backed up to ${Z}`);const ee=w(_,"wyrm_cli_restore_temp.db");u(ee,X),S.getDatabase().close(),c(ee,U);for(const C of["-wal","-shm"])try{y(U+C)&&m(U+C)}catch{}try{m(ee)}catch{}v(`Restored from ${$}. Backup at ${Z}`)}async function He(l){const{flags:i}=x(l),e=i.project,s=i.path,r=z(i["min-confidence"],.3),t=R(i["older-than"],90),n=i["no-dry-run"]===!0,a=i.yes===!0,o=E(),u=o.getDatabase();let c=null;if(s||e){const w=s?o.getProject(s):P(o,e);w||(p(`Project not found: ${s??e}`),o.close(),process.exit(1)),c=w.id}const m=new G(u),{candidates:y}=m.pruneStale({projectId:c,minConfidence:r,olderThanDays:t,dryRun:!0});if(j(`Prune Candidates${n?" (LIVE DELETE)":" (dry-run)"}`),y.length===0){console.log(d.dim(" No artifacts match prune criteria.")),o.close();return}const f=y.map(w=>[String(w.id),w.kind,w.problem.slice(0,60),(w.confidence*100).toFixed(0)+"%",w.last_accessed_at??"never"]);if(console.log(M(["ID","Kind","Problem","Conf","Last Accessed"],f)),console.log(`
60
- Total: ${y.length} candidate(s)`),!n){console.log(d.dim(`
61
- This is a dry run. Use --no-dry-run to delete (confirm each ID).`)),o.close();return}if(!a){const w=K({input:process.stdin,output:process.stdout}),h=await new Promise(b=>{w.question(`
62
- Delete these ${y.length} artifact(s)? Type CONFIRM to proceed: `,b)});if(w.close(),h.trim()!=="CONFIRM"){console.log(d.dim("Aborted.")),o.close();return}}const g=m.deleteArtifacts(y.map(w=>w.id));v(`Deleted ${g} artifact(s).`),o.close()}function me(){return V(0,"utf-8").trim()}async function Ve(){const{initializeLicense:l,getLicenseInfo:i,getTier:e}=await import("./license.js");l();const s=i();j("Wyrm License");const r=[["Tier",e()],["Status",s.valid?"valid":"free tier (no license key)"]];s.valid&&s.key&&(r.push(["Key",s.key]),r.push(["Issued to",s.issuedTo??"unknown"]),r.push(["Expires",s.expiresAt?new Date(s.expiresAt).toLocaleDateString():"never"])),r.push(["Features",s.features.join(", ")||"(free)"]),console.log(M(["Field","Value"],r)),s.valid||console.log(d.dim(`
57
+ `).all();if(i.length===0){console.log(d.dim(` No artifacts pending review${e?` for ${e}`:""}.`)),s.close();return}S(`Review Queue (${i.length} items)`);const o=J({input:process.stdin,output:process.stdout}),f=c=>new Promise(m=>{o.question(c,m)});for(const c of i){console.log(`
58
+ ${d.bold(`[${c.kind}] #${c.id}`)}`),console.log(d.dim("\u2500".repeat(60))),console.log(c.problem.slice(0,300)),console.log(d.dim("\u2500".repeat(60)));const y=(await f(`${d.cyan("[a]")}pprove / ${d.red("[r]")}eject / ${d.yellow("[s]")}kip? `)).trim().toLowerCase();y==="a"?(n.prepare("UPDATE memory_artifacts SET needs_review = 0, updated_at = datetime('now') WHERE id = ?").run(c.id),v(`Approved #${c.id}`)):y==="r"?(n.prepare("DELETE FROM memory_artifacts WHERE id = ?").run(c.id),console.log(`${O.cross} Rejected #${c.id}`)):console.log(d.dim(` Skipped #${c.id}`))}o.close(),s.close(),console.log(`
59
+ Review complete.`)}async function Je(l){const{positional:a,flags:e}=R(l),s=a[0];(!s||!["export","import","preview"].includes(s))&&(u("Usage: wyrm sync export --out <path> | wyrm sync import --from <path> | wyrm sync preview --from <path>"),process.exit(1));const{randomBytes:n,pbkdf2Sync:t,createCipheriv:r,createDecipheriv:i}=await import("crypto"),{readFileSync:o,writeFileSync:f,copyFileSync:c,unlinkSync:m,existsSync:y,chmodSync:p}=await import("fs"),{homedir:h}=await import("os"),{join:g}=await import("path"),w=(await import("better-sqlite3")).default;let $=process.env.WYRM_SYNC_PASSPHRASE??"";if(!$){const C=J({input:process.stdin,output:process.stdout});$=await new Promise(I=>{process.stdout.write("Passphrase: "),process.stdin.isTTY&&process.stdin.setRawMode?.(!0),C.question("",A=>{process.stdin.isTTY&&process.stdin.setRawMode?.(!1),console.log(""),C.close(),I(A)})})}$||(u("Passphrase is required. Set WYRM_SYNC_PASSPHRASE or enter interactively."),process.exit(1));const E=g(h(),".wyrm"),b=_();if(s==="export"){const C=e.out;C||(u("--out <path> is required"),process.exit(1));const I=g(E,"wyrm_cli_export_temp.db");try{const A=b.getDatabase();y(I)&&m(I),A.prepare("VACUUM INTO ?").run(I);const F=o(I),Y=n(32),V=n(16),Se=t($,Y,6e5,32,"sha256"),oe=r("aes-256-gcm",Se,V),je=Buffer.concat([oe.update(F),oe.final()]),_e=oe.getAuthTag(),Re=Buffer.from("WYRM"),ie=Buffer.alloc(1);ie.writeUInt8(1,0);const ae=Buffer.concat([Re,ie,Y,V,_e,je]);f(C,ae);try{p(C,384)}catch{}try{m(I)}catch{}const Ee=(ae.length/(1024*1024)).toFixed(2);v(`Exported to ${C} (${Ee} MB)`)}catch(A){try{y(I)&&m(I)}catch{}u(`Export failed: ${A}`)}b.close();return}const x=e.from;x||(u("--from <path> is required"),process.exit(1));const j=o(x);j.subarray(0,4).toString("ascii")!=="WYRM"&&(u("Invalid Wyrm snapshot file."),process.exit(1));const W=j.readUInt8(4);W!==1&&(u(`Unsupported snapshot version: ${W}`),process.exit(1));const N=j.subarray(5,37),he=j.subarray(37,53),we=j.subarray(53,69),be=j.subarray(69),ve=t($,N,6e5,32,"sha256"),X=i("aes-256-gcm",ve,he);X.setAuthTag(we);let Z;try{Z=Buffer.concat([X.update(be),X.final()])}catch{u("Decryption failed \u2014 wrong passphrase or corrupted file."),process.exit(1)}if(s==="preview"){const C=g(E,"wyrm_cli_preview_temp.db");y(C)&&m(C),f(C,Z);try{const I=new w(C,{readonly:!0}),A=["projects","sessions","ground_truths","memory_artifacts","quests"];S("Snapshot Preview");const F=[];for(const Y of A)try{const V=I.prepare(`SELECT COUNT(*) as n FROM ${Y}`).get();F.push([Y,String(V.n)])}catch{F.push([Y,"?"])}console.log(T(["Table","Count"],F)),I.close()}catch(I){u(`Preview failed: ${I}`)}try{y(C)&&m(C)}catch{}b.close();return}const U=b.getDatabasePath(),ne=J({input:process.stdin,output:process.stdout}),$e=await new Promise(C=>{ne.question("This will REPLACE your current database. Type CONFIRM to proceed: ",C)});if(ne.close(),$e.trim()!=="CONFIRM"){console.log(d.dim("Aborted.")),b.close();return}const ke=new Date().toISOString().replace(/[:.]/g,"-"),ee=`${U}.backup.${ke}`;c(U,ee),v(`Backed up to ${ee}`);const te=g(E,"wyrm_cli_restore_temp.db");f(te,Z),b.getDatabase().close(),c(te,U);for(const C of["-wal","-shm"])try{y(U+C)&&m(U+C)}catch{}try{m(te)}catch{}v(`Restored from ${x}. Backup at ${ee}`)}async function ze(l){const{flags:a}=R(l),e=a.project,s=a.path,n=Q(a["min-confidence"],.3),t=D(a["older-than"],90),r=a["no-dry-run"]===!0,i=a.yes===!0,o=_(),f=o.getDatabase();let c=null;if(s||e){const g=s?o.getProject(s):P(o,e);g||(u(`Project not found: ${s??e}`),o.close(),process.exit(1)),c=g.id}const m=new H(f),{candidates:y}=m.pruneStale({projectId:c,minConfidence:n,olderThanDays:t,dryRun:!0});if(S(`Prune Candidates${r?" (LIVE DELETE)":" (dry-run)"}`),y.length===0){console.log(d.dim(" No artifacts match prune criteria.")),o.close();return}const p=y.map(g=>[String(g.id),g.kind,g.problem.slice(0,60),(g.confidence*100).toFixed(0)+"%",g.last_accessed_at??"never"]);if(console.log(T(["ID","Kind","Problem","Conf","Last Accessed"],p)),console.log(`
60
+ Total: ${y.length} candidate(s)`),!r){console.log(d.dim(`
61
+ This is a dry run. Use --no-dry-run to delete (confirm each ID).`)),o.close();return}if(!i){const g=J({input:process.stdin,output:process.stdout}),w=await new Promise($=>{g.question(`
62
+ Delete these ${y.length} artifact(s)? Type CONFIRM to proceed: `,$)});if(g.close(),w.trim()!=="CONFIRM"){console.log(d.dim("Aborted.")),o.close();return}}const h=m.deleteArtifacts(y.map(g=>g.id));v(`Deleted ${h} artifact(s).`),o.close()}function fe(){return q(0,"utf-8").trim()}async function Qe(){const{initializeLicense:l,getLicenseInfo:a,getTier:e}=await import("./license.js");l();const s=a();S("Wyrm License");const n=[["Tier",e()],["Status",s.valid?"valid":"free tier (no license key)"]];s.valid&&s.key&&(n.push(["Key",s.key]),n.push(["Issued to",s.issuedTo??"unknown"]),n.push(["Expires",s.expiresAt?new Date(s.expiresAt).toLocaleDateString():"never"])),n.push(["Features",s.features.join(", ")||"(free)"]),console.log(T(["Field","Value"],n)),s.valid||console.log(d.dim(`
63
63
  Activate with: wyrm login (free) \xB7 or: wyrm activate <license.json | key>`)),console.log(d.dim(`
64
- \xA9 2026 Ghost Protocol (Pvt) Ltd \xB7 Proprietary \xB7 https://wyrm.ghosts.lk`)),console.log(d.dim(" Licensed under the Wyrm Terms of Service. No open-source license is granted."))}async function Be(){const l=(process.env.WYRM_ACCOUNT_URL??"https://account.ghosts.lk").replace(/\/$/,""),i=(()=>{try{return O().version??"unknown"}catch{return"unknown"}})();let e;try{const c=await fetch(`${l}/api/v1/cli/auth/start`,{method:"POST",headers:{"x-wyrm-version":i}});if(!c.ok)throw new Error(`HTTP ${c.status}`);e=await c.json()}catch(c){p(`Couldn't reach ${l} (${c instanceof Error?c.message:"network error"}).`),process.exitCode=1;return}const s=e.verification_uri_complete||e.verification_uri||`${l}/cli`;console.log(`
64
+ \xA9 2026 Ghost Protocol (Pvt) Ltd \xB7 Proprietary \xB7 https://wyrm.ghosts.lk`)),console.log(d.dim(" Licensed under the Wyrm Terms of Service. No open-source license is granted."))}async function Xe(){const l=(process.env.WYRM_ACCOUNT_URL??"https://account.ghosts.lk").replace(/\/$/,""),a=(()=>{try{return L().version??"unknown"}catch{return"unknown"}})();let e;try{const c=await fetch(`${l}/api/v1/cli/auth/start`,{method:"POST",headers:{"x-wyrm-version":a}});if(!c.ok)throw new Error(`HTTP ${c.status}`);e=await c.json()}catch(c){u(`Couldn't reach ${l} (${c instanceof Error?c.message:"network error"}).`),process.exitCode=1;return}const s=e.verification_uri_complete||e.verification_uri||`${l}/cli`;console.log(`
65
65
  ${d.cyan("Sign in to activate Wyrm (free):")}`),console.log(` 1. Open ${d.cyan(s)}`),console.log(` 2. Approve the code ${d.cyan(e.user_code)}`),console.log(d.dim(`
66
- Waiting for approval\u2026 (Ctrl-C to cancel)`));const r=(e.interval??3)*1e3,t=Date.now()+(e.expires_in??600)*1e3;let n="";for(;Date.now()<t;){await new Promise(c=>{setTimeout(c,r)});try{const m=await(await fetch(`${l}/api/v1/cli/auth/poll`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({device_code:e.device_code})})).json();if(m.status==="approved"&&m.token){n=m.token;break}if(m.status==="denied"||m.error==="expired"){p("Login was denied or the code expired. Run `wyrm login` again."),process.exitCode=1;return}}catch{}}if(!n){p("Login timed out. Run `wyrm login` again."),process.exitCode=1;return}let a;try{const c=await fetch(`${l}/api/v1/license/free`,{method:"POST",headers:{authorization:`Bearer ${n}`,"x-wyrm-version":i}});if(!c.ok){const m=await c.json().catch(()=>({}));p(`Activation failed (${c.status}): ${m.hint||m.error||"unknown error"}`),process.exitCode=1;return}a=JSON.stringify(await c.json())}catch(c){p(`Activation request failed (${c instanceof Error?c.message:"network error"}).`),process.exitCode=1;return}const{activateLicense:o}=await import("./license.js"),u=o(a);u.valid?(v(`Signed in & activated \u2014 ${u.tier} tier (expires ${u.expiresAt??"never"}).`),console.log(d.dim(" Restart the Wyrm MCP server / daemon to apply."))):(p(`Activation failed: ${u.error??"unknown error"}`),process.exitCode=1)}async function Ke(l){const{positional:i}=x(l),e=i[0];let s;e&&ce(e)?s=V(e,"utf-8"):e?s=e:process.stdin.isTTY?(p("Usage: wyrm activate <license.json path | license JSON> (or pipe the JSON on stdin)"),process.exit(1)):s=me();const{activateLicense:r}=await import("./license.js");try{const t=r(s);t.valid?(v(`License activated \u2014 ${t.tier} tier (${t.features.join(", ")})`),console.log(d.dim(" Restart Wyrm (MCP server / daemon) to apply all features."))):(p(`License activation failed: ${t.error??"unknown error"}`),process.exitCode=1)}catch{p("Invalid license format. Please verify your license key."),process.exitCode=1}}async function Ge(l){const{flags:i}=x(l),{runMaintenance:e}=await import("./maintenance.js"),{FailurePatterns:s}=await import("./failure-patterns.js"),{SessionSeen:r}=await import("./session-seen.js"),{AgentPresence:t}=await import("./presence.js"),n=E();try{const a=n.getDatabase(),o=R(i["archive-days"],0),u=e({db:n,sessionSeen:new r(a),failures:new s(a),presence:new t(a)},{vacuum:i.vacuum===!0,archiveDays:o>0?o:void 0});j("Maintenance complete");for(const c of u.lines)console.log(` - ${c}`);console.log(d.dim(`
67
- Database size: ${u.dbSize}`))}finally{n.close()}}async function Je(l){const{positional:i,flags:e}=x(l),s=i[0]??"status",{createVectorStore:r}=await import("./vectors.js"),n={provider:e.provider??process.env.WYRM_VECTOR_PROVIDER??"auto",model:e.model,apiKey:e["api-key"]??process.env.OPENAI_API_KEY,ollamaUrl:e["ollama-url"]};if(s==="setup"){const{createProvider:o}=await import("./providers/embedding-provider.js"),u=o(n);if(!await u.isReady()&&n.provider!=="none"){p(`Provider not ready: ${u.name}. Check the configuration and try again.`),process.exitCode=1;return}v(`Vector provider verified: ${u.name} (model ${u.model}, ${u.dimensions}d)`),console.log(d.dim(" The MCP server reads WYRM_VECTOR_PROVIDER (and provider-specific env) at boot \u2014")),console.log(d.dim(` set WYRM_VECTOR_PROVIDER=${u.name==="none"?"none":n.provider} in the server env, then: wyrm index rebuild`));return}const a=E();try{const o=a.getDatabase(),u=r(n,o);if(s==="status"){const c=u.getStats();j("Vector index");const m=[["Provider",c.provider],["Model",c.model],["Vectors",String(c.total)],...Object.entries(c.byType).map(([y,f])=>[` ${y}`,String(f)])];console.log(M(["Field","Value"],m));return}if(s==="rebuild"){const{reindexProjects:c}=await import("./reindex.js"),m=e["dry-run"]===!0,y=e.project;let f;if(y){const h=a.getProject(y)??P(a,y);if(!h){p(`Project not found: ${y}`),process.exitCode=1;return}f=[h.id]}else f=a.getAllProjects(1e3).map(h=>h.id);const{indexed:g,skipped:w}=await c(o,u,f,{dryRun:m,onError:(h,b)=>p(`${h}: ${JSON.stringify(b)}`)});v(`Reindex ${m?"(dry run) ":""}\u2014 ${f.length} project(s), ${g} indexed, ${w} skipped`);return}p("Usage: wyrm index <setup|rebuild|status> [--provider auto|local|ollama|openai|none] [--model M] [--project P] [--dry-run]"),process.exitCode=1}finally{a.close()}}async function ze(l){const{flags:i}=x(l),{getUpdateStatus:e}=await import("./version-check.js"),s=O().version??"0.0.0",r=E();let t;try{t=await e(r.getDatabase(),s,{force:i.force===!0||i.check===!0})}finally{r.close()}if(j("Wyrm update"),console.log(M(["Field","Value"],[["Current",t.current],["Latest",t.latest??"unknown (offline?)"],["Update available",t.updateAvailable?"yes":"no"],["Checked",`${t.checkedAt} (${t.source})`]])),i.check===!0)return;if(!t.updateAvailable&&i.force!==!0){console.log(d.dim(`
66
+ Waiting for approval\u2026 (Ctrl-C to cancel)`));const n=(e.interval??3)*1e3,t=Date.now()+(e.expires_in??600)*1e3;let r="";for(;Date.now()<t;){await new Promise(c=>{setTimeout(c,n)});try{const m=await(await fetch(`${l}/api/v1/cli/auth/poll`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({device_code:e.device_code})})).json();if(m.status==="approved"&&m.token){r=m.token;break}if(m.status==="denied"||m.error==="expired"){u("Login was denied or the code expired. Run `wyrm login` again."),process.exitCode=1;return}}catch{}}if(!r){u("Login timed out. Run `wyrm login` again."),process.exitCode=1;return}let i;try{const c=await fetch(`${l}/api/v1/license/free`,{method:"POST",headers:{authorization:`Bearer ${r}`,"x-wyrm-version":a}});if(!c.ok){const m=await c.json().catch(()=>({}));u(`Activation failed (${c.status}): ${m.hint||m.error||"unknown error"}`),process.exitCode=1;return}i=JSON.stringify(await c.json())}catch(c){u(`Activation request failed (${c instanceof Error?c.message:"network error"}).`),process.exitCode=1;return}const{activateLicense:o}=await import("./license.js"),f=o(i);f.valid?(v(`Signed in & activated \u2014 ${f.tier} tier (expires ${f.expiresAt??"never"}).`),console.log(d.dim(" Restart the Wyrm MCP server / daemon to apply."))):(u(`Activation failed: ${f.error??"unknown error"}`),process.exitCode=1)}async function Ze(l){const{positional:a}=R(l),e=a[0];let s;e&&K(e)?s=q(e,"utf-8"):e?s=e:process.stdin.isTTY?(u("Usage: wyrm activate <license.json path | license JSON> (or pipe the JSON on stdin)"),process.exit(1)):s=fe();const{activateLicense:n}=await import("./license.js");try{const t=n(s);t.valid?(v(`License activated \u2014 ${t.tier} tier (${t.features.join(", ")})`),console.log(d.dim(" Restart Wyrm (MCP server / daemon) to apply all features."))):(u(`License activation failed: ${t.error??"unknown error"}`),process.exitCode=1)}catch{u("Invalid license format. Please verify your license key."),process.exitCode=1}}async function et(l){const{flags:a}=R(l),{runMaintenance:e}=await import("./maintenance.js"),{FailurePatterns:s}=await import("./failure-patterns.js"),{SessionSeen:n}=await import("./session-seen.js"),{AgentPresence:t}=await import("./presence.js"),r=_();try{const i=r.getDatabase(),o=D(a["archive-days"],0),f=e({db:r,sessionSeen:new n(i),failures:new s(i),presence:new t(i)},{vacuum:a.vacuum===!0,archiveDays:o>0?o:void 0});S("Maintenance complete");for(const c of f.lines)console.log(` - ${c}`);console.log(d.dim(`
67
+ Database size: ${f.dbSize}`))}finally{r.close()}}async function tt(l){const{positional:a,flags:e}=R(l),s=a[0]??"status",{createVectorStore:n}=await import("./vectors.js"),r={provider:e.provider??process.env.WYRM_VECTOR_PROVIDER??"auto",model:e.model,apiKey:e["api-key"]??process.env.OPENAI_API_KEY,ollamaUrl:e["ollama-url"]};if(s==="setup"){const{createProvider:o}=await import("./providers/embedding-provider.js"),f=o(r);if(!await f.isReady()&&r.provider!=="none"){u(`Provider not ready: ${f.name}. Check the configuration and try again.`),process.exitCode=1;return}v(`Vector provider verified: ${f.name} (model ${f.model}, ${f.dimensions}d)`),console.log(d.dim(" The MCP server reads WYRM_VECTOR_PROVIDER (and provider-specific env) at boot \u2014")),console.log(d.dim(` set WYRM_VECTOR_PROVIDER=${f.name==="none"?"none":r.provider} in the server env, then: wyrm index rebuild`));return}const i=_();try{const o=i.getDatabase(),f=n(r,o);if(s==="status"){const c=f.getStats();S("Vector index");const m=[["Provider",c.provider],["Model",c.model],["Vectors",String(c.total)],...Object.entries(c.byType).map(([y,p])=>[` ${y}`,String(p)])];console.log(T(["Field","Value"],m));return}if(s==="rebuild"){const{reindexProjects:c}=await import("./reindex.js"),m=e["dry-run"]===!0,y=e.project;let p;if(y){const w=i.getProject(y)??P(i,y);if(!w){u(`Project not found: ${y}`),process.exitCode=1;return}p=[w.id]}else p=i.getAllProjects(1e3).map(w=>w.id);const{indexed:h,skipped:g}=await c(o,f,p,{dryRun:m,onError:(w,$)=>u(`${w}: ${JSON.stringify($)}`)});v(`Reindex ${m?"(dry run) ":""}\u2014 ${p.length} project(s), ${h} indexed, ${g} skipped`);return}u("Usage: wyrm index <setup|rebuild|status> [--provider auto|local|ollama|openai|none] [--model M] [--project P] [--dry-run]"),process.exitCode=1}finally{i.close()}}async function ot(l){const{flags:a}=R(l),{getUpdateStatus:e}=await import("./version-check.js"),s=L().version??"0.0.0",n=_();let t;try{t=await e(n.getDatabase(),s,{force:a.force===!0||a.check===!0})}finally{n.close()}if(S("Wyrm update"),console.log(T(["Field","Value"],[["Current",t.current],["Latest",t.latest??"unknown (offline?)"],["Update available",t.updateAvailable?"yes":"no"],["Checked",`${t.checkedAt} (${t.source})`]])),a.check===!0)return;if(!t.updateAvailable&&a.force!==!0){console.log(d.dim(`
68
68
  Already up to date. (Use --force to reinstall anyway.)`));return}console.log(d.dim(`
69
69
  Running: npm install -g wyrm-mcp@latest
70
- `));const n=B("npm",["install","-g","wyrm-mcp@latest"],{stdio:"inherit",shell:!1});n.status===0?v("Updated. Restart your MCP clients to pick up the new binary."):(p(`npm install exited with ${n.status??"unknown"}`),process.exitCode=n.status??1)}async function Qe(l){const{positional:i,flags:e}=x(l),s=i[0],r=e.project??process.cwd();if(s==="inject"){const{injectSystemPrompt:t}=await import("./autoconfig.js"),n=typeof e.clients=="string"?e.clients.split(",").map(o=>o.trim()).filter(Boolean):[],a=t(r,n);j("System prompt injection");for(const o of a.injected)console.log(` + ${o}`);for(const o of a.skipped)console.log(d.dim(` o skipped (unknown client): ${o}`));for(const o of a.errors)p(o);a.injected.length>0&&v("AI clients in this project will now call wyrm_session_prime at conversation start."),a.errors.length>0&&(process.exitCode=1);return}if(s==="migrate"){const{migrateProject:t,renderMigrationReport:n}=await import("./migrate-prompt.js"),{WYRM_INJECT_BLOCK:a}=await import("./autoconfig.js"),o=e.apply===!0,u=t({projectPath:r,newBlock:a,apply:o});console.log(n(u,o));return}p("Usage: wyrm prompt inject [--project <path>] [--clients copilot,cursor] | wyrm prompt migrate [--project <path>] [--apply]"),process.exitCode=1}async function Xe(l){const{positional:i,flags:e}=x(l);(i[0]??"report")!=="report"&&(p("Usage: wyrm hours report --from YYYY-MM-DD --to YYYY-MM-DD [--project <name>] [--session-hours H] [--json]"),process.exit(1));const r=e.from,t=e.to;(!r||!t)&&(p("--from and --to are required (YYYY-MM-DD)"),process.exit(1));const{HourLedger:n}=await import("./hours.js"),a=E();try{const o=e.project,u=o?P(a,o):null;if(o&&!u){p(`Project not found: ${o}`),process.exitCode=1;return}const c=new n(a.getDatabase()).report({range_start:r,range_end:t,project_id:u?.id,default_session_hours:z(e["session-hours"],1)});if(e.json===!0){console.log(JSON.stringify(c,null,2));return}if(j(`Hours ${c.range.start} \u2192 ${c.range.end}`),c.by_project.length===0){console.log(d.dim(" No sessions in range."));return}console.log(M(["Project","Sessions","Hours"],c.by_project.map(m=>[m.project_name,String(m.session_count),m.hours.toFixed(2)]))),console.log(`
71
- Total: ${c.total_hours.toFixed(2)}h across ${c.entries.length} session(s)`+(c.estimated_sessions>0?d.dim(` (${c.estimated_sessions} estimated)`):""))}finally{a.close()}}async function Ze(l){const{positional:i,flags:e}=x(l),s=i[0]??"generate",r=e.client,t=z(e.rate,NaN),n=e.from,a=e.to;(s!=="generate"||!r||!Number.isFinite(t)||!n||!a)&&(p('Usage: wyrm invoice generate --client <name> --rate <usd/hour> --from YYYY-MM-DD --to YYYY-MM-DD [--project <name>] [--number INV-X] [--currency USD] [--notes "\u2026"] [--out <path>]'),process.exit(1));const{HourLedger:o}=await import("./hours.js"),u=E();try{const c=e.project,m=c?P(u,c):null;if(c&&!m){p(`Project not found: ${c}`),process.exitCode=1;return}const y=new o(u.getDatabase()).invoice({client_name:r,hourly_rate_usd:t,range_start:n,range_end:a,project_id:m?.id,invoice_number:e.number,currency:e.currency,notes:e.notes,business_name:e["business-name"],business_address:e["business-address"],business_contact:e["business-contact"],client_address:e["client-address"],default_session_hours:z(e["session-hours"],1)}),f=e.out;if(f){const{writeFileSync:g}=await import("node:fs");g(f,y+`
72
- `,"utf-8"),v(`Invoice written to ${f}`)}else process.stdout.write(y+`
73
- `)}finally{u.close()}}async function et(l){const{positional:i,flags:e}=x(l),s=i[0]??"status",{AgentDaemon:r}=await import("./agent-daemon.js"),t=E();try{const n=new r(t.getDatabase());switch(s){case"init":case"start":{const a=Math.max(10,Math.min(R(e.interval,600),86400)),o=n.start({interval_seconds:a,max_steps:e["max-steps"]!==void 0&&R(e["max-steps"],0)||void 0,project_path:e.project,verbose:e.verbose===!0});if(!o.ok){p(`Agent init failed: ${o.error}`),process.exitCode=1;return}const u=o.status;v(`Agent ${u.pid!=null&&u.pid!==o.pid?"already running":"started"} \u2014 pid ${u.pid}`),console.log(` Interval: ${a}s \xB7 Active goals: ${u.active_goals} \xB7 Total iterations: ${u.total_iterations}`),console.log(d.dim(` Log: ${u.log_file}`));return}case"status":{const a=n.status();j("Wyrm agent"),console.log(a.running?` RUNNING \u2014 pid ${a.pid}${a.started_at?` (since ${a.started_at})`:""}`:" NOT RUNNING. Start it with: wyrm agent init"),console.log(` Active goals: ${a.active_goals} \xB7 Total iterations: ${a.total_iterations}`),a.last_action&&console.log(` Last action (${a.last_action.ran_at}): ${a.last_action.summary} [${a.last_action.result_status??"?"}]`),e.log===!0&&(console.log(`
74
- === Recent log ===`),console.log(n.recentLog(40)));return}case"stop":{const a=await n.stop({grace_ms:e.grace!==void 0?R(e.grace,3e3):void 0});if(!a.ok){p(`Stop failed: ${a.error}`),process.exitCode=1;return}v(a.was_running?`Agent stopped (was pid ${a.pid}). Goals remain in DB \u2014 wyrm agent init resumes.`:"Agent was not running. (No-op.)");return}case"restart":{const a=await n.restart({interval_seconds:e.interval!==void 0?R(e.interval,600):void 0,max_steps:e["max-steps"]!==void 0&&R(e["max-steps"],0)||void 0,project_path:e.project,verbose:e.verbose===!0});if(!a.ok){p(`Restart failed: ${a.error}`),process.exitCode=1;return}v(`Agent restarted \u2014 pid ${a.status.pid}. Active goals: ${a.status.active_goals}.`);return}default:p("Usage: wyrm agent <init|status|stop|restart> [--interval N] [--max-steps N] [--project <path>] [--verbose] [--log]"),process.exitCode=1}}finally{t.close()}}async function tt(l){if(l.includes("--encrypt")){const{initializeLicense:s,hasFeature:r}=await import("./license.js");if(s(),!r("encryption")){p("Encryption setup requires a Pro license or higher. See: wyrm license"),process.exitCode=1;return}const{getCrypto:t,initializeCrypto:n}=await import("./crypto.js"),a=l.includes("--enable"),o=l.includes("--test");if(a){const c=process.env.WYRM_ENCRYPTION_KEY??(process.stdin.isTTY?"":me());if(!c||c.length<8){p('Password must be at least 8 characters. Set WYRM_ENCRYPTION_KEY or pipe it: printf %s "$PW" | wyrm setup --encrypt --enable'),process.exitCode=1;return}n(c),v("Encryption enabled (AES-256-GCM, key derived via PBKDF2). Store your password safely \u2014 it cannot be recovered.");return}const u=t();if(o){if(!u.isEnabled()){p("Encryption not enabled. Run: wyrm setup --encrypt --enable"),process.exitCode=1;return}const c="Wyrm encryption test "+Date.now();u.decrypt(u.encrypt(c))===c?v("Encryption test PASSED (encrypt \u2192 decrypt roundtrip)."):(p("Encryption test FAILED."),process.exitCode=1);return}j("Encryption status"),console.log(` Enabled: ${u.isEnabled()?"yes \u2014 new data is encrypted at rest":"no"}`),console.log(" Algorithm: AES-256-GCM"),u.isEnabled()||console.log(d.dim(" Enable with: wyrm setup --encrypt --enable (password via WYRM_ENCRYPTION_KEY or stdin)"));return}const i=ae(le(import.meta.url)),e=B(process.execPath,[oe(i,"setup.js"),...l],{stdio:"inherit",shell:!1});process.exitCode=e.status??0}function fe(){console.log(`
75
- ${J.brightMagenta}\u{F115D} Wyrm CLI v${O().version??"unknown"}${J.reset}
76
- ${J.dim}Persistent AI Memory System${J.reset}
70
+ `));const r=G("npm",["install","-g","wyrm-mcp@latest"],{stdio:"inherit",shell:!1});r.status===0?v("Updated. Restart your MCP clients to pick up the new binary."):(u(`npm install exited with ${r.status??"unknown"}`),process.exitCode=r.status??1)}async function st(l){const{positional:a,flags:e}=R(l),s=a[0],n=e.project??process.cwd();if(s==="inject"){const{injectSystemPrompt:t}=await import("./autoconfig.js"),r=typeof e.clients=="string"?e.clients.split(",").map(o=>o.trim()).filter(Boolean):[],i=t(n,r);S("System prompt injection");for(const o of i.injected)console.log(` + ${o}`);for(const o of i.skipped)console.log(d.dim(` o skipped (unknown client): ${o}`));for(const o of i.errors)u(o);i.injected.length>0&&v("AI clients in this project will now call wyrm_session_prime at conversation start."),i.errors.length>0&&(process.exitCode=1);return}if(s==="migrate"){const{migrateProject:t,renderMigrationReport:r}=await import("./migrate-prompt.js"),{WYRM_INJECT_BLOCK:i}=await import("./autoconfig.js"),o=e.apply===!0,f=t({projectPath:n,newBlock:i,apply:o});console.log(r(f,o));return}u("Usage: wyrm prompt inject [--project <path>] [--clients copilot,cursor] | wyrm prompt migrate [--project <path>] [--apply]"),process.exitCode=1}async function rt(l){const{positional:a,flags:e}=R(l);(a[0]??"report")!=="report"&&(u("Usage: wyrm hours report --from YYYY-MM-DD --to YYYY-MM-DD [--project <name>] [--session-hours H] [--json]"),process.exit(1));const n=e.from,t=e.to;(!n||!t)&&(u("--from and --to are required (YYYY-MM-DD)"),process.exit(1));const{HourLedger:r}=await import("./hours.js"),i=_();try{const o=e.project,f=o?P(i,o):null;if(o&&!f){u(`Project not found: ${o}`),process.exitCode=1;return}const c=new r(i.getDatabase()).report({range_start:n,range_end:t,project_id:f?.id,default_session_hours:Q(e["session-hours"],1)});if(e.json===!0){console.log(JSON.stringify(c,null,2));return}if(S(`Hours ${c.range.start} \u2192 ${c.range.end}`),c.by_project.length===0){console.log(d.dim(" No sessions in range."));return}console.log(T(["Project","Sessions","Hours"],c.by_project.map(m=>[m.project_name,String(m.session_count),m.hours.toFixed(2)]))),console.log(`
71
+ Total: ${c.total_hours.toFixed(2)}h across ${c.entries.length} session(s)`+(c.estimated_sessions>0?d.dim(` (${c.estimated_sessions} estimated)`):""))}finally{i.close()}}async function nt(l){const{positional:a,flags:e}=R(l),s=a[0]??"generate",n=e.client,t=Q(e.rate,NaN),r=e.from,i=e.to;(s!=="generate"||!n||!Number.isFinite(t)||!r||!i)&&(u('Usage: wyrm invoice generate --client <name> --rate <usd/hour> --from YYYY-MM-DD --to YYYY-MM-DD [--project <name>] [--number INV-X] [--currency USD] [--notes "\u2026"] [--out <path>]'),process.exit(1));const{HourLedger:o}=await import("./hours.js"),f=_();try{const c=e.project,m=c?P(f,c):null;if(c&&!m){u(`Project not found: ${c}`),process.exitCode=1;return}const y=new o(f.getDatabase()).invoice({client_name:n,hourly_rate_usd:t,range_start:r,range_end:i,project_id:m?.id,invoice_number:e.number,currency:e.currency,notes:e.notes,business_name:e["business-name"],business_address:e["business-address"],business_contact:e["business-contact"],client_address:e["client-address"],default_session_hours:Q(e["session-hours"],1)}),p=e.out;if(p){const{writeFileSync:h}=await import("node:fs");h(p,y+`
72
+ `,"utf-8"),v(`Invoice written to ${p}`)}else process.stdout.write(y+`
73
+ `)}finally{f.close()}}async function it(l){const{positional:a,flags:e}=R(l),s=a[0]??"status",{AgentDaemon:n}=await import("./agent-daemon.js"),t=_();try{const r=new n(t.getDatabase());switch(s){case"init":case"start":{const i=Math.max(10,Math.min(D(e.interval,600),86400)),o=r.start({interval_seconds:i,max_steps:e["max-steps"]!==void 0&&D(e["max-steps"],0)||void 0,project_path:e.project,verbose:e.verbose===!0});if(!o.ok){u(`Agent init failed: ${o.error}`),process.exitCode=1;return}const f=o.status;v(`Agent ${f.pid!=null&&f.pid!==o.pid?"already running":"started"} \u2014 pid ${f.pid}`),console.log(` Interval: ${i}s \xB7 Active goals: ${f.active_goals} \xB7 Total iterations: ${f.total_iterations}`),console.log(d.dim(` Log: ${f.log_file}`));return}case"status":{const i=r.status();S("Wyrm agent"),console.log(i.running?` RUNNING \u2014 pid ${i.pid}${i.started_at?` (since ${i.started_at})`:""}`:" NOT RUNNING. Start it with: wyrm agent init"),console.log(` Active goals: ${i.active_goals} \xB7 Total iterations: ${i.total_iterations}`),i.last_action&&console.log(` Last action (${i.last_action.ran_at}): ${i.last_action.summary} [${i.last_action.result_status??"?"}]`),e.log===!0&&(console.log(`
74
+ === Recent log ===`),console.log(r.recentLog(40)));return}case"stop":{const i=await r.stop({grace_ms:e.grace!==void 0?D(e.grace,3e3):void 0});if(!i.ok){u(`Stop failed: ${i.error}`),process.exitCode=1;return}v(i.was_running?`Agent stopped (was pid ${i.pid}). Goals remain in DB \u2014 wyrm agent init resumes.`:"Agent was not running. (No-op.)");return}case"restart":{const i=await r.restart({interval_seconds:e.interval!==void 0?D(e.interval,600):void 0,max_steps:e["max-steps"]!==void 0&&D(e["max-steps"],0)||void 0,project_path:e.project,verbose:e.verbose===!0});if(!i.ok){u(`Restart failed: ${i.error}`),process.exitCode=1;return}v(`Agent restarted \u2014 pid ${i.status.pid}. Active goals: ${i.status.active_goals}.`);return}default:u("Usage: wyrm agent <init|status|stop|restart> [--interval N] [--max-steps N] [--project <path>] [--verbose] [--log]"),process.exitCode=1}}finally{t.close()}}async function at(l){if(l.includes("--encrypt")){const{initializeLicense:s,hasFeature:n}=await import("./license.js");if(s(),!n("encryption")){u("Encryption setup requires a Pro license or higher. See: wyrm license"),process.exitCode=1;return}const{getCrypto:t,initializeCrypto:r}=await import("./crypto.js"),i=l.includes("--enable"),o=l.includes("--test");if(i){const c=process.env.WYRM_ENCRYPTION_KEY??(process.stdin.isTTY?"":fe());if(!c||c.length<8){u('Password must be at least 8 characters. Set WYRM_ENCRYPTION_KEY or pipe it: printf %s "$PW" | wyrm setup --encrypt --enable'),process.exitCode=1;return}r(c),v("Encryption enabled (AES-256-GCM, key derived via PBKDF2). Store your password safely \u2014 it cannot be recovered.");return}const f=t();if(o){if(!f.isEnabled()){u("Encryption not enabled. Run: wyrm setup --encrypt --enable"),process.exitCode=1;return}const c="Wyrm encryption test "+Date.now();f.decrypt(f.encrypt(c))===c?v("Encryption test PASSED (encrypt \u2192 decrypt roundtrip)."):(u("Encryption test FAILED."),process.exitCode=1);return}S("Encryption status"),console.log(` Enabled: ${f.isEnabled()?"yes \u2014 new data is encrypted at rest":"no"}`),console.log(" Algorithm: AES-256-GCM"),f.isEnabled()||console.log(d.dim(" Enable with: wyrm setup --encrypt --enable (password via WYRM_ENCRYPTION_KEY or stdin)"));return}const a=ce(le(import.meta.url)),e=G(process.execPath,[B(a,"setup.js"),...l],{stdio:"inherit",shell:!1});process.exitCode=e.status??0}function ye(){console.log(`
75
+ ${z.brightMagenta}\u{F115D} Wyrm CLI v${L().version??"unknown"}${z.reset}
76
+ ${z.dim}Persistent AI Memory System${z.reset}
77
77
 
78
78
  ${d.bold("Usage:")} wyrm <command> [options]
79
79
 
@@ -94,6 +94,7 @@ ${d.bold("Commands:")}
94
94
  ${d.cyan("prune")} [--project <name>] Prune stale low-confidence artifacts (dry-run by default)
95
95
  ${d.cyan("cloud")} <login|sync|export|\u2026> Cloud backup & multi-device sync (Pro license)
96
96
  ${d.cyan("grove")} <status|policy> Per-project sync lanes (private|cloud|team) + leak audit
97
+ ${d.cyan("run")} <start|join|end|status> Fleet-run lifecycle (records the migration-20 runs/run_agents tables)
97
98
  ${d.cyan("skill")} <backfill-content|export|share> Portable skills: store SKILL.md content + sync/export
98
99
  ${d.cyan("serve")} [--ui] Start the Wyrm HTTP server (optionally open the dashboard)
99
100
  ${d.cyan("setup")} [--check|--encrypt|\u2026] Auto-configure AI clients (wyrm-setup); --encrypt for at-rest crypto
@@ -137,11 +138,13 @@ ${d.bold("Examples:")}
137
138
  wyrm sync export --out ~/wyrm-backup.wyrm
138
139
  wyrm sync preview --from ~/wyrm-backup.wyrm
139
140
  wyrm prune --project MyApp --min-confidence 0.2 --older-than 30
140
- `)}const[,,I,...k]=process.argv;function ye(l){const i=l.ref_table?`${l.ref_table}${l.ref_id?"#"+l.ref_id:""}`:"",e=new Date(l.created_at).toLocaleTimeString();return`#${l.cursor} ${l.kind.padEnd(15)} ${i.padEnd(16)} ${Te(l.actor)} ${e}`}async function ot(l){const{positional:i,flags:e}=x(l),s=i[0]||"since",r=E();try{if(!r.liveMemoryEnabled()){p("Live Memory is disabled (set WYRM_LIVE_MEMORY=1)."),process.exitCode=1;return}const t=(typeof e.project=="string"?e.project:"")||process.cwd(),n=r.getProject(t)??r.getProjectByName(t);if(!n){p(`Project not found: ${t}`),process.exitCode=1;return}if(s==="publish"){const a=i[1]||(typeof e.kind=="string"?e.kind:"");if(!a){p("usage: wyrm events publish <kind> --project <p> [--actor A] [--ref-table T --ref-id ID]"),process.exitCode=1;return}r.publishEvent({projectId:n.id,kind:a,refTable:typeof e["ref-table"]=="string"?e["ref-table"]:void 0,refId:typeof e["ref-id"]=="string"?e["ref-id"]:void 0,actor:typeof e.actor=="string"?e.actor:void 0}),v(`Event published (${a}) to ${n.name}`);return}if(s==="since"){const a=R(e.cursor,0),o=R(e.limit,50),u=r.eventsSince(n.id,a,o);for(const c of u)console.log(ye(c));j(`${u.length} event(s) for '${n.name}' since cursor ${a}`);return}p("usage: wyrm events <publish|since> ..."),process.exitCode=1}finally{r.close()}}async function st(l){const{flags:i}=x(l),e=E();if(!e.liveMemoryEnabled()){p("Live Memory is disabled (set WYRM_LIVE_MEMORY=1)."),e.close(),process.exitCode=1;return}const s=(typeof i.project=="string"?i.project:"")||process.cwd(),r=e.getProject(s)??P(e,s);if(!r){p(`Project not found: ${s}`),e.close(),process.exitCode=1;return}const t=Math.max(250,R(i.interval,1e3));let n=R(i.since,e.subscribeEvents(r.id,1).cursor);j(`Watching '${r.name}' (cursor ${n}, every ${t}ms) \u2014 Ctrl-C to stop`);const a=()=>{try{for(const c of e.eventsSince(r.id,n,200))n=c.cursor,console.log(ye(c))}catch{}};a();const o=setInterval(a,t),u=()=>{clearInterval(o);try{e.close()}catch{}process.exit(0)};process.on("SIGINT",u),process.on("SIGTERM",u)}async function rt(l){const{flags:i}=x(l),{embedAll:e,removeAll:s,statusAll:r}=await import("./priority-embed.js"),t={projectDir:typeof i.project=="string"?i.project:void 0,allClients:i.all===!0},n=a=>console.log(` ${String(a.result??a.status).padEnd(9)} [${a.scope}] ${a.file}`);if(i.status){j("Wyrm priority embedding \u2014 status"),r(t).forEach(n);return}if(i.remove){j("Wyrm priority embedding \u2014 removed"),s(t).forEach(n);return}j("Wyrm is now FIRST-PRIORITY memory"),e(t).forEach(n);try{const{installClaudeCodeHooks:a,installClaudeStatusline:o}=await import("./autoconfig.js");a()?v("Proactive hooks installed (SessionStart rehydrate + capture + tool-trace)."):console.log(" (Claude Code not detected \u2014 skipped hook install.)");const c=o();c&&v(`Buddy statusline: ${c.message}`)}catch{}v("Wyrm will now be read first, primed proactively, and shown in the TUI at all times.")}async function nt(l){const{flags:i}=x(l),{harvestProjects:e}=await import("./harvest.js"),{MemoryArtifacts:s}=await import("./memory-artifacts.js"),{escapeLikePattern:r}=await import("./auto-capture.js"),t=E();try{const n=t.getDatabase(),a=new s(n),o={existsBySig:(h,b)=>!!n.prepare("SELECT 1 FROM memory_artifacts WHERE project_id = ? AND tags LIKE ? ESCAPE '\\' LIMIT 1").get(h,"%"+r(b)+"%"),addCandidate:(h,b)=>a.add(h,{kind:b.kind,problem:b.text,tags:[...b.tags,b.sig],confidence:b.confidence,needsReview:1,createdBy:"harvest"})},u=typeof i.project=="string"?i.project:void 0;let c;if(u){const h=t.getProject(u)??t.getProjectByName(u);if(!h){p(`Project not found: ${u}`),process.exitCode=1;return}c=[{id:h.id,name:h.name,path:h.path}]}else c=t.getAllProjects(500).map(h=>({id:h.id,name:h.name,path:h.path}));const m=i["dry-run"]===!0||i.dry===!0,y=i.code===!0||i["include-code"]===!0,{reports:f,totalAdded:g,totalSkipped:w}=e(o,c,{dryRun:m,gitLimit:R(i.limit,30),includeCode:y});j(`Harvest ${m?"(dry run) ":""}\u2014 ${g} candidate(s), ${w} already present (${c.length} project(s))`);for(const h of f.filter(b=>b.added>0).sort((b,_)=>_.added-b.added).slice(0,25))console.log(` +${String(h.added).padStart(3)} (skip ${h.skipped}) ${h.project}`);if(y&&!m){const{SymbolGraph:h}=await import("./symbols.js"),b=new h(n);let _=0,S=0;for(const $ of c)try{const T=b.indexProject($.id,$.path);_+=T.symbols,S+=T.files}catch{}console.log(` \u{1F4D0} Indexed ${_} code symbols (${S} files) \u2192 searchable via 'wyrm search'`)}!m&&g>0&&v("Review with: wyrm review")}finally{t.close()}}async function it(l){const i=await import("./vault.js"),[e,...s]=l;try{switch(e){case"set":{const r=s[0];if(!r){p("usage: wyrm vault set <name> (the secret is read from STDIN, never argv)"),process.exitCode=1;return}if(process.stdin.isTTY){p(`pipe the secret in, e.g.: printf %s "$TOKEN" | wyrm vault set ${r}`),process.exitCode=1;return}const n=(await import("node:fs")).readFileSync(0,"utf8").replace(/\r?\n$/,"");if(!n){p("empty secret on stdin"),process.exitCode=1;return}i.vaultSet(r,n),v(`Stored "${r}" (AES-256-GCM). Use it without exposing it: wyrm vault exec ${r} -- <command>`);break}case"get":{const r=s[0];if(!r){p("usage: wyrm vault get <name>"),process.exitCode=1;return}const t=i.vaultGet(r);if(t===void 0){p(`no secret named "${r}"`),process.exitCode=1;return}process.stdout.write(t);break}case"list":case"ls":{const r=i.vaultList();if(!r.length){console.log("(vault is empty)");break}j(`Vault \u2014 ${r.length} secret(s)`);for(const t of r)console.log(" \u2022 "+t);break}case"rm":case"remove":case"delete":{const r=s[0];if(!r){p("usage: wyrm vault rm <name>"),process.exitCode=1;return}v(i.vaultRemove(r)?`Removed "${r}"`:`(no secret named "${r}")`);break}case"exec":{const r=s[0],t=s.indexOf("--");if(!r||t===-1||t+1>=s.length){p("usage: wyrm vault exec <name> [--as ENVVAR] -- <command...>"),process.exitCode=1;return}const n=s.slice(1,t),a=n.indexOf("--as"),o=a>=0?n[a+1]:r.toUpperCase().replace(/[^A-Z0-9]+/g,"_"),u=s.slice(t+1),c=i.vaultGet(r);if(c===void 0){p(`no secret named "${r}"`),process.exitCode=1;return}const m=B(u[0],u.slice(1),{stdio:"inherit",env:{...process.env,[o]:c}});process.exitCode=m.status??1;break}case"import-npm":{const r=await import("node:fs"),t=await import("node:os"),a=(await import("node:path")).join(t.homedir(),".npmrc");if(!r.existsSync(a)){p("~/.npmrc not found"),process.exitCode=1;return}const o=r.readFileSync(a,"utf8").match(/\/\/registry\.npmjs\.org\/:_authToken=(.+)/);if(!o){p("no npm authToken found in ~/.npmrc"),process.exitCode=1;return}i.vaultSet("npm-token",o[1].trim()),v('Imported npm token \u2192 vault as "npm-token". You can now scrub the plaintext from ~/.npmrc and use: wyrm vault exec npm-token --as NODE_AUTH_TOKEN -- npm publish');break}case"setup":{const r=i.vaultPaths();if(r.secure)v(`Vault is already secure (backend: ${r.backend}, ${r.count} secret(s)).`);else{const t=r.keychainAvailable?"keychain":"passphrase";if(t==="passphrase"&&!process.env.WYRM_VAULT_PASSPHRASE){p("No OS keychain on this host. Set WYRM_VAULT_PASSPHRASE, then re-run: wyrm vault setup"),process.exitCode=1;return}const n=i.vaultSecure({backend:t});v(`Vault secured: ${n.from} \u2192 ${n.to}${n.rotated?" (key rotated)":""}.`),n.keyfileShredded&&console.log(" plaintext vault.key: shredded \u2714"),n.backup&&console.log(` ciphertext backup: ${n.backup} (delete once confirmed)`)}j("Store & use credentials safely"),console.log(' store: printf %s "$TOKEN" | wyrm vault set <name> # reads STDIN \u2014 never argv/shell history'),console.log(" use: wyrm vault exec <name> --as ENV_VAR -- <cmd> # injected as env var, never printed"),console.log(" list: wyrm vault list inspect: wyrm vault info");break}case"secure":{const r=s.indexOf("--backend"),t=r>=0?s[r+1]:"keychain";if(t!=="keychain"&&t!=="passphrase"){p("usage: wyrm vault secure [--backend keychain|passphrase] [--no-rotate]"),process.exitCode=1;return}if(t==="passphrase"&&!process.env.WYRM_VAULT_PASSPHRASE){p("set WYRM_VAULT_PASSPHRASE before: wyrm vault secure --backend passphrase"),process.exitCode=1;return}const n=!s.includes("--no-rotate"),a=i.vaultSecure({backend:t,rotate:n});v(`Vault secured: ${a.from} \u2192 ${a.to}${a.rotated?" (key rotated)":""}.`),console.log(` secrets re-encrypted: ${a.secrets}`),a.keyfileShredded&&console.log(" plaintext vault.key: shredded \u2714"),a.backup&&console.log(` ciphertext backup: ${a.backup} (delete once you've confirmed)`),console.log(t==="keychain"?" master key now lives in the OS keychain \u2014 not on disk.":" master key now derived from WYRM_VAULT_PASSPHRASE \u2014 keep that set for future use.");break}case"info":{const r=i.vaultPaths();j("Vault"),console.log(` backend: ${r.backend}`),console.log(` secrets: ${r.count}`),console.log(` store: ${r.vault} (0600)`),console.log(` key: ${r.backend==="keyfile"?r.key+" (0600)":r.backend==="keychain"?"(OS keychain \u2014 no key on disk)":"(derived from WYRM_VAULT_PASSPHRASE \u2014 no key on disk)"}`),console.log(` secure: ${r.secure?"yes \u2014 key is not a plaintext file beside the ciphertext":"NO \u2014 key sits beside ciphertext"}`),r.secure||console.log(r.keychainAvailable?" \u26A0 run `wyrm vault secure` to move the key into the OS keychain.":" \u26A0 no OS keychain found \u2014 set WYRM_VAULT_PASSPHRASE and run `wyrm vault secure --backend passphrase`.");break}default:p("usage: wyrm vault <setup|set|get|list|rm|exec|import-npm|secure|info>"),process.exitCode=1}}catch(r){p(`vault: ${r.message}`),process.exitCode=1}}async function at(l){const i=l[0];if(!i||i==="help"||i==="--help"){console.log(`Usage:
141
+ `)}const[,,M,...k]=process.argv;function ge(l){const a=l.ref_table?`${l.ref_table}${l.ref_id?"#"+l.ref_id:""}`:"",e=new Date(l.created_at).toLocaleTimeString();return`#${l.cursor} ${l.kind.padEnd(15)} ${a.padEnd(16)} ${Ae(l.actor)} ${e}`}async function ct(l){const{positional:a,flags:e}=R(l),s=a[0]||"since",n=_();try{if(!n.liveMemoryEnabled()){u("Live Memory is disabled (set WYRM_LIVE_MEMORY=1)."),process.exitCode=1;return}const t=(typeof e.project=="string"?e.project:"")||process.cwd(),r=n.getProject(t)??n.getProjectByName(t);if(!r){u(`Project not found: ${t}`),process.exitCode=1;return}if(s==="publish"){const i=a[1]||(typeof e.kind=="string"?e.kind:"");if(!i){u("usage: wyrm events publish <kind> --project <p> [--actor A] [--ref-table T --ref-id ID]"),process.exitCode=1;return}n.publishEvent({projectId:r.id,kind:i,refTable:typeof e["ref-table"]=="string"?e["ref-table"]:void 0,refId:typeof e["ref-id"]=="string"?e["ref-id"]:void 0,actor:typeof e.actor=="string"?e.actor:void 0}),v(`Event published (${i}) to ${r.name}`);return}if(s==="since"){const i=D(e.cursor,0),o=D(e.limit,50),f=n.eventsSince(r.id,i,o);for(const c of f)console.log(ge(c));S(`${f.length} event(s) for '${r.name}' since cursor ${i}`);return}u("usage: wyrm events <publish|since> ..."),process.exitCode=1}finally{n.close()}}async function lt(l){const{flags:a}=R(l),e=_();if(!e.liveMemoryEnabled()){u("Live Memory is disabled (set WYRM_LIVE_MEMORY=1)."),e.close(),process.exitCode=1;return}const s=(typeof a.project=="string"?a.project:"")||process.cwd(),n=e.getProject(s)??P(e,s);if(!n){u(`Project not found: ${s}`),e.close(),process.exitCode=1;return}const t=Math.max(250,D(a.interval,1e3));let r=D(a.since,e.subscribeEvents(n.id,1).cursor);S(`Watching '${n.name}' (cursor ${r}, every ${t}ms) \u2014 Ctrl-C to stop`);const i=()=>{try{for(const c of e.eventsSince(n.id,r,200))r=c.cursor,console.log(ge(c))}catch{}};i();const o=setInterval(i,t),f=()=>{clearInterval(o);try{e.close()}catch{}process.exit(0)};process.on("SIGINT",f),process.on("SIGTERM",f)}async function dt(l){const{flags:a}=R(l),{embedAll:e,removeAll:s,statusAll:n}=await import("./priority-embed.js"),t={projectDir:typeof a.project=="string"?a.project:void 0,allClients:a.all===!0},r=i=>console.log(` ${String(i.result??i.status).padEnd(9)} [${i.scope}] ${i.file}`);if(a.status){S("Wyrm priority embedding \u2014 status"),n(t).forEach(r);return}if(a.remove){S("Wyrm priority embedding \u2014 removed"),s(t).forEach(r);return}S("Wyrm is now FIRST-PRIORITY memory"),e(t).forEach(r);try{const{installClaudeCodeHooks:i,installClaudeStatusline:o}=await import("./autoconfig.js");i()?v("Proactive hooks installed (SessionStart rehydrate + capture + tool-trace)."):console.log(" (Claude Code not detected \u2014 skipped hook install.)");const c=o();c&&v(`Buddy statusline: ${c.message}`)}catch{}v("Wyrm will now be read first, primed proactively, and shown in the TUI at all times.")}async function pt(l){const{flags:a}=R(l),{harvestProjects:e}=await import("./harvest.js"),{MemoryArtifacts:s}=await import("./memory-artifacts.js"),{escapeLikePattern:n}=await import("./auto-capture.js"),t=_();try{const r=t.getDatabase(),i=new s(r),o={existsBySig:(w,$)=>!!r.prepare("SELECT 1 FROM memory_artifacts WHERE project_id = ? AND tags LIKE ? ESCAPE '\\' LIMIT 1").get(w,"%"+n($)+"%"),addCandidate:(w,$)=>i.add(w,{kind:$.kind,problem:$.text,tags:[...$.tags,$.sig],confidence:$.confidence,needsReview:1,createdBy:"harvest"})},f=typeof a.project=="string"?a.project:void 0;let c;if(f){const w=t.getProject(f)??t.getProjectByName(f);if(!w){u(`Project not found: ${f}`),process.exitCode=1;return}c=[{id:w.id,name:w.name,path:w.path}]}else c=t.getAllProjects(500).map(w=>({id:w.id,name:w.name,path:w.path}));const m=a["dry-run"]===!0||a.dry===!0,y=a.code===!0||a["include-code"]===!0,{reports:p,totalAdded:h,totalSkipped:g}=e(o,c,{dryRun:m,gitLimit:D(a.limit,30),includeCode:y});S(`Harvest ${m?"(dry run) ":""}\u2014 ${h} candidate(s), ${g} already present (${c.length} project(s))`);for(const w of p.filter($=>$.added>0).sort(($,E)=>E.added-$.added).slice(0,25))console.log(` +${String(w.added).padStart(3)} (skip ${w.skipped}) ${w.project}`);if(y&&!m){const{SymbolGraph:w}=await import("./symbols.js"),$=new w(r);let E=0,b=0;for(const x of c)try{const j=$.indexProject(x.id,x.path);E+=j.symbols,b+=j.files}catch{}console.log(` \u{1F4D0} Indexed ${E} code symbols (${b} files) \u2192 searchable via 'wyrm search'`)}!m&&h>0&&v("Review with: wyrm review")}finally{t.close()}}async function ut(l){const a=await import("./vault.js"),[e,...s]=l;try{switch(e){case"set":{const n=s[0];if(!n){u("usage: wyrm vault set <name> (the secret is read from STDIN, never argv)"),process.exitCode=1;return}if(process.stdin.isTTY){u(`pipe the secret in, e.g.: printf %s "$TOKEN" | wyrm vault set ${n}`),process.exitCode=1;return}const r=(await import("node:fs")).readFileSync(0,"utf8").replace(/\r?\n$/,"");if(!r){u("empty secret on stdin"),process.exitCode=1;return}a.vaultSet(n,r),v(`Stored "${n}" (AES-256-GCM). Use it without exposing it: wyrm vault exec ${n} -- <command>`);break}case"get":{const n=s[0];if(!n){u("usage: wyrm vault get <name>"),process.exitCode=1;return}const t=a.vaultGet(n);if(t===void 0){u(`no secret named "${n}"`),process.exitCode=1;return}process.stdout.write(t);break}case"list":case"ls":{const n=a.vaultList();if(!n.length){console.log("(vault is empty)");break}S(`Vault \u2014 ${n.length} secret(s)`);for(const t of n)console.log(" \u2022 "+t);break}case"rm":case"remove":case"delete":{const n=s[0];if(!n){u("usage: wyrm vault rm <name>"),process.exitCode=1;return}v(a.vaultRemove(n)?`Removed "${n}"`:`(no secret named "${n}")`);break}case"exec":{const n=s[0],t=s.indexOf("--");if(!n||t===-1||t+1>=s.length){u("usage: wyrm vault exec <name> [--as ENVVAR] -- <command...>"),process.exitCode=1;return}const r=s.slice(1,t),i=r.indexOf("--as"),o=i>=0?r[i+1]:n.toUpperCase().replace(/[^A-Z0-9]+/g,"_"),f=s.slice(t+1),c=a.vaultGet(n);if(c===void 0){u(`no secret named "${n}"`),process.exitCode=1;return}const m=G(f[0],f.slice(1),{stdio:"inherit",env:{...process.env,[o]:c}});process.exitCode=m.status??1;break}case"import-npm":{const n=await import("node:fs"),t=await import("node:os"),i=(await import("node:path")).join(t.homedir(),".npmrc");if(!n.existsSync(i)){u("~/.npmrc not found"),process.exitCode=1;return}const o=n.readFileSync(i,"utf8").match(/\/\/registry\.npmjs\.org\/:_authToken=(.+)/);if(!o){u("no npm authToken found in ~/.npmrc"),process.exitCode=1;return}a.vaultSet("npm-token",o[1].trim()),v('Imported npm token \u2192 vault as "npm-token". You can now scrub the plaintext from ~/.npmrc and use: wyrm vault exec npm-token --as NODE_AUTH_TOKEN -- npm publish');break}case"setup":{const n=a.vaultPaths();if(n.secure)v(`Vault is already secure (backend: ${n.backend}, ${n.count} secret(s)).`);else{const t=n.keychainAvailable?"keychain":"passphrase";if(t==="passphrase"&&!process.env.WYRM_VAULT_PASSPHRASE){u("No OS keychain on this host. Set WYRM_VAULT_PASSPHRASE, then re-run: wyrm vault setup"),process.exitCode=1;return}const r=a.vaultSecure({backend:t});v(`Vault secured: ${r.from} \u2192 ${r.to}${r.rotated?" (key rotated)":""}.`),r.keyfileShredded&&console.log(" plaintext vault.key: shredded \u2714"),r.backup&&console.log(` ciphertext backup: ${r.backup} (delete once confirmed)`)}S("Store & use credentials safely"),console.log(' store: printf %s "$TOKEN" | wyrm vault set <name> # reads STDIN \u2014 never argv/shell history'),console.log(" use: wyrm vault exec <name> --as ENV_VAR -- <cmd> # injected as env var, never printed"),console.log(" list: wyrm vault list inspect: wyrm vault info");break}case"secure":{const n=s.indexOf("--backend"),t=n>=0?s[n+1]:"keychain";if(t!=="keychain"&&t!=="passphrase"){u("usage: wyrm vault secure [--backend keychain|passphrase] [--no-rotate]"),process.exitCode=1;return}if(t==="passphrase"&&!process.env.WYRM_VAULT_PASSPHRASE){u("set WYRM_VAULT_PASSPHRASE before: wyrm vault secure --backend passphrase"),process.exitCode=1;return}const r=!s.includes("--no-rotate"),i=a.vaultSecure({backend:t,rotate:r});v(`Vault secured: ${i.from} \u2192 ${i.to}${i.rotated?" (key rotated)":""}.`),console.log(` secrets re-encrypted: ${i.secrets}`),i.keyfileShredded&&console.log(" plaintext vault.key: shredded \u2714"),i.backup&&console.log(` ciphertext backup: ${i.backup} (delete once you've confirmed)`),console.log(t==="keychain"?" master key now lives in the OS keychain \u2014 not on disk.":" master key now derived from WYRM_VAULT_PASSPHRASE \u2014 keep that set for future use.");break}case"info":{const n=a.vaultPaths();S("Vault"),console.log(` backend: ${n.backend}`),console.log(` secrets: ${n.count}`),console.log(` store: ${n.vault} (0600)`),console.log(` key: ${n.backend==="keyfile"?n.key+" (0600)":n.backend==="keychain"?"(OS keychain \u2014 no key on disk)":"(derived from WYRM_VAULT_PASSPHRASE \u2014 no key on disk)"}`),console.log(` secure: ${n.secure?"yes \u2014 key is not a plaintext file beside the ciphertext":"NO \u2014 key sits beside ciphertext"}`),n.secure||console.log(n.keychainAvailable?" \u26A0 run `wyrm vault secure` to move the key into the OS keychain.":" \u26A0 no OS keychain found \u2014 set WYRM_VAULT_PASSPHRASE and run `wyrm vault secure --backend passphrase`.");break}default:u("usage: wyrm vault <setup|set|get|list|rm|exec|import-npm|secure|info>"),process.exitCode=1}}catch(n){u(`vault: ${n.message}`),process.exitCode=1}}async function mt(l){const a=l[0];if(!a||a==="help"||a==="--help"){console.log(`Usage:
141
142
  wyrm skill backfill-content read every skill's SKILL.md into the registry (idempotent)
142
143
  wyrm skill export <targetDir> [--all] materialize SKILL.md files from stored content
143
- wyrm skill share <name|--all|--tier T> [--public|--private] [--include-inactive] set cloud-sync visibility (single or bulk)`);return}const e=E();try{if(i==="backfill-content"||i==="backfill"){j("Backfill SKILL.md content into the registry");const s=e.backfillSkillContent();v(`${s.filled} filled . ${s.unchanged} unchanged . ${s.missing} missing-file (of ${s.total} registered)`),s.missing>0&&console.log(d.yellow(` ${s.missing} skill(s) had no readable SKILL.md \u2014 re-run after restoring their files.`));return}if(i==="export"){const s=l[1];s||(p("Usage: wyrm skill export <targetDir> [--all]"),e.close(),process.exit(1));const r=l.includes("--all");j(`Export skills \u2192 ${s}`);const t=e.exportSkillContent(s,{includeInactive:r});v(`${t.written} SKILL.md written . ${t.skipped_no_content} skipped (no stored content) (of ${t.total} ${r?"total":"active"})`),t.skipped_no_content>0&&console.log(d.yellow(" Run `wyrm skill backfill-content` on the source machine to populate content first.")),t.collisions>0&&console.log(d.yellow(` ${t.collisions} slug collision(s) disambiguated with a name-hash suffix (distinct skills, same slug) \u2014 no content lost.`));return}if(i==="share"){const s=l.includes("--private")?"within":l.includes("--public")?"public":"org",r=l.includes("--include-inactive"),t=l.indexOf("--tier"),n=t>=0?l[t+1]:void 0;if(t>=0&&(!n||n.startsWith("--"))&&(p("Usage: wyrm skill share --tier <god|mega|atomic> [--public|--private] [--include-inactive]"),e.close(),process.exit(1)),l.includes("--all")||!!n){const c=e.setAllSkillsVisibility(s,{tier:n,includeInactive:r}),m=n?`tier '${n}'`:r?"all skills":"all active skills";v(`${c} skill(s) (${m}) visibility \u2192 '${s}'.`),console.log(s==="within"?" These skills will NOT egress on cloud sync (private).":` ${c} skills are now cloud-sync-eligible (they leave on the next \`wyrm cloud sync\`).`);return}const o=l[1];o||(p("Usage: wyrm skill share <name|--all|--tier <tier>> [--public|--private] [--include-inactive]"),e.close(),process.exit(1)),e.setSkillVisibility(o,s)||(p(`Skill not found: ${o}`),e.close(),process.exit(1)),v(`Skill "${o}" visibility \u2192 '${s}'.`),console.log(s==="within"?" This skill will NOT egress on cloud sync (private).":" This skill is now cloud-sync-eligible (it leaves on the next `wyrm cloud sync`).");return}p(`Unknown skill subcommand: ${i}`),process.exit(1)}finally{e.close()}}async function ct(l){const i=l[0]??"status",e=E(),s=e.getDatabase();if(s.prepare("PRAGMA table_info(projects)").all().some(t=>t.name==="sync_policy")||(p("Grove sync policy is not on this database yet (upgrade Wyrm so migrations apply)."),e.close(),process.exit(1)),i==="status"||i==="ls"||i==="list"){j("Grove sync policy + leak audit");const t=s.prepare("SELECT id, name, sync_policy FROM projects ORDER BY id").all(),n=["ground_truths","memory_artifacts","quests","design_tokens","design_references"],a=["ground_truths","memory_artifacts","quests","sessions","decision_edges"],o=(m,y,f)=>{let g=0;for(const w of m)try{g+=s.prepare(`SELECT COUNT(*) AS n FROM ${w} WHERE project_id = ? AND ${f}`).get(y).n}catch{}return g},u=[];let c=0;for(const m of t){const y=o(n,m.id,"cross_project_visibility IN ('org','public')"),f=o(a,m.id,"is_shared = 1"),g=m.sync_policy==="private"&&y+f>0;g&&c++;const w=g?d.red(`LEAK: ${y} promoted + ${f} shared in a PRIVATE grove`):`${y} promoted / ${f} shared`;u.push([String(m.id),m.name,m.sync_policy,w])}console.log(M(["#","Grove","sync_policy","rows eligible to leave"],u)),console.log(`
144
+ wyrm skill share <name|--all|--tier T> [--public|--private] [--include-inactive] set cloud-sync visibility (single or bulk)`);return}const e=_();try{if(a==="backfill-content"||a==="backfill"){S("Backfill SKILL.md content into the registry");const s=e.backfillSkillContent();v(`${s.filled} filled . ${s.unchanged} unchanged . ${s.missing} missing-file (of ${s.total} registered)`),s.missing>0&&console.log(d.yellow(` ${s.missing} skill(s) had no readable SKILL.md \u2014 re-run after restoring their files.`));return}if(a==="export"){const s=l[1];s||(u("Usage: wyrm skill export <targetDir> [--all]"),e.close(),process.exit(1));const n=l.includes("--all");S(`Export skills \u2192 ${s}`);const t=e.exportSkillContent(s,{includeInactive:n});v(`${t.written} SKILL.md written . ${t.skipped_no_content} skipped (no stored content) (of ${t.total} ${n?"total":"active"})`),t.skipped_no_content>0&&console.log(d.yellow(" Run `wyrm skill backfill-content` on the source machine to populate content first.")),t.collisions>0&&console.log(d.yellow(` ${t.collisions} slug collision(s) disambiguated with a name-hash suffix (distinct skills, same slug) \u2014 no content lost.`));return}if(a==="share"){const s=l.includes("--private")?"within":l.includes("--public")?"public":"org",n=l.includes("--include-inactive"),t=l.indexOf("--tier"),r=t>=0?l[t+1]:void 0;if(t>=0&&(!r||r.startsWith("--"))&&(u("Usage: wyrm skill share --tier <god|mega|atomic> [--public|--private] [--include-inactive]"),e.close(),process.exit(1)),l.includes("--all")||!!r){const c=e.setAllSkillsVisibility(s,{tier:r,includeInactive:n}),m=r?`tier '${r}'`:n?"all skills":"all active skills";v(`${c} skill(s) (${m}) visibility \u2192 '${s}'.`),console.log(s==="within"?" These skills will NOT egress on cloud sync (private).":` ${c} skills are now cloud-sync-eligible (they leave on the next \`wyrm cloud sync\`).`);return}const o=l[1];o||(u("Usage: wyrm skill share <name|--all|--tier <tier>> [--public|--private] [--include-inactive]"),e.close(),process.exit(1)),e.setSkillVisibility(o,s)||(u(`Skill not found: ${o}`),e.close(),process.exit(1)),v(`Skill "${o}" visibility \u2192 '${s}'.`),console.log(s==="within"?" This skill will NOT egress on cloud sync (private).":" This skill is now cloud-sync-eligible (it leaves on the next `wyrm cloud sync`).");return}u(`Unknown skill subcommand: ${a}`),process.exit(1)}finally{e.close()}}async function ft(l){const a=l[0]??"status",e=_(),s=e.getDatabase();if(s.prepare("PRAGMA table_info(projects)").all().some(t=>t.name==="sync_policy")||(u("Grove sync policy is not on this database yet (upgrade Wyrm so migrations apply)."),e.close(),process.exit(1)),a==="status"||a==="ls"||a==="list"){S("Grove sync policy + leak audit");const t=s.prepare("SELECT id, name, sync_policy FROM projects ORDER BY id").all(),r=["ground_truths","memory_artifacts","quests","design_tokens","design_references"],i=["ground_truths","memory_artifacts","quests","sessions","decision_edges"],o=(m,y,p)=>{let h=0;for(const g of m)try{h+=s.prepare(`SELECT COUNT(*) AS n FROM ${g} WHERE project_id = ? AND ${p}`).get(y).n}catch{}return h},f=[];let c=0;for(const m of t){const y=o(r,m.id,"cross_project_visibility IN ('org','public')"),p=o(i,m.id,"is_shared = 1"),h=m.sync_policy==="private"&&y+p>0;h&&c++;const g=h?d.red(`LEAK: ${y} promoted + ${p} shared in a PRIVATE grove`):`${y} promoted / ${p} shared`;f.push([String(m.id),m.name,m.sync_policy,g])}console.log(T(["#","Grove","sync_policy","rows eligible to leave"],f)),console.log(`
144
145
  private = never replicates . cloud = your own cloud backup . team = federates to a team Wyrm`),c>0&&console.log(d.red(`
145
- ! ${c} private grove(s) hold rows marked to leave. Re-private those rows or change the grove lane.`)),e.close();return}if(i==="policy"||i==="set"){const t=l[1],n=l[2];(!t||!["private","cloud","team"].includes(n))&&(p("Usage: wyrm grove policy <project|id> <private|cloud|team>"),e.close(),process.exit(1));let a=P(e,t);!a&&/^\d+$/.test(t)&&(a=s.prepare("SELECT id, name FROM projects WHERE id = ?").get(Number(t))),a||(p(`Grove not found: ${t}`),e.close(),process.exit(1)),s.prepare("UPDATE projects SET sync_policy = ? WHERE id = ?").run(n,a.id),v(`Grove "${a.name}" set to '${n}'.`),n!=="private"&&console.log(` Rows still only leave when also marked ${n==="team"?"is_shared (team)":"org/public (cloud)"}. The grove is the outer gate.`),e.close();return}p(`Unknown grove subcommand: ${i}`),console.log(`Usage:
146
+ ! ${c} private grove(s) hold rows marked to leave. Re-private those rows or change the grove lane.`)),e.close();return}if(a==="policy"||a==="set"){const t=l[1],r=l[2];(!t||!["private","cloud","team"].includes(r))&&(u("Usage: wyrm grove policy <project|id> <private|cloud|team>"),e.close(),process.exit(1));let i=P(e,t);!i&&/^\d+$/.test(t)&&(i=s.prepare("SELECT id, name FROM projects WHERE id = ?").get(Number(t))),i||(u(`Grove not found: ${t}`),e.close(),process.exit(1)),s.prepare("UPDATE projects SET sync_policy = ? WHERE id = ?").run(r,i.id),v(`Grove "${i.name}" set to '${r}'.`),r!=="private"&&console.log(` Rows still only leave when also marked ${r==="team"?"is_shared (team)":"org/public (cloud)"}. The grove is the outer gate.`),e.close();return}u(`Unknown grove subcommand: ${a}`),console.log(`Usage:
146
147
  wyrm grove status
147
- wyrm grove policy <project|id> <private|cloud|team>`),e.close(),process.exit(1)}if(I==="--version"||I==="-v"||I==="version"){const l=O();console.log(`${l.name??"wyrm-mcp"} v${l.version??"unknown"}`)}else!I||I==="--help"||I==="-h"||I==="help"?fe():(async()=>{try{switch(I){case"search":await Me(k);break;case"ls":await Ie(k);break;case"show":await Ae(k);break;case"capture":await Ne(k);break;case"rehydrate":await Le(k);break;case"render":await Oe(k);break;case"reverse-bridge":await We(k);break;case"import":await Ye(k);break;case"stats":await qe(k);break;case"review":await Ue(k);break;case"sync":await Fe(k);break;case"cloud":{const{cmdCloud:l}=await import("./cloud/cli.js");await l(k);break}case"grove":await ct(k);break;case"skill":await at(k);break;case"prune":await He(k);break;case"license":await Ve();break;case"login":await Be();break;case"activate":await Ke(k);break;case"maintenance":await Ge(k);break;case"index":await Je(k);break;case"update":await ze(k);break;case"prompt":await Qe(k);break;case"hours":await Xe(k);break;case"invoice":await Ze(k);break;case"agent":await et(k);break;case"setup":await tt(k);break;case"intro":{const{renderIntro:l}=await import("./visibility.js");console.log(l(O().version??"unknown"));break}case"events":await ot(k);break;case"watch":await st(k);break;case"embed":await rt(k);break;case"harvest":await nt(k);break;case"vault":await it(k);break;case"statusline":{const{installClaudeStatusline:l,removeClaudeStatusline:i}=await import("./autoconfig.js"),e=k.includes("--remove")?i():l();e?v(e.message):p("Claude Code not detected (~/.claude missing).");break}case"ui":case"dashboard":k.includes("--ui")||k.push("--ui");case"serve":{const l=k.includes("--ui");if(l){const{enableDevMode:r}=await import("./http-auth.js");r()}const{server:i}=await import("./http-fast.js"),e=parseInt(process.env.WYRM_PORT??process.env.PORT??"3333",10),s=process.env.WYRM_BIND_HOST||"127.0.0.1";i.listen(e,s,()=>{if(v(`Wyrm HTTP server running on ${s}:${e}`),process.env.WYRM_UI_READONLY==="1"&&console.log("\u{1F512} READ-ONLY mode: writes + off-box egress are blocked; safe to expose."),l){const r=`http://localhost:${e}/ui`;console.log(`\u{1F5A5}\uFE0F Dashboard: ${r}`),import("child_process").then(({spawn:t})=>{const n=process.platform;try{const a=n==="darwin"?t("open",[r],{stdio:"ignore",detached:!0}):n==="win32"?t("cmd",["/c","start","",r],{stdio:"ignore",detached:!0}):t("xdg-open",[r],{stdio:"ignore",detached:!0});a.on("error",()=>{}),a.unref()}catch{}}).catch(()=>{})}});break}default:p(`Unknown command: ${I}`),fe(),process.exit(1)}}catch(l){p(String(l)),process.exit(1)}})();
148
+ wyrm grove policy <project|id> <private|cloud|team>`),e.close(),process.exit(1)}async function yt(l){const a=l[0],{flags:e}=R(l.slice(1)),{ulid:s}=await import("./ulid.js"),{createRun:n,setRunStatus:t,registerAgent:r,getRun:i,getAgents:o}=await import("./handlers/run.js"),{sanitizeActorId:f}=await import("./handlers/boundary.js"),c=p=>typeof p=="string"?f(p):null,m=_(),y=m.getDatabase();try{switch(a){case"start":{const p=(typeof e.orchestrator=="string"?e.orchestrator:"cli").slice(0,200),h=c(e.agent)??f(p)??"cli",g=c(e.parent);if(g&&!i(y,g)){u(`Parent run not found: ${g}`),process.exitCode=1;return}const w=s();n(y,w,g,p),r(y,w,h,"orchestrator"),process.stdout.write(w+`
149
+ `),process.stderr.write(`Run ${w} started by ${p}.
150
+ `);break}case"join":{const p=c(e.run),h=c(e.agent);if(!p||!h){u("Usage: wyrm run join --run <RUNID> --agent <ID> [--role <ROLE>]"),process.exitCode=1;return}if(!i(y,p)){u(`Run not found: ${p}`),process.exitCode=1;return}const g=typeof e.role=="string"?e.role.slice(0,200):null;r(y,p,h,g),v(`${h} joined run ${p}${g?` as ${g}`:""}.`);break}case"end":{const p=c(e.run);if(!p){u("Usage: wyrm run end --run <RUNID> [--status completed|failed|abandoned]"),process.exitCode=1;return}if(!i(y,p)){u(`Run not found: ${p}`),process.exitCode=1;return}const h=typeof e.status=="string"?e.status:"completed",w=["completed","failed","abandoned"].includes(h)?h:"completed";t(y,p,w),v(`Run ${p} ended (${w}).`);break}case"status":{const p=c(e.run);if(!p){u("Usage: wyrm run status --run <RUNID>"),process.exitCode=1;return}const h=i(y,p);if(!h){u(`Run not found: ${p}`),process.exitCode=1;return}const g=o(y,p);S(`Run ${h.run_id} [${h.status}]`),h.orchestrator&&console.log(d.bold("Orchestrator: ")+h.orchestrator),h.parent_run_id&&console.log(d.bold("Parent: ")+h.parent_run_id),console.log(d.bold("Created: ")+h.created_at),console.log(d.bold("Updated: ")+h.updated_at),g.length>0?console.log(T(["Agent","Role","Joined"],g.map(w=>[w.agent_id,w.role??"",w.joined_at]))):console.log(d.dim(" No agents registered."));break}default:u("Usage: wyrm run <start|join|end|status> [options]"),console.log(" wyrm run start [--orchestrator NAME] [--agent ID] [--parent RUNID]"),console.log(" wyrm run join --run RUNID --agent ID [--role ROLE]"),console.log(" wyrm run end --run RUNID [--status completed|failed|abandoned]"),console.log(" wyrm run status --run RUNID"),process.exitCode=1;return}}finally{m.close()}}if(M==="--version"||M==="-v"||M==="version"){const l=L();console.log(`${l.name??"wyrm-mcp"} v${l.version??"unknown"}`)}else!M||M==="--help"||M==="-h"||M==="help"?ye():(async()=>{try{switch(M){case"search":await Le(k);break;case"ls":await We(k);break;case"show":await Ye(k);break;case"capture":await Ue(k);break;case"rehydrate":await Fe(k);break;case"render":await qe(k);break;case"reverse-bridge":await He(k);break;case"import":await Ve(k);break;case"stats":await Ke(k);break;case"review":await Ge(k);break;case"sync":await Je(k);break;case"cloud":{const{cmdCloud:l}=await import("./cloud/cli.js");await l(k);break}case"grove":await ft(k);break;case"run":await yt(k);break;case"skill":await mt(k);break;case"prune":await ze(k);break;case"license":await Qe();break;case"login":await Xe();break;case"activate":await Ze(k);break;case"maintenance":await et(k);break;case"index":await tt(k);break;case"update":await ot(k);break;case"prompt":await st(k);break;case"hours":await rt(k);break;case"invoice":await nt(k);break;case"agent":await it(k);break;case"setup":await at(k);break;case"intro":{const{renderIntro:l}=await import("./visibility.js");console.log(l(L().version??"unknown"));break}case"events":await ct(k);break;case"watch":await lt(k);break;case"embed":await dt(k);break;case"harvest":await pt(k);break;case"vault":await ut(k);break;case"statusline":{const{installClaudeStatusline:l,removeClaudeStatusline:a}=await import("./autoconfig.js"),e=k.includes("--remove")?a():l();e?v(e.message):u("Claude Code not detected (~/.claude missing).");break}case"ui":case"dashboard":k.includes("--ui")||k.push("--ui");case"serve":{const l=k.includes("--ui");if(l){const{enableDevMode:n}=await import("./http-auth.js");n()}const{server:a}=await import("./http-fast.js"),e=parseInt(process.env.WYRM_PORT??process.env.PORT??"3333",10),s=process.env.WYRM_BIND_HOST||"127.0.0.1";a.listen(e,s,()=>{if(v(`Wyrm HTTP server running on ${s}:${e}`),process.env.WYRM_UI_READONLY==="1"&&console.log("\u{1F512} READ-ONLY mode: writes + off-box egress are blocked; safe to expose."),l){const n=`http://localhost:${e}/ui`;console.log(`\u{1F5A5}\uFE0F Dashboard: ${n}`),import("child_process").then(({spawn:t})=>{const r=process.platform;try{const i=r==="darwin"?t("open",[n],{stdio:"ignore",detached:!0}):r==="win32"?t("cmd",["/c","start","",n],{stdio:"ignore",detached:!0}):t("xdg-open",[n],{stdio:"ignore",detached:!0});i.on("error",()=>{}),i.unref()}catch{}}).catch(()=>{})}});break}default:u(`Unknown command: ${M}`),ye(),process.exit(1)}}catch(l){u(String(l)),process.exit(1)}})();