agentix-cli 0.22.0 → 0.25.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 (187) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +102 -103
  3. package/agentx.example.json +9 -3
  4. package/dist/App-6Y7B3YQH.js +8 -0
  5. package/dist/App-6Y7B3YQH.js.map +1 -0
  6. package/dist/ChatApp-KIEUJ2DS.js +7 -0
  7. package/dist/ChatApp-KIEUJ2DS.js.map +1 -0
  8. package/dist/{agent-Y4U4R3ZZ.js → agent-QGZIAZMY.js} +2 -2
  9. package/dist/architect-SEPWMWS4.js +2 -0
  10. package/dist/{board-dashboard-2CPBWFOB.js → board-dashboard-CQ5F4GPD.js} +3756 -1010
  11. package/dist/board-dashboard-CQ5F4GPD.js.map +1 -0
  12. package/dist/builtin-FRKAQMFS.js +2 -0
  13. package/dist/chunk-2WVX3LEC.js +2 -0
  14. package/dist/chunk-2WVX3LEC.js.map +1 -0
  15. package/dist/chunk-3WQFCIB6.js +24 -0
  16. package/dist/chunk-3WQFCIB6.js.map +1 -0
  17. package/dist/chunk-53GCFZPR.js +116 -0
  18. package/dist/chunk-53GCFZPR.js.map +1 -0
  19. package/dist/{chunk-IKBHHTIT.js → chunk-5U35WAZF.js} +2 -2
  20. package/dist/chunk-67Q4DCPS.js +54 -0
  21. package/dist/chunk-67Q4DCPS.js.map +1 -0
  22. package/dist/chunk-76Z4VHDX.js +8 -0
  23. package/dist/chunk-76Z4VHDX.js.map +1 -0
  24. package/dist/{chunk-7P5QRX5W.js → chunk-BFXONUUN.js} +2 -2
  25. package/dist/{chunk-P3TJHGO5.js → chunk-FDMWOPEL.js} +2 -2
  26. package/dist/chunk-FDWOECJY.js +54 -0
  27. package/dist/chunk-FDWOECJY.js.map +1 -0
  28. package/dist/chunk-G57RZCXM.js +5 -0
  29. package/dist/chunk-G57RZCXM.js.map +1 -0
  30. package/dist/{chunk-NOF4HSGK.js → chunk-H5D7ATXS.js} +2 -2
  31. package/dist/chunk-HA3XI7Z2.js +155 -0
  32. package/dist/chunk-HA3XI7Z2.js.map +1 -0
  33. package/dist/chunk-HR4JSRZS.js +3 -0
  34. package/dist/{chunk-A6BKSZQA.js.map → chunk-HR4JSRZS.js.map} +1 -1
  35. package/dist/chunk-I3SIXTJO.js +44 -0
  36. package/dist/chunk-I3SIXTJO.js.map +1 -0
  37. package/dist/chunk-ISEBLHWX.js +118 -0
  38. package/dist/chunk-ISEBLHWX.js.map +1 -0
  39. package/dist/{chunk-4ITQP3XL.js → chunk-JP5IEXR7.js} +2 -2
  40. package/dist/chunk-JXWAGMI2.js +13 -0
  41. package/dist/chunk-JXWAGMI2.js.map +1 -0
  42. package/dist/chunk-KUCCW2VG.js +431 -0
  43. package/dist/chunk-KUCCW2VG.js.map +1 -0
  44. package/dist/chunk-LDHPMJ7M.js +257 -0
  45. package/dist/chunk-LDHPMJ7M.js.map +1 -0
  46. package/dist/{chunk-WHP4I44J.js → chunk-LNXCJWOU.js} +29 -30
  47. package/dist/chunk-LNXCJWOU.js.map +1 -0
  48. package/dist/chunk-M4AMWP3H.js +178 -0
  49. package/dist/chunk-M4AMWP3H.js.map +1 -0
  50. package/dist/chunk-NGSIRE4X.js +126 -0
  51. package/dist/chunk-NGSIRE4X.js.map +1 -0
  52. package/dist/{chunk-CNNLREHD.js → chunk-P4VWIVKN.js} +268 -410
  53. package/dist/chunk-P4VWIVKN.js.map +1 -0
  54. package/dist/chunk-Q6YO7LYE.js +47 -0
  55. package/dist/chunk-Q6YO7LYE.js.map +1 -0
  56. package/dist/chunk-TMKLHTCG.js +971 -0
  57. package/dist/chunk-TMKLHTCG.js.map +1 -0
  58. package/dist/chunk-TRJTTBE2.js +7 -0
  59. package/dist/chunk-TRJTTBE2.js.map +1 -0
  60. package/dist/chunk-U2CKL755.js +4 -0
  61. package/dist/chunk-U2CKL755.js.map +1 -0
  62. package/dist/chunk-UCQ2N3KO.js +9 -0
  63. package/dist/chunk-UCQ2N3KO.js.map +1 -0
  64. package/dist/chunk-W4YR2RO2.js +2 -0
  65. package/dist/chunk-W4YR2RO2.js.map +1 -0
  66. package/dist/cli.js +1 -1
  67. package/dist/cli.js.map +1 -1
  68. package/dist/client-GGVVLOOF.js +2 -0
  69. package/dist/{compaction-BTUJJ3LJ.js → compaction-NWEMOSHA.js} +3 -3
  70. package/dist/config-7FSZFRTQ.js +2 -0
  71. package/dist/config-mutator-POCOYYJ4.js +2 -0
  72. package/dist/{context-planner-I7ID7XF3.js → context-planner-45GEWKGL.js} +2 -2
  73. package/dist/doctor-DHLBEKA4.js +2 -0
  74. package/dist/heal-HZ3EQD52.js +2 -0
  75. package/dist/index.d.ts +1244 -225
  76. package/dist/index.js +12 -12
  77. package/dist/index.js.map +1 -1
  78. package/dist/memory-extract-AENFVVO5.js +2 -0
  79. package/dist/processes-UIC4XXA6.js +2 -0
  80. package/dist/program-WVAVJFND.js +2 -0
  81. package/dist/projects-api-KRME2YKZ.js +2 -0
  82. package/dist/projects-api-KRME2YKZ.js.map +1 -0
  83. package/dist/projects-mutate-3NHVG437.js +7 -0
  84. package/dist/projects-mutate-3NHVG437.js.map +1 -0
  85. package/dist/providers-L27BJ5A6.js +2 -0
  86. package/dist/registry-T36ZIAWV.js +2 -0
  87. package/dist/rotation-memo-B3EXXSEH.js +13 -0
  88. package/dist/rotation-memo-B3EXXSEH.js.map +1 -0
  89. package/dist/scheduler-GH7TIKYS.js +2 -0
  90. package/dist/surface-usage-PRL6Z4U3.js +2 -0
  91. package/dist/wiki-M6YNRFC4.js +2 -0
  92. package/dist/workflows/templates/branching.yaml +75 -0
  93. package/dist/workflows/templates/extract.yaml +59 -0
  94. package/dist/workflows/templates/linear.yaml +37 -0
  95. package/dist/workflows/templates/retry.yaml +61 -0
  96. package/dist/workspace-setup-TBQAHYHJ.js +2 -0
  97. package/package.json +11 -3
  98. package/dist/board-dashboard-2CPBWFOB.js.map +0 -1
  99. package/dist/chunk-2HXSJYAJ.js +0 -15
  100. package/dist/chunk-2HXSJYAJ.js.map +0 -1
  101. package/dist/chunk-5F3J55LG.js +0 -2
  102. package/dist/chunk-5F3J55LG.js.map +0 -1
  103. package/dist/chunk-A6BKSZQA.js +0 -3
  104. package/dist/chunk-ABVZHJUO.js +0 -7
  105. package/dist/chunk-ABVZHJUO.js.map +0 -1
  106. package/dist/chunk-BHSPAP4U.js +0 -44
  107. package/dist/chunk-BHSPAP4U.js.map +0 -1
  108. package/dist/chunk-CDNWCYF7.js +0 -2
  109. package/dist/chunk-CDNWCYF7.js.map +0 -1
  110. package/dist/chunk-CNNLREHD.js.map +0 -1
  111. package/dist/chunk-CO4YCIIA.js +0 -13
  112. package/dist/chunk-CO4YCIIA.js.map +0 -1
  113. package/dist/chunk-EW27SCCC.js +0 -2
  114. package/dist/chunk-EW27SCCC.js.map +0 -1
  115. package/dist/chunk-GOLZ6NEL.js +0 -111
  116. package/dist/chunk-GOLZ6NEL.js.map +0 -1
  117. package/dist/chunk-JGXE2YNV.js +0 -46
  118. package/dist/chunk-JGXE2YNV.js.map +0 -1
  119. package/dist/chunk-KX626FN4.js +0 -6
  120. package/dist/chunk-KX626FN4.js.map +0 -1
  121. package/dist/chunk-MTRWXRFF.js +0 -380
  122. package/dist/chunk-MTRWXRFF.js.map +0 -1
  123. package/dist/chunk-N67JZAXU.js +0 -2
  124. package/dist/chunk-N67JZAXU.js.map +0 -1
  125. package/dist/chunk-OPKQNLGX.js +0 -450
  126. package/dist/chunk-OPKQNLGX.js.map +0 -1
  127. package/dist/chunk-OZOQKPJV.js +0 -102
  128. package/dist/chunk-OZOQKPJV.js.map +0 -1
  129. package/dist/chunk-VYLZ7UYM.js +0 -118
  130. package/dist/chunk-VYLZ7UYM.js.map +0 -1
  131. package/dist/chunk-WHP4I44J.js.map +0 -1
  132. package/dist/chunk-WJXU5F4Z.js +0 -53
  133. package/dist/chunk-WJXU5F4Z.js.map +0 -1
  134. package/dist/chunk-X5D5AV5N.js +0 -833
  135. package/dist/chunk-X5D5AV5N.js.map +0 -1
  136. package/dist/config-BLOKGMER.js +0 -2
  137. package/dist/config-mutator-RG7QY2EL.js +0 -2
  138. package/dist/doctor-ZTECTUHX.js +0 -2
  139. package/dist/editor-chat-RVLCKKB7.js +0 -137
  140. package/dist/editor-chat-RVLCKKB7.js.map +0 -1
  141. package/dist/heal-YEQYWX2L.js +0 -2
  142. package/dist/inbox-BIN2S5XN.js +0 -2
  143. package/dist/memory-extract-KRCHD3OP.js +0 -2
  144. package/dist/processes-RJ6RDP32.js +0 -2
  145. package/dist/program-GZOJIWTB.js +0 -2
  146. package/dist/providers-WVCMIW67.js +0 -2
  147. package/dist/registry-QZIUSQ5J.js +0 -2
  148. package/dist/rotation-memo-GMTZPMNE.js +0 -13
  149. package/dist/rotation-memo-GMTZPMNE.js.map +0 -1
  150. package/dist/scheduler-B2BQPV4M.js +0 -2
  151. package/dist/slack-6P7ZJ32Y.js +0 -3
  152. package/dist/slack-6P7ZJ32Y.js.map +0 -1
  153. package/dist/store-BAPDZHXE.js +0 -2
  154. package/dist/task-store-RHISEU6V.js +0 -2
  155. package/dist/telegram-PIYDRM46.js +0 -3
  156. package/dist/telegram-PIYDRM46.js.map +0 -1
  157. package/dist/types-ZRLHHCGB.js +0 -2
  158. package/dist/web/workflow-editor.global.js +0 -1452
  159. package/dist/web/workflow-editor.global.js.map +0 -1
  160. package/dist/whatsapp-PFRDMUMT.js +0 -3
  161. package/dist/whatsapp-PFRDMUMT.js.map +0 -1
  162. package/dist/wiki-53W5NLZV.js +0 -2
  163. package/dist/workspace-setup-DQCZX2NE.js +0 -2
  164. /package/dist/{agent-Y4U4R3ZZ.js.map → agent-QGZIAZMY.js.map} +0 -0
  165. /package/dist/{config-BLOKGMER.js.map → architect-SEPWMWS4.js.map} +0 -0
  166. /package/dist/{config-mutator-RG7QY2EL.js.map → builtin-FRKAQMFS.js.map} +0 -0
  167. /package/dist/{chunk-IKBHHTIT.js.map → chunk-5U35WAZF.js.map} +0 -0
  168. /package/dist/{chunk-7P5QRX5W.js.map → chunk-BFXONUUN.js.map} +0 -0
  169. /package/dist/{chunk-P3TJHGO5.js.map → chunk-FDMWOPEL.js.map} +0 -0
  170. /package/dist/{chunk-NOF4HSGK.js.map → chunk-H5D7ATXS.js.map} +0 -0
  171. /package/dist/{chunk-4ITQP3XL.js.map → chunk-JP5IEXR7.js.map} +0 -0
  172. /package/dist/{doctor-ZTECTUHX.js.map → client-GGVVLOOF.js.map} +0 -0
  173. /package/dist/{compaction-BTUJJ3LJ.js.map → compaction-NWEMOSHA.js.map} +0 -0
  174. /package/dist/{heal-YEQYWX2L.js.map → config-7FSZFRTQ.js.map} +0 -0
  175. /package/dist/{inbox-BIN2S5XN.js.map → config-mutator-POCOYYJ4.js.map} +0 -0
  176. /package/dist/{context-planner-I7ID7XF3.js.map → context-planner-45GEWKGL.js.map} +0 -0
  177. /package/dist/{memory-extract-KRCHD3OP.js.map → doctor-DHLBEKA4.js.map} +0 -0
  178. /package/dist/{processes-RJ6RDP32.js.map → heal-HZ3EQD52.js.map} +0 -0
  179. /package/dist/{program-GZOJIWTB.js.map → memory-extract-AENFVVO5.js.map} +0 -0
  180. /package/dist/{providers-WVCMIW67.js.map → processes-UIC4XXA6.js.map} +0 -0
  181. /package/dist/{registry-QZIUSQ5J.js.map → program-WVAVJFND.js.map} +0 -0
  182. /package/dist/{scheduler-B2BQPV4M.js.map → providers-L27BJ5A6.js.map} +0 -0
  183. /package/dist/{store-BAPDZHXE.js.map → registry-T36ZIAWV.js.map} +0 -0
  184. /package/dist/{task-store-RHISEU6V.js.map → scheduler-GH7TIKYS.js.map} +0 -0
  185. /package/dist/{types-ZRLHHCGB.js.map → surface-usage-PRL6Z4U3.js.map} +0 -0
  186. /package/dist/{wiki-53W5NLZV.js.map → wiki-M6YNRFC4.js.map} +0 -0
  187. /package/dist/{workspace-setup-DQCZX2NE.js.map → workspace-setup-TBQAHYHJ.js.map} +0 -0
@@ -0,0 +1,431 @@
1
+ import{a as Ks,b as kt,g as Vs}from"./chunk-G57RZCXM.js";import{b as qs}from"./chunk-TRJTTBE2.js";import{a as Ao,b as Fs,f as bt,j as Ys}from"./chunk-LNXCJWOU.js";import{a as zs,c as Hs,d as Gs}from"./chunk-UCQ2N3KO.js";import{a as Eo}from"./chunk-GV7S5BAX.js";import{e as cs}from"./chunk-GSX4NK2N.js";import{c as ls}from"./chunk-SZDACK6O.js";import{a as Is,g as As,h as Os,i as Yo,j as $t,k as Cs,l as Rs,m as Ts,n as Ds,o as Ns,p as _s,q as Ps,r as Ms,s as Ws,t as an,u as cn,v as Js,w as Bs}from"./chunk-TMKLHTCG.js";import{a as Ls,b as sn,c as Us,d as rn}from"./chunk-3WQFCIB6.js";import{i as ss,j as So,l as en,m as as,o as He,u as Es}from"./chunk-HA3XI7Z2.js";import{A as Ss,B as js,b as ft,c as jo,d as ht,e as Ue,f as Go,k as Io,m as us,q as ys,r as ws,s as wt,t as $s,u as tn,v as nn,w as bs,x as ks,y as vs,z as xs}from"./chunk-Q6YO7LYE.js";import{a as ps}from"./chunk-M4AMWP3H.js";import{f as ds,g as gs,h as on}from"./chunk-76Z4VHDX.js";import{b as rs,f as ms}from"./chunk-53GCFZPR.js";import{a as K,b as X,c as co,d as hs}from"./chunk-FDMWOPEL.js";import{e as fs,f as L,g as yt}from"./chunk-JXWAGMI2.js";import{a as ze,b as ut}from"./chunk-ISEBLHWX.js";import{b as ao,d as is,f as Zt}from"./chunk-BFXONUUN.js";import{a as Le}from"./chunk-VJUTQAPJ.js";import{a as Ar}from"./chunk-4YCH6IZV.js";import{Command as rd}from"commander";import{Command as Or}from"commander";import j from"chalk";import{existsSync as Cr,readFileSync as Rr}from"fs";async function Tr(e){try{let{execFileSync:o}=await import("child_process"),t=o("ps",["-p",String(e),"-o","command="],{encoding:"utf-8",timeout:2e3}).trim();return t?/\bnode\b/.test(t)||/agentx|agentix|cli\.js/.test(t):!1}catch{return!0}}var De=new Or().name("daemon").description("manage the agentx daemon \u2014 start, stop, status, logs");De.command("start").description("start the daemon (default if no subcommand)").option("-c, --config <path>","path to agentx.json").option("-d, --detach","run in background (detached)").option("--port <port>","override bind port").action(async e=>{if(e.detach){let{spawn:d}=await import("child_process"),{openSync:g,writeFileSync:l,existsSync:u}=await import("fs"),m=process.argv[1];(!m||!u(m))&&(console.error(j.red(` Cannot resolve agentx CLI entrypoint (process.argv[1]=${m||"?"}).`)),console.error(j.dim(" Try running without --detach to see the underlying error.")),process.exit(1));let f=[m,"daemon","start"];e.config&&f.push("-c",e.config),e.port&&f.push("--port",e.port);let y="/tmp/agentx-daemon.log",$;try{$=g(y,"a")}catch(b){console.error(j.red(` Failed to open ${y} for writing: ${b.message}`)),process.exit(1)}let v=d(process.execPath,f,{detached:!0,stdio:["ignore",$,$],cwd:process.cwd()});v.unref(),l("/tmp/agentx-daemon.pid",String(v.pid)),console.log(j.green(`Daemon started in background (PID: ${v.pid})`)),console.log(j.dim(` Logs: tail -f ${y}`)),console.log(j.dim(" Stop: agentx daemon stop")),await new Promise(b=>setTimeout(b,1e3));try{process.kill(v.pid,0)}catch{console.log(),console.log(j.yellow(` Daemon exited within 1s \u2014 check ${y} for the error:`)),console.log(j.dim(` tail -n 40 ${y}`))}return}let{readFileSync:o,writeFileSync:t,mkdirSync:n,existsSync:s}=await import("fs"),{resolve:i}=await import("path"),r=i(process.cwd(),".agentx");s(r)||n(r,{recursive:!0});let a=i(r,"daemon.pid");if(s(a)){let d=parseInt(o(a,"utf-8").trim(),10);if(d&&d!==process.pid){let g=!1;try{process.kill(d,0),g=!0}catch{}g&&await Tr(d)&&(console.error(`Another agentx daemon is already running (PID ${d}). Exiting.`),process.exit(0))}}t(a,String(process.pid));try{Dr(i(process.cwd(),".env"))}catch{}await new cn(e.config).start()});function Dr(e){if(!Cr(e))return;let o=Rr(e,"utf-8");for(let t of o.split(`
2
+ `)){let n=t.trim();if(!n||n.startsWith("#"))continue;let s=n.indexOf("=");if(s<0)continue;let i=n.slice(0,s).trim();if(!i||process.env[i]!==void 0)continue;let r=n.slice(s+1).trim();(r.startsWith('"')&&r.endsWith('"')||r.startsWith("'")&&r.endsWith("'"))&&(r=r.slice(1,-1)),process.env[i]=r}}De.command("stop").description("stop the running daemon").action(async()=>{let{readFileSync:e,existsSync:o,unlinkSync:t}=await import("fs"),{resolve:n}=await import("path"),s=[n(process.cwd(),".agentx/daemon.pid"),"/tmp/agentx-daemon.pid"];for(let r of s)if(o(r))try{let a=parseInt(e(r,"utf-8").trim(),10);process.kill(a,"SIGTERM"),await new Promise(c=>setTimeout(c,2e3));try{t(r)}catch{}console.log(j.green(`Daemon stopped (PID: ${a})`));return}catch{try{t(r)}catch{}}let{execSync:i}=await import("child_process");try{i("pkill -f 'node dist/cli.js daemon'",{stdio:"ignore"}),console.log(j.green("Daemon stopped"))}catch{console.log(j.yellow("No daemon process found"))}});De.command("status").alias("st").description("show daemon status, agents, crons, and mesh").option("-c, --config <path>","path to agentx.json").option("--json","output as JSON").action(async e=>{try{let o=L(e.config),[t,n]=o.node.bind.split(":"),s=`http://${t||"127.0.0.1"}:${n}/health`,r=await(await fetch(s,{signal:AbortSignal.timeout(3e3)})).json();if(e.json){console.log(JSON.stringify(r,null,2));return}console.log(),console.log(j.bold(` ${r.node.name}`)+j.dim(` (${r.node.id})`)),console.log(j.green(" Status: running")+j.dim(` \u2014 uptime ${Nr(r.uptime)}`)),console.log(),console.log(j.bold(" Agents:"));for(let c of r.agents){let d=c.active>0?j.yellow(` [${c.active} active]`):"",g=c.errors>0?j.red(` (${c.errors} errors)`):"";console.log(` ${j.cyan(c.name)} ${j.dim(`(${c.tier})`)}${d}${g}`),console.log(j.dim(` ${c.workspace}`))}let a=r.crons.filter(c=>c.enabled);if(r.crons.length){console.log(),console.log(j.bold(` Crons: ${a.length} enabled / ${r.crons.length} total`));for(let c of r.crons){let d=c.enabled?j.green("\u25CF"):j.dim("\u25CB"),g=c.nextRun?j.dim(` next: ${new Date(c.nextRun).toLocaleTimeString()}`):"";console.log(` ${d} ${c.id}${g}`)}}if(r.mesh?.length){console.log(),console.log(j.bold(" Mesh:"));for(let c of r.mesh){let d=c.healthy?j.green("\u25CF"):j.red("\u25CF"),g=c.skills?.length?j.dim(` (${c.skills.length} agents)`):"";console.log(` ${d} ${c.peer} ${j.dim(c.peerUrl)}${g}`)}}console.log()}catch(o){if(o.cause?.code==="ECONNREFUSED"||o.name==="TimeoutError")console.log(j.red(" Daemon is not running")),console.log(j.dim(" Start with: agentx daemon start"));else try{let t=L(e.config),n=yt(t);if(console.log(),console.log(j.bold(` ${t.node.name}`)+j.dim(` (${t.node.id})`)),console.log(j.yellow(" Status: stopped")),console.log(j.dim(` ${Object.keys(t.agents).length} agents, ${Object.keys(t.crons).length} crons configured`)),n.length){console.log();for(let s of n)console.log(j.yellow(` \u26A0 ${s}`))}console.log()}catch(t){console.log(j.red(` ${t.message}`))}}});De.command("logs").description("tail daemon logs").option("-n, --lines <n>","number of lines","50").option("-f, --follow","follow log output",!1).action(async e=>{let{execSync:o,spawn:t}=await import("child_process"),n="/tmp/agentx-daemon.log",{existsSync:s}=await import("fs");if(!s(n)){console.log(j.yellow("No log file found at /tmp/agentx-daemon.log"));return}if(e.follow){let i=t("tail",["-f","-n",e.lines,n],{stdio:"inherit"});process.on("SIGINT",()=>{i.kill(),process.exit(0)})}else o(`tail -n ${e.lines} ${n}`,{stdio:"inherit"})});De.command("watch").description("stream live agent activity (SSE)").option("-c, --config <path>","path to agentx.json").action(async e=>{let n=`http://${(L(e.config).node.bind||"127.0.0.1:18800").replace("0.0.0.0","127.0.0.1")}/events`;console.log(j.dim(` Connecting to ${n}...`)),console.log(j.dim(` Press Ctrl+C to stop
3
+ `));try{let s=await fetch(n);if(!s.ok||!s.body){console.log(j.red(` Failed: ${s.status}`));return}let i=s.body.getReader(),r=new TextDecoder,a="";for(;;){let{done:c,value:d}=await i.read();if(c)break;a+=r.decode(d,{stream:!0});let g=a.split(`
4
+ `);a=g.pop()||"";for(let l of g)if(l.startsWith("data: "))try{let u=JSON.parse(l.slice(6)),m=new Date(u.time).toLocaleTimeString(),f=u.message?.replace(/^\[agentx\]\s*/,"")||"";f.includes("executing")?console.log(`${j.dim(m)} ${j.cyan("\u25B6")} ${f}`):f.includes("completed")?console.log(`${j.dim(m)} ${j.green("\u2713")} ${f}`):f.includes("error")||f.includes("Error")?console.log(`${j.dim(m)} ${j.red("\u2717")} ${f}`):f.includes("Routing")?console.log(`${j.dim(m)} ${j.yellow("\u2192")} ${f}`):f.includes("mention")?console.log(`${j.dim(m)} ${j.magenta("@")} ${f}`):f.includes("Skipping")?console.log(`${j.dim(m)} ${j.dim("\u2298")} ${j.dim(f)}`):console.log(`${j.dim(m)} ${f}`)}catch{}else l.startsWith("event: status")}}catch(s){console.log(j.red(` Connection failed: ${s.message}`)),console.log(j.dim(" Is the daemon running? Diagnostic order:")),console.log(j.dim(" 1) agentx daemon status # is the daemon up?")),console.log(j.dim(" 2) tail -n 60 /tmp/agentx-daemon.log # detach-mode crash log")),console.log(j.dim(" 3) journalctl -u agentx -n 60 # systemd-managed installs")),console.log(j.dim(" If the log file is missing, the daemon never started \u2014 re-run without --detach")),console.log(j.dim(" to see the error in the foreground."))}});De.command("send <agent> <message...>").description("send a task to an agent via the daemon API").option("-c, --config <path>","path to agentx.json").option("--peer <peer>","send to a mesh peer's agent").option("--json","output raw JSON response").action(async(e,o,t)=>{let n=o.join(" ");try{let s=L(t.config),[i,r]=s.node.bind.split(":"),a=`http://${i||"127.0.0.1"}:${r}`,c,d;t.peer?(c=`${a}/mesh/task`,d={peer:t.peer,message:n,agent:e}):(c=`${a}/task`,d={agent:e,message:n}),console.log(j.dim(` Sending to ${t.peer?`${t.peer}/${e}`:e}...`));let l=await(await fetch(c,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(d)})).json();t.json?console.log(JSON.stringify(l,null,2)):l.error?console.log(j.red(` Error: ${l.error}`)):(console.log(),console.log(l.content||l.response||"No response"),l.duration&&console.log(j.dim(`
5
+ (${(l.duration/1e3).toFixed(1)}s)`)))}catch(s){console.log(j.red(` Failed: ${s.message}`)),console.log(j.dim(" Is the daemon running? Try: agentx daemon status"))}});De.command("deploy <host>").description("deploy agentx to a remote server via rsync").option("-i, --identity <key>","SSH identity file").option("-u, --user <user>","SSH user","root").option("-p, --path <path>","remote agentx path","~/agentx").option("--restart","restart remote daemon after deploy",!1).option("--skip-checks","skip build and test before deploy",!1).action(async(e,o)=>{let{execSync:t}=await import("child_process"),n=o.identity?`-e "ssh -i ${o.identity}"`:"",s=`${o.user}@${e}:${o.path}/dist/`;if(!o.skipChecks){try{console.log(j.dim(" Running tests...")),t("npx vitest run --reporter=dot",{stdio:"pipe",cwd:process.cwd()}),console.log(j.green(" \u2713 Tests passed"))}catch{console.log(j.red(" \u2717 Tests failed \u2014 aborting deploy")),console.log(j.dim(" Use --skip-checks to deploy anyway"));return}let{existsSync:i,statSync:r}=await import("fs"),{resolve:a}=await import("path"),c=a(process.cwd(),"dist/cli.js");if(!i(c)){console.log(j.red(" \u2717 dist/ not found \u2014 run build first"));return}Date.now()-r(c).mtimeMs>3e5&&(console.log(j.yellow(" \u26A0 dist/ is older than 5 minutes \u2014 rebuilding...")),t("npx tsup --no-dts",{stdio:"pipe",cwd:process.cwd()}),console.log(j.green(" \u2713 Build complete")))}console.log(j.dim(` Deploying to ${o.user}@${e}:${o.path}...`));try{if(t(`rsync -avzL --delete ${n} dist/ ${s}`,{stdio:"inherit",cwd:process.cwd()}),console.log(j.green(" \u2713 Deploy complete")),o.restart){console.log(j.dim(" Restarting remote daemon..."));let i=o.identity?`ssh -i ${o.identity}`:"ssh";t(`${i} ${o.user}@${e} "pkill -f 'node dist/cli.js daemon' 2>/dev/null; sleep 2; cd ${o.path} && nohup node dist/cli.js daemon start > /tmp/agentx-daemon.log 2>&1 &"`,{stdio:"inherit"}),console.log(j.green(" Remote daemon restarted"))}}catch(i){console.log(j.red(` Deploy failed: ${i.message}`))}});De.action(async e=>{await new cn(e.config).start()});function Nr(e){return e<60?`${Math.round(e)}s`:e<3600?`${Math.round(e/60)}m`:e<86400?`${Math.round(e/3600)}h ${Math.round(e%3600/60)}m`:`${Math.round(e/86400)}d ${Math.round(e%86400/3600)}h`}import{Command as _r}from"commander";import Ce from"chalk";import{existsSync as Ko,readFileSync as Pr,writeFileSync as Qs,mkdirSync as Xs}from"fs";import{hostname as Zs}from"os";import{resolve as Oo}from"path";var ei=new _r().name("init").description("initialize agentx configuration in the current directory").option("--force","overwrite existing config").action(async e=>{let o=process.cwd(),t=Oo(o,"agentx.json"),n=Oo(o,".env"),s=Oo(o,".agentx/sessions");if(console.log(),console.log(Ce.bold(" agentx init")),console.log(),Ko(t)&&!e.force){console.log(Ce.yellow(" agentx.json already exists. Use --force to overwrite.")),console.log();return}let i=[Oo(o,"agentx.example.json"),Oo(o,"node_modules/agentix-cli/agentx.example.json")],r;for(let c of i)if(Ko(c)){r=Pr(c,"utf-8");break}r||(r=JSON.stringify({node:{id:Zs().toLowerCase().replace(/[^a-z0-9-]/g,"-"),name:Zs(),bind:"127.0.0.1:18800"},providers:{claude:{apiKey:"${ANTHROPIC_API_KEY}"}},agents:{assistant:{name:"Assistant",workspace:o,tier:"claude-code",model:"claude-sonnet-4-6",mentions:["@assistant","assistant"],maxConcurrent:2,systemPrompt:"You are a helpful assistant.",permissionMode:"default"}},channels:{telegram:{enabled:!1,accounts:{},policy:{dm:"pair",group:"mention-required"}},whatsapp:{enabled:!1}},crons:{},mesh:{enabled:!1,peers:[],discovery:"static",healthCheck:{interval:60,timeout:10}}},null,2)),Qs(t,r),console.log(Ce.green(" \u2713 Created agentx.json")),Ko(n)||(Qs(n,["# AgentX environment variables","# ANTHROPIC_API_KEY=sk-ant-...","# TG_BOT_TOKEN=123456:ABC...","# MESH_TOKEN=your-mesh-secret",""].join(`
6
+ `)),console.log(Ce.green(" \u2713 Created .env template"))),Ko(s)||(Xs(s,{recursive:!0}),console.log(Ce.green(" \u2713 Created .agentx/sessions/")));let a=Oo(o,".claude");Ko(a)||(Xs(a,{recursive:!0}),console.log(Ce.green(" \u2713 Created .claude/"))),console.log(),console.log(Ce.bold(" Next steps:")),console.log(` 1. Edit ${Ce.cyan("agentx.json")} \u2014 configure agents, channels, crons`),console.log(` 2. Edit ${Ce.cyan(".env")} \u2014 add API keys and bot tokens`),console.log(` 3. Run ${Ce.cyan("agentx daemon start")} \u2014 start the daemon`),console.log(` 4. Run ${Ce.cyan("agentx daemon status")} \u2014 verify everything`),console.log()});import{Command as Mr}from"commander";import lo from"chalk";import{spawn as ti}from"child_process";import{existsSync as Lr}from"fs";import{dirname as Ur,resolve as Wr}from"path";import{fileURLToPath as Jr}from"url";var ni=new Mr().name("setup").description("open the web setup wizard to create or extend your AgentX install").option("--port <n>","dashboard port (default: from agentx.json, else 4202)").option("--no-open","don't try to open the browser").action(async e=>{let o=Fs(),t=e.port?parseInt(e.port,10):Br(),n=`http://127.0.0.1:${t}/setup`;if(console.log(),o.configExists?console.log(lo.dim(" Found agentx.json \u2014 the wizard will extend your current install.")):console.log(lo.dim(" No agentx.json yet \u2014 the wizard will create one.")),console.log(lo.bold(` Opening ${n}`)),console.log(),await qr(n).catch(()=>!1))console.log(lo.dim(" Dashboard is already running.")),e.open!==!1&&oi(n);else{console.log(lo.dim(` Starting dashboard server on port ${t} (Ctrl-C to quit)\u2026`));let i=Fr(),r=ti(process.execPath,[i,"board","serve","--port",String(t)],{stdio:"inherit",detached:!1});await new Promise(a=>setTimeout(a,1200)),e.open!==!1&&oi(n),await new Promise(a=>r.on("exit",()=>a()))}});function Br(){try{return L().dashboard.port||4202}catch{return 4202}}function Fr(){let e=Wr(Ur(Jr(import.meta.url)),"cli.js");return Lr(e)?e:process.argv[1]||e}async function qr(e){try{let o=await fetch(e,{method:"GET"});return o.ok||o.status===302}catch{return!1}}function oi(e){let o=process.platform==="darwin"?"open":process.platform==="win32"?"start":"xdg-open";try{let t=ti(o,[e],{stdio:"ignore",detached:!0});t.once("error",()=>{console.log(lo.dim(` Couldn't auto-open the browser. Visit ${e} manually.`))}),t.unref()}catch{console.log(lo.dim(` Couldn't auto-open the browser. Visit ${e} manually.`))}}import{Command as zr}from"commander";import ge from"chalk";var Vo=new zr().name("token").description("scoped API tokens for external access (mesh peers, integrations)");Vo.command("create").description("mint a new token \u2014 prints the secret once, store it somewhere safe").requiredOption("--name <name>","human-readable label (e.g. 'Slack integration')").option("--scope <scopes>","comma-separated scopes (default: dashboard:read)","dashboard:read").option("--expires <days>","expire after N days (default: no expiry)").action(e=>{try{let o=new bt,t=String(e.scope).split(",").map(r=>r.trim()).filter(Boolean),n=e.expires?parseInt(e.expires,10):void 0;if(e.expires&&(!n||n<1))throw new Error("--expires must be a positive integer (days)");let{token:s,record:i}=o.create({name:e.name,scopes:t,expiresInDays:n});console.log(),console.log(ge.bold(" Token created.")),console.log(),console.log(` id: ${ge.cyan(i.id)}`),console.log(` name: ${i.name}`),console.log(` scopes: ${i.scopes.join(", ")}`),console.log(` expires: ${i.expiresAt||ge.dim("never")}`),console.log(),console.log(ge.yellow(" \u26A0 This is the only time the full secret will be shown.")),console.log(),console.log(` ${ge.green.bold(s)}`),console.log(),console.log(ge.dim(" Use it via the Authorization header:")),console.log(ge.dim(` curl -H "Authorization: Bearer ${s.slice(0,16)}..." ...`)),console.log()}catch(o){console.log(ge.red(` ${o.message}`)),process.exit(1)}});Vo.command("list").alias("ls").description("show all issued tokens (secrets not included)").action(()=>{try{let e=new bt().list();if(e.length===0){console.log(ge.dim(`
7
+ No tokens issued.
8
+ `));return}console.log();for(let o of e){let t=o.revokedAt?ge.red("revoked"):o.expiresAt&&Date.parse(o.expiresAt)<Date.now()?ge.red("expired"):ge.green("active");console.log(` ${ge.cyan(o.id)} ${t} ${o.name}`),console.log(ge.dim(` prefix: ${o.prefix}\u2026 scopes: ${o.scopes.join(", ")}`)),console.log(ge.dim(` created: ${o.createdAt}${o.expiresAt?` expires: ${o.expiresAt}`:""}${o.lastUsedAt?` last used: ${o.lastUsedAt}`:""}`))}console.log()}catch(e){console.log(ge.red(` ${e.message}`)),process.exit(1)}});Vo.command("revoke <id>").description("immediately invalidate a token").action(e=>{try{let o=new bt().revoke(e);o||(console.log(ge.red(` Token ${e} not found.`)),process.exit(1)),console.log(ge.green(`
9
+ \u2713 Revoked ${e} (${o.name})
10
+ `))}catch(o){console.log(ge.red(` ${o.message}`)),process.exit(1)}});import{Command as _e}from"commander";import h from"chalk";import me from"prompts";import{readFileSync as Ye,writeFileSync as Co,existsSync as ne,mkdirSync as Ne,cpSync as ln,readdirSync as gn}from"fs";import{resolve as _,join as Ge,dirname as Hr}from"path";import{fileURLToPath as Gr}from"url";function Z(){let e=_(process.cwd(),"agentx.json");if(!ne(e))throw new Error("No agentx.json found. Run: agentx init");return JSON.parse(Ye(e,"utf-8"))}function ye(e){return K(o=>{for(let t of Object.keys(o))delete o[t];Object.assign(o,e)}).then(o=>{if(!o.success){console.error(h.red(` Config save failed: ${o.error}`));return}o.reloaded?console.error(h.dim(" (daemon hot-reloaded)")):o.reloadSkipped&&!/ECONNREFUSED|unreachable|fetch failed/i.test(o.reloadSkipped)&&console.error(h.dim(` (daemon reload skipped: ${o.reloadSkipped})`))})}var go=new _e().name("agent").description("manage agents \u2014 add, list, remove");go.command("list").alias("ls").description("list configured agents").action(()=>{let e=Z();console.log();for(let[o,t]of Object.entries(e.agents||{})){let n=t.mentions?.join(", ")||"";console.log(` ${h.cyan(o)} \u2014 ${t.name} (${t.tier})`),console.log(h.dim(` workspace: ${t.workspace}`)),n&&console.log(h.dim(` mentions: ${n}`))}console.log()});go.command("add").description("add a new agent interactively").action(async()=>{let e=Z(),o=await me([{type:"text",name:"id",message:"Agent ID (kebab-case)",validate:d=>/^[a-z0-9-]+$/.test(d)||"Use lowercase, numbers, hyphens"},{type:"text",name:"name",message:"Display name"},{type:"text",name:"workspace",message:"Workspace path",initial:_(process.cwd(),"agents/")},{type:"select",name:"tier",message:"Execution tier",choices:[{title:"claude-code (subscription, full features)",value:"claude-code"},{title:"codex-cli (OpenAI Codex CLI)",value:"codex-cli"},{title:"opencode (configured OpenCode providers)",value:"opencode"},{title:"sdk (API key, programmatic)",value:"sdk"},{title:"orchestrator (any provider)",value:"orchestrator"}]},{type:"text",name:"model",message:"Model",initial:"claude-sonnet-4-6"},{type:"list",name:"mentions",message:"Mention patterns (comma-separated)",separator:","},{type:"number",name:"maxConcurrent",message:"Max concurrent tasks",initial:2},{type:"text",name:"systemPrompt",message:"System prompt"}]);if(!o.id)return;let t=o.workspace.endsWith(o.id)?o.workspace:Ge(o.workspace,o.id);Ne(_(t,".claude/skills"),{recursive:!0});let n={name:o.name,workspace:t,tier:o.tier,model:o.model,mentions:o.mentions?.map(d=>d.trim()).filter(Boolean)||[],maxConcurrent:o.maxConcurrent,systemPrompt:o.systemPrompt,permissionMode:"default"},{setupWorkspace:s}=await import("./workspace-setup-TBQAHYHJ.js"),i=s(o.id,n,"19900",(...d)=>{console.log(h.dim(` ${d.join(" ")}`))}),r=_(process.cwd(),"src/wiki/SKILL.md"),a=_(Hr(Gr(import.meta.url)),"../wiki/SKILL.md"),c=ne(r)?r:a;ne(c)&&(Ne(_(t,".claude/skills/wiki"),{recursive:!0}),ln(c,_(t,".claude/skills/wiki/SKILL.md"))),e.agents=e.agents||{},e.agents[o.id]=n,await ye(e),console.log(h.green(`
11
+ Agent "${o.id}" added`)),console.log(h.dim(` Workspace: ${t}`)),console.log(h.dim(` Created: ${i.created.length} files (CLAUDE.md, AGENTS.md, settings, rules)`)),console.log()});go.command("remove <id>").alias("rm").description("remove an agent from config (keeps workspace)").action(async e=>{let o=Z();if(!o.agents?.[e]){console.log(h.red(` Agent "${e}" not found`));return}delete o.agents[e],await ye(o),console.log(h.green(` Agent "${e}" removed from config (workspace preserved)`))});go.command("capability <id>").alias("caps").description("set agent capability flags (intents, maxDelegationDepth, contextReferences, contextStrategy)").option("--intents <csv>","comma-separated intent allow-list (set to '-' to clear)").option("--max-delegation-depth <n>","max distinct upstream agents on the same subject before refusal (0 disables)").option("--context-references <bool>","render the deterministic [Verified References] block (true|false)").option("--context-strategy <name>","per-agent override: layered | planner").option("--max-execution-minutes <n>","wall-clock cap on a single Claude Code call (1\u2013240)").option("--show","just print the current capability fields").action(async(e,o)=>{let t=Z(),n=t.agents?.[e];if(n||(console.log(h.red(` Agent "${e}" not found`)),process.exit(1)),o.show){console.log(),console.log(h.bold(` ${e}`)),console.log(` intents ${n.intents&&n.intents.length?n.intents.join(", "):h.dim("(any)")}`),console.log(` maxDelegationDepth ${n.maxDelegationDepth??5}`),console.log(` contextReferences ${n.contextReferences?"on":h.dim("off")}`),console.log(` contextStrategy ${n.contextStrategy||h.dim("(global default)")}`),console.log(` maxExecutionMinutes ${n.maxExecutionMinutes??20}`),console.log();return}let s=[];if(o.intents!==void 0&&(o.intents==="-"||o.intents===""?(n.intents=[],s.push("intents cleared (permissive)")):(n.intents=String(o.intents).split(",").map(i=>i.trim()).filter(Boolean),s.push(`intents=[${n.intents.join(", ")}]`))),o.maxDelegationDepth!==void 0){let i=parseInt(o.maxDelegationDepth,10);(!Number.isFinite(i)||i<0||i>50)&&(console.log(h.red(" --max-delegation-depth must be 0..50")),process.exit(1)),n.maxDelegationDepth=i,s.push(`maxDelegationDepth=${i}`)}if(o.contextReferences!==void 0){let i=String(o.contextReferences).toLowerCase();["true","false","on","off","1","0"].includes(i)||(console.log(h.red(" --context-references must be true|false")),process.exit(1)),n.contextReferences=i==="true"||i==="on"||i==="1",s.push(`contextReferences=${n.contextReferences}`)}if(o.contextStrategy!==void 0&&(o.contextStrategy==="-"||o.contextStrategy===""?(delete n.contextStrategy,s.push("contextStrategy cleared (uses global default)")):["layered","planner"].includes(o.contextStrategy)?(n.contextStrategy=o.contextStrategy,s.push(`contextStrategy=${o.contextStrategy}`)):(console.log(h.red(" --context-strategy must be layered|planner (or '-' to clear)")),process.exit(1))),o.maxExecutionMinutes!==void 0){let i=parseInt(o.maxExecutionMinutes,10);(!Number.isFinite(i)||i<1||i>240)&&(console.log(h.red(" --max-execution-minutes must be 1..240")),process.exit(1)),n.maxExecutionMinutes=i,s.push(`maxExecutionMinutes=${i}`)}s.length===0&&(console.log(h.yellow(" no changes \u2014 pass at least one flag, or use --show")),process.exit(1)),await ye(t),console.log(h.green(` \u2713 ${e}: ${s.join(", ")}`)),console.log(h.dim(" Restart the daemon (or POST /reload) for the change to take effect."))});var Qo=new _e().name("integrations").alias("integ").description("manage per-agent third-party credentials (telegram-bot, hubspot, gitlab-user, gmail, etc.)");Qo.command("list <agentId>").alias("ls").description("list integrations declared on an agent").option("--json","emit JSON").action(async(e,o)=>{let n=Z().agents?.[e];n||(console.log(h.red(` Agent "${e}" not found`)),process.exit(1));let s=n.integrations??[];if(o.json){console.log(JSON.stringify(s,null,2));return}if(!s.length){console.log(h.dim(`
12
+ ${e}: no integrations declared. Add one with: agentx agent integrations add ${e}
13
+ `));return}console.log(),console.log(h.bold(` ${e}`));for(let i of s){let r=i.enabled!==!1,a=[],c=i.credentials??{};for(let[g,l]of Object.entries(c))typeof l=="string"&&/^[A-Z][A-Z0-9_]*$/.test(l)?a.push(process.env[l]?h.green(`${g}:${l}=set`):h.yellow(`${g}:${l}=UNSET`)):g==="auth"&&l==="keyring"?a.push(h.dim("auth:keyring")):g==="sessionDir"&&typeof l=="string"&&a.push(h.dim(`sessionDir:${l}`));let d=Object.entries(i.metadata??{}).map(([g,l])=>`${g}=${l}`).join(" ");console.log(` ${r?h.cyan(i.kind.padEnd(14)):h.dim(i.kind.padEnd(14))} ${h.bold(i.label)}`),a.length&&console.log(` ${a.join(" ")}`),d&&console.log(` ${h.dim(d)}`),r||console.log(` ${h.dim("(disabled)")}`)}console.log()});Qo.command("add <agentId>").description("declare a new integration on an agent").option("--kind <name>","service kind (e.g. telegram-bot, hubspot, gitlab-user, gmail)").option("--label <text>","human-readable label (must be unique within (agent, kind))").option("--token-env <name>","env-var holding the secret (uppercase identifier)").option("--auth <mode>","alternative to --token-env: 'keyring' for OS keyring").option("--session-dir <path>","for whatsapp / mtproto: file-based session directory").option("--metadata <kvList>","comma-separated key=value pairs (e.g. 'username=anis,email=anis@noqta.tn')").option("--no-prompt","fail instead of prompting for missing values").action(async(e,o)=>{let t=Z();t.agents?.[e]||(console.log(h.red(` Agent "${e}" not found`)),process.exit(1));let n=o.prompt!==!1,{INTEGRATION_KINDS:s}=await import("./config-7FSZFRTQ.js"),i=String(o.kind||"").trim();!i&&n&&(i=((await me({type:"autocomplete",name:"kind",message:"Service kind",choices:s.map(m=>({title:m,value:m}))})).kind||"").trim()),i||(console.log(h.red(" --kind is required")),process.exit(1));let r=String(o.label||"").trim();!r&&n&&(r=((await me({type:"text",name:"label",message:'Label (e.g. "@cx_bot", "Noqta CRM", "anis@noqta.tn")'})).label||"").trim()),r||(console.log(h.red(" --label is required")),process.exit(1));let a=String(o.tokenEnv||"").trim(),c=String(o.auth||"").trim(),d=String(o.sessionDir||"").trim();if(!a&&!c&&!d&&n){let u=await me({type:"select",name:"scheme",message:"Credential scheme",choices:[{title:"Env var (e.g. HUBSPOT_TOKEN_CX)",value:"tokenEnv"},{title:"OS keyring",value:"keyring"},{title:"File-based session (whatsapp / mtproto)",value:"sessionDir"}]});u.scheme==="tokenEnv"?a=((await me({type:"text",name:"tokenEnv",message:"Env var name (uppercase)",initial:`${i.replace(/-/g,"_").toUpperCase()}_${e.replace(/-/g,"_").toUpperCase()}`})).tokenEnv||"").trim():u.scheme==="keyring"?c="keyring":u.scheme==="sessionDir"&&(d=((await me({type:"text",name:"sessionDir",message:"Session directory path",initial:`.agentx/${i}-sessions/${e}`})).sessionDir||"").trim())}let g={};if(o.metadata)for(let u of String(o.metadata).split(",")){let[m,...f]=u.split("=");m&&(g[m.trim()]=f.join("=").trim())}else if(n){let u=await me({type:"text",name:"metadata",message:"Metadata (k=v,k=v) \u2014 non-secret only, optional"});if(u.metadata)for(let m of String(u.metadata).split(",")){let[f,...y]=m.split("=");f&&(g[f.trim()]=y.join("=").trim())}}let l={};a&&(/^[A-Z][A-Z0-9_]*$/.test(a)||(console.log(h.red(` --token-env must be uppercase identifier (got "${a}")`)),process.exit(1)),l.tokenEnv=a),c==="keyring"&&(l.auth="keyring"),d&&(l.sessionDir=d),Object.keys(l).length||(console.log(h.red(" one of --token-env / --auth / --session-dir is required")),process.exit(1)),t.agents[e].integrations=t.agents[e].integrations??[],t.agents[e].integrations.find(u=>u.kind===i&&u.label===r)&&(console.log(h.red(` Integration (${i}, ${r}) already exists on ${e}`)),process.exit(1)),t.agents[e].integrations.push({kind:i,label:r,credentials:l,metadata:g,enabled:!0}),await ye(t),console.log(h.green(` \u2713 ${e}: added ${i} integration "${r}"`)),a&&!process.env[a]&&console.log(h.yellow(` \u26A0 env var ${a} is not set \u2014 set it on the daemon environment before use`))});Qo.command("remove <agentId> <kindOrLabel>").alias("rm").description("remove an integration (matched by kind, label, or 'kind:label')").action(async(e,o)=>{let t=Z(),n=t.agents?.[e];n||(console.log(h.red(` Agent "${e}" not found`)),process.exit(1));let s=n.integrations??[],[i,r]=o.includes(":")?o.split(":",2):[o,""],a=s.findIndex(d=>d.kind===i&&(!r||d.label===r)||d.label===o);a===-1&&(console.log(h.red(` No integration matched "${o}"`)),process.exit(1));let c=s.splice(a,1)[0];await ye(t),console.log(h.green(` \u2713 removed ${c.kind} integration "${c.label}" from ${e}`))});Qo.command("test <agentId> [kindOrLabel]").description("validate env vars are set for one or all integrations on an agent").action(async(e,o)=>{let n=Z().agents?.[e];n||(console.log(h.red(` Agent "${e}" not found`)),process.exit(1));let s=n.integrations??[],i=o?s.filter(a=>a.kind===o||a.label===o||`${a.kind}:${a.label}`===o):s;if(!i.length){console.log(h.yellow(" no integrations to test"));return}let r=0;console.log();for(let a of i){let c=a.credentials??{},d=[];for(let[g,l]of Object.entries(c))typeof l=="string"&&/^[A-Z][A-Z0-9_]*$/.test(l)&&!process.env[l]&&d.push(`${g}: env var ${l} is unset`);if(d.length){r+=1,console.log(` ${h.red("\u2717")} ${a.kind} "${a.label}"`);for(let g of d)console.log(` ${h.dim(g)}`)}else console.log(` ${h.green("\u2713")} ${a.kind} "${a.label}"`)}console.log(),r>0&&process.exit(1)});go.addCommand(Qo);var vt=new _e().name("channel").description("manage channels \u2014 add telegram/whatsapp/discord, list");vt.command("list").alias("ls").description("list configured channels and bindings").action(()=>{let e=Z();console.log();let o=e.channels?.telegram;if(o?.enabled){console.log(h.bold(" Telegram:"));for(let[i,r]of Object.entries(o.accounts||{}))console.log(` ${h.cyan(i)} -> agent: ${r.agentBinding}`)}else console.log(h.dim(" Telegram: disabled"));let t=e.channels?.whatsapp;if(t?.enabled){console.log(h.bold(" WhatsApp:")),console.log(` default agent: ${t.defaultAgent||"(none)"}`),console.log(` session: ${t.sessionDir||".agentx/whatsapp-sessions"}`);for(let i of t.routes||[]){let r=i.contact?`contact ${i.contact}`:`group "${i.group}"`;console.log(` ${r} -> agent: ${i.agent}`)}}else console.log(h.dim(" WhatsApp: disabled"));let n=e.channels?.discord;n?.enabled?(console.log(h.bold(" Discord:")),console.log(` agent: ${n.agentBinding||"(none)"}`)):console.log(h.dim(" Discord: disabled"));let s=e.channels?.gitlab;if(s?.enabled){console.log(h.bold(" GitLab:")),console.log(` host: ${s.host}`),console.log(` webhook: :${s.webhookPort}`);for(let i of s.routes||[])console.log(` ${i.project} -> agent: ${i.agent}`)}else console.log(h.dim(" GitLab: disabled"));console.log()});vt.command("add").description("add a channel (telegram, whatsapp, or discord)").action(async()=>{let e=Z(),o=Object.keys(e.agents||{});if(!o.length){console.log(h.red(" No agents configured. Run: agentx agent add"));return}let t=o.map(s=>({title:`${s} (${e.agents[s].name})`,value:s})),{channelType:n}=await me({type:"select",name:"channelType",message:"Channel type",choices:[{title:"Telegram \u2014 bot via BotFather token",value:"telegram"},{title:"WhatsApp \u2014 link via QR code (self-chat or contacts)",value:"whatsapp"},{title:"Discord \u2014 bot via Discord developer portal",value:"discord"},{title:"GitLab \u2014 webhook for issues, MRs, comments, pipelines",value:"gitlab"}]});if(n){if(e.channels=e.channels||{},n==="telegram"){let s=await me([{type:"text",name:"accountName",message:"Account name (e.g. 'default', 'devops')"},{type:"text",name:"token",message:"Bot token (from @BotFather)"},{type:"select",name:"agentBinding",message:"Bind to agent",choices:t}]);if(!s.accountName||!s.token)return;try{let r=await(await fetch(`https://api.telegram.org/bot${s.token}/getMe`)).json();if(!r.ok)throw new Error(r.description);console.log(h.green(` Bot verified: @${r.result.username}`));let a=`@${r.result.username}`,c=e.agents[s.agentBinding].mentions||[];c.includes(a)||(c.push(a,r.result.username),e.agents[s.agentBinding].mentions=c)}catch(i){console.log(h.red(` Invalid token: ${i.message}`));return}e.channels.telegram=e.channels.telegram||{enabled:!1,accounts:{},policy:{dm:"pair",group:"mention-required"}},e.channels.telegram.enabled=!0,e.channels.telegram.accounts[s.accountName]={token:s.token,agentBinding:s.agentBinding},await ye(e),console.log(h.green(` Telegram "${s.accountName}" added -> ${s.agentBinding}`))}if(n==="whatsapp"){let s=await me([{type:"select",name:"defaultAgent",message:"Default agent for WhatsApp messages",choices:t},{type:"text",name:"sessionDir",message:"Session directory",initial:".agentx/whatsapp-sessions"},{type:"confirm",name:"addRoute",message:"Add a contact/group route?",initial:!0}]);if(!s.defaultAgent)return;let i=[],r=s.addRoute;for(;r;){let a=await me([{type:"select",name:"type",message:"Route type",choices:[{title:"Contact (phone number)",value:"contact"},{title:"Group (name match)",value:"group"}]},{type:"text",name:"value",message:c=>c==="contact"?"Phone number (e.g. +21624309128)":"Group name (partial match)"},{type:"select",name:"agent",message:"Route to agent",choices:t},{type:"confirm",name:"more",message:"Add another route?",initial:!1}]);a.value&&a.agent&&i.push({[a.type]:a.value,agent:a.agent}),r=a.more}e.channels.whatsapp={enabled:!0,sessionDir:s.sessionDir,defaultAgent:s.defaultAgent,routes:i},await ye(e),Ne(_(process.cwd(),s.sessionDir),{recursive:!0}),console.log(h.green(` WhatsApp enabled (${i.length} routes, default: ${s.defaultAgent})`)),console.log(h.dim(" Start daemon to scan QR code: agentx daemon start"))}if(n==="discord"){let s=await me([{type:"text",name:"token",message:"Discord bot token (from developer portal)"},{type:"select",name:"agentBinding",message:"Bind to agent",choices:t}]);if(!s.token)return;e.channels.discord={enabled:!0,token:s.token,agentBinding:s.agentBinding},await ye(e),console.log(h.green(` Discord added -> ${s.agentBinding}`))}if(n==="gitlab"){let s=await me([{type:"text",name:"host",message:"GitLab instance URL",initial:"https://gitlab.example.com"},{type:"text",name:"token",message:"GitLab API token (PRIVATE-TOKEN)"},{type:"number",name:"webhookPort",message:"Webhook listen port",initial:18810},{type:"text",name:"webhookSecret",message:"Webhook secret (X-Gitlab-Token, optional)"},{type:"confirm",name:"addRoute",message:"Add a project route?",initial:!0}]);if(!s.token)return;let i=[],r=s.addRoute;for(;r;){let a=await me([{type:"text",name:"project",message:"GitLab project path (e.g. group/project, or * for default)"},{type:"select",name:"agent",message:"Route to agent",choices:t},{type:"confirm",name:"more",message:"Add another route?",initial:!1}]);a.project&&a.agent&&i.push({project:a.project,agent:a.agent}),r=a.more}e.channels.gitlab={enabled:!0,host:s.host,token:s.token,webhookPort:s.webhookPort,webhookSecret:s.webhookSecret||void 0,routes:i},await ye(e),console.log(h.green(` GitLab enabled (${i.length} routes, webhook :${s.webhookPort})`)),console.log(h.dim(` Add webhook in GitLab: ${s.host}/<project>/-/hooks`)),console.log(h.dim(` URL: http://your-server:${s.webhookPort}/`)),s.webhookSecret&&console.log(h.dim(` Secret token: ${s.webhookSecret}`)),console.log(h.dim(" Events: Comments, Issues, Merge requests, Pipeline"))}console.log(h.dim(" Restart daemon to apply: agentx daemon stop && agentx daemon start")),console.log()}});var Ro=new _e().name("cron").description("manage cron jobs \u2014 add, list, enable, disable");Ro.command("list").alias("ls").description("list cron jobs").action(()=>{let e=Z();console.log();for(let[o,t]of Object.entries(e.crons||{})){let n=t.enabled?h.green("\u25CF"):h.dim("\u25CB");console.log(` ${n} ${h.cyan(o)} \u2014 ${t.schedule} (${t.timezone})`),console.log(h.dim(` agent: ${t.agent} | timeout: ${t.timeout}s`))}Object.keys(e.crons||{}).length||console.log(h.dim(" No cron jobs configured")),console.log()});Ro.command("add").description("add a cron job").action(async()=>{let e=Z(),o=Object.keys(e.agents||{}),t=await me([{type:"text",name:"id",message:"Job ID (kebab-case)"},{type:"text",name:"schedule",message:"Cron expression (e.g. '0 9 * * *')"},{type:"text",name:"timezone",message:"Timezone",initial:"UTC"},{type:"select",name:"agent",message:"Agent",choices:o.map(n=>({title:n,value:n}))},{type:"text",name:"prompt",message:"Task prompt"},{type:"number",name:"timeout",message:"Timeout (seconds)",initial:600},{type:"confirm",name:"enabled",message:"Enable now?",initial:!1}]);!t.id||!t.schedule||(e.crons=e.crons||{},e.crons[t.id]={enabled:t.enabled,schedule:t.schedule,timezone:t.timezone,agent:t.agent,prompt:t.prompt,timeout:t.timeout,onError:["log"]},await ye(e),console.log(h.green(` Cron "${t.id}" added (${t.enabled?"enabled":"disabled"})`)),console.log())});Ro.command("enable <id>").description("enable a cron job").action(async e=>{let o=Z();if(!o.crons?.[e]){console.log(h.red(` Cron "${e}" not found`));return}o.crons[e].enabled=!0,await ye(o),console.log(h.green(` Cron "${e}" enabled`))});Ro.command("disable <id>").description("disable a cron job").action(async e=>{let o=Z();if(!o.crons?.[e]){console.log(h.red(` Cron "${e}" not found`));return}o.crons[e].enabled=!1,await ye(o),console.log(h.green(` Cron "${e}" disabled`))});var To=new _e().name("mesh").description("manage mesh peers \u2014 add, list, remove");To.command("list").alias("ls").description("list mesh peers").action(async()=>{let e=Z();if(console.log(),!e.mesh?.enabled)console.log(h.dim(" Mesh: disabled"));else{console.log(h.bold(" Mesh peers:"));for(let o of e.mesh.peers||[]){let t=h.red("unreachable");try{let s=await(await fetch(`${o.url}/health`,{signal:AbortSignal.timeout(3e3)})).json();t=h.green(`healthy \u2014 ${s.agents?.length||0} agents`)}catch{}console.log(` ${h.cyan(o.name)} ${h.dim(o.url)} ${t}`)}}console.log()});To.command("add").description("add a mesh peer (another agentx server)").action(async()=>{let e=Z(),o=await me([{type:"text",name:"name",message:"Peer name (e.g. 'server-2')"},{type:"text",name:"url",message:"Peer URL (e.g. 'http://100.67.108.119:18800')"},{type:"text",name:"token",message:"Auth token (optional, press enter to skip)"}]);if(!(!o.name||!o.url)){try{let n=await(await fetch(`${o.url}/.well-known/agent-card.json`,{signal:AbortSignal.timeout(5e3)})).json();console.log(h.green(` Connected: ${n.name} (${n.skills?.length||0} agents)`))}catch{console.log(h.yellow(" Could not reach peer (adding anyway)"))}e.mesh=e.mesh||{enabled:!1,peers:[],discovery:"static",healthCheck:{interval:60,timeout:10}},e.mesh.enabled=!0,e.mesh.peers=e.mesh.peers||[],e.mesh.peers.push({url:o.url,name:o.name,...o.token?{token:o.token}:{}}),await ye(e),console.log(h.green(` Peer "${o.name}" added`)),console.log()}});To.command("remove <name>").alias("rm").description("remove a mesh peer").action(async e=>{let o=Z();o.mesh?.peers&&(o.mesh.peers=o.mesh.peers.filter(t=>t.name!==e),o.mesh.peers.length===0&&(o.mesh.enabled=!1),await ye(o),console.log(h.green(` Peer "${e}" removed`)))});To.command("health").description("tune mesh peer health checks (interval + timeout, in seconds)").option("--interval <s>","seconds between health probes (default 60)").option("--timeout <s>","per-probe timeout in seconds (default 10)").option("--show","just print the current values").action(async e=>{let o=Z();if(o.mesh=o.mesh||{},o.mesh.healthCheck=o.mesh.healthCheck||{},e.show){console.log(` interval ${o.mesh.healthCheck.interval??60}s`),console.log(` timeout ${o.mesh.healthCheck.timeout??10}s`);return}let t=[];if(e.interval!==void 0){let n=parseInt(e.interval,10);(!Number.isFinite(n)||n<5||n>3600)&&(console.log(h.red(" --interval must be 5..3600 seconds")),process.exit(1)),o.mesh.healthCheck.interval=n,t.push(`interval=${n}s`)}if(e.timeout!==void 0){let n=parseInt(e.timeout,10);(!Number.isFinite(n)||n<1||n>60)&&(console.log(h.red(" --timeout must be 1..60 seconds")),process.exit(1)),o.mesh.healthCheck.timeout=n,t.push(`timeout=${n}s`)}t.length===0&&(console.log(h.yellow(" no changes \u2014 pass --interval and/or --timeout, or --show")),process.exit(1)),await ye(o),console.log(h.green(` \u2713 mesh.healthCheck: ${t.join(", ")}`)),console.log(h.dim(" Restart the daemon for the new cadence to take effect."))});var Do=new _e().name("skill").description("manage skills \u2014 add to agent(s), list");Do.command("add <skillPath>").description("add a skill to agent(s)").option("-a, --agent <agents...>","target agent ID(s)").option("--all","add to all agents").action(async(e,o)=>{let t=Z(),n=_(e);if(!ne(n)){console.log(h.red(` Skill not found: ${n}`));return}let s=ne(Ge(n,"SKILL.md"))?n.split("/").pop():n.replace(/\/SKILL\.md$/,"").split("/").pop(),i;if(o.all)i=Object.keys(t.agents||{});else if(o.agent)i=o.agent;else{let r=Object.keys(t.agents||{});i=(await me({type:"multiselect",name:"agents",message:"Install to which agents?",choices:r.map(c=>({title:`${c} (${t.agents[c].name})`,value:c}))})).agents||[]}for(let r of i){let a=t.agents[r];if(!a){console.log(h.yellow(` Agent "${r}" not found, skipping`));continue}let c=_(a.workspace,".claude/skills",s);Ne(c,{recursive:!0}),ne(Ge(n,"SKILL.md"))?ln(n,c,{recursive:!0}):n.endsWith(".md")&&ln(n,Ge(c,"SKILL.md")),console.log(h.green(` ${s} -> ${r} (${a.workspace})`))}console.log()});Do.command("list").alias("ls").description("list skills per agent").action(()=>{let e=Z();console.log();for(let[o,t]of Object.entries(e.agents||{})){let n=_(t.workspace,".claude/skills"),s=ne(n)?gn(n).filter(i=>!i.startsWith(".")):[];console.log(` ${h.cyan(o)}: ${s.length?s.join(", "):h.dim("(none)")}`)}console.log()});Do.command("sync <name>").description("redeploy a skill's SKILL.md from source to agent workspaces (by default, only those that already have it)").option("--source <path>","explicit source path (defaults to src/<name>/SKILL.md or skills/<name>/SKILL.md)").option("--agent <id>","restrict to a single agent's workspace").option("--all-workspaces","also seed into workspaces that don't have the skill yet").option("--dry-run","report what would change, don't write").action(async(e,o)=>{let{createHash:t}=await import("node:crypto"),n=process.cwd(),s=o.source?[o.source]:[_(n,"src",e,"SKILL.md"),_(n,"skills",e,"SKILL.md")],i=s.find(v=>ne(v));if(!i){console.log(h.red(` No SKILL.md found for "${e}". Searched:`));for(let v of s)console.log(h.dim(` ${v}`));return}let r=Ye(i,"utf-8"),a=t("sha256").update(r).digest("hex").slice(0,12),c=(r.match(/^version:\s*(.+)$/m)?.[1]||"?").trim(),d;try{d=L()}catch(v){console.log(h.red(` Can't load agentx.json: ${v.message}`));return}let g=Object.entries(d.agents||{}),l=o.agent?g.filter(([v])=>v===o.agent):g;if(l.length===0){console.log(h.yellow(o.agent?` Agent "${o.agent}" not in config.`:" No agents in config."));return}console.log(),console.log(h.bold(` Skill sync \u2014 ${e}`)),console.log(h.dim(` Source: ${i}`)),console.log(h.dim(` Version: ${c} \xB7 hash ${a}`)),console.log(h.dim(` Mode: ${o.dryRun?"dry-run":"commit"}${o.allWorkspaces?" \xB7 seed missing":" \xB7 only-existing"}`)),console.log();let u=0,m=0,f=0,y=0;for(let[v,b]of l){let S=b.workspace;if(!S||!ne(S)){console.log(` ${h.yellow("\u2717")} ${v.padEnd(22)} ${h.dim("(workspace missing: "+(S||"undefined")+")")}`),y++;continue}let x=_(S,".claude/skills",e),k=_(x,"SKILL.md");if(!ne(k)){if(!o.allWorkspaces){console.log(` ${h.dim("\xB7")} ${v.padEnd(22)} ${h.dim("(skipped \u2014 no existing copy; use --all-workspaces to seed)")}`),y++;continue}o.dryRun||(Ne(x,{recursive:!0}),Co(k,r)),console.log(` ${h.green("+")} ${v.padEnd(22)} ${h.dim("\u2192 seeded v"+c)}`),u++;continue}let A=Ye(k,"utf-8");if(t("sha256").update(A).digest("hex").slice(0,12)===a){console.log(` ${h.dim("=")} ${v.padEnd(22)} ${h.dim("up to date (v"+c+")")}`),f++;continue}let J=(A.match(/^version:\s*(.+)$/m)?.[1]||"?").trim();o.dryRun||Co(k,r),console.log(` ${h.yellow("\u21BB")} ${v.padEnd(22)} ${h.dim(`v${J} \u2192 v${c}`)}`),m++}console.log();let $=o.dryRun?"would":"did";console.log(h.dim(` Summary: ${$} seed ${u}, ${$} update ${m}, ${f} already current, ${y} skipped.`)),o.dryRun&&(u>0||m>0)&&console.log(h.dim(" Dry-run \u2014 rerun without --dry-run to write.")),console.log()});Do.command("audit").description("lint installed skills against the references registry; exits non-zero on FAILING").option("--cwd <cwd>","where to load skills from",process.cwd()).option("--references-cwd <cwd>","where to load references and recipes from (defaults to --cwd; useful when references live in the agentx repo and skills live under ~/.claude)").option("--json","emit JSON instead of a human report").option("--workspace <id>","audit a single agent workspace by id (skills + references; overrides --cwd)").option("--all-workspaces","audit every agent workspace in agentx.json; references default to the current cwd").action(async e=>{let{loadLocalSkills:o}=await import("./loader-5CA3O5GM.js"),{auditAll:t}=await import("./audit-JU3HWISB.js"),{loadReferences:n}=await import("./loader-6IHUCPVO.js"),{loadRecipes:s}=await import("./recipes-EPLE2SNT.js"),i=[];if(e.workspace){let m=Z().agents?.[e.workspace];m||(console.log(h.red(` Agent "${e.workspace}" not found in agentx.json`)),process.exit(1)),i.push(_(m.workspace))}else if(e.allWorkspaces){let u=Z();for(let m of Object.values(u.agents||{}))m?.workspace&&i.push(_(m.workspace))}else i.push(_(e.cwd));let r=_(e.referencesCwd||e.cwd),c=(await Promise.all(i.map(u=>o(u)))).flat(),[d,g]=await Promise.all([n(r),s(r)]),l=t({skills:c,references:d,recipes:g});if(e.json)console.log(JSON.stringify(l.map(u=>({name:u.name,verdict:u.verdict,reasons:u.reasons,path:u.skill?.path})),null,2));else{let u={PASS:0,REVIEW:0,FAILING:0};for(let m of l)u[m.verdict]++;console.log();for(let m of l){let f=m.verdict==="PASS"?h.green("PASS "):m.verdict==="REVIEW"?h.yellow("REVIEW "):h.red("FAILING");console.log(` ${f} ${h.bold(m.name)}${m.skill?.path?h.dim(` (${m.skill.path})`):""}`);for(let y of m.reasons)console.log(` ${h.dim("\xB7")} ${y}`)}console.log(),console.log(` ${h.green(`${u.PASS} PASS`)} ${h.yellow(`${u.REVIEW} REVIEW`)} ${h.red(`${u.FAILING} FAILING`)}`),console.log()}process.exit(l.some(u=>u.verdict==="FAILING")?1:0)});var Xo=new _e().name("references").alias("refs").description("manage the deterministic references registry \u2014 facts cited by skills and resolved into agent context");Xo.command("init <namespace>").description("scaffold .agentx/references/<namespace>/ from the example template (operator-private)").option("--cwd <cwd>","working directory",process.cwd()).option("--force","overwrite existing files").action((e,o)=>{/^[a-z0-9][a-z0-9-]*$/.test(e)||(console.log(h.red(` Invalid namespace "${e}". Use lowercase letters, digits, and hyphens.`)),process.exit(1));let t=_(o.cwd),n=_(t,"references/example"),s=_(t,"references/recipes/example.yaml");ne(n)||(console.log(h.red(` Example template not found at ${n}. Are you running this from the agentx repo root?`)),process.exit(1));let i=_(t,".agentx/references",e),r=_(t,".agentx/references/recipes");Ne(i,{recursive:!0}),Ne(r,{recursive:!0});let a=gn(n).filter(l=>l.endsWith(".yaml")||l.endsWith(".yml")),c=0,d=0;for(let l of a){let u=Ge(i,l);if(ne(u)&&!o.force){console.log(h.dim(` skip ${u} (exists; --force to overwrite)`)),d++;continue}let f=Ye(Ge(n,l),"utf-8").replace(/^namespace:\s*example\./m,`namespace: ${e}.`);Co(u,f),console.log(h.green(` + ${u}`)),c++}let g=Ge(r,`${e}.yaml`);if(ne(g)&&!o.force)console.log(h.dim(` skip ${g} (exists; --force to overwrite)`)),d++;else if(ne(s)){let l=Ye(s,"utf-8").replaceAll("example.",`${e}.`);Co(g,l),console.log(h.green(` + ${g}`)),c++}console.log(),console.log(h.dim(` ${c} written, ${d} skipped. Edit the placeholders, then:`)),console.log(h.dim(" 1. set contextReferences: true on the relevant agents in agentx.json")),console.log(h.dim(" 2. agentx skill audit # verify references resolve")),console.log(h.dim(" 3. systemctl restart agentx # pick up the new registry")),console.log()});Xo.command("discover <namespace>").description("scan installed skills and write detected facts to .agentx/references/<namespace>/").option("--cwd <cwd>","where to scan for skills",process.cwd()).option("--references-cwd <cwd>","where to WRITE the YAML files (defaults to --cwd; useful when skills live under ~/.claude and references live in the agentx repo)").option("--from <skills>","comma-separated skill name/tag substrings to filter by (default: namespace itself)").option("--gitlab-host <url>","validate project URLs against this host (e.g. https://gitlab.noqta.tn)").option("--write","write the YAML files (default: dry-run preview)").option("--force","overwrite existing files when --write is set").action(async(e,o)=>{/^[a-z0-9][a-z0-9-]*$/.test(e)||(console.log(h.red(` Invalid namespace "${e}". Use lowercase letters, digits, and hyphens.`)),process.exit(1));let{loadLocalSkills:t}=await import("./loader-5CA3O5GM.js"),{discoverFromSkills:n,renderDiscovery:s}=await import("./discover-XJXQXXIO.js"),i=_(o.cwd),r=o.from?String(o.from).split(",").map(y=>y.trim()).filter(Boolean):[e],a=await t(i),c=n(a,{namespace:e,filter:r,gitlabHost:o.gitlabHost});if(console.log(),c.scannedSkills.length===0){console.log(h.yellow(` No skills matched filter [${r.join(", ")}] under ${i}`)),console.log(h.dim(" Tip: pass --from <skill-name>,<other> to widen the filter.")),console.log();return}console.log(h.dim(` Scanned skills: ${c.scannedSkills.join(", ")}`));let d=Object.fromEntries(Object.entries(c.byKind).map(([y,$])=>[y,$.length]));console.log(` Discovered: ssh=${d.ssh} gitlab=${d.gitlab} paths=${d.path} contacts=${d.contact}`);let g=s(c,e),l=_(o.referencesCwd||o.cwd),u=_(l,".agentx/references",e);if(!o.write){console.log(),console.log(h.dim(` Dry-run \u2014 would write to ${u}/`));for(let[y,$]of Object.entries(g))console.log(h.cyan(`
14
+ --- ${y} ---`)),console.log($.split(`
15
+ `).slice(0,30).map(v=>` ${v}`).join(`
16
+ `)),$.split(`
17
+ `).length>30&&console.log(h.dim(` \u2026 (${$.split(`
18
+ `).length-30} more lines truncated in preview)`));console.log(),console.log(h.dim(` Re-run with --write to commit these to ${u}/`)),console.log();return}Ne(u,{recursive:!0});let m=0,f=0;for(let[y,$]of Object.entries(g)){let v=Ge(u,y);if(ne(v)&&!o.force){console.log(h.dim(` skip ${v} (exists; --force to overwrite)`)),f++;continue}Co(v,$),console.log(h.green(` + ${v}`)),m++}console.log(),console.log(h.dim(` ${m} written, ${f} skipped. Review every card \u2014 flagged ones (tags include "needs-review") are best-effort guesses.`)),console.log(h.dim(" Then: agentx skill audit \u2192 set contextReferences: true on the relevant agents \u2192 systemctl restart agentx")),console.log()});Xo.command("list").alias("ls").description("list every loaded reference (debug)").option("--cwd <cwd>","working directory",process.cwd()).option("--json","emit JSON").action(async e=>{let{loadReferences:o}=await import("./loader-6IHUCPVO.js"),t=await o(_(e.cwd)),n=[...t.byId.values()].sort((s,i)=>s.id.localeCompare(i.id));if(e.json){console.log(JSON.stringify(n,null,2));return}if(console.log(),n.length===0){console.log(h.dim(" (no references loaded \u2014 try `agentx references init <namespace>`)")),console.log();return}for(let s of n){let i=t.sourceById.get(s.id);console.log(` ${h.cyan(s.id)} ${h.dim(`(${s.kind})`)}`),console.log(` ${s.summary}`),i&&console.log(h.dim(` ${i}`))}console.log(),console.log(h.dim(` ${n.length} reference(s) loaded.`)),console.log()});var mn=new _e().name("hook").description("manage hooks \u2014 add to agent workspace");mn.command("add <agent>").description("add a hook to an agent's workspace settings").action(async e=>{let t=Z().agents?.[e];if(!t){console.log(h.red(` Agent "${e}" not found`));return}let n=_(t.workspace,".claude/settings.json"),s=ne(n)?JSON.parse(Ye(n,"utf-8")):{},i=await me([{type:"select",name:"event",message:"Hook event",choices:[{title:"PreToolUse \u2014 before any tool runs",value:"PreToolUse"},{title:"PostToolUse \u2014 after any tool runs",value:"PostToolUse"},{title:"SessionStart \u2014 when session begins",value:"SessionStart"},{title:"Notification \u2014 on notifications",value:"Notification"},{title:"Stop \u2014 before session stops",value:"Stop"}]},{type:"select",name:"type",message:"Hook type",choices:[{title:"command \u2014 run a shell command",value:"command"},{title:"http \u2014 POST to a URL",value:"http"}]},{type:"text",name:"value",message:c=>c==="command"?"Shell command":"URL"},{type:"text",name:"matcher",message:"Matcher regex (optional, e.g. 'Bash' to match tool)"}]);if(!i.event||!i.value)return;s.hooks=s.hooks||{},s.hooks[i.event]=s.hooks[i.event]||[];let a={hooks:[{type:i.type,...i.type==="command"?{command:i.value}:{url:i.value}}]};i.matcher&&(a.matcher=i.matcher),s.hooks[i.event].push(a),Ne(_(t.workspace,".claude"),{recursive:!0}),Co(n,JSON.stringify(s,null,2)),console.log(h.green(` Hook added to ${e}: ${i.event} -> ${i.type}`)),console.log()});var pn=new _e().name("migrate").description("import configuration from OpenClaw or other tools");pn.command("openclaw [configPath]").description("import agents, channels, and crons from OpenClaw config").option("--dry-run","show what would be imported without writing").action(async(e,o)=>{let t=[e,_(process.env.HOME||"",".openclaw/openclaw.json"),_(process.env.HOME||"",".openclaw/clawdbot.json")].filter(Boolean),n;for(let m of t)if(m&&ne(m)){n=m;break}if(!n){console.log(h.red(" OpenClaw config not found. Pass the path: agentx migrate openclaw /path/to/config.json"));return}console.log(h.dim(` Reading: ${n}`));let s=JSON.parse(Ye(n,"utf-8")),i=ne(_(process.cwd(),"agentx.json"))?Z():{node:{id:"imported",name:"Imported",bind:"127.0.0.1:18800"},providers:{},agents:{},channels:{telegram:{enabled:!1,accounts:{},policy:{dm:"pair",group:"mention-required"}},whatsapp:{enabled:!1}},crons:{},mesh:{enabled:!1,peers:[]}},r=s.agents?.list||[],a=s.bindings||[],c={};for(let m of a)m.match?.channel==="telegram"&&(c[m.match.accountId]=m.agentId);r.find(m=>m.id==="main"||m.default)&&(c.default="main");let d=0;for(let m of r){if(m.workspace?.startsWith("/data"))continue;let f=m.id.replace(/-agent$/,"").replace(/^main$/,"main-agent"),y=m.groupChat?.mentionPatterns||[];if(y.some($=>$===f)||y.push(f),i.agents[f]={name:m.name,workspace:m.workspace,tier:"claude-code",model:"claude-sonnet-4-6",mentions:y,maxConcurrent:m.id==="main"?2:1,systemPrompt:`You are ${m.name} on AgentX.`,permissionMode:"bypassPermissions"},d++,!o.dryRun){Ne(_(m.workspace,".claude/skills"),{recursive:!0});let{setupWorkspace:$}=await import("./workspace-setup-TBQAHYHJ.js");$(f,i.agents[f])}}console.log(` ${h.green(d)} agents imported (workspaces set up with Claude Code best practices)`);let g=s.channels?.telegram?.accounts||{},l=0;for(let[m,f]of Object.entries(g)){if(!f.botToken)continue;let $=c[m]?.replace(/-agent$/,"").replace(/^main$/,"main-agent")||"default";i.channels.telegram.accounts[m]={token:f.botToken,agentBinding:$},i.channels.telegram.enabled=!0,l++}console.log(` ${h.green(l)} Telegram accounts imported`);let u=_(n,"../cron/jobs.json");if(ne(u)){let m=JSON.parse(Ye(u,"utf-8")),f=0;for(let y of m.jobs||[]){let $=y.name||y.id,v=y.agentId?.replace(/-agent$/,"").replace(/^main$/,"main-agent")||"default";i.crons[$]={enabled:y.enabled||!1,schedule:y.schedule?.expr||"0 9 * * *",timezone:y.schedule?.tz||"UTC",agent:v,prompt:y.payload?.message||"",timeout:y.payload?.timeoutSeconds||600,onError:["log"]},f++}console.log(` ${h.green(f)} cron jobs imported`)}o.dryRun?(console.log(h.yellow(`
19
+ Dry run \u2014 nothing written`)),console.log(JSON.stringify(i,null,2))):(await ye(i),console.log(h.green(`
20
+ Migration complete! Review agentx.json and run: agentx daemon start`))),console.log()});var Ke=new _e().name("config").description("validate and inspect configuration");Ke.command("check").description("validate agentx.json and check all workspaces").option("-c, --config <path>","path to agentx.json").action(async e=>{console.log();try{let o=L(e.config);console.log(h.green(" \u2713 Config valid")),console.log(h.dim(` Node: ${o.node.name} (${o.node.id})`)),console.log(h.dim(` Bind: ${o.node.bind}`)),console.log(h.dim(` Agents: ${Object.keys(o.agents).length}`)),console.log(h.dim(` Crons: ${Object.keys(o.crons).length}`)),console.log(h.dim(` Mesh peers: ${o.mesh.peers.length}`));let t=Object.keys(o.channels.telegram.accounts).length;console.log(h.dim(` Telegram: ${o.channels.telegram.enabled?`${t} accounts`:"disabled"}`)),console.log(h.dim(` WhatsApp: ${o.channels.whatsapp.enabled?`${o.channels.whatsapp.routes.length} routes`:"disabled"}`));let n=yt(o);if(n.length){console.log(),console.log(h.yellow(" Warnings:"));for(let s of n)console.log(h.yellow(` \u26A0 ${s}`))}else console.log(h.green(" \u2713 All workspaces valid"));console.log();for(let[s,i]of Object.entries(o.agents)){let r=i.workspace,a=ne(_(r,".claude")),c=ne(_(r,"CLAUDE.md")),d=ne(_(r,".claude/settings.json")),g=ne(_(r,".claude/skills"))?gn(_(r,".claude/skills")).filter(u=>!u.startsWith(".")).length:0,l=[a?h.green("\u2713"):h.red("\u2717"),".claude",c?h.green("\u2713"):h.yellow("\u25CB"),"CLAUDE.md",d?h.green("\u2713"):h.yellow("\u25CB"),"settings",`${g} skills`];console.log(` ${h.cyan(s)}: ${l.join(" ")}`)}}catch(o){console.log(h.red(` \u2717 ${o.message}`))}console.log()});Ke.command("show").description("print the resolved configuration").option("-c, --config <path>","path to agentx.json").action(e=>{try{let o=L(e.config);console.log(JSON.stringify(o,null,2))}catch(o){console.log(h.red(o.message))}});function Yr(e,o){if(o)return e;try{return JSON.parse(e)}catch{}return e.includes(",")&&!e.includes(" ")?e.split(",").map(t=>t.trim()).filter(Boolean):e}function dn(e){return e===void 0?h.dim("(unset)"):typeof e=="string"?e:JSON.stringify(e,null,2)}Ke.command("get <path>").description("read a config value by dot-path (e.g. agents.devops.model)").option("-c, --config <path>","path to agentx.json").option("--raw","show ${VAR} tokens instead of env-expanded values").option("--json","output as JSON (for scripting)").action(async(e,o)=>{let{readFileSync:t,existsSync:n}=await import("fs"),{resolve:s}=await import("path"),{getAtPath:i}=await import("./config-mutator-POCOYYJ4.js"),{expandEnvVars:r}=await import("./config-7FSZFRTQ.js"),a=o.config||s(process.cwd(),"agentx.json");n(a)||(console.log(h.red(` No config at ${a}`)),process.exit(1));let c=JSON.parse(t(a,"utf-8")),d=o.raw?c:r(c),g=i(d,e);o.json?console.log(JSON.stringify(g)):console.log(dn(g))});Ke.command("set <path> <value>").description("write a config value by dot-path; validates against schema and hot-reloads the daemon").option("-c, --config <path>","path to agentx.json").option("--string","treat the value as a literal string (skip JSON parsing)").option("--dry-run","validate and diff without writing").action(async(e,o,t)=>{let{applyConfigMutation:n,setAtPath:s,getAtPath:i}=await import("./config-mutator-POCOYYJ4.js"),r=Yr(o,!!t.string),a=await n(d=>{s(d,e,r)},{configPath:t.config,dryRun:!!t.dryRun});a.success||(console.log(h.red(` \u2717 ${a.error}`)),process.exit(1));let c=i(a.after,e);t.dryRun?console.log(h.yellow(` (dry-run) ${h.cyan(e)} would become: ${dn(c)}`)):(console.log(h.green(` \u2713 ${h.cyan(e)} = ${dn(c)}`)),a.reloaded?console.log(h.dim(" Daemon hot-reloaded.")):a.reloadSkipped&&!/ECONNREFUSED|unreachable|fetch failed/i.test(a.reloadSkipped)&&console.log(h.dim(` (daemon reload skipped: ${a.reloadSkipped})`)))});Ke.command("unset <path>").description("remove a config value by dot-path (validates + hot-reloads)").option("-c, --config <path>","path to agentx.json").option("--dry-run","validate without writing").action(async(e,o)=>{let{applyConfigMutation:t,getAtPath:n,unsetAtPath:s}=await import("./config-mutator-POCOYYJ4.js"),i=await t(a=>{n(a,e)!==void 0&&s(a,e)},{configPath:o.config,dryRun:!!o.dryRun});if(i.success||(console.log(h.red(` \u2717 ${i.error}`)),process.exit(1)),n(i.after,e)!==void 0){console.log(h.dim(` (no-op) ${h.cyan(e)} was not set`));return}o.dryRun?console.log(h.yellow(` (dry-run) ${h.cyan(e)} would be removed`)):(console.log(h.green(` \u2713 ${h.cyan(e)} removed`)),i.reloaded&&console.log(h.dim(" Daemon hot-reloaded.")))});Ke.command("governance").description("show resolved governance flags (read-only; flags read once at startup)").option("-c, --config <path>","path to agentx.json").option("--json","emit JSON").action(e=>{let o=!1,t=0,n;try{let l=L(e.config);o=!!l.business?.enabled,t=(l.business?.projects??[]).length}catch(l){n=l?.message??String(l)}let s=(process.env.INTENT_LEDGER_MODE||"off").toLowerCase(),i=(process.env.INTENT_PM_GATE_ENABLED||"").toLowerCase(),r=i==="true"||i==="1"||i==="yes",a=r&&o,c=["off","shadow","authoritative"].includes(s);if(e.json){console.log(JSON.stringify({ledger:{mode:s,valid:c},pmGate:{envSet:r,businessEnabled:o,active:a,projects:t},configError:n},null,2));return}console.log(),console.log(h.bold(" Governance flags")),console.log(),n&&(console.log(h.yellow(` Note: agentx.json couldn't be loaded \u2014 ${n}`)),console.log());let d=s==="authoritative"?h.green:s==="shadow"?h.cyan:s==="off"?h.dim:h.red;console.log(` INTENT_LEDGER_MODE ${d(s)}${c?"":h.red(" (invalid \u2014 must be off|shadow|authoritative)")}`);let g=a?h.green("active"):r?h.yellow("env=true but business.enabled=false \u2192 inactive"):h.dim("disabled");console.log(` INTENT_PM_GATE_ENABLED ${g}`),console.log(h.dim(` business.enabled ${o?h.green("true"):h.dim("false")}`)),console.log(h.dim(` business.projects[] ${t}`)),console.log(),console.log(h.dim(" These flags are read once at daemon startup. Flipping requires a restart.")),console.log(h.dim(" See: https://github.com/anis-marrouchi/agentx/blob/master/docs/architecture/research-rescue-plan.md")),console.log()});import{Command as Kr}from"commander";import Ve from"chalk";import{resolve as Qe}from"path";import{existsSync as Vr}from"fs";var We=new Kr().name("db").description("explore the operational SQLite store at .agentx/db.sqlite");function mo(e){let o=Qe(e,".agentx/db.sqlite");Vr(o)||(console.log(Ve.red(` No SQLite at ${o}`)),console.log(Ve.dim(" Make sure the daemon has started since Move 2 shipped \u2014 the file is created on first boot.")),process.exit(1));let t=ps({path:o});return t||(console.log(Ve.red(` Could not open ${o}`)),console.log(Ve.dim(' If you see "NODE_MODULE_VERSION mismatch", run `pnpm rebuild better-sqlite3` under the daemon\'s Node version.')),process.exit(1)),t}function po(e,o){if(o){console.log(JSON.stringify(e,null,2));return}if(e.length===0){console.log(Ve.dim(" (no rows)"));return}let t=Object.keys(e[0]),n=t.map(i=>Math.max(i.length,...e.map(r=>String(r[i]??"").length))),s=i=>i.map((r,a)=>String(r??"").padEnd(n[a])).join(" ");console.log(s(t)),console.log(t.map((i,r)=>"-".repeat(n[r])).join(" "));for(let i of e)console.log(s(t.map(r=>i[r])))}We.command("tasks").description("recent task_history rows (one per agent task)").option("--cwd <cwd>","working directory",process.cwd()).option("-a, --agent <id>","filter by agent id").option("-d, --day <YYYY-MM-DD>","filter by start day (UTC)").option("-s, --status <status>","ok | error").option("-n, --limit <n>","max rows","20").option("--json","emit JSON").action(e=>{let o=mo(Qe(e.cwd)),t=[],n={};e.agent&&(t.push("agent_id = @agent"),n.agent=e.agent),e.day&&(t.push("started_at LIKE @day"),n.day=`${e.day}%`),e.status&&(t.push("status = @status"),n.status=e.status);let s=`
21
+ SELECT agent_id, channel, status, duration_ms,
22
+ input_tokens, output_tokens,
23
+ substr(message_preview, 1, 60) AS message,
24
+ started_at
25
+ FROM task_history
26
+ ${t.length?"WHERE "+t.join(" AND "):""}
27
+ ORDER BY started_at DESC
28
+ LIMIT @limit
29
+ `,i=o.prepare(s).all({...n,limit:Number(e.limit)});po(i,!!e.json)});We.command("rotations").description("session rotation events (stale | tier-2 | max-turns)").option("--cwd <cwd>","working directory",process.cwd()).option("-a, --agent <id>","filter by agent id").option("-r, --reason <r>","stale | tier-2 | max-turns").option("-n, --limit <n>","max rows","20").option("--summary","group by agent + reason").option("--json","emit JSON").action(e=>{let o=mo(Qe(e.cwd)),t=[],n={};e.agent&&(t.push("agent_id = @agent"),n.agent=e.agent),e.reason&&(t.push("reason = @reason"),n.reason=e.reason);let s=t.length?"WHERE "+t.join(" AND "):"",i=e.summary?`
30
+ SELECT agent_id, reason, COUNT(*) AS n,
31
+ ROUND(AVG(last_turn_input_tokens)) AS avg_input_tokens,
32
+ MAX(last_turn_input_tokens) AS max_input_tokens
33
+ FROM rotations
34
+ ${s}
35
+ GROUP BY agent_id, reason
36
+ ORDER BY n DESC
37
+ `:`
38
+ SELECT agent_id, channel, reason, last_turn_input_tokens, rotated_at
39
+ FROM rotations
40
+ ${s}
41
+ ORDER BY rotated_at DESC
42
+ LIMIT @limit
43
+ `,r=o.prepare(i).all({...n,limit:Number(e.limit)});po(r,!!e.json)});We.command("usage").description("token usage rolled up per (agent, model, day)").option("--cwd <cwd>","working directory",process.cwd()).option("-a, --agent <id>","filter by agent id").option("-d, --day <YYYY-MM-DD>","filter by day (UTC)").option("--json","emit JSON").action(e=>{let o=mo(Qe(e.cwd)),t=[],n={};e.agent&&(t.push("agent_id = @agent"),n.agent=e.agent),e.day&&(t.push("day = @day"),n.day=e.day);let s=`
44
+ SELECT day, agent_id, model, tasks,
45
+ input_tokens AS in_tok, output_tokens AS out_tok,
46
+ cache_read_tokens AS cache_read, cache_create_tokens AS cache_create
47
+ FROM usage_daily
48
+ ${t.length?"WHERE "+t.join(" AND "):""}
49
+ ORDER BY day DESC, agent_id
50
+ `,i=o.prepare(s).all(n);po(i,!!e.json)});We.command("routes").description("inbound routing decisions captured by the pipeline trace").option("--cwd <cwd>","working directory",process.cwd()).option("-c, --channel <ch>","filter by channel (telegram, gitlab, github, ...)").option("-k, --kind <k>","match | drop").option("-n, --limit <n>","max rows","20").option("--summary","group by channel + kind + deciding stage").option("--json","emit JSON").action(e=>{let o=mo(Qe(e.cwd)),t=[],n={};e.channel&&(t.push("channel = @channel"),n.channel=e.channel),e.kind&&(t.push("kind = @kind"),n.kind=e.kind);let s=t.length?"WHERE "+t.join(" AND "):"",i=e.summary?`
51
+ SELECT channel, kind, deciding_stage, COUNT(*) AS n
52
+ FROM route_traces
53
+ ${s}
54
+ GROUP BY channel, kind, deciding_stage
55
+ ORDER BY n DESC
56
+ `:`
57
+ SELECT channel, chat_id, kind, deciding_stage, agent_id,
58
+ substr(reason, 1, 60) AS reason, at
59
+ FROM route_traces
60
+ ${s}
61
+ ORDER BY at DESC
62
+ LIMIT @limit
63
+ `,r=o.prepare(i).all({...n,limit:Number(e.limit)});po(r,!!e.json)});We.command("errors").description("failed tasks (status='error') with their error message").option("--cwd <cwd>","working directory",process.cwd()).option("-a, --agent <id>","filter by agent id").option("-n, --limit <n>","max rows","20").option("--json","emit JSON").action(e=>{let o=mo(Qe(e.cwd)),t=["status = 'error'"],n={};e.agent&&(t.push("agent_id = @agent"),n.agent=e.agent);let s=`
64
+ SELECT agent_id, channel, duration_ms,
65
+ substr(error, 1, 120) AS error,
66
+ started_at
67
+ FROM task_history
68
+ WHERE ${t.join(" AND ")}
69
+ ORDER BY started_at DESC
70
+ LIMIT @limit
71
+ `,i=o.prepare(s).all({...n,limit:Number(e.limit)});po(i,!!e.json)});We.command("tables").description("list tables + row counts").option("--cwd <cwd>","working directory",process.cwd()).option("--json","emit JSON").action(e=>{let o=mo(Qe(e.cwd)),n=o.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name").all().map(s=>({table:s.name,rows:o.prepare(`SELECT COUNT(*) AS c FROM ${s.name}`).get().c}));po(n,!!e.json)});We.command("query <sql>").description("run an arbitrary read-only SQL query (escape hatch)").option("--cwd <cwd>","working directory",process.cwd()).option("--json","emit JSON").action((e,o)=>{let t=mo(Qe(o.cwd));/^\s*(insert|update|delete|drop|alter|create|attach|vacuum|pragma)\b/i.test(e)&&(console.log(Ve.red(" Refusing to run a write/DDL statement via `agentx db query`.")),console.log(Ve.dim(" Use `sqlite3 .agentx/db.sqlite` directly if you really mean it.")),process.exit(2));try{let n=t.prepare(e).all();po(n,!!o.json)}catch(n){console.log(Ve.red(` ${n.message}`)),process.exit(1)}});import{Command as Zr}from"commander";import M from"chalk";import{mkdtempSync as ea}from"fs";import{tmpdir as oa}from"os";import{resolve as ii,join as ri}from"path";import{existsSync as ta}from"fs";import na from"better-sqlite3";function si(e,o,t,n=[]){let s=new Map;for(let c of o)s.set(c.id,c);let i=[];for(let c of t)i.push({kind:"decision",ts:c.decidedAt,decision:c});for(let c of n)i.push({kind:"resolution",ts:c.resolvedAt,resolution:c});i.sort((c,d)=>c.ts!==d.ts?c.ts-d.ts:c.kind===d.kind?0:c.kind==="decision"?-1:1);let r=[],a=0;for(let c of i){if(c.kind==="resolution"){try{e.recordResolution(c.resolution)}catch(f){r.push({eventId:c.resolution.decisionEventId,decidedBy:c.resolution.decisionDecidedBy,expected:{agentId:null,outcome:"resolved"},actual:{agentId:null,outcome:"resolution-failed"},reason:`recordResolution threw: ${f?.message??f}`})}continue}let d=c.decision,g=s.get(d.eventId);if(!g){r.push({eventId:d.eventId,decidedBy:d.decidedBy,expected:{agentId:d.agentId,outcome:d.outcome},actual:{agentId:null,outcome:"skipped"},reason:"decision references event not in snapshot"});continue}let l=Qr(d),u={id:g.id,ts:g.ts,source:g.source,sourceEventId:g.sourceEventId,project:g.project,subject:g.subject,intent:g.intent,rawJson:g.rawJson},m=ms(e,u,l,()=>d.decidedAt);a+=1,(m.outcome!==d.outcome||m.agentId!==d.agentId)&&r.push({eventId:g.id,decidedBy:d.decidedBy,expected:{agentId:d.agentId,outcome:d.outcome},actual:{agentId:m.agentId,outcome:m.outcome},reason:Xr(d,m)})}return{eventsCount:o.length,decisionsCount:a,divergences:r,skipped:[]}}function Qr(e){return{decidedBy:e.decidedBy,decide:()=>e.outcome==="deduped"?null:{agentId:e.agentId,outcome:e.outcome,reason:e.reason}}}function Xr(e,o){return e.outcome==="dispatched"&&o.outcome==="deduped"?"active-task-safety-now-fires (slot held by earlier decision)":e.outcome==="deduped"&&o.outcome==="halted"?"policy-returned-null (recorded as deduped means the live system hit active-task safety; playback policy returned null because deduped is illegal at the policy layer \u2014 investigate whether the in-flight decision is present in the snapshot)":e.agentId!==o.agentId?`agent-changed (recorded=${e.agentId} replayed=${o.agentId})`:e.outcome!==o.outcome?`outcome-changed (recorded=${e.outcome} replayed=${o.outcome})`:"unknown"}var Xe=new Zr().name("ledger").description("inspect the intent ledger (.agentx/intent/ledger.sqlite)");function No(e){let o=ii(e.cwd??process.cwd()),t=ii(o,e.path??".agentx/intent/ledger.sqlite");ta(t)||(console.log(M.red(` No ledger at ${t}`)),console.log(M.dim(' Make sure the daemon has run with INTENT_LEDGER_MODE != "off". The file is created on first ledger write.')),process.exit(1));try{return new na(t,{readonly:!0})}catch(n){console.log(M.red(` Could not open ${t}: ${n.message}`)),/NODE_MODULE_VERSION/.test(n.message)&&console.log(M.dim(" Run `pnpm rebuild better-sqlite3` under the daemon's Node version.")),process.exit(1)}}function uo(e,o){if(o){console.log(JSON.stringify(e,null,2));return}if(e.length===0){console.log(M.dim(" (no rows)"));return}let t=Object.keys(e[0]),n=t.map(i=>Math.max(i.length,...e.map(r=>String(r[i]??"").length))),s=i=>i.map((r,a)=>String(r??"").padEnd(n[a])).join(" ");console.log(s(t)),console.log(t.map((i,r)=>"-".repeat(n[r])).join(" "));for(let i of e)console.log(s(t.map(r=>i[r])))}function sa(e){let o=/^(\d+)\s*([smhd])$/.exec(e.trim());if(!o)return null;let t=Number(o[1]),n=o[2];return t*(n==="s"?1e3:n==="m"?6e4:n==="h"?36e5:864e5)}function xt(e){if(!e)return;let o=sa(e);if(o===null){let t=Number(e);if(Number.isFinite(t)&&t>0)return t;console.log(M.red(` Invalid --since "${e}". Use a duration like "1h", "30m", "7d", or an ms epoch.`)),process.exit(1)}return Date.now()-o}Xe.command("stats").description("overview: events by source, decisions, divergences, in-flight count").option("--cwd <cwd>","working directory",process.cwd()).option("--path <path>","ledger path relative to cwd",".agentx/intent/ledger.sqlite").option("--since <duration>","limit to events newer than (e.g. 1h, 24h, 7d)").option("--json","emit JSON").action(e=>{let o=No(e),t=xt(e.since),n=t!==void 0?"WHERE ts >= @since":"",s=t!==void 0?"WHERE decided_at >= @since":"",i=n,r=o.prepare(`SELECT source, COUNT(*) as n FROM intent_events ${n} GROUP BY source ORDER BY n DESC`).all({since:t}),a=o.prepare(`SELECT COUNT(*) as n FROM intent_decisions ${s}`).get({since:t}).n,c=o.prepare(`SELECT outcome, COUNT(*) as n FROM intent_decisions ${s} GROUP BY outcome ORDER BY n DESC`).all({since:t}),d=o.prepare(`SELECT COUNT(*) as n FROM intent_divergences ${i}`).get({since:t}).n,g=o.prepare(`SELECT source, COUNT(*) as n FROM intent_divergences ${i} GROUP BY source ORDER BY n DESC`).all({since:t}),l=o.prepare(`
72
+ SELECT COUNT(*) as n FROM intent_decisions d
73
+ LEFT JOIN intent_resolutions r
74
+ ON r.decision_event_id = d.event_id AND r.decision_decided_by = d.decided_by
75
+ WHERE d.outcome = 'dispatched' AND r.decision_event_id IS NULL
76
+ `).get().n;if(e.json){console.log(JSON.stringify({since:t??null,eventsBySource:r,totalDecisions:a,decisionsByOutcome:c,totalDivergences:d,divergencesBySource:g,inFlight:l},null,2));return}console.log(M.bold("Events by source")),uo(r,!1),console.log(),console.log(M.bold(`Decisions: ${a}`)),uo(c,!1),console.log(),console.log(M.bold(`Divergences: ${d}`)),uo(g,!1),console.log(),console.log(M.bold(`In-flight (dispatched, no resolution): ${l}`))});Xe.command("divergences").description("recent divergence rows (newest first)").option("--cwd <cwd>","working directory",process.cwd()).option("--path <path>","ledger path relative to cwd",".agentx/intent/ledger.sqlite").option("-s, --source <name>","filter by source (telegram, gitlab, workflow, cron, mesh, github)").option("--since <duration>","limit to divergences newer than (e.g. 1h, 24h)").option("-n, --limit <n>","max rows","50").option("--json","emit JSON").action(e=>{let o=No(e),t=xt(e.since),n=[],s={limit:Number(e.limit)};e.source&&(n.push("source = @source"),s.source=e.source),t!==void 0&&(n.push("ts >= @since"),s.since=t);let i=n.length?`WHERE ${n.join(" AND ")}`:"",r=o.prepare(`
77
+ SELECT
78
+ datetime(ts/1000, 'unixepoch') AS at,
79
+ source,
80
+ decided_by,
81
+ ledger_outcome AS L_outcome,
82
+ COALESCE(ledger_agent_id, '-') AS L_agent,
83
+ legacy_outcome AS X_outcome,
84
+ COALESCE(legacy_agent_id, '-') AS X_agent,
85
+ substr(COALESCE(legacy_reason, ledger_reason, ''), 1, 60) AS reason
86
+ FROM intent_divergences
87
+ ${i}
88
+ ORDER BY ts DESC
89
+ LIMIT @limit
90
+ `).all(s);uo(r,!!e.json)});Xe.command("active").description("currently in-flight dispatched decisions (no resolution yet)").option("--cwd <cwd>","working directory",process.cwd()).option("--path <path>","ledger path relative to cwd",".agentx/intent/ledger.sqlite").option("-s, --source <name>","filter by source").option("-n, --limit <n>","max rows","50").option("--json","emit JSON").action(e=>{let o=No(e),t=["d.outcome = 'dispatched'","r.decision_event_id IS NULL"],n={limit:Number(e.limit)};e.source&&(t.push("e.source = @source"),n.source=e.source);let s=o.prepare(`
91
+ SELECT
92
+ datetime(d.decided_at/1000, 'unixepoch') AS dispatched_at,
93
+ e.source,
94
+ d.decided_by,
95
+ COALESCE(d.agent_id, '-') AS agent,
96
+ COALESCE(e.project, '-') AS project,
97
+ COALESCE(e.subject, '-') AS subject,
98
+ ROUND((strftime('%s', 'now')*1000 - d.decided_at) / 1000.0) AS age_s
99
+ FROM intent_decisions d
100
+ JOIN intent_events e ON e.id = d.event_id
101
+ LEFT JOIN intent_resolutions r
102
+ ON r.decision_event_id = d.event_id AND r.decision_decided_by = d.decided_by
103
+ WHERE ${t.join(" AND ")}
104
+ ORDER BY d.decided_at DESC
105
+ LIMIT @limit
106
+ `).all(n);uo(s,!!e.json)});Xe.command("events").description("recent events (newest first)").option("--cwd <cwd>","working directory",process.cwd()).option("--path <path>","ledger path relative to cwd",".agentx/intent/ledger.sqlite").option("-s, --source <name>","filter by source").option("-p, --project <name>","filter by project").option("--since <duration>","limit to newer than (e.g. 1h, 24h)").option("-n, --limit <n>","max rows","30").option("--json","emit JSON").action(e=>{let o=No(e),t=xt(e.since),n=[],s={limit:Number(e.limit)};e.source&&(n.push("source = @source"),s.source=e.source),e.project&&(n.push("project = @project"),s.project=e.project),t!==void 0&&(n.push("ts >= @since"),s.since=t);let i=n.length?`WHERE ${n.join(" AND ")}`:"",r=o.prepare(`
107
+ SELECT
108
+ datetime(ts/1000, 'unixepoch') AS at,
109
+ source,
110
+ COALESCE(project, '-') AS project,
111
+ COALESCE(subject, '-') AS subject,
112
+ COALESCE(intent, '-') AS intent
113
+ FROM intent_events
114
+ ${i}
115
+ ORDER BY ts DESC
116
+ LIMIT @limit
117
+ `).all(s);uo(r,!!e.json)});Xe.command("lineage <eventOrSubject>").description("walk the dispatch chain on the same (project, subject) and print every decision + resolution").option("--cwd <cwd>","working directory",process.cwd()).option("--path <path>","ledger path relative to cwd",".agentx/intent/ledger.sqlite").option("--by-subject","treat the argument as `<project>:<subject>` instead of an event id").option("--json","emit JSON").action((e,o)=>{let t=No(o),n=null,s=null;if(o.bySubject){let l=e.indexOf(":");l<0&&(console.log(M.red(' --by-subject expects "<project>:<subject>"')),process.exit(1)),n=e.slice(0,l),s=e.slice(l+1)}else{let l=t.prepare("SELECT project, subject FROM intent_events WHERE id = ?").get(e);l||(console.log(M.red(` no event with id ${e}`)),process.exit(1)),n=l.project,s=l.subject,(n===null||s===null)&&(console.log(M.yellow(` event ${e} has no (project, subject) \u2014 lineage requires both`)),n&&console.log(M.dim(` project: ${n}`)),s&&console.log(M.dim(` subject: ${s}`)),process.exit(1))}let i=t.prepare(`SELECT id, ts, source, COALESCE(intent, '-') AS intent, COALESCE(source_event_id, '-') AS source_event_id
118
+ FROM intent_events WHERE project = ? AND subject = ? ORDER BY ts ASC`).all(n,s);if(i.length===0){console.log(M.dim(` (no events for ${n}:${s})`));return}let r=i.map(l=>l.id),a=r.map((l,u)=>`@id${u}`).join(", "),c=Object.fromEntries(r.map((l,u)=>[`id${u}`,l])),d=t.prepare(`
119
+ SELECT d.event_id, d.decided_at, d.decided_by, d.agent_id, d.outcome, d.reason,
120
+ r.resolved_at, r.status AS resolution_status, r.duration_ms, r.result_summary
121
+ FROM intent_decisions d
122
+ LEFT JOIN intent_resolutions r
123
+ ON r.decision_event_id = d.event_id AND r.decision_decided_by = d.decided_by
124
+ WHERE d.event_id IN (${a})
125
+ ORDER BY d.decided_at ASC
126
+ `).all(c);if(o.json){let l=i.map(u=>({event:u,decisions:d.filter(m=>m.event_id===u.id)}));console.log(JSON.stringify({project:n,subject:s,lineage:l},null,2));return}let g=new Set;for(let l of d)l.outcome==="dispatched"&&l.agent_id&&g.add(l.agent_id);console.log(),console.log(M.bold(` Lineage for ${M.cyan(n+":"+s)}`)),console.log(M.dim(` ${i.length} event(s), ${d.length} decision(s), ${g.size} distinct agent(s) dispatched`)),console.log();for(let l of i){let u=new Date(l.ts).toISOString().slice(0,19).replace("T"," ");console.log(` ${M.dim(u)} ${M.bold(l.id)} ${M.dim(`(${l.source}/${l.intent})`)}`);let m=d.filter(f=>f.event_id===l.id);if(m.length===0){console.log(M.dim(" (no decisions yet)"));continue}for(let f of m){let y=f.outcome==="dispatched"?M.green(f.outcome):f.outcome==="dropped"?M.dim(f.outcome):M.yellow(f.outcome),$=f.agent_id?M.cyan(f.agent_id):M.dim("-"),v=f.reason?M.dim(` \u2014 ${f.reason.slice(0,60)}`):"";if(console.log(` ${M.dim(f.decided_by)} \u2192 ${y} ${$}${v}`),f.resolution_status){let b=f.resolution_status==="ok"?M.green(f.resolution_status):M.red(f.resolution_status),S=f.duration_ms?`${(f.duration_ms/1e3).toFixed(1)}s`:"-";console.log(` ${M.dim("resolved")} ${b} ${M.dim(S)}`)}else f.outcome==="dispatched"&&console.log(` ${M.yellow("(in-flight, no resolution)")}`)}}console.log()});Xe.command("replay").description("replay the source ledger onto a fresh tmp ledger; report divergences").option("--cwd <cwd>","working directory",process.cwd()).option("--path <path>","ledger path relative to cwd",".agentx/intent/ledger.sqlite").option("--since <duration>","limit to events newer than (e.g. 1h, 24h, 7d)").option("-s, --source <name>","filter to one source").option("-n, --limit <n>","max events to replay (defaults to all)").option("--json","emit JSON").action(e=>{let o=No(e),t=xt(e.since),n=[],s={};e.source&&(n.push("source = @source"),s.source=e.source),t!==void 0&&(n.push("ts >= @since"),s.since=t);let i=e.limit?`LIMIT ${Number(e.limit)}`:"",r=o.prepare(`
127
+ SELECT id, ts, source, source_event_id, project, subject, intent, raw_json
128
+ FROM intent_events
129
+ ${n.length?"WHERE "+n.join(" AND "):""}
130
+ ORDER BY ts ASC
131
+ ${i}
132
+ `).all(s).map(f=>({id:f.id,ts:f.ts,source:f.source,sourceEventId:f.source_event_id,project:f.project,subject:f.subject,intent:f.intent,rawJson:f.raw_json}));if(r.length===0){console.log(M.dim(" (no events match filter)"));return}let a=new Set(r.map(f=>f.id)),c=r.map((f,y)=>`@id${y}`).join(", "),d=Object.fromEntries(r.map((f,y)=>[`id${y}`,f.id])),g=o.prepare(`
133
+ SELECT event_id, decided_at, decided_by, agent_id, outcome, reason
134
+ FROM intent_decisions
135
+ WHERE event_id IN (${c})
136
+ `).all(d).map(f=>({eventId:f.event_id,decidedAt:f.decided_at,decidedBy:f.decided_by,agentId:f.agent_id,outcome:f.outcome,reason:f.reason})),l=o.prepare(`
137
+ SELECT decision_event_id, decision_decided_by, resolved_at, status, duration_ms, result_summary
138
+ FROM intent_resolutions
139
+ WHERE decision_event_id IN (${c})
140
+ `).all(d).map(f=>({decisionEventId:f.decision_event_id,decisionDecidedBy:f.decision_decided_by,resolvedAt:f.resolved_at,status:f.status,durationMs:f.duration_ms,resultSummary:f.result_summary})),u=ea(ri(oa(),"agentx-ledger-replay-")),m=new rs({path:ri(u,"target.sqlite")});try{let f=si(m,r,g,l);if(e.json){console.log(JSON.stringify(f,null,2));return}console.log(M.bold(`Replayed ${f.eventsCount} events, ${f.decisionsCount} decisions, ${l.length} resolutions`)),f.divergences.length===0?console.log(M.green(" \u2713 0 divergences \u2014 ledger mechanics are deterministic on this snapshot")):(console.log(M.red(` \u2717 ${f.divergences.length} divergences`)),uo(f.divergences.map(y=>({event_id:y.eventId,decided_by:y.decidedBy,recorded:`${y.expected.outcome}/${y.expected.agentId??"-"}`,replayed:`${y.actual.outcome}/${y.actual.agentId??"-"}`,reason:y.reason.slice(0,80)})),!1))}finally{m.close()}});import{Command as ia}from"commander";import E from"chalk";import{resolve as ai}from"path";import{existsSync as ra}from"fs";import aa from"better-sqlite3";var Zo=new ia().name("trace").description("inspect per-task execution traces (.agentx/db.sqlite)");function fn(e){let o=ai(e.cwd??process.cwd()),t=ai(o,e.path??".agentx/db.sqlite");ra(t)||(console.log(E.red(` No db at ${t}`)),console.log(E.dim(" The file is created on first SQLite write \u2014 start the daemon and run a task.")),process.exit(1));try{return new aa(t,{readonly:!0})}catch(n){console.log(E.red(` Could not open ${t}: ${n.message}`)),/NODE_MODULE_VERSION/.test(n.message)&&console.log(E.dim(" Run `pnpm rebuild better-sqlite3` under the daemon's Node version.")),process.exit(1)}}function ca(e){let o=/^(\d+)\s*([smhd])$/.exec(e.trim());if(!o)return null;let t=Number(o[1]),n=o[2];return t*(n==="s"?1e3:n==="m"?6e4:n==="h"?36e5:864e5)}function la(e){if(!e)return;let o=ca(e);if(o===null){let t=Number(e);if(Number.isFinite(t)&&t>0)return t;console.log(E.red(` Invalid --since "${e}". Use "1h", "30m", "7d", or an ms epoch.`)),process.exit(1)}return Date.now()-o}function da(e){let o=Math.round((Date.now()-e)/1e3);return o<60?`${o}s ago`:o<3600?`${Math.round(o/60)}m ago`:o<86400?`${Math.round(o/3600)}h ago`:`${Math.round(o/86400)}d ago`}function un(e){return e==="ok"?E.green:e==="error"||e==="timeout"?E.red:e==="in-flight"?E.yellow:E.dim}Zo.command("list").description("list recent traces (newest first)").option("--cwd <cwd>","working directory",process.cwd()).option("--path <path>","db path relative to cwd",".agentx/db.sqlite").option("--agent <id>","filter by agentId").option("--channel <name>","filter by channel (telegram|whatsapp|gitlab|...)").option("--chat <id>","filter by chatId").option("--workflow <runId>","filter by workflowRunId").option("--status <s>","filter by status (in-flight|ok|error|timeout)").option("--since <duration>","limit to traces newer than (e.g. 1h, 24h, 7d)").option("--limit <n>","max rows","50").option("--json","emit JSON").action(e=>{let o=fn(e),t=us(o,{agentId:e.agent,channel:e.channel,chatId:e.chat,workflowRunId:e.workflow,status:e.status,since:la(e.since),limit:parseInt(e.limit,10)||50});if(e.json){console.log(JSON.stringify(t,null,2));return}if(t.length===0){console.log(E.dim(" (no traces)"));return}for(let n of t){let s=un(n.status)(n.status.padEnd(9)),i=n.durationMs!=null?`${(n.durationMs/1e3).toFixed(2)}s`:"\u2014",r=n.workflowRunId?E.cyan(` wf=${n.workflowRunId}`):"",a=n.inputTokens!=null?E.dim(` ${n.inputTokens}/${n.outputTokens??0} tok`):"";console.log(`${E.dim(n.taskId)} ${s} ${E.bold(n.agentId)}${E.dim(` ${n.channel??"\u2014"}:${n.chatId??"\u2014"}`)}${r} ${E.dim("\xB7")} ${i}${a} ${E.dim(da(n.startedAt))}`),n.error&&console.log(` ${E.red("error:")} ${n.error}`)}});Zo.command("show <taskId>").description("show one trace with its full ordered step log").option("--cwd <cwd>","working directory",process.cwd()).option("--path <path>","db path relative to cwd",".agentx/db.sqlite").option("--json","emit JSON").action((e,o)=>{let t=fn(o),n=Io(t,e);if(n||(console.log(E.red(` No trace at ${e}`)),process.exit(1)),o.json){console.log(JSON.stringify(n,null,2));return}ga(n.task,n.steps)});function ga(e,o){let t=un(e.status)(e.status);if(console.log(E.bold(`Trace ${e.taskId}`)),console.log(` ${E.dim("agent")} ${e.agentId}`),console.log(` ${E.dim("channel")} ${e.channel??"\u2014"}:${e.chatId??"\u2014"}`),e.workflowRunId){let n=[e.workflowId,e.workflowNodeId].filter(Boolean).join("/");console.log(` ${E.dim("workflow")} ${E.cyan(e.workflowRunId)}${n?E.dim(` (${n})`):""}`)}if(e.intentEventId&&console.log(` ${E.dim("intent")} ${e.intentEventId} ${E.dim(`(${e.intentDecidedBy??"?"})`)}`),e.model&&console.log(` ${E.dim("model")} ${e.model}`),e.resumeSessionId&&console.log(` ${E.dim("resume")} ${e.resumeSessionId.slice(0,12)}\u2026`),e.finalSessionId&&console.log(` ${E.dim("final")} ${e.finalSessionId.slice(0,12)}\u2026`),console.log(` ${E.dim("status")} ${t}`),e.durationMs!=null&&console.log(` ${E.dim("duration")} ${(e.durationMs/1e3).toFixed(2)}s`),(e.inputTokens!=null||e.outputTokens!=null)&&console.log(` ${E.dim("tokens")} in=${e.inputTokens??0} out=${e.outputTokens??0} cache_read=${e.cacheReadTokens??0} cache_create=${e.cacheCreateTokens??0}`),e.error&&console.log(` ${E.red("error")} ${e.error}`),e.messagePreview&&console.log(` ${E.dim("message")} ${e.messagePreview}`),console.log(),o.length===0){console.log(E.dim(" (no recorded steps \u2014 task may have been on a non-streaming tier or completed before any tool call)"));return}console.log(E.bold(`Steps (${o.length}):`));for(let n of o){let s=n.status?un(n.status)(n.status):E.dim("\u2014"),i=n.action?E.cyan(n.action):"";if(console.log(` ${E.dim(`#${n.seq}`)} ${E.bold(n.name)}${i?" "+i:""} ${s}${n.ms!=null?E.dim(` ${n.ms}ms`):""}`),n.inputSummary){let r=n.inputSummary.split(`
141
+ `).slice(0,8);console.log(r.map(a=>` ${E.dim("\u2192")} ${a}`).join(`
142
+ `)),n.inputSummary.split(`
143
+ `).length>8&&console.log(` ${E.dim("\u2026")}`)}if(n.outputSummary){let r=n.outputSummary.split(`
144
+ `).slice(0,8);console.log(r.map(a=>` ${E.dim("\u2190")} ${a}`).join(`
145
+ `)),n.outputSummary.split(`
146
+ `).length>8&&console.log(` ${E.dim("\u2026")}`)}n.error&&console.log(` ${E.red("error:")} ${n.error}`)}}Zo.command("replay <taskId>").description("re-run a recorded task against the current agent config (handoff item #11)").option("--cwd <dir>","project root",process.cwd()).option("--path <p>","sqlite db path (relative to --cwd)",".agentx/db.sqlite").option("--daemon <url>","daemon API base URL","http://127.0.0.1:18800").option("--diff","show original input + output vs new output side-by-side").option("--no-fresh","do NOT freshSession (default is fresh \u2014 required for clean replay)").action(async(e,o)=>{let t=fn(o),n=Io(t,e);n||(console.log(E.red(` task not found: ${e}`)),console.log(E.dim(" agentx trace list \u2014 see what's available")),process.exit(1));let s=n.task,i=s.originalMessage??s.messagePreview;i||(console.log(E.red(` task ${e} has no recorded input message \u2014 cannot replay`)),process.exit(1)),!s.originalMessage&&s.messagePreview!=null&&console.log(E.yellow(" \u26A0 pre-migration-v8 row: input message was capped at 200 chars; replay uses the preview")),console.log(),console.log(E.bold(` replay ${E.cyan(e)}`)),console.log(E.dim(` agent=${s.agentId} channel=${s.channel??"\u2014"} chatId=${s.chatId??"\u2014"}`)),console.log(E.dim(` original status=${s.status} duration=${s.durationMs??"?"}ms tokens=in:${s.inputTokens??0}/out:${s.outputTokens??0}`)),console.log();let a=String(o.daemon).replace(/\/+$/,""),c={agent:s.agentId,message:i,freshSession:o.fresh!==!1,context:{channel:s.channel??"api",chatId:`replay-${e.toLowerCase().slice(-8)}-${Date.now().toString(36)}`}},d=Date.now(),g;try{g=await fetch(`${a}/task`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(c)})}catch(m){console.log(E.red(` daemon unreachable at ${a}: ${m.message||m}`)),process.exit(1)}let l=await g.json().catch(()=>({})),u=Date.now()-d;if(g.ok||(console.log(E.red(` replay failed (${g.status}): ${l.error||"unknown error"}`)),process.exit(1)),o.diff){console.log(E.bold(" INPUT (replayed verbatim)")),console.log(),St(i),console.log(),console.log(E.bold(` ORIGINAL OUTPUT ${E.dim(`status=${s.status} ${s.durationMs??"?"}ms`)}`)),console.log(),s.finalResponse?St(s.finalResponse):console.log(E.yellow(" (not captured \u2014 pre-migration-v8 row, or response was empty)")),console.log(),console.log(E.bold(` CURRENT OUTPUT ${E.dim(`status=${l.error?"error":"ok"} ${u}ms tokens=in:${l.usage?.inputTokens??0}/out:${l.usage?.outputTokens??0}`)}`)),console.log(),St(l.content??"(empty reply)"),l.error&&console.log(E.red(` error: ${l.error}`)),console.log();let m=(s.finalResponse??"").trim()===(l.content??"").trim();s.finalResponse&&(console.log(m?E.dim(" outputs are identical"):E.yellow(" outputs differ \u2014 read both blocks to see what changed")),console.log())}else console.log(E.green(` \u2713 replayed in ${u}ms`)),console.log(),St(l.content??"(empty reply)"),l.error&&console.log(E.red(`
147
+ error: ${l.error}`)),console.log(),console.log(E.dim(` agentx trace replay ${e} --diff to compare against the original output`)),console.log()});function St(e){let o=e.split(`
148
+ `),t=60,n=o.slice(0,t);for(let s of n)console.log(` ${E.dim("\u2502")} ${s}`);o.length>t&&console.log(` ${E.dim("\u2502")} ${E.dim(`\u2026+${o.length-t} more lines`)}`)}import{Command as fa}from"commander";import ee from"chalk";import{existsSync as ha,mkdirSync as ya,writeFileSync as wa}from"fs";import{resolve as di,join as li,dirname as $a}from"path";var ma=`# AgentX destructive-action guardrails \u2014 global policy
149
+ #
150
+ # This file + environments/*.yaml + agents/<id>.yaml are deep-merged
151
+ # (most specific wins). The built-in catalog (prisma / rm -rf / docker /
152
+ # terraform / git push --force ... patterns) is ALWAYS applied underneath.
153
+ # The guard reads policy live on every tool call \u2014 edits take effect on the
154
+ # next command, no daemon restart needed.
155
+ version: 1
156
+
157
+ # warn = compute + audit but never block (Phase 1 rollout stance).
158
+ # enforce = honor the verdict (deny blocks; escalate asks).
159
+ # off = kill-switch, allow everything.
160
+ # Flip to enforce once \`agentx guard log\` shows an acceptable false-positive rate.
161
+ mode: warn
162
+
163
+ defaults:
164
+ fail_mode: closed # guard error on a protected-target command => deny
165
+ unmatched: allow # commands no rule matches run freely (keep routine work fast)
166
+
167
+ # Extra global rules beyond the catalog go here. Example:
168
+ # rules:
169
+ # - id: no-prod-ssh
170
+ # match: { tool: Bash, command_regex: "ssh ", target_in: production }
171
+ # action: escalate
172
+ # severity: high
173
+ rules: []
174
+ `,pa=`# Production protected resources.
175
+ #
176
+ # The catalog rules reference \`target_in: production\`, so DECLARING this set is
177
+ # what arms them. \`resolve_env: true\` expands $VAR / \${VAR} and reads .env* in
178
+ # the workspace before matching \u2014 so \`--shadow-database-url $DATABASE_URL\` is
179
+ # caught even though the literal string "production" never appears in the command
180
+ # (this is exactly the 2026-07-21 incident class).
181
+ protected_resources:
182
+ production:
183
+ resolve_env: true
184
+ db_urls:
185
+ - "\${PROD_DATABASE_URL}"
186
+ hosts:
187
+ - "api.hackathonat.com"
188
+ - "185.164.25.107"
189
+ # buckets:
190
+ # - "my-prod-uploads"
191
+ # contexts: # k8s contexts, droplet ids, cluster names
192
+ # - "do-fra1-prod"
193
+
194
+ # Production-only rules beyond the catalog:
195
+ rules: []
196
+ `,ua=`# Per-agent override for coder-agent. Top-level rules here are auto-scoped to
197
+ # this agent (the filename names it). coder agents never touch production.
198
+ rules:
199
+ - id: coder-no-prod
200
+ match:
201
+ target_in: production
202
+ action: deny
203
+ severity: critical
204
+ message: "coder-agent is not permitted to run commands against production."
205
+ `,ci=[{relPath:"policy.yaml",content:ma},{relPath:"environments/production.yaml",content:pa},{relPath:"agents/coder-agent.yaml",content:ua}];var Ze=new fa().name("guard").description("destructive-action guardrails \u2014 policy, checks, audit");function hn(e){return e==="deny"?ee.red:e==="escalate"||e==="ask"?ee.yellow:e==="warn"?ee.magenta:ee.green}function jt(e){return e.root?di(e.root):Ms()}Ze.command("check").description("PreToolUse hook entrypoint: read a tool-call payload on stdin, emit a Claude Code decision").option("--agent <id>","agent id this workspace belongs to").option("--root <dir>","AgentX install root (holds .agentx/guardrails + .agentx/db.sqlite)").option("--env <name>","environment scope (e.g. production)").action(async e=>{await Ps({root:jt(e),agentId:e.agent,env:e.env}),process.exit(0)});Ze.command("test <command>").description("dry-run a command through the policy engine and print the verdict").option("--agent <id>","evaluate as this agent").option("--env <name>","environment scope").option("--tool <name>","tool name","Bash").option("--cwd <dir>","workspace cwd for $VAR/.env resolution",process.cwd()).option("--root <dir>","AgentX install root (policy source)").option("--enforce","evaluate as if mode=enforce (show what WOULD block)").option("--json","emit JSON").action((e,o)=>{let t=jt(o),n=o.enforce?"enforce":void 0,{verdict:s,mode:i,ms:r}=_s({tool:o.tool,command:e,cwd:o.cwd,agentId:o.agent,env:o.env},{root:t,agentId:o.agent,env:o.env,modeOverride:n,noAudit:!0});if(o.json){console.log(JSON.stringify({verdict:s,mode:i,ms:r},null,2));return}ba(s,i,r)});function ba(e,o,t){let n=hn(e.action);console.log(),console.log(` policy verdict ${n(e.action.toUpperCase())}${e.matched?"":ee.dim(" (no rule matched)")}`),console.log(` enforced as ${hn(e.effectiveAction)(e.effectiveAction)} ${ee.dim(`(mode=${o})`)}`),e.ruleId&&console.log(` rule ${ee.cyan(e.ruleId)}${e.severity?ee.dim(` [${e.severity}]`):""}`),e.resolvedTarget&&console.log(` resolved target ${ee.yellow(e.resolvedTarget)}`),e.message&&console.log(` message ${e.message}`),console.log(` ${ee.dim(`evaluated in ${t}ms`)}`),console.log()}Ze.command("log").description("read the guardrail decision audit trail (newest first)").option("--root <dir>","AgentX install root (holds .agentx/db.sqlite)").option("--verdict <v>","filter by verdict (deny|escalate|warn|allow)").option("--agent <id>","filter by agent").option("--task <id>","filter by task id").option("-n, --limit <n>","max rows","30").option("--json","emit JSON").action(e=>{let o=Ns({root:jt(e),verdict:e.verdict,agentId:e.agent,taskId:e.task,limit:parseInt(e.limit,10)||30});if(e.json){console.log(JSON.stringify(o,null,2));return}if(o.length===0){console.log(ee.dim(" (no guardrail decisions recorded yet)"));return}for(let t of o){let n=new Date(t.ts).toISOString().replace("T"," ").slice(0,19),s=hn(t.verdict),i=t.effective_action!==t.verdict?ee.dim(`->${t.effective_action}`):"";console.log(` ${ee.dim(n)} ${s(t.verdict.toUpperCase().padEnd(8))}${i} ${ee.cyan((t.agent_id??"?").padEnd(16))} ${ee.dim((t.matched_rule??"-").padEnd(24))} ${(t.command??"").slice(0,70)}`),t.resolved_target&&console.log(ee.dim(` target: ${t.resolved_target}`))}});Ze.command("init").description("scaffold .agentx/guardrails/ with a starter policy + protected-resource example").option("--root <dir>","where to create .agentx/guardrails/",process.cwd()).option("--force","overwrite existing files").action(e=>{let o=li(di(e.root),Ts),t=0;for(let n of ci){let s=li(o,n.relPath);if(ha(s)&&!e.force){console.log(ee.dim(` skip (exists) ${n.relPath}`));continue}ya($a(s),{recursive:!0}),wa(s,n.content),console.log(ee.green(` created ${n.relPath}`)),t++}console.log(),console.log(` Guardrails scaffolded under ${ee.cyan(o)}`),console.log(ee.dim(" Edit environments/production.yaml with YOUR prod hosts/URLs, then")),console.log(ee.dim(' `agentx guard test "<cmd>" --enforce` to preview, and flip mode: enforce when ready.'))});Ze.command("reload").description("signal the daemon to reload (note: guard policy is read live per tool call already)").option("--url <url>","daemon base url",process.env.AGENTX_DAEMON_URL||"http://localhost:19900").action(async e=>{try{let o=await fetch(`${String(e.url).replace(/\/$/,"")}/reload`,{method:"POST"});o.ok?console.log(ee.green(` daemon reloaded (${e.url})`)):console.log(ee.yellow(` daemon reachable but returned ${o.status}`))}catch(o){console.log(ee.dim(` daemon not reachable (${o.message}). Policy files are still read live on each check.`))}});Ze.command("policy").description("print the resolved (merged) policy for an agent").option("--agent <id>","agent id").option("--root <dir>","AgentX install root").action(e=>{let o=Ds(jt(e),e.agent);console.log(JSON.stringify(o,null,2))});import{Command as Ea}from"commander";import C from"chalk";import{existsSync as At,mkdirSync as ka,readFileSync as Ot,writeFileSync as gi}from"fs";import{homedir as va}from"os";import{dirname as xa,resolve as yn}from"path";var wn="/attach/",mi="/guard/check";function pi(e,o,t=""){return`curl -s --max-time 3 -H 'Content-Type: application/json' --data-binary @- '${`http://127.0.0.1:${o}${e}${t}`}' 2>/dev/null || true`}function Et(e,o,t){let n={hooks:[{type:"command",command:pi(e,o),timeout:10}]};return t!==void 0&&(n.matcher=t),n}function Sa(e){return{SessionStart:[Et("/attach/session-start",e,"")],UserPromptSubmit:[Et("/attach/prompt",e)],Stop:[Et("/attach/stop",e)],SessionEnd:[Et("/attach/session-end",e)]}}function ja(e){let o=pi("/guard/check",e);return[{matcher:"Bash",hooks:[{type:"command",command:o,timeout:10}]},{matcher:"Write|Edit",hooks:[{type:"command",command:o,timeout:10}]}]}function It(e,o){return Array.isArray(e?.hooks)&&e.hooks.some(t=>typeof t?.command=="string"&&t.command.includes(o))}function et(){return yn(va(),".claude/settings.json")}function $n(e=process.cwd()){for(let o of[e,yn(e,"..")]){let t=yn(o,"agentx.json");if(At(t))try{let n=JSON.parse(Ot(t,"utf-8"))?.node?.bind,s=String(n??"").split(":")[1];if(s)return s}catch{}}return"19900"}function ui(e,o={}){let t=o.path??et(),n=o.withGuard!==!1,s={};if(At(t))try{s=JSON.parse(Ot(t,"utf-8"))}catch(g){throw new Error(`${t} is not valid JSON (${g?.message}) \u2014 refusing to overwrite it`)}let i=JSON.stringify(s),r=s.hooks??={},a=[];for(let[g,l]of Object.entries(Sa(e))){let u=Array.isArray(r[g])?r[g].filter(m=>!It(m,wn)):[];r[g]=[...u,...l],a.push(g)}let c=!1;if(n){let g=Array.isArray(r.PreToolUse)?r.PreToolUse.filter(l=>!It(l,mi)):[];r.PreToolUse=[...g,...ja(e)],c=!0}let d=JSON.stringify(s)!==i;return d&&(ka(xa(t),{recursive:!0}),gi(t,`${JSON.stringify(s,null,2)}
206
+ `)),{path:t,changed:d,events:a,guardInstalled:c}}function fi(e={}){let o=e.path??et();if(!At(o))return{path:o,changed:!1,events:[],guardInstalled:!1};let t;try{t=JSON.parse(Ot(o,"utf-8"))}catch(a){throw new Error(`${o} is not valid JSON (${a?.message}) \u2014 refusing to rewrite it`)}let n=JSON.stringify(t),s=t.hooks,i=[];if(s&&typeof s=="object"){for(let a of Object.keys(s)){if(!Array.isArray(s[a]))continue;let c=a==="PreToolUse"?mi:wn;if(a==="PreToolUse"&&e.keepGuard!==!1)continue;let d=s[a].filter(g=>!It(g,c));d.length!==s[a].length&&i.push(a),d.length===0?delete s[a]:s[a]=d}Object.keys(s).length===0&&delete t.hooks}let r=JSON.stringify(t)!==n;return r&&gi(o,`${JSON.stringify(t,null,2)}
207
+ `),{path:o,changed:r,events:i,guardInstalled:!1}}function Ct(e=et()){if(!At(e))return!1;try{return!!JSON.parse(Ot(e,"utf-8"))?.hooks?.Stop?.some?.(t=>It(t,wn))}catch{return!1}}var fo=new Ea().name("attach").description("wear an agentx agent identity in this Claude Code session"),bn=process.env.AGENTX_DAEMON_URL||`http://127.0.0.1:${$n()}`;function kn(e){return e||process.env.CLAUDE_CODE_SESSION_ID||void 0}async function vn(e,o,t){let n=await fetch(`${e}${o}`,{method:t?"POST":"GET",headers:{"Content-Type":"application/json"},body:t?JSON.stringify(t):void 0}),s=await n.text(),i=null;try{i=s?JSON.parse(s):null}catch{}if(!n.ok)throw new Error(i?.error||s||`HTTP ${n.status}`);return i}function xn(e){let o=e?.message??String(e);return/ECONNREFUSED|fetch failed/i.test(o)?`daemon not reachable \u2014 start it with ${C.cyan("agentx daemon start")}`:o}function hi(e){return e==="auto"?C.yellow:e==="manual"?C.gray:C.green}function Ia(e,o=!1){let t=e.agentIds.length?e.agentIds.join(", "):C.gray("(not bound)"),n=e.pending?C.yellow(`${e.pending} pending`):C.gray("empty"),s=o?C.cyan(" \u2190 this session"):"";console.log(` ${C.bold(t)} ${hi(e.mode)(e.mode)} ${n} ${C.gray(e.sessionId)}${s}`),e.cwd&&console.log(` ${C.gray(e.cwd)}`)}fo.command("as <agent>",{isDefault:!0}).description("bind this Claude Code session to an agent identity").option("-m, --mode <mode>",`delivery mode: ${en.join(" | ")}`,"notify").option("--session <id>","Claude Code session id (defaults to $CLAUDE_CODE_SESSION_ID)").option("--url <url>","daemon base url",bn).action(async(e,o)=>{let t=kn(o.session);t||(console.error(C.red("Could not determine the Claude Code session id.")),console.error(`Run this inside a Claude Code session, or pass ${C.cyan("--session <id>")} (list them with ${C.cyan("agentx attach list")}).`),process.exit(1)),as(o.mode)||(console.error(C.red(`Invalid mode "${o.mode}". Expected one of: ${en.join(", ")}`)),process.exit(1)),Ct()||(console.error(C.yellow("Attach hooks are not installed yet.")),console.error(`Run ${C.cyan("agentx attach install")} once, then try again.`),process.exit(1));try{let n=await vn(o.url,"/attach/bind",{sessionId:t,agentId:e,mode:o.mode,cwd:process.cwd()});console.log(`${C.green("\u2713")} This session is now wearing ${C.bold(e)} (${hi(o.mode)(o.mode)}).`);let s=Number(n?.pending??0);s>0&&console.log(C.yellow(` ${s} message(s) already queued for it.`)),console.log(C.gray(o.mode==="auto"?" Queued messages will be answered automatically at the end of each turn.":o.mode==="notify"?" You'll be told when messages arrive; run /inbox to answer them.":" Nothing will interrupt you; run /inbox to check for messages."))}catch(n){console.error(C.red(`Bind failed: ${xn(n)}`)),process.exit(1)}});fo.command("detach").description("stop wearing an identity in this session (queued work falls back to spawned agents)").option("--agent <id>","release only this identity (default: all)").option("--session <id>","Claude Code session id (defaults to $CLAUDE_CODE_SESSION_ID)").option("--url <url>","daemon base url",bn).action(async e=>{let o=kn(e.session);o||(console.error(C.red("Could not determine the Claude Code session id. Pass --session <id>.")),process.exit(1));try{await vn(e.url,"/attach/detach",{sessionId:o,agentId:e.agent}),console.log(`${C.green("\u2713")} Detached. New messages will spawn agents the normal way.`)}catch(t){console.error(C.red(`Detach failed: ${xn(t)}`)),process.exit(1)}});fo.command("list").alias("status").description("show every attached session on this machine").option("--url <url>","daemon base url",bn).option("--json","raw JSON output").action(async e=>{let o=kn();try{let n=(await vn(e.url,"/attach/sessions"))?.sessions??[];if(e.json){console.log(JSON.stringify({hooksInstalled:Ct(),sessions:n},null,2));return}console.log(),console.log(Ct()?`${C.green("\u2713")} Attach hooks installed ${C.gray(et())}`:`${C.yellow("!")} Attach hooks not installed \u2014 run ${C.cyan("agentx attach install")}`),console.log();let s=n.filter(r=>r.agentIds.length>0);if(s.length===0)console.log(C.gray(" No session is wearing an identity right now."));else for(let r of s)Ia(r,r.sessionId===o);let i=n.length-s.length;i>0&&console.log(C.gray(`
208
+ ${i} registered session(s) with no binding.`)),console.log()}catch(t){console.error(C.red(`Could not read attach state: ${xn(t)}`)),process.exit(1)}});fo.command("install").description("wire the attach hooks into ~/.claude/settings.json (one time, all sessions)").option("--port <n>","daemon port the hooks should call (default: node.bind in agentx.json)").option("--path <file>","settings file to patch (default: ~/.claude/settings.json)").option("--no-guard","skip the PreToolUse guard hook (not recommended)").action(e=>{try{let o=String(e.port||$n()),t=ui(o,{path:e.path,withGuard:e.guard});console.log(t.changed?`${C.green("\u2713")} Installed attach hooks in ${C.gray(t.path)}`:`${C.green("\u2713")} Attach hooks already up to date in ${C.gray(t.path)}`),console.log(C.gray(` Events: ${t.events.join(", ")} \u2192 127.0.0.1:${o}`)),t.guardInstalled?(console.log(C.gray(" PreToolUse guard installed at user scope.")),console.log(C.gray(` (An attached session runs under your permissions, not the agent's workspace
209
+ settings \u2014 without this an attached production identity would be less
210
+ guarded than a spawned one.)`))):console.log(C.yellow(" ! Guard hook skipped. An attached production identity will run unguarded.")),console.log(),console.log(`Next: ${C.cyan("agentx attach <agent>")} inside a Claude Code session.`)}catch(o){console.error(C.red(`Install failed: ${o?.message??o}`)),process.exit(1)}});fo.command("uninstall").description("remove the attach hooks from ~/.claude/settings.json").option("--path <file>","settings file to patch (default: ~/.claude/settings.json)").option("--remove-guard","also remove the PreToolUse guard hook").action(e=>{try{let o=fi({path:e.path,keepGuard:!e.removeGuard});console.log(o.changed?`${C.green("\u2713")} Removed attach hooks from ${C.gray(o.path)} (${o.events.join(", ")})`:`${C.gray("Nothing to remove.")}`),e.removeGuard||console.log(C.gray(" PreToolUse guard left in place \u2014 pass --remove-guard to drop it too."))}catch(o){console.error(C.red(`Uninstall failed: ${o?.message??o}`)),process.exit(1)}});import{Command as Aa}from"commander";import se from"chalk";var Rt=new Aa().name("process").description("inspect / rotate persistent claude processes (--persistentProcess agents)");function wi(e){let o=typeof e.node=="string"?e.node:"",t=typeof e.token=="string"?e.token:"";if(!o||!t)try{let n=L(e.config);o||(o=n.dashboard?.daemonUrl||""),t||(t=n.dashboard?.token||"")}catch{}return o||(o="http://localhost:18800"),{baseUrl:o.replace(/\/+$/,""),token:t}}function $i(e,o){let t={};return e&&(t.Authorization=`Bearer ${e}`),o&&(t["Content-Type"]=o),t}function yi(e){let o=Math.round((Date.now()-e)/1e3);return o<60?`${o}s`:o<3600?`${Math.round(o/60)}m`:o<86400?`${Math.round(o/3600)}h`:`${Math.round(o/86400)}d`}function Oa(e){return e==="busy"?se.magenta:e==="warm-hot"?se.green:e==="warm-cold"?se.cyan:e==="idle"?se.yellow:e==="dead"?se.red:se.dim}Rt.command("list").description("list every live persistent claude process").option("-c, --config <path>","daemon config file").option("--node <url>","daemon URL (defaults to dashboard.daemonUrl)").option("--token <token>","bearer token (defaults to dashboard.token)").option("--json","emit JSON").action(async e=>{let{baseUrl:o,token:t}=wi(e),n;try{n=await fetch(`${o}/api/processes`,{headers:$i(t)})}catch(r){console.log(se.red(` could not reach daemon at ${o}: ${r.message||r}`)),process.exit(1)}if(n.status===503){console.log(se.dim(` daemon at ${o} has no agents with persistentProcess: true`));return}n.ok||(console.log(se.red(` HTTP ${n.status} from ${o}/processes`)),process.exit(1));let i=(await n.json()).processes??[];if(e.json){console.log(JSON.stringify(i,null,2));return}if(i.length===0){console.log(se.dim(" (no processes)"));return}for(let r of i){let a=Oa(r.state)(r.state.padEnd(9)),c=yi(r.spawnedAt),d=r.lastTurnAt?yi(r.lastTurnAt)+" ago":"\u2014",g=r.lastInputTokens?se.dim(`last_in=${r.lastInputTokens} tok`):"",l=r.claudeSessionId?se.dim(`sess=${r.claudeSessionId.slice(0,8)}\u2026`):se.dim("sess=\u2014"),u=r.pendingTaskId?se.cyan(`task=${String(r.pendingTaskId).slice(0,12)}`):"";console.log(`${a} ${se.bold(r.key.agentId)}${se.dim(":"+r.key.channel+":"+r.key.chatId)} ${se.dim("\xB7")} pid=${r.pid??"?"} age=${c} turn#${r.turnCount} last=${d} ${l} ${g} ${u}`),r.deadReason&&console.log(` ${se.red("dead:")} ${r.deadReason}`)}});Rt.command("kill <agentId> <channel> <chatId>").description("force-kill one persistent process (the next dispatch will spawn fresh)").option("-c, --config <path>","daemon config file").option("--node <url>","daemon URL (defaults to dashboard.daemonUrl)").option("--token <token>","bearer token (defaults to dashboard.token)").option("-r, --reason <reason>","kill reason (recorded on the dead-process snapshot)","operator-cli").action(async(e,o,t,n)=>{let{baseUrl:s,token:i}=wi(n),r;try{r=await fetch(`${s}/api/processes/kill`,{method:"POST",headers:$i(i,"application/json"),body:JSON.stringify({agentId:e,channel:o,chatId:t,reason:n.reason})})}catch(a){console.log(se.red(` could not reach daemon at ${s}: ${a.message||a}`)),process.exit(1)}if(r.status===503){console.log(se.dim(" daemon has no persistent-process registry"));return}if(!r.ok){let a=`HTTP ${r.status}`;try{let c=await r.json();c.error&&(a+=` \u2014 ${c.error}`)}catch{}console.log(se.red(` ${a}`)),process.exit(1)}console.log(se.green(` killed ${e}:${o}:${t}`))});import{Command as Ca}from"commander";import we from"chalk";import{existsSync as Sn,rmSync as Ra}from"fs";import{resolve as jn}from"path";var _o=new Ca().name("rag").description("manage per-agent lexical (BM25) RAG indexes (improvement plan #7)");_o.command("add <agentId>").description("build or refresh an agent's lexical index from one or more globs").argument("<globs...>","glob patterns relative to cwd (e.g. './docs/**/*.md')").option("-v, --verbose","log each indexed file").action(async(e,o,t)=>{o.length===0&&(console.log(we.red(` No globs provided. Example: agentx rag add ${e} './docs/**/*.md'`)),process.exit(1));let n=!!t.verbose,s=Date.now(),i=await ds(e,o,{verbose:n}),r=Date.now()-s;console.log(we.green(` \u2713 indexed ${i.docs} doc(s) for ${we.bold(e)} in ${r}ms`)),console.log(we.dim(` \u2192 ${i.path}`))});_o.command("list").description("list every agent's index, sizes, paths").option("-c, --cwd <path>","search root",process.cwd()).action(async e=>{let o=jn(e.cwd,".agentx","rag");if(!Sn(o)){console.log(we.dim(" (no indexes yet \u2014 try `agentx rag add <agentId> <glob>`)"));return}let{readdirSync:t}=await import("fs"),n=t(o).filter(s=>Sn(jn(o,s,"index.json")));if(n.length===0){console.log(we.dim(" (no indexes)"));return}for(let s of n.sort()){let i=on(s);console.log(` ${we.bold(s)} ${i.docs} doc(s) ${we.dim(i.path)}`)}});_o.command("search <agentId> <query>").description("query an agent's index \u2014 useful for debugging recall before the agent calls rag.lexical").option("-k, --k <n>","top-k results","5").option("--json","emit JSON").action((e,o,t)=>{let n=parseInt(t.k,10)||5,s=gs(e,o,{k:n});if(t.json){console.log(JSON.stringify(s,null,2));return}if(s.length===0){let i=on(e);i.exists?console.log(we.dim(` (no hits in ${i.docs} indexed docs)`)):console.log(we.red(` no index for ${e} \u2014 run \`agentx rag add ${e} <glob>\` first`));return}for(let i of s)console.log(` ${we.bold(i.title)} ${we.dim(`(${i.score.toFixed(2)})`)}`),console.log(` ${we.dim(i.path)}`),console.log(` ${we.dim(i.snippet.slice(0,200))}`)});_o.command("clear <agentId>").description("delete an agent's index").action(e=>{let o=jn(process.cwd(),".agentx","rag",e);if(!Sn(o)){console.log(we.dim(` no index for ${e} \u2014 nothing to clear`));return}Ra(o,{recursive:!0,force:!0}),console.log(we.green(` \u2713 cleared index for ${e}`))});import{Command as Ta}from"commander";import q from"chalk";import Po from"prompts";var ho=new Ta().name("backlog").description("manage the local backlog used by business workSource=backlog");function ot(e){let o=L(e),t=o.business?.workSource,n=t?.type==="backlog"?t.path:".agentx/backlog.md";return{config:o,storePath:n,store:new Ls(n)}}function Da(e){return e?q.cyan(`${e.type}:${e.project}#${e.iid}`):q.dim("manual")}function Na(e){return{todo:q.gray("todo"),doing:q.yellow("doing"),blocked:q.red("blocked"),done:q.green("done")}[e]}ho.command("list",{isDefault:!0}).description("list backlog items").option("--status <status>","filter by status (todo|doing|blocked|done)").option("--assignee <agent>","filter by assignee").option("--source <type>","filter by source.type (gitlab|github|manual)").option("-c, --config <path>","config path").action(async e=>{let{store:o}=ot(e.config),t=o.list();if(e.status&&(t=t.filter(n=>n.status===e.status)),e.assignee&&(t=t.filter(n=>n.assignee===e.assignee)),e.source&&(t=t.filter(n=>e.source==="manual"?!n.source:n.source?.type===e.source)),!t.length){console.log(q.dim(" (no items)"));return}console.log();for(let n of t){let s=n.assignee?q.magenta(`@${n.assignee}`):q.dim("unassigned");console.log(` ${Na(n.status).padEnd(7)} ${Da(n.source)} ${s} ${n.title}`),n.source?.url&&console.log(q.dim(` ${n.source.url}`))}console.log(),console.log(q.dim(` ${t.length} item${t.length===1?"":"s"} \u2014 ${o.jsonPath()}`))});ho.command("claim <id> <agent>").description("assign an item to an agent and set status=doing").option("-c, --config <path>","config path").action(async(e,o,t)=>{let n=ot(t.config);n.store.findById(e)||(console.error(q.red(` item ${e} not found`)),process.exit(1));let i=n.store.update(e,{assignee:o,status:"doing"});if(console.log(q.green(` claimed: ${i.title} -> ${o}`)),i.source){let r=Us([o],i.source,n.config),a=await rn(i,{assigneeUsernames:r,addLabels:["Doing"],removeLabels:["To Do"]},n.config,(...c)=>console.log(q.dim(" sync:"),...c));console.log(a.ok?q.dim(` synced -> ${a.url??i.source.type}`):q.yellow(` sync failed: ${a.error}`))}});ho.command("done <id>").description("mark an item as done; closes the upstream issue if linked").option("--note <text>","comment to post upstream").option("--close","also close the upstream issue (default: only labels change)",!1).option("-c, --config <path>","config path").action(async(e,o)=>{let t=ot(o.config);t.store.findById(e)||(console.error(q.red(` item ${e} not found`)),process.exit(1));let s=t.store.update(e,{status:"done"});if(console.log(q.green(` done: ${s.title}`)),s.source){let i=await rn(s,{addLabels:["Done"],removeLabels:["Doing"],state:o.close?"closed":void 0,note:o.note},t.config,(...r)=>console.log(q.dim(" sync:"),...r));console.log(i.ok?q.dim(` synced -> ${i.url??s.source.type}`):q.yellow(` sync failed: ${i.error}`))}});ho.command("remove <id>").description("remove an item from the local backlog (does NOT touch upstream)").option("-c, --config <path>","config path").action(async(e,o)=>{let{store:t}=ot(o.config);t.remove(e)||(console.error(q.red(` item ${e} not found`)),process.exit(1)),console.log(q.green(` removed ${e}`))});ho.command("import").description("import open issues from gitlab/github into the backlog (interactive)").option("--source <type>","source type (gitlab|github)").option("--project <id>","project id (group/repo for gitlab, owner/repo for github)").option("--assignee <agent>","assign all imported items to this agentId").option("-c, --config <path>","config path").action(async e=>{let o=ot(e.config),t=!!o.config.channels.gitlab?.token,n=!!o.config.channels.github?.token;!t&&!n&&(console.error(q.red(" no source configured \u2014 set channels.gitlab.token or channels.github.token in agentx.json")),process.exit(1));let s=e.source;if(!s){let $=[t&&{title:"GitLab",value:"gitlab"},n&&{title:"GitHub",value:"github"}].filter(Boolean);if(s=(await Po({type:"select",name:"source",message:"Source",choices:$})).source,!s)return}s==="gitlab"&&!t&&(console.error(q.red(" channels.gitlab.token not configured")),process.exit(1)),s==="github"&&!n&&(console.error(q.red(" channels.github.token not configured")),process.exit(1));let i=e.project;if(!i){let $=s==="gitlab"?Pa(o.config):Ma(o.config);$.length?(i=(await Po({type:"autocomplete",name:"project",message:"Project (type to filter, or pick + then Enter for custom)",choices:[...$.map(b=>({title:b,value:b})),{title:"(custom \u2014 type below)",value:"__CUSTOM__"}]})).project,i==="__CUSTOM__"&&(i=(await Po({type:"text",name:"p",message:s==="gitlab"?"Group/path":"owner/repo"})).p)):i=(await Po({type:"text",name:"p",message:s==="gitlab"?"Group/path":"owner/repo"})).p}if(!i)return;let a=((await Po({type:"text",name:"search",message:"Search filter (optional \u2014 leave empty for all open)"})).search||"").trim();console.log(q.dim(` fetching open issues from ${s}:${i}${a?` (search: ${a})`:""}\u2026`));let c=s==="gitlab"?await La(o.config,i,a):await Ua(o.config,i,a);if(!c.length){console.log(q.yellow(" no open issues matched"));return}console.log(q.dim(` ${c.length} open issue${c.length===1?"":"s"}`));let d=i,g=new Set(o.store.list().map($=>$.id)),l=c.map($=>{let v=sn({type:s,project:d,iid:$.iid,url:$.url}),b=g.has(v),S=$.labels.length?` [${$.labels.slice(0,3).join(",")}]`:"";return{title:`#${$.iid} ${$.title}${S}${b?q.dim(" (already imported)"):""}`,value:$,disabled:b}}),m=(await Po({type:"autocompleteMultiselect",name:"selected",message:"Pick issues to import (space to toggle, Enter to confirm)",instructions:!1,choices:l,min:1})).selected||[];if(!m.length){console.log(q.dim(" nothing selected"));return}let f=m.map($=>{let v={type:s,host:s==="gitlab"?_a(o.config):void 0,project:d,iid:$.iid,url:$.url};return{id:sn(v),title:$.title,description:$.description,assignee:e.assignee||$.assignee,labels:$.labels,milestone:$.milestone,status:"todo",source:v,importedAt:new Date().toISOString()}}),y=o.store.addMany(f);console.log(q.green(` imported ${y.length} item${y.length===1?"":"s"}`));for(let $ of y)console.log(q.dim(` ${$.id} \u2014 ${$.title}`))});function _a(e){let o=e.channels.gitlab?.host;if(o)return o.replace(/^https?:\/\//,"").replace(/\/+$/,"")}function Pa(e){let o=new Set;for(let n of e.channels.gitlab?.routes??[])n.project&&n.project!=="*"&&o.add(n.project);let t=e.business?.workSource;if(t?.type==="gitlab")for(let n of t.projects??[])o.add(n);for(let n of e.business?.projects??[])n?.id&&n.id.includes("/")&&o.add(n.id);return[...o].sort()}function Ma(e){let o=new Set;for(let t of e.channels.github?.routes??[])t.repo&&t.repo!=="*"&&o.add(t.repo);return[...o].sort()}async function La(e,o,t){let n=e.channels.gitlab,s=new URLSearchParams({state:"opened",scope:"all",per_page:"100",order_by:"updated_at",sort:"desc"});t&&s.set("search",t);let i=await fetch(`${n.host}/api/v4/projects/${encodeURIComponent(o)}/issues?${s}`,{headers:{"PRIVATE-TOKEN":n.token}});if(!i.ok)throw new Error(`GitLab ${i.status}: ${await i.text().catch(()=>"")}`);let r=await i.json();return Array.isArray(r)?r.map(a=>({iid:a.iid,title:a.title,description:a.description||void 0,labels:Array.isArray(a.labels)?a.labels.map(c=>typeof c=="string"?c:c.name):[],milestone:a.milestone?.title||void 0,assignee:a.assignee?.username||a.assignees?.[0]?.username||void 0,url:a.web_url})):[]}async function Ua(e,o,t){let s={Authorization:`Bearer ${e.channels.github.token}`,Accept:"application/vnd.github+json","X-GitHub-Api-Version":"2022-11-28"},i;if(t){let c=`repo:${o} is:issue is:open ${t}`;i=`https://api.github.com/search/issues?q=${encodeURIComponent(c)}&per_page=100&sort=updated&order=desc`;let d=await fetch(i,{headers:s});if(!d.ok)throw new Error(`GitHub ${d.status}: ${await d.text().catch(()=>"")}`);return((await d.json()).items??[]).map(bi)}i=`https://api.github.com/repos/${o}/issues?state=open&per_page=100&sort=updated&direction=desc`;let r=await fetch(i,{headers:s});if(!r.ok)throw new Error(`GitHub ${r.status}: ${await r.text().catch(()=>"")}`);let a=await r.json();return Array.isArray(a)?a.filter(c=>!c.pull_request).map(bi):[]}function bi(e){return{iid:e.number,title:e.title,description:e.body||void 0,labels:Array.isArray(e.labels)?e.labels.map(o=>typeof o=="string"?o:o.name):[],milestone:e.milestone?.title||void 0,assignee:e.assignee?.login||e.assignees?.[0]?.login||void 0,url:e.html_url}}import{Command as Ba}from"commander";import U from"chalk";import{existsSync as Fa,readFileSync as qa}from"fs";import{resolve as za}from"path";import Ha from"prompts";import Wa from"cronstrue";var En={sun:0,sunday:0,mon:1,monday:1,tue:2,tuesday:2,tues:2,wed:3,wednesday:3,thu:4,thursday:4,thurs:4,fri:5,friday:5,sat:6,saturday:6},Tt={morning:{h:9,m:0},afternoon:{h:14,m:0},evening:{h:18,m:0},night:{h:22,m:0},noon:{h:12,m:0},midnight:{h:0,m:0}};function Pe(e){try{return Wa.toString(e,{use24HourTimeFormat:!1})}catch{return e}}function Ja(e){if(/\b(noon|midday)\b/.test(e))return Tt.noon;if(/\bmidnight\b/.test(e))return Tt.midnight;let o=e.match(/\b(?:at|by|@)\s*(\d{1,2})(?::(\d{2}))?\s*(am|pm)?\b/);if(o){let t=parseInt(o[1],10),n=o[2]?parseInt(o[2],10):0,s=o[3];return s==="pm"&&t<12&&(t+=12),s==="am"&&t===12&&(t=0),t>23||n>59?null:{h:t,m:n}}return null}function In(e){let o=e.trim().toLowerCase();if(!o)return null;let t;if(t=o.match(/\bevery\s+(\d+)\s*(minutes|mins|m)\b/)){let l=parseInt(t[1],10);return l<1||l>59?null:{cron:`*/${l} * * * *`,human:Pe(`*/${l} * * * *`),matched:t[0]}}if(/\bevery\s+minute\b/.test(o))return{cron:"* * * * *",human:Pe("* * * * *"),matched:"every minute"};if(t=o.match(/\bevery\s+(\d+)\s*(hours|hrs|h)\b/)){let l=parseInt(t[1],10);return l<1||l>23?null:{cron:`0 */${l} * * *`,human:Pe(`0 */${l} * * *`),matched:t[0]}}if(/\b(every hour|hourly)\b/.test(o))return{cron:"0 * * * *",human:Pe("0 * * * *"),matched:"every hour"};let n=Ja(o),s=null;for(let[l,u]of Object.entries(Tt))if(!(l==="noon"||l==="midnight")&&new RegExp(`\\b(every\\s+)?${l}s?\\b`).test(o)){s=u;break}let i=n||s,r=o.match(/\b(first|1st|2nd|3rd|last|(\d+)(?:st|nd|rd|th)?)\s+(day\s+)?of\s+(every|each|the)\s+month\b/);if(r){let l=r[1],u=1;if(l==="last"||(/^\d+(st|nd|rd|th)?$/.test(l)?u=parseInt(l,10):l==="first"||l==="1st"?u=1:l==="2nd"?u=2:l==="3rd"&&(u=3),u<1||u>31))return null;let m=i||{h:9,m:0},f=`${m.m} ${m.h} ${u} * *`;return{cron:f,human:Pe(f),matched:r[0]}}let a=[],c=Object.keys(En).join("|"),d=new RegExp(`\\b(?:every|on|each|by)\\s+((?:${c})s?(?:\\s*(?:,|and|&)\\s*(?:${c})s?)*)\\b`),g=o.match(d);if(g){let l=g[1];for(let u of Object.keys(En))if(new RegExp(`\\b${u}s?\\b`).test(l)){let m=En[u];a.includes(m)||a.push(m)}}if(a.length){let l=i||{h:9,m:0};a.sort((f,y)=>f-y);let u=a.join(","),m=`${l.m} ${l.h} * * ${u}`;return{cron:m,human:Pe(m),matched:`on ${u}`}}if(/\b(weekday|workday|business day)s?\b/.test(o)){let l=i||{h:9,m:0},u=`${l.m} ${l.h} * * 1-5`;return{cron:u,human:Pe(u),matched:"weekdays"}}if(/\bweekends?\b/.test(o)){let l=i||{h:9,m:0},u=`${l.m} ${l.h} * * 0,6`;return{cron:u,human:Pe(u),matched:"weekends"}}if(/\b(every day|daily|each day)\b/.test(o)||s){let l=i||{h:9,m:0},u=`${l.m} ${l.h} * * *`;return{cron:u,human:Pe(u),matched:s&&Object.entries(Tt).find(([,m])=>m===s)?.[0]||"daily"}}if(n){let l=`${n.m} ${n.h} * * *`;return{cron:l,human:Pe(l),matched:`at ${n.h}:${String(n.m).padStart(2,"0")}`}}return null}function ki(e,o){return`${e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40)||"job"}-${o}`}function vi(e){let o=e||za(process.cwd(),"agentx.json");return Fa(o)||(console.log(U.red(` No config at ${o}. Run: agentx init`)),process.exit(1)),JSON.parse(qa(o,"utf-8"))}function Ga(e){return e?e.split(",").map(o=>o.trim().toLowerCase()).filter(o=>o==="log"||o==="notify"||o==="disable"):["log"]}async function Ya(e,o){if(!e)return;if(e==="me"){let n=o?.notifications?.destination;if(n?.channel&&n?.chatId)return{channel:n.channel,chatId:n.chatId,accountId:n.accountId};console.log(U.yellow(" --notify me: no notifications.destination set yet."));let{channel:s,chatId:i,accountId:r}=await Ha([{type:"text",name:"channel",message:"Destination channel (telegram/whatsapp/discord):"},{type:"text",name:"chatId",message:"Destination chatId:"},{type:"text",name:"accountId",message:"Account id (optional):"}]);return!s||!i?void 0:{channel:s,chatId:i,accountId:r||void 0}}let t=e.split(":");if(t.length>=2)return{channel:t[0],chatId:t[1],accountId:t[2]};console.log(U.red(` Unrecognized --notify value: ${e} (use "me" or "channel:chatId[:account]")`))}var eo=new Ba().name("schedule").description('schedule agents with natural-language cron: "every morning at 9" --agent devops --do "Post standup"');eo.argument("[when]",'natural-language schedule, e.g. "every morning at 9"').option("--agent <id>","agent to run").option("--do <prompt>","prompt to run at each tick (required unless --id exists)").option("--id <name>","explicit cron id (default: auto-slug of <when>-<agent>)").option("--notify <target>",'"me" (use notifications.destination) or "channel:chatId[:accountId]"').option("--on-error <modes>",'comma list of "log|notify|disable" (default: log; notify implies "notify")').option("--timezone <tz>","IANA timezone (default: Africa/Tunis)","Africa/Tunis").option("--timeout <seconds>","max run time","600").option("--model <model>","override model").option("--disabled","create but leave disabled").option("--dry-run","print what would be written without writing").action(async(e,o)=>{if(!e){eo.help();return}o.agent||(console.log(U.red(' --agent is required. Try: agentx schedule "every morning at 9" --agent devops --do "..."')),process.exit(1)),!o.do&&!o.id&&(console.log(U.red(' --do "<prompt>" is required')),process.exit(1));let t=In(e);t||(console.log(U.red(` Couldn't parse: "${e}"`)),console.log(U.dim(' Try: "every morning at 9", "weekdays at 6pm", "every 15 minutes",')),console.log(U.dim(' "every monday at 10am", "1st of every month at noon",')),console.log(U.dim(' "daily at 9:30am", "every hour", "hourly"')),process.exit(1));let n=vi(o.config),s=fs(n);n.agents?.[o.agent]||(console.log(U.red(` Agent "${o.agent}" not found in agentx.json`)),console.log(U.dim(` Available: ${Object.keys(n.agents||{}).join(", ")||"(none)"}`)),process.exit(1));let i=Ga(o.onError),r=await Ya(o.notify,s);r&&!i.includes("notify")&&i.push("notify");let a=o.id||ki(t.matched,o.agent),c={enabled:!o.disabled,schedule:t.cron,timezone:o.timezone,agent:o.agent,prompt:o.do,timeout:parseInt(o.timeout,10),onError:i};o.model&&(c.model=o.model),r&&(c.notify=r);let d=await K(l=>X(l,`crons.${a}`,c),{configPath:o.config,dryRun:!!o.dryRun});d.success||(console.log(U.red(` \u2717 ${d.error}`)),process.exit(1));let g=o.dryRun?"would add":"Added";console.log(U.green(` \u2713 ${g} cron ${U.cyan(a)}`)),console.log(U.dim(` Schedule: ${t.cron} (${t.human}, ${o.timezone})`)),console.log(U.dim(` Agent: ${o.agent}`)),r&&console.log(U.dim(` Notify: ${r.channel} ${r.chatId}`)),i.length&&console.log(U.dim(` On error: ${i.join(", ")}`)),!o.dryRun&&d.reloaded&&console.log(U.dim(" Daemon hot-reloaded."))});eo.command("list").alias("ls").description("list all scheduled jobs with human-readable descriptions").option("-c, --config <path>","path to agentx.json").action(async e=>{let t=vi(e.config).crons||{},n=Object.entries(t);if(!n.length){console.log(U.dim(" No scheduled jobs"));return}console.log();for(let[s,i]of n){let r=i.enabled?U.green("\u25CF"):U.dim("\u25CB"),a=i.schedule;try{a=(await import("cronstrue")).default.toString(i.schedule,{use24HourTimeFormat:!1})}catch{}console.log(` ${r} ${U.cyan(s.padEnd(32))} ${U.dim(i.schedule.padEnd(14))} ${U.dim("\u2192")} ${i.agent}`),console.log(U.dim(` ${a} (${i.timezone||"UTC"})`)),i.notify&&console.log(U.dim(` notify: ${i.notify.channel} ${i.notify.chatId}`))}console.log()});for(let[e,o]of[["on",!0],["off",!1]])eo.command(`${e} <id>`).description(`${o?"enable":"disable"} a scheduled job`).action(async t=>{let n=await K(s=>{if(!co(s,`crons.${t}`))throw new Error(`cron "${t}" not found`);X(s,`crons.${t}.enabled`,o)});n.success||(console.log(U.red(` \u2717 ${n.error}`)),process.exit(1)),console.log(U.green(` \u2713 ${t} ${o?"enabled":"disabled"}`)),n.reloaded&&console.log(U.dim(" Daemon hot-reloaded."))});eo.command("remove <id>").alias("rm").description("remove a scheduled job").action(async e=>{let o=await K(t=>{if(!co(t,`crons.${e}`))throw new Error(`cron "${e}" not found`);hs(t,`crons.${e}`)});o.success||(console.log(U.red(` \u2717 ${o.error}`)),process.exit(1)),console.log(U.green(` \u2713 ${e} removed`)),o.reloaded&&console.log(U.dim(" Daemon hot-reloaded."))});eo.command("parse <when...>").description("preview a natural-language parse without writing anything").action(e=>{let o=e.join(" "),t=In(o);t||(console.log(U.red(` \u2717 could not parse: "${o}"`)),process.exit(1)),console.log(U.green(` "${o}"`)),console.log(U.dim(` \u2192 ${t.cron}`)),console.log(U.dim(` \u2192 ${t.human}`))});import{Command as bc}from"commander";import wo from"chalk";import H from"chalk";import tt from"prompts";import{readFileSync as Ka,writeFileSync as Va,existsSync as Qa}from"fs";import{resolve as xi}from"path";var Xa=/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/;function Za(e){let o=e.trim();return o.startsWith('"')&&o.endsWith('"')||o.startsWith("'")&&o.endsWith("'")?o.slice(1,-1):o}function An(e){return/[\s#"'\\]/.test(e)||e===""?JSON.stringify(e):e}function Si(e=".env"){let o=xi(e);if(!Qa(o))return[];let t=Ka(o,"utf-8"),n=[];for(let s of t.split(/\r?\n/)){if(s.trim()===""){n.push({kind:"blank",raw:s});continue}if(s.trim().startsWith("#")){n.push({kind:"comment",raw:s});continue}let i=s.match(Xa);i?n.push({kind:"kv",key:i[1],value:Za(i[2]),raw:s}):n.push({kind:"comment",raw:s})}return n.length&&n[n.length-1].kind==="blank"&&n.pop(),n}function ec(e){return e.map(o=>o.kind==="kv"?`${o.key}=${An(o.value)}`:o.raw).join(`
211
+ `)+`
212
+ `}function yo(e,o,t=".env"){let n=xi(t),s=Si(n),i=s.findIndex(r=>r.kind==="kv"&&r.key===e);i>=0?s[i]={kind:"kv",key:e,value:o,raw:`${e}=${An(o)}`}:(s.length&&s[s.length-1].kind!=="blank"&&s.push({kind:"blank",raw:""}),s.push({kind:"kv",key:e,value:o,raw:`${e}=${An(o)}`})),Va(n,ec(s))}function On(e,o=".env"){return Si(o).find(s=>s.kind==="kv"&&s.key===e)?.value}var ji="https://t.me/BotFather";async function oc(e){try{let o=await fetch(`https://api.telegram.org/bot${e}/getMe`,{signal:AbortSignal.timeout(8e3)});if(!o.ok)return null;let t=await o.json();return t?.ok?t.result:null}catch{return null}}async function tc(e,o){let t=Date.now()+o,n=0;try{let i=await(await fetch(`https://api.telegram.org/bot${e}/getUpdates?offset=-1&timeout=0`,{signal:AbortSignal.timeout(5e3)})).json();i?.result?.length&&(n=i.result[0].update_id+1)}catch{}for(;Date.now()<t;)try{let i=await(await fetch(`https://api.telegram.org/bot${e}/getUpdates?offset=${n}&timeout=10`,{signal:AbortSignal.timeout(15e3)})).json();for(let r of i?.result||[]){n=Math.max(n,r.update_id+1);let a=r.message||r.edited_message||r.channel_post;if(a?.chat?.id)return{chatId:a.chat.id,chatTitle:a.chat.title||a.chat.username||a.chat.first_name,from:a.from?.first_name||a.from?.username||"unknown"}}}catch{}return null}async function Ei(e={}){console.log(),console.log(H.bold(" Connect Telegram")),console.log(),console.log(H.dim(` 1. Open ${ji}`)),console.log(H.dim(" /newbot (or /mybots \u2192 pick existing \u2192 API Token)")),console.log(H.dim(" 2. Copy the HTTP API token \u2014 looks like 123456789:ABCdef...")),console.log();let{openBrowser:o}=await tt({type:"confirm",name:"openBrowser",message:"Open BotFather in your browser?",initial:!0});if(o)try{let{spawn:b}=await import("child_process"),S=process.platform==="darwin"?"open":process.platform==="win32"?"start":"xdg-open";b(S,[ji],{stdio:"ignore",detached:!0}).unref()}catch{}let{token:t}=await tt({type:"password",name:"token",message:"Paste the bot token from BotFather:",validate:b=>/^\d+:[A-Za-z0-9_-]{20,}$/.test(b.trim())||"That doesn't look like a Telegram token"});t||(console.log(H.red(" Aborted")),process.exit(1)),process.stdout.write(" Verifying token... ");let n=await oc(t.trim());n||(console.log(H.red("\u2717 invalid or revoked")),process.exit(1)),console.log(H.green(`\u2713 ${n.first_name} (@${n.username})`));let{readFileSync:s,existsSync:i}=await import("fs"),{resolve:r}=await import("path"),a=e.configPath||r(process.cwd(),"agentx.json");i(a)||(console.log(H.red(` No agentx.json at ${a}. Run: agentx init`)),process.exit(1));let c=JSON.parse(s(a,"utf-8")),d=Object.keys(c.agents||{});d.length===0&&(console.log(H.red(" No agents configured. Run: agentx agent add")),process.exit(1));let g=e.agent;g||(g=(await tt({type:"select",name:"agent",message:"Bind this bot to which agent?",choices:d.map(S=>({title:S,value:S})),initial:0})).agent),g||(console.log(H.red(" Aborted")),process.exit(1));let l=c.channels?.telegram?.accounts||{},u=e.account||g;l[u]&&l[u].agentBinding!==g&&(u=(await tt({type:"text",name:"account",message:`Account "${u}" is already in use. Pick a different label:`,initial:`${g}-2`,validate:S=>/^[a-z0-9-]+$/i.test(S)||"lowercase letters, digits, hyphens only"})).account),u||(console.log(H.red(" Aborted")),process.exit(1));let m=`TG_${u.toUpperCase().replace(/-/g,"_")}_BOT_TOKEN`;yo(m,t.trim()),process.env[m]=t.trim();let f=await K(b=>{X(b,"channels.telegram.enabled",!0),X(b,`channels.telegram.accounts.${u}`,{token:`\${${m}}`,agentBinding:g});let S=co(b,`agents.${g}.mentions`)||[],x=`@${n.username}`;S.includes(x)||(S.push(x),X(b,`agents.${g}.mentions`,S))},{configPath:e.configPath});if(f.success||(console.log(H.red(` \u2717 ${f.error}`)),process.exit(1)),console.log(),console.log(H.green(` \u2713 Bot @${n.username} bound to agent "${g}"`)),console.log(H.dim(` Token stored in .env as ${m}`)),console.log(H.dim(` Mentions updated to include @${n.username}`)),f.reloaded?console.log(H.dim(" Daemon hot-reloaded.")):console.log(H.dim(" Start the daemon to begin polling (agentx daemon start)")),e.skipChatCapture)return;console.log();let{capture:y}=await tt({type:"confirm",name:"capture",message:"Listen for a message so we can set this as your default --notify target?",initial:!0});if(!y)return;console.log(),console.log(H.dim(` Send any message to @${n.username} from the chat you want to use.`)),console.log(H.dim(" Waiting up to 90s..."));let $=await tc(t.trim(),9e4);if(!$){console.log(H.yellow(" No message received \u2014 skipping. You can set it later with:")),console.log(H.dim(" agentx config set notifications.destination.channel telegram")),console.log(H.dim(" agentx config set notifications.destination.chatId <chatId>")),console.log(H.dim(` agentx config set notifications.destination.accountId ${u}`));return}let v=await K(b=>{X(b,"notifications.destination",{channel:"telegram",chatId:String($.chatId),accountId:u})},{configPath:e.configPath});if(!v.success){console.log(H.red(` \u2717 failed to save destination: ${v.error}`));return}console.log(H.green(" \u2713 Default --notify target set")),console.log(H.dim(` Chat: ${$.chatTitle||$.chatId} (from ${$.from})`)),console.log(H.dim(" From now on, --notify me resolves to this chat.")),v.reloaded&&console.log(H.dim(" Daemon hot-reloaded."))}import ce from"chalk";import Dt from"prompts";import{spawn as nc}from"child_process";var Ii="https://discord.com/developers/applications",sc="67584";async function ic(e){try{let o=await fetch("https://discord.com/api/v10/users/@me",{headers:{Authorization:`Bot ${e}`},signal:AbortSignal.timeout(8e3)});return o.ok?await o.json():null}catch{return null}}function Ai(e){try{let o=process.platform==="darwin"?"open":process.platform==="win32"?"start":"xdg-open";nc(o,[e],{stdio:"ignore",detached:!0}).unref()}catch{}}async function Oi(e={}){console.log(),console.log(ce.bold(" Connect Discord")),console.log(),console.log(ce.dim(" 1. Open the Discord Developer Portal")),console.log(ce.dim(` ${Ii}`)),console.log(ce.dim(" 2. New Application \u2192 Bot \u2192 Reset Token \u2192 copy the token")),console.log(ce.dim(" 3. Under Bot \u2192 Privileged Gateway Intents,")),console.log(ce.dim(" enable 'MESSAGE CONTENT INTENT'")),console.log();let{openBrowser:o}=await Dt({type:"confirm",name:"openBrowser",message:"Open the Developer Portal in your browser?",initial:!0});o&&Ai(Ii);let{token:t}=await Dt({type:"password",name:"token",message:"Paste the bot token:",validate:$=>$.trim().length>20||"that looks too short for a Discord token"});t||(console.log(ce.red(" Aborted")),process.exit(1)),process.stdout.write(" Verifying token... ");let n=await ic(t.trim());n||(console.log(ce.red("\u2717 invalid or revoked \u2014 did you enable MESSAGE CONTENT INTENT and copy the right token?")),process.exit(1));let s=n.discriminator&&n.discriminator!=="0"?`${n.username}#${n.discriminator}`:n.username;console.log(ce.green(`\u2713 ${s} (bot id ${n.id})`));let{readFileSync:i,existsSync:r}=await import("fs"),{resolve:a}=await import("path"),c=e.configPath||a(process.cwd(),"agentx.json");r(c)||(console.log(ce.red(` No agentx.json at ${c}. Run: agentx init`)),process.exit(1));let d=JSON.parse(i(c,"utf-8")),g=Object.keys(d.agents||{});g.length===0&&(console.log(ce.red(" No agents configured. Run: agentx agent add")),process.exit(1));let l=e.agent;l||(l=(await Dt({type:"select",name:"agent",message:"Bind this bot to which agent?",choices:g.map(v=>({title:v,value:v})),initial:0})).agent),l||(console.log(ce.red(" Aborted")),process.exit(1));let u="DISCORD_BOT_TOKEN";yo(u,t.trim()),process.env[u]=t.trim();let m=await K($=>{X($,"channels.discord.enabled",!0),X($,"channels.discord.token",`\${${u}}`),X($,"channels.discord.agentBinding",l)},{configPath:e.configPath});m.success||(console.log(ce.red(` \u2717 ${m.error}`)),process.exit(1)),console.log(),console.log(ce.green(` \u2713 Discord bot ${s} bound to agent "${l}"`)),console.log(ce.dim(` Token stored in .env as ${u}`)),m.reloaded?console.log(ce.dim(" Daemon hot-reloaded.")):console.log(ce.dim(" Restart the daemon to start Discord polling.")),console.log(),console.log(ce.bold(" Install the bot to a server")),console.log();let f=`https://discord.com/oauth2/authorize?client_id=${n.id}&scope=bot&permissions=${sc}`;console.log(` ${ce.cyan(f)}`),console.log();let{openInstall:y}=await Dt({type:"confirm",name:"openInstall",message:"Open the install URL in your browser now?",initial:!0});y&&Ai(f),console.log(),console.log(ce.dim(" After you pick a server, the bot will come online on the next daemon restart.")),console.log(ce.dim(" Mention the bot in any channel to route messages to the agent."))}import ie from"chalk";import Cn from"prompts";import{existsSync as Tn,mkdirSync as rc,readdirSync as ac,readFileSync as Ci}from"fs";import{resolve as Rn}from"path";var cc=".agentx/whatsapp-sessions";function lc(){let e=Rn(process.cwd(),".agentx/daemon.pid");if(!Tn(e))return!1;try{let o=parseInt(Ci(e,"utf-8").trim(),10);return o?(process.kill(o,0),!0):!1}catch{return!1}}function dc(e){if(!Tn(e))return!1;try{return ac(e).some(o=>o.includes("creds")||o.endsWith(".json"))}catch{return!1}}async function gc(e,o){let t;try{t=await import("@whiskeysockets/baileys")}catch{return o(ie.red(" WhatsApp pairing needs @whiskeysockets/baileys.")),o(ie.red(" Install with: pnpm add @whiskeysockets/baileys (or npm install)")),!1}let{makeWASocket:n,useMultiFileAuthState:s}=t.default||t;rc(e,{recursive:!0});let{state:i,saveCreds:r}=await s(e),a;try{a=(await t.fetchLatestBaileysVersion()).version}catch{}let c={level:"silent",trace:()=>{},debug:()=>{},info:()=>{},warn:()=>{},fatal:()=>{},error:()=>{},child:()=>c},d=n({auth:{creds:i.creds,keys:t.makeCacheableSignalKeyStore?t.makeCacheableSignalKeyStore(i.keys,c):i.keys},...a?{version:a}:{},logger:c,printQRInTerminal:!1,browser:["agentx-connect","pairing","1.0"],syncFullHistory:!1,markOnlineOnConnect:!1});return d.ev.on("creds.update",r),await new Promise(g=>{let u=setTimeout(()=>{o(ie.yellow(" Timed out waiting for scan (2min)."));try{d.end(void 0)}catch{}g(!1)},12e4);d.ev.on("connection.update",async m=>{if(m.qr){o(ie.dim(" Scan this QR with WhatsApp \u2192 Linked devices \u2192 Link a device:")),o("");try{let{default:f}=await import("qrcode-terminal");f.generate(m.qr,{small:!0})}catch{o(` QR: ${m.qr}`)}o("")}if(m.connection==="open"){clearTimeout(u),o(ie.green(" \u2713 Paired"));try{d.end(void 0)}catch{}g(!0)}m.connection==="close"&&!m.qr&&m.lastDisconnect?.error?.output?.statusCode===401&&(clearTimeout(u),o(ie.red(" \u2717 Authorization failed \u2014 rescan required")),g(!1))})})}async function Ri(e={}){if(console.log(),console.log(ie.bold(" Connect WhatsApp")),console.log(),lc()){console.log(ie.yellow(" \u26A0 The daemon is running. Stop it first so pairing can open a fresh Baileys session:")),console.log(ie.dim(" agentx daemon stop")),console.log();let{cont:d}=await Cn({type:"confirm",name:"cont",message:"Continue anyway? (expect errors)",initial:!1});d||(console.log(ie.red(" Aborted")),process.exit(1))}let o=e.configPath||Rn(process.cwd(),"agentx.json");Tn(o)||(console.log(ie.red(` No agentx.json at ${o}. Run: agentx init`)),process.exit(1));let t=JSON.parse(Ci(o,"utf-8")),n=e.sessionDir||t.channels?.whatsapp?.sessionDir||cc,s=Rn(process.cwd(),n),i=Object.keys(t.agents||{});i.length===0&&(console.log(ie.red(" No agents configured. Run: agentx agent add")),process.exit(1));let r=e.agent;r||(r=(await Cn({type:"select",name:"agent",message:"Default agent for incoming messages:",choices:i.map(g=>({title:g,value:g})),initial:0})).agent),r||(console.log(ie.red(" Aborted")),process.exit(1));let a=!e.skipPair;if(dc(s)&&a){let{keep:d}=await Cn({type:"confirm",name:"keep",message:`An existing session was found at ${n}. Keep it (recommended), or pair again?`,initial:!0});a=!d,d&&console.log(ie.dim(" Keeping existing session \u2014 skipping QR pair."))}a&&(await gc(s,g=>console.log(g))||(console.log(ie.red(" Pairing failed. You can retry with `agentx connect whatsapp`.")),process.exit(1)));let c=await K(d=>{X(d,"channels.whatsapp.enabled",!0),X(d,"channels.whatsapp.sessionDir",n),X(d,"channels.whatsapp.defaultAgent",r)},{configPath:e.configPath});c.success||(console.log(ie.red(` \u2717 ${c.error}`)),process.exit(1)),console.log(),console.log(ie.green(` \u2713 WhatsApp bound to agent "${r}"`)),console.log(ie.dim(` Session: ${n}`)),console.log(ie.dim(" Start the daemon to begin receiving:")),console.log(ie.dim(" agentx daemon start")),console.log(ie.dim(" Add routes later with:")),console.log(ie.dim(` agentx config set channels.whatsapp.routes '[{"contact":"+1234567890","agent":"${r}"}]'`)),console.log()}import{randomBytes as mc}from"crypto";import le from"chalk";import Ti from"prompts";var Nt="agentx-mesh:",_t="//join/";function pc(e){return Buffer.from(e,"utf-8").toString("base64url")}function uc(e){return Buffer.from(e,"base64url").toString("utf-8")}function fc(e){return`${Nt}${_t}${pc(JSON.stringify(e))}`}function hc(e){if(!e.startsWith(`${Nt}${_t}`))throw new Error(`Not a mesh invite link \u2014 expected ${Nt}${_t}\u2026`);let o=e.slice(Nt.length+_t.length),t=JSON.parse(uc(o));if(t.version!==1)throw new Error(`Unsupported invite version: ${t.version}`);if(typeof t.url!="string"||typeof t.token!="string"||typeof t.name!="string")throw new Error("Invite payload missing required fields (url, token, name)");return t}function yc(){return mc(32).toString("hex")}function wc(e){let o=e?.node?.bind||"127.0.0.1:18800",[t,n]=o.split(":");return`http://${t}:${n}`}function $c(e){return e?.node?.name||e?.node?.id||"node"}async function Di(e){let{readFileSync:o,existsSync:t}=await import("fs"),{resolve:n}=await import("path"),s=e.configPath||n(process.cwd(),"agentx.json");t(s)||(console.log(le.red(` No agentx.json at ${s}. Run: agentx init`)),process.exit(1));let i=JSON.parse(o(s,"utf-8")),r=On("MESH_TOKEN");r||(r=yc(),yo("MESH_TOKEN",r),console.log(le.dim(" Generated new MESH_TOKEN (32 bytes) and saved to .env")));let a=e.url||wc(i);if((a.includes("0.0.0.0")||a.includes("127.0.0.1")||a.includes("localhost"))&&!e.url){let{url:l}=await Ti({type:"text",name:"url",message:"This node's URL as peers should reach it (e.g. http://100.x.x.x:18800):",initial:a,validate:u=>/^https?:\/\//.test(u)||"must start with http:// or https://"});l||(console.log(le.red(" Aborted")),process.exit(1)),a=l}let d=await K(l=>{X(l,"mesh.enabled",!0)},{configPath:e.configPath});d.success||(console.log(le.red(` \u2717 ${d.error}`)),process.exit(1));let g=fc({url:a,token:r,name:$c(i),version:1});console.log(),console.log(le.bold(" Mesh invite")),console.log(),console.log(` ${le.cyan(g)}`),console.log(),console.log(le.dim(" Run on the other node:")),console.log(le.dim(` agentx connect mesh join "${g}"`)),console.log(),console.log(le.yellow(" Share over a trusted channel only \u2014 whoever holds this link can join your mesh.")),console.log()}async function Ni(e,o={}){let t;try{t=hc(e)}catch(i){console.log(le.red(` \u2717 ${i.message}`)),process.exit(1)}let n=On("MESH_TOKEN");if(n&&n!==t.token){let{overwrite:i}=await Ti({type:"confirm",name:"overwrite",message:"A different MESH_TOKEN already exists in .env. Overwrite with the invite's token?",initial:!1});i||(console.log(le.red(" Aborted (keeping existing .env)")),process.exit(1))}yo("MESH_TOKEN",t.token);let s=await K(i=>{X(i,"mesh.enabled",!0);let r=co(i,"mesh.peers")||[],a=r.findIndex(d=>d.name===t.name),c={name:t.name,url:t.url,token:"${MESH_TOKEN}"};a>=0?r[a]=c:r.push(c),X(i,"mesh.peers",r)},{configPath:o.configPath});s.success||(console.log(le.red(` \u2717 ${s.error}`)),process.exit(1)),console.log(le.green(" \u2713 Joined mesh")),console.log(le.dim(` Peer: ${t.name} @ ${t.url}`)),console.log(le.dim(" Shared token stored in .env as MESH_TOKEN")),s.reloaded?console.log(le.dim(" Daemon hot-reloaded.")):console.log(le.dim(" Restart the daemon to start the mesh (agentx daemon start).")),console.log();try{let i=await fetch(`${t.url.replace(/\/$/,"")}/.well-known/agent-card.json`,{signal:AbortSignal.timeout(5e3)});if(i.ok){let r=await i.json(),a=Array.isArray(r.agents)?r.agents.length:"?";console.log(le.dim(` Reachable \u2014 ${t.name} exposes ${a} agents`))}else console.log(le.yellow(` Peer returned HTTP ${i.status} \u2014 double-check the URL`))}catch(i){console.log(le.yellow(` Could not reach ${t.url} right now (${i?.cause?.code||i.message}) \u2014 verify VPN / Tailscale when the peer daemon is up`))}}var $o=new bc().name("connect").description("connect a channel or mesh peer \u2014 no manual file editing required");$o.command("telegram").description("pair a Telegram bot \u2014 persists token in .env, binds to an agent, auto-detects default chat").option("--agent <id>","agent to bind the bot to (skips the prompt)").option("--account <name>","account label in channels.telegram.accounts (defaults to agent id)").option("--skip-chat-capture","don't listen for the first message after pairing").option("-c, --config <path>","path to agentx.json").action(async e=>{await Ei({agent:e.agent,account:e.account,configPath:e.config,skipChatCapture:!!e.skipChatCapture})});$o.command("discord").description("pair a Discord bot \u2014 verify token, emit install URL, save to .env").option("--agent <id>","agent to bind the bot to").option("-c, --config <path>","path to agentx.json").action(async e=>{await Oi({agent:e.agent,configPath:e.config})});$o.command("whatsapp").description("pair WhatsApp via QR (prints in terminal), then writes channels.whatsapp config").option("--agent <id>","default agent for inbound messages").option("--session-dir <path>","session directory (default: .agentx/whatsapp-sessions)").option("--skip-pair","skip QR pairing (use when session already exists)").option("-c, --config <path>","path to agentx.json").action(async e=>{await Ri({agent:e.agent,sessionDir:e.sessionDir,skipPair:!!e.skipPair,configPath:e.config})});var _i=$o.command("mesh").description("mesh invite + join \u2014 replaces manual MESH_TOKEN copy-paste");_i.command("invite").description("emit a single-use join link for another AgentX node").option("--url <url>","this node's URL as peers should reach it (Tailscale IP etc.)").option("-c, --config <path>","path to agentx.json").action(async e=>{await Di({url:e.url,configPath:e.config})});_i.command("join <link>").description("accept an invite link from another AgentX node").option("-c, --config <path>","path to agentx.json").action(async(e,o)=>{await Ni(e,{configPath:o.config})});$o.action(()=>{console.log(),console.log(wo.bold(" agentx connect \u2014 which channel?")),console.log(),console.log(` ${wo.cyan("agentx connect telegram")} \u2014 pair a Telegram bot`),console.log(` ${wo.cyan("agentx connect discord")} \u2014 pair a Discord bot, emit install URL`),console.log(` ${wo.cyan("agentx connect whatsapp")} \u2014 pair WhatsApp via QR`),console.log(` ${wo.cyan("agentx connect mesh invite")} \u2014 emit a mesh join link for another node`),console.log(` ${wo.cyan("agentx connect mesh join <link>")} \u2014 accept a mesh invite`),console.log(),console.log(wo.dim(" GitLab connect is the next slice.")),console.log()});import{Command as kc}from"commander";import oe from"chalk";var nt=new kc().name("usage").description("token usage analysis and reporting");nt.command("today",{isDefault:!0}).description("show today's token usage").action(async()=>{try{let e=L(),[o,t]=e.node.bind.split(":"),s=await(await fetch(`http://${o||"127.0.0.1"}:${t}/usage`,{signal:AbortSignal.timeout(3e3)})).json();if(console.log(),console.log(oe.bold(" Token Usage (last 7 days)")),console.log(),s.totalTasks===0){console.log(oe.dim(" No tasks recorded yet")),console.log();return}let i=a=>a.toLocaleString();console.log(` Total: ${oe.bold(i(s.totalTokens))} tokens across ${s.totalTasks} tasks`),console.log(` Input: ${i(s.totalInput)}`),console.log(` Output: ${i(s.totalOutput)}`),console.log(` Cache read: ${i(s.totalCacheRead)}`),console.log(` Cache create: ${i(s.totalCacheCreate)}`),s.cacheHitRatio>0&&console.log(` Cache hit: ${(s.cacheHitRatio*100).toFixed(1)}%`),s.totalErrors>0&&console.log(` Errors: ${oe.red(String(s.totalErrors))}`),console.log(),console.log(oe.bold(" By Agent:"));let r=Object.entries(s.byAgent||{});r.sort((a,c)=>c[1].total-a[1].total);for(let[a,c]of r){let d=i(c.total||c.input+c.output+c.cacheRead+c.cacheCreate),g=c.avgDuration?`${(c.avgDuration/1e3).toFixed(1)}s avg`:"";console.log(` ${oe.cyan(a)}: ${d} tokens (${c.tasks} tasks) ${oe.dim(g)}`)}console.log()}catch(e){e.cause?.code==="ECONNREFUSED"?console.log(oe.red(" Daemon not running. Start with: agentx daemon start")):console.log(oe.red(` ${e.message}`))}});nt.command("report").description("run full session analysis (parses Claude Code JSONL files)").option("--days <n>","analyze last N days","7").action(async e=>{let{execSync:o}=await import("child_process"),{existsSync:t}=await import("fs"),{resolve:n}=await import("path"),s=n(process.cwd(),"scripts/token-report.py");if(!t(s)){console.log(oe.red(` Script not found: ${s}`)),console.log(oe.dim(" The token analyzer script should be at scripts/token-report.py"));return}console.log(oe.dim(" Analyzing Claude Code sessions..."));try{o(`SINCE_DAYS=${e.days} python3 "${s}"`,{stdio:"inherit"})}catch{console.log(oe.red(" Analysis failed. Is Python 3 installed?"))}});nt.command("surfaces").description("which CLI commands and dashboard pages are actually used").option("--days <n>","window in days","30").option("--kind <kind>","cli | page (default: both)").option("--unused","list registered surfaces with ZERO recorded use instead").option("--json","raw JSON output").action(async e=>{let{listSurfaceUse:o}=await import("./surface-usage-PRL6Z4U3.js"),t=e.kind==="cli"||e.kind==="page"?e.kind:void 0,n=o({kind:t,sinceDays:Number(e.days)||30});if(e.unused){let{buildProgram:i}=await import("./program-WVAVJFND.js"),r=await i(),a=new Set(n.filter(l=>l.kind==="cli").map(l=>l.name)),c=[],d=(l,u)=>{for(let m of l.commands??[]){let f=u?`${u} ${m.name()}`:m.name();c.push(f),d(m,f)}};d(r,"");let g=c.filter(l=>!a.has(l)).sort();if(e.json){console.log(JSON.stringify({windowDays:Number(e.days)||30,unused:g},null,2));return}console.log(),console.log(oe.bold(` Never used in the last ${e.days} days (${g.length}/${c.length})`)),console.log();for(let l of g)console.log(` ${oe.dim("\xB7")} ${l}`);console.log(),console.log(oe.dim(" Zero use is necessary but not sufficient to remove a surface \u2014")),console.log(oe.dim(" it must also fail the impact test. See docs/architecture/surface-reduction.md")),console.log();return}if(e.json){console.log(JSON.stringify(n,null,2));return}if(n.length===0){console.log(),console.log(oe.dim(` No surface usage recorded in the last ${e.days} days.`)),console.log(oe.dim(" Counting starts once this build has been running for a while.")),console.log();return}console.log(),console.log(oe.bold(` Surface usage \u2014 last ${e.days} days`)),console.log();let s="";for(let i of n){i.kind!==s&&(console.log(oe.dim(` ${i.kind==="cli"?"CLI commands":"Dashboard pages"}`)),s=i.kind);let r=String(i.count).padStart(6),a=oe.dim(`${i.days}d`);console.log(` ${oe.cyan(r)} ${i.name.padEnd(28)} ${a}`)}console.log(),console.log(oe.dim(` ${n.length} surface(s) with recorded use. See --unused for the rest.`)),console.log()});import{Command as vc}from"commander";import re from"chalk";var oo=new vc().name("board").description("kanban board dashboard \u2014 visual work view over configured sources");oo.command("serve",{isDefault:!0}).description("start the dashboard server (live view always; boards if configured)").option("--port <n>","override dashboard.port").option("--bind <host>","override dashboard.bind (e.g. 0.0.0.0)").action(async e=>{try{let o=xc(e.port,e.bind),{startBoardDashboard:t}=await import("./board-dashboard-CQ5F4GPD.js");t(o)}catch(o){console.log(re.red(` ${o.message}`)),process.env.DEBUG&&o.stack&&console.log(re.dim(o.stack)),process.exit(1)}});function xc(e,o){try{let t=L();return e&&(t.dashboard.port=parseInt(e,10)),o&&(t.dashboard.bind=o),t}catch{return console.log(re.yellow(" No agentx.json found \u2014 starting dashboard in setup-only mode.")),console.log(re.dim(" Visit http://127.0.0.1:"+(e||"4202")+` to run the wizard.
213
+ `)),{node:{id:"setup",name:"Setup",bind:"127.0.0.1:18800"},providers:{},agents:{},channels:{},crons:{},mesh:{enabled:!1,peers:[],discovery:"static",healthCheck:{interval:60,timeout:10}},boards:[],dashboard:{enabled:!0,port:e?parseInt(e,10):4202,bind:o||"127.0.0.1",daemonUrl:"http://localhost:18800",daemons:[]},business:void 0,session:{staleMinutes:120}}}}oo.command("list").description("list configured boards").action(()=>{try{let e=L();if(!e.boards?.length){console.log(re.dim(`
214
+ No boards configured.
215
+ `));return}console.log();for(let o of e.boards){console.log(` ${re.cyan(o.id)} \u2014 ${o.name}`);let t=o.source;console.log(re.dim(` source: ${t.type}${t.projects?" ("+t.projects.join(", ")+")":""}`)),o.primaryToolLabel&&console.log(re.dim(` primary label: ${o.primaryToolLabel}`)),console.log(re.dim(` window: ${o.timeRangeDays} days`))}console.log()}catch(e){console.log(re.red(` ${e.message}`)),process.exit(1)}});oo.command("add <id>").description("add a GitLab board to agentx.json").requiredOption("--name <name>","human-readable board name").requiredOption("--projects <paths>","comma-separated GitLab project paths (e.g. 'mtgl/system,mtgl/website')").option("--label <label>","primary tool label ANDed into every query (e.g. 'Tool::Claude')").option("--days <n>","open-window time range in days","30").option("--closed-days <n>","closed-window in days","30").action((e,o)=>{try{st(t=>{if(Array.isArray(t.boards)||(t.boards=[]),t.boards.find(i=>i.id===e))throw new Error(`board "${e}" already exists`);let n=String(o.projects).split(",").map(i=>i.trim()).filter(Boolean);if(n.length===0)throw new Error("--projects must include at least one path");let s={id:e,name:o.name,source:{type:"gitlab",projects:n},timeRangeDays:parseInt(o.days,10)||30,closedWindowDays:parseInt(o.closedDays,10)||30};return o.label&&(s.primaryToolLabel=o.label),t.boards.push(s),`added board "${e}" (${n.join(", ")})`})}catch(t){console.log(re.red(` ${t.message}`)),process.exit(1)}});oo.command("edit <id>").description("edit board fields without re-creating it").option("--name <name>","rename").option("--projects <paths>","comma-separated GitLab project paths (replaces the list)").option("--label <label>","primary tool label (set to '-' to clear)").option("--days <n>","open-window time range in days").option("--closed-days <n>","closed-window in days").action((e,o)=>{try{st(t=>{let s=(Array.isArray(t.boards)?t.boards:[]).find(r=>r.id===e);if(!s)throw new Error(`board "${e}" not found`);let i=[];if(o.name&&(s.name=o.name,i.push(`name="${o.name}"`)),o.projects){let r=String(o.projects).split(",").map(a=>a.trim()).filter(Boolean);if(r.length===0)throw new Error("--projects must include at least one path");s.source={...s.source||{type:"gitlab"},type:"gitlab",projects:r},i.push(`projects=${r.length}`)}if(o.label!==void 0&&(o.label==="-"?(delete s.primaryToolLabel,i.push("label cleared")):(s.primaryToolLabel=o.label,i.push(`label="${o.label}"`))),o.days&&(s.timeRangeDays=parseInt(o.days,10)||s.timeRangeDays,i.push(`days=${s.timeRangeDays}`)),o.closedDays&&(s.closedWindowDays=parseInt(o.closedDays,10)||s.closedWindowDays,i.push(`closed-days=${s.closedWindowDays}`)),i.length===0)throw new Error("no changes \u2014 pass at least one option");return`board "${e}" updated (${i.join(", ")})`})}catch(t){console.log(re.red(` ${t.message}`)),process.exit(1)}});var Dn=oo.command("column").description("manage a board's columns (add / remove / edit / list)");Dn.command("list <boardId>").description("list columns on a board, in order").action(e=>{try{let t=(JSON.parse(Ar("fs").readFileSync("agentx.json","utf-8")).boards||[]).find(s=>s.id===e);if(!t)throw new Error(`board "${e}" not found`);let n=t.columns||[];if(n.length===0){console.log(re.dim(" no columns (board falls back to GitLab default flow)"));return}for(let s of n){let i=s.kind==="scoped-label"?`scoped="${s.scopedLabel}"`:s.kind==="label"?`label="${s.mapsToLabel||""}"`:s.kind==="open-backlog"?`prefix="${s.scopedPrefix}"`:s.kind;console.log(` ${re.cyan(s.id.padEnd(12))} ${s.title.padEnd(14)} ${re.dim(i)}`)}}catch(o){console.log(re.red(` ${o.message}`)),process.exit(1)}});Dn.command("add <boardId> <columnId>").description("add a column to a board").requiredOption("--title <title>","column title").option("--kind <kind>","open-backlog | scoped-label | closed | label","scoped-label").option("--scoped <label>","for kind=scoped-label, the full label (e.g. 'Status::Doing')").option("--label <label>","for kind=label, the label name to add/remove").option("--scoped-prefix <prefix>","for kind=open-backlog/scoped-label","Status").option("--accent <color>","hex/CSS color for the column accent bar").action((e,o,t)=>{try{st(n=>{let i=(Array.isArray(n.boards)?n.boards:[]).find(a=>a.id===e);if(!i)throw new Error(`board "${e}" not found`);if(i.columns=i.columns||[],i.columns.find(a=>a.id===o))throw new Error(`column "${o}" already exists on board "${e}"`);let r={id:o,title:t.title,kind:t.kind,scopedPrefix:t.scopedPrefix};if(t.kind==="scoped-label"){if(!t.scoped)throw new Error("--scoped required for kind=scoped-label");r.scopedLabel=t.scoped}if(t.kind==="label"){if(!t.label)throw new Error("--label required for kind=label");r.mapsToLabel=t.label}return t.accent&&(r.accent=t.accent),i.columns.push(r),`column "${o}" added to board "${e}"`})}catch(n){console.log(re.red(` ${n.message}`)),process.exit(1)}});Dn.command("remove <boardId> <columnId>").alias("rm").description("remove a column from a board").action((e,o)=>{try{st(t=>{let s=(Array.isArray(t.boards)?t.boards:[]).find(r=>r.id===e);if(!s)throw new Error(`board "${e}" not found`);let i=(s.columns||[]).length;if(s.columns=(s.columns||[]).filter(r=>r.id!==o),s.columns.length===i)throw new Error(`column "${o}" not found`);return`column "${o}" removed from board "${e}"`})}catch(t){console.log(re.red(` ${t.message}`)),process.exit(1)}});oo.command("remove <id>").alias("rm").description("remove a board from agentx.json").action(e=>{try{st(o=>{if(!Array.isArray(o.boards)||o.boards.length===0)throw new Error("no boards configured");let t=o.boards.length;if(o.boards=o.boards.filter(n=>n.id!==e),o.boards.length===t)throw new Error(`board "${e}" not found`);return`removed board "${e}"`})}catch(o){console.log(re.red(` ${o.message}`)),process.exit(1)}});function st(e){let{summary:o,backupPath:t}=Ao(n=>e(n));console.log(re.green(`
216
+ \u2713 ${o}
217
+ `)),t&&console.log(re.dim(` Backup: ${t}`)),console.log(re.dim(` Restart the dashboard to pick up the change if not auto-reloading.
218
+ `))}import{Command as Sc}from"commander";import p from"chalk";import{resolve as N,relative as Pi,dirname as Mi}from"path";import{execSync as bo}from"child_process";import{writeFileSync as ko,mkdirSync as Je,readFileSync as it,readdirSync as jc,renameSync as Pt,rmSync as Ec,existsSync as Re}from"fs";function ve(e,o){return new Zt(e||N(process.cwd(),".agentx/wiki"),void 0,o||"graph")}function _n(e){return e==="graph"?"Knowledge Graph":e==="flat"?"Karpathy Flat":"Unified"}var ae=new Sc().name("wiki").description("wiki knowledge base management");ae.command("status").description("show wiki status per agent").option("--dir <path>","wiki directory").option("--mode <mode>","graph (default, canonical) | unified | flat (legacy, back-compat)","graph").action(e=>{let o=e.mode,t=ve(e.dir,o),n=t.summary(),i=t.getSharedStore().listEntries().length;console.log(),console.log(p.bold(` Wiki Hub Status [${_n(o)}]`)),console.log(),console.log(` Total raw entries: ${i}`),console.log(` Agents: ${n.length}`),console.log();for(let a of n){let c=a.unabsorbed>0?p.yellow(`${a.unabsorbed} unabsorbed`):p.green("up to date");if(console.log(` ${p.cyan(a.agentId)}`),console.log(` Entries: ${a.totalEntries} Articles: ${a.totalArticles} ${c}`),a.articles.length>0){for(let d of a.articles.slice(0,3))console.log(p.dim(` - ${d.title} [${(d.tags||[]).slice(0,3).join(", ")}]`));a.articles.length>3&&console.log(p.dim(` ... and ${a.articles.length-3} more`))}console.log()}let r=n.reduce((a,c)=>a+c.unabsorbed,0);r>0&&(console.log(p.dim(` ${r} raw entries on disk. Absorb is deprecated \u2014 see 'agentx wiki absorb --help'.`)),console.log())});ae.command("lint").description("check wiki for issues per agent").option("--dir <path>","wiki directory").option("--mode <mode>","graph (default, canonical) | unified | flat (legacy, back-compat)","graph").option("--agent <id>","lint a specific agent's wiki").action(e=>{let o=ve(e.dir,e.mode),t=e.agent?[e.agent]:o.listAgents(),n=0;console.log();for(let s of t){let r=o.getAgentWiki(s).lint();if(n+=r.length,r.length===0)console.log(` ${p.cyan(s)}: ${p.green("healthy")}`);else{console.log(` ${p.cyan(s)}: ${p.yellow(`${r.length} issues`)}`);for(let a of r){let c=a.type==="broken-link"?"x":a.type==="orphan"?"?":"!";console.log(` [${c}] ${p.dim(a.type)} ${a.article}: ${a.message}`)}}}console.log(),n===0&&console.log(p.green(" All agent wikis are healthy")),console.log()});ae.command("absorb").description("compile unabsorbed entries into typed per-agent wiki articles (Farzapedia-faithful)").option("--dir <path>","wiki directory").option("--mode <mode>","graph (default, canonical) | unified | flat (legacy, back-compat)","graph").option("--agent <id>","absorb only this agent").option("--dry-run","preview without running").option("--max <n>","max entries per agent","10").action(async e=>{let o=e.mode,t=ve(e.dir,o),n=e.agent?[e.agent]:t.listAgents(),s=parseInt(e.max),i=(()=>{try{let l=N(process.cwd(),".agentx/graph");return Re(l)?new ao({baseDir:l}):null}catch{return null}})(),r=new Map;if(i)try{let l=i.loadIndex();for(let[u,m]of Object.entries(l.entries))r.set(u,m.path)}catch{}let a=l=>{let u=l.meta?.intentPath;if(Array.isArray(u)&&u.every(y=>typeof y=="string")&&u.length>0)return u;if(!i||r.size===0)return;let m=String(l.meta?.sender??""),f=i.fingerprint({text:l.content,channel:l.source,sender:m});return r.get(f)},c=(l,u)=>{let m=new Map;for(let y of l){let $=u.find(x=>x.id===y);if(!$)continue;let v=a($);if(!v?.length)continue;let b=v.join("/"),S=m.get(b);S?S.n++:m.set(b,{path:v,n:1})}if(m.size===0)return;let f;for(let y of m.values())(!f||y.n>f.n)&&(f=y);return f?.path},d=0,g=0;for(let l of n){let u=t.getUnabsorbedEntries(l).slice(0,s);if(u.length===0){console.log(` ${p.cyan(l)}: ${p.green("all absorbed")}`);continue}console.log(),console.log(p.bold(` ${p.cyan(l)}: ${u.length} entries to absorb`));for(let x of u.slice(0,3))console.log(p.dim(` [${x.date} via ${x.source}] ${x.content.slice(0,80)}...`));if(u.length>3&&console.log(p.dim(` ... and ${u.length-3} more`)),e.dryRun)continue;let m=t.getAgentWiki(l),f=m.rebuildIndex(),y=m.getWorldview()||t.getSharedStore().getWorldview()||"",$=u.map(x=>`--- ENTRY ${x.id} [${x.date} ${x.agentId} via ${x.source}] ---
219
+ ${x.content}
220
+ --- END ENTRY ---`).join(`
221
+
222
+ `),v=As(o,l,y,f.articles,$,u.length);console.log(p.dim(` Mode: ${_n(o)}`));let b=N(m.baseDir,"_tmp");Je(b,{recursive:!0});let S=N(b,"absorb-prompt.txt");ko(S,v),console.log(p.dim(" Compiling with Claude..."));try{let x;try{x=bo(`cat '${S}' | claude -p - --output-format json --max-turns 3 --model sonnet --disallowedTools "Bash Read Write Edit Glob Grep Agent WebSearch WebFetch NotebookEdit"`,{encoding:"utf-8",timeout:9e5,maxBuffer:10*1024*1024})}catch(O){if(x=O.stdout||"",!x)throw O}let k=x;try{let O=JSON.parse(x);k=O.result||O.content||"",k||(console.log(p.dim(` Claude envelope keys: ${Object.keys(O).join(", ")}`)),console.log(p.dim(` is_error: ${O.is_error}, stop_reason: ${O.stop_reason}`)))}catch{}let A,I=[],J=k.indexOf("{"),B=k.indexOf("["),D=J>=0&&(B<0||J<B)?J:B;if(D===-1){console.log(p.red(" No JSON found in response")),console.log(p.dim(k.slice(0,500)));continue}let ke=k[D],Se=ke==="{"?"}":"]",Fe=0,he=-1;for(let O=D;O<k.length;O++)if(k[O]===ke)Fe++;else if(k[O]===Se&&(Fe--,Fe===0)){he=O+1;break}if(he===-1){console.log(p.red(" Unbalanced JSON"));continue}let Ie=k.slice(D,he);try{let O=JSON.parse(Ie);Array.isArray(O)?A=O:(A=O.articles||[],I=O.gaps||[])}catch(O){console.log(p.red(` JSON parse error: ${O.message}`)),console.log(p.dim(` First 300 chars: ${Ie.slice(0,300)}`));continue}for(let O of A){let Me=new Date().toISOString().slice(0,10),Ae=c(O.sources||[],u);m.writeArticle(O.path,{title:O.title,type:O.type,related:Array.isArray(O.related)?O.related:void 0,tags:O.tags||[],owner:l,access:"public",created:Me,lastUpdated:Me,sources:O.sources||[],graphPath:Ae},O.content,l);let ts=O.type?p.magenta(`[${O.type}]`)+" ":"",Vt=Array.isArray(O.related)&&O.related.length?` \u2192 ${O.related.slice(0,3).join(", ")}${O.related.length>3?", \u2026":""}`:"";console.log(` ${p.green("+")} ${ts}${O.path}: ${O.title}${p.dim(Vt)}`);let zo=(O.tags||[]).slice(0,4).join(", ");zo&&console.log(p.dim(` tags: ${zo}`)),Ae?.length&&(console.log(p.dim(` graphPath: ${Ae.join(" \u203A ")}`)),g++),d++}if(I.length>0){console.log(),console.log(p.yellow(` Gaps detected (${I.length} missing pieces):`));for(let O of I)console.log(p.yellow(` ? ${O}`))}m.rebuildIndex()}catch(x){console.log(p.red(` Absorb failed: ${x.message?.slice(0,200)}`)),x.stderr&&console.log(p.dim(String(x.stderr).slice(0,300))),x.stdout&&console.log(p.dim("stdout: "+String(x.stdout).slice(0,300)))}}if(console.log(),e.dryRun)console.log(p.dim(" Dry run \u2014 no changes made"));else if(d>0&&(console.log(p.green(` ${d} articles compiled across ${n.length} agent(s)`)),i)){let l=d>0?Math.round(g/d*100):0;console.log(p.dim(` ${g}/${d} carry graphPath (${l}%) \u2014 wiki retrieval graph weight is now non-zero for those`))}console.log()});ae.command("promote").description("promote per-agent memories into shared, authoritative wiki articles").option("--dir <path>","wiki directory (default .agentx/wiki)").option("--memory-dir <path>","the .agentx dir holding agent-memory/ (default .agentx)").option("--since <duration>","memory window, e.g. 24h, 7d","7d").option("--agent <id>","only this agent's memories").option("--types <list>","comma-separated memory types","project,reference,feedback").option("--max <n>","max candidate memories per run","20").option("--via <agentId>","route the LLM call through an agent \u2014 uses the agent's own session, no API key").option("--model <model>","direct Anthropic API \u2014 needs ANTHROPIC_API_KEY").option("--daemon <url>","daemon API base URL for --via","http://127.0.0.1:18800").option("--commit","write articles and ledger (default: dry-run)",!1).action(async e=>{let o=Ic(e.since),t=String(e.types).split(",").map(r=>r.trim()).filter(Boolean),n=["user","feedback","project","reference"],s=t.find(r=>!n.includes(r));s&&(console.log(p.red(` Invalid memory type "${s}". Valid: ${n.join(", ")}`)),process.exit(1)),e.commit&&!e.via&&!e.model&&(console.log(p.red(" --commit needs an LLM: pass --via <agentId> or --model <model>")),process.exit(1)),console.log(),console.log(p.bold(" Memory \u2192 Wiki Promotion")),console.log();let i=await Os({wikiDir:e.dir?N(e.dir):void 0,memoryRoot:e.memoryDir?N(e.memoryDir):void 0,sinceMs:o,agentFilter:e.agent,types:t,max:Number(e.max)||20,viaAgent:e.via,model:e.model,daemonUrl:e.daemon,commit:e.commit,log:r=>console.log(p.dim(` ${r}`))});if(i.candidates.length===0){console.log(p.dim(" Nothing unpromoted in window")),console.log();return}console.log(` ${i.candidates.length} candidate(s) in ${i.clusters.length} cluster(s):`);for(let r of i.clusters){let a=r.candidates[0],c=r.corroboratingAgents.length>1?p.green(` \xD7${r.corroboratingAgents.length} agents`):"";console.log(` ${p.cyan(`[${a.memory.type}]`)} ${a.memory.name}${c} ${p.dim(`(${r.corroboratingAgents.join(", ")}, conf ${r.confidence.toFixed(2)})`)}`),console.log(p.dim(` ${a.memory.description}`))}if(console.log(),i.dryRun){console.log(p.dim(" Dry run \u2014 pass --commit to promote")),console.log();return}for(let r of i.written){let a=r.type?p.magenta(`[${r.type}]`)+" ":"",c=r.related?.length?` \u2192 ${r.related.slice(0,3).join(", ")}${r.related.length>3?", \u2026":""}`:"";console.log(` ${p.green("+")} ${a}${r.path}: ${r.title}${p.dim(c)}`),console.log(p.dim(` from: ${r.stamps.join(", ")}`))}for(let r of i.skipped)console.log(` ${p.yellow("-")} ${r.stamp} ${p.dim(`\u2014 ${r.reason}`)}`);if(i.gaps.length){console.log(),console.log(p.yellow(` Gaps (${i.gaps.length}):`));for(let r of i.gaps)console.log(p.yellow(` ? ${r}`))}for(let r of i.warnings)console.log(p.dim(` ! ${r}`));for(let r of i.errors)console.log(p.red(` x ${r}`));console.log(),console.log(i.errors.length?p.red(` ${i.written.length} promoted, ${i.errors.length} error(s)`):p.green(` ${i.written.length} promoted, ${i.skipped.length} skipped`)),console.log(),i.errors.length&&process.exit(1)});function Ic(e){if(!e)return;let o=/^(\d+)\s*([smhd])$/.exec(e.trim());o||(console.log(p.red(` Invalid --since "${e}". Use "30m", "24h", or "7d".`)),process.exit(1));let t=Number(o[1]),n=o[2];return t*(n==="s"?1e3:n==="m"?6e4:n==="h"?36e5:864e5)}ae.command("ab-test").description("side-by-side comparison: BM25 preload (old) vs agentic query (new) on real task-history messages").option("--dir <path>","wiki directory").option("--agent <id>","which agent to test against (required)").option("--history <path>","task-history root",N(process.cwd(),".agentx/task-history")).option("--n <n>","number of messages to sample (newest first)","10").option("--out <path>","markdown output file (default: stdout)").option("--selector-model <m>","agentic selector model","haiku").option("--synth-model <m>","agentic synthesis model","sonnet").action(async e=>{let{agenticQuery:o}=await import("./query-HZYYRCLU.js"),{readdirSync:t,readFileSync:n,existsSync:s}=await import("fs"),{resolve:i,join:r}=await import("path"),a=ve(e.dir),c=e.agent;if(!c){console.log(p.red(" --agent <id> is required"));return}let d=a.getAgentWiki(c),g=parseInt(e.n),l=i(e.history,c);if(!s(l)){console.log(p.yellow(` no task-history at ${l}`));return}let u=[],m=t(l).sort().reverse();for(let $ of m){let v=r(l,$),b=[];try{b=t(v).filter(S=>S.endsWith(".json")).sort().reverse()}catch{continue}for(let S of b){if(u.length>=g)break;try{let x=JSON.parse(n(r(v,S),"utf-8")),k=x.message||x.task?.message;typeof k=="string"&&k.trim().length>10&&u.push({id:x.id||S.replace(".json",""),message:k.trim(),ts:x.at||x.timestamp||$})}catch{}}if(u.length>=g)break}if(u.length===0){console.log(p.yellow(` no task messages found under ${l}`));return}console.log(),console.log(p.bold(` A/B harness \u2014 ${u.length} messages from ${c}`)),console.log(p.dim(" OLD: findRelevant() BM25 over title+tags+content, top 3 truncated")),console.log(p.dim(` NEW: agenticQuery() catalog+wikilink walk (selector=${e.selectorModel}, synth=${e.synthModel})`)),console.log();let f=[`# Wiki A/B: ${c}`,"",`_Generated ${new Date().toISOString()} \xB7 ${u.length} messages sampled_`,"","For each message, the OLD BM25 retrieval (what Layer 10 used to preload) is shown alongside the NEW agentic query's selected articles + synthesized answer. Rate each pair on relevance 0\u20132 in the notes column.",""];for(let $=0;$<u.length;$++){let v=u[$];process.stdout.write(p.dim(` [${$+1}/${u.length}] querying... `));let S=d.findRelevant(v.message,c,3).map(k=>`- **${k.meta.title}** [${k.meta.type||"?"}] (${k.path})`).join(`
223
+ `)||"_(none returned)_",x;try{let k=await o(v.message,d,c,{selectorModel:e.selectorModel,synthModel:e.synthModel});if(k.status!=="ok")x=`_(status: ${k.status}${k.error?" \u2014 "+k.error:""})_`;else{let A=k.citations.map(I=>`\`${I.title}\` [${I.type||"?"}]`).join(", ");x=`**Answer:**
224
+
225
+ ${k.answer}
226
+
227
+ **Citations:** ${A}`}}catch(k){x=`_(error: ${k.message})_`}console.log(p.dim("done")),f.push(`## ${$+1}. ${v.id}`,"",`> ${v.message.replace(/\n/g," ").slice(0,400)}${v.message.length>400?"\u2026":""}`,"","### OLD \u2014 BM25 preload (top 3)","",S,"","### NEW \u2014 agentic query","",x,"","| Path | Rating 0\u20132 | Notes |","|---|---|---|","| OLD | | |","| NEW | | |","")}let y=f.join(`
228
+ `);e.out?((await import("fs")).writeFileSync(e.out,y),console.log(),console.log(p.green(` Wrote ${y.length} bytes to ${e.out}`))):(console.log(),console.log(y))});ae.command("interview").description("interactive interview session \u2014 Q&A with an LLM synthesizer, produces one typed wiki article").option("--dir <path>","wiki directory").option("--agent <id>","which agent's wiki to write to (required)").option("--topic <text>","what to interview about (e.g. 'MTGL deployment procedure')").option("--type <t>","article type hint (person|project|place|concept|event|decision|pattern)").option("--model <m>","synthesis model","sonnet").option("--no-commit","show the draft but don't write").option("--answers <path>","non-interactive: one answer per line (same order as questions); last line = save|edit|scrap").action(async e=>{let o=await import("node:readline/promises"),{randomUUID:t}=await import("node:crypto"),{WIKI_ARTICLE_TYPES:n}=await import("./types-VECTMYTO.js"),s=e.answers?it(e.answers,"utf-8").split(/\r?\n/).filter(T=>T.length>0):null,i=0;if(!e.agent){console.log(p.red(" --agent <id> is required. The article will be owned by this agent."));return}let r=ve(e.dir),a;try{a=r.getAgentWiki(e.agent)}catch(T){console.log(p.red(` can't open wiki for agent "${e.agent}": ${T.message}`));return}let c=s?null:o.createInterface({input:process.stdin,output:process.stdout}),d=async T=>{if(s){let fe=s[i++]??"";return process.stdout.write(T+p.dim(`[scripted] ${fe.slice(0,80)}${fe.length>80?"\u2026":""}`)+`
229
+ `),fe.trim()}return(await c.question(T)).trim()};console.log(),console.log(p.bold(" Wiki Interview")),console.log(p.dim(" One article per session. Empty answer = skip field. /done = stop & synthesize. /abort = quit.")),console.log();let g=e.topic||"";if(!g&&(g=await d(p.cyan(" Topic? ")+p.dim("(a person, a procedure, a past event, \u2026) ")),!g||g==="/abort")){c?.close();return}let l=(e.type||"").toLowerCase(),u=new Set(n);if(!u.has(l)){console.log(p.dim(` Types: ${n.join(" | ")}`));let T=await d(p.cyan(" Type? "));if(T==="/abort"){c?.close();return}u.has(T)?l=T:(console.log(p.yellow(` "${T}" isn't a valid type. Falling back to "concept".`)),l="concept")}let m={person:["Full name (and any aliases / Telegram handle / GitLab username)?","Role or title?","Which org or team are they part of (use the article title if you have one)?","Key responsibilities in OUR work?","Preferred channel to reach them (Telegram, email, in-person, \u2026)?","Who do they report to? (skip if none)","Notable decisions, events, or patterns they're involved in?","Anything quirky we should remember (timezone, working hours, style)?"],project:["One-line description \u2014 what is this project?","Who owns it? (person article title)","Tech stack or key tools involved?","Current status (active / paused / archived)?","Repo / URL / path on disk?","Two or three key decisions made so far?","Any open blockers, risks, or known issues?","Related projects or patterns?"],place:["Kind \u2014 office / server / environment / domain / URL?","Address \u2014 URL, IP, hostname, or physical location?","Owner (person or team)?","How to access it (SSH host, login, VPN, \u2026)?","What lives there (services, people, data)?","Known quirks (timezone, uptime, access restrictions)?"],concept:["Define it in one sentence \u2014 what does this term mean in OUR work?","Where did we first adopt it (origin story, date)?","Two or three concrete examples from our team?","What does it replace or extend (older concept, related concept)?","Common misunderstandings to avoid?"],event:["Date (YYYY-MM-DD)?","One-line summary \u2014 what happened?","Who was involved?","Timeline \u2014 key moments (start \u2192 resolution)?","Impact \u2014 what broke, what got delivered, who was affected?","Resolution / outcome?","Follow-ups or lessons captured?"],decision:["Context \u2014 what was the situation forcing a choice?","Options that were considered?","Chosen option \u2014 and one-sentence why?","Alternatives rejected \u2014 and one-sentence why not?","Who decided, and when (date)?","Is this reversible, and what would trigger a revisit?"],pattern:["Trigger \u2014 when should someone follow this pattern?","Inputs required (what do you need before starting)?","Steps in order \u2014 keep it tight, one line per step?","Expected output \u2014 how do you know it worked?","Common failure modes?","Related patterns, decisions, or runbooks?"]},f=m[l]||m.concept;console.log(),console.log(p.bold(` Topic: ${p.cyan(g)} \xB7 Type: ${p.magenta(l)}`)),console.log(p.dim(` ${f.length} questions. Skip with empty answer. /done to stop early.`)),console.log();let y=[],$=!1;for(let T=0;T<f.length;T++){let fe=f[T],Oe=await d(` ${p.dim(`[${T+1}/${f.length}]`)} ${fe}
230
+ ${p.green(">")} `);if(Oe==="/abort"){$=!0;break}if(Oe==="/done")break;Oe!=="/skip"&&Oe&&y.push({q:fe,a:Oe})}if(!$){let T=await d(`
231
+ ${p.dim("Anything else worth capturing?")}
232
+ ${p.green(">")} `);T&&T!=="/abort"&&T!=="/done"&&y.push({q:"Anything else worth capturing?",a:T})}if($||y.length===0){console.log(p.yellow(`
233
+ Nothing captured. Exiting.`)),c?.close();return}let b=a.listArticles(e.agent).slice(0,40).map(T=>`- ${T.meta.title} [${T.meta.type||"?"}]`).join(`
234
+ `),S=["You are compiling one Farzapedia-style wiki article from an operator interview transcript.","",`**Topic:** ${g}`,`**Type:** ${l}`,`**Owning agent:** ${e.agent}`,"","## Transcript",...y.map(T=>`
235
+ Q: ${T.q}
236
+ A: ${T.a}`),"","## Existing articles (use [[Title]] wikilinks when referencing these)",b||"(none yet)","","## Output rules","- Output EXACTLY one markdown file: frontmatter (---) + body. No code fences, no preamble.","- Frontmatter fields in this exact order: title, type, related, tags, owner, access, created, last_updated, sources.",' - `title`: "\u2026"',` - \`type\`: ${l}`,' - `related`: ["Article Title", \u2026] (titles that exist in the catalog above; drop if none)'," - `tags`: 2-4 specific tags, lowercase-kebab \u2014 no dates unless type=event",` - \`owner\`: ${e.agent}`," - `access`: public (unless the content is sensitive)",` - \`created\`: ${new Date().toISOString().slice(0,10)}`,` - \`last_updated\`: ${new Date().toISOString().slice(0,10)}`,` - \`sources\`: ["interview-${new Date().toISOString().slice(0,10)}"]`,"- Body: 20-80 lines, Wikipedia-style, organized by theme (not by question), synthesized (not verbatim Q&A).","- Use [[Title]] inline every time you reference something that has an article in the catalog.","- Do NOT invent facts. If the transcript didn't cover something, skip that sub-topic."].join(`
237
+ `),x=N(a.baseDir,"_tmp");Je(x,{recursive:!0});let k=N(x,`interview-${t().slice(0,8)}.txt`);ko(k,S),console.log(),console.log(p.dim(` Synthesizing draft with ${e.model}...`));let A="";try{let T=`cat '${k}' | claude -p - --output-format json --max-turns 1 --model ${e.model} --disallowedTools "Bash Read Write Edit Glob Grep Agent WebSearch WebFetch NotebookEdit"`,fe=bo(T,{encoding:"utf-8",timeout:18e4,maxBuffer:4*1024*1024});try{let Oe=JSON.parse(fe);A=String(Oe.result||Oe.content||"")}catch{A=fe}}catch(T){console.log(p.red(` synthesis failed: ${T.message?.slice(0,200)}`)),c?.close();return}A=A.trim();let I=A.match(/```(?:markdown|md)?\s*\n([\s\S]*?)\n```\s*$/);I&&(A=I[1].trim());let J=A.indexOf("---");J>0&&(A=A.slice(J));let B=A.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);if(!B){console.log(p.red(" LLM output is not a valid frontmatter+body article:")),console.log(A.slice(0,600)),c?.close();return}let D={};for(let T of B[1].split(`
238
+ `)){let fe=T.match(/^(\w+):\s*(.+)$/);fe&&(D[fe[1]]=fe[2].trim().replace(/^"(.*)"$/,"$1"))}let ke=B[2],Se=D.title||g,Fe=Se.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,60),Ie=`${{person:"people",project:"projects",place:"places",concept:"concepts",event:"events",decision:"decisions",pattern:"patterns"}[l]||l+"s"}/${Fe}.md`;console.log(),console.log(p.bold(" === Draft preview ===")),console.log(p.dim(` Path: ${Ie}`)),console.log(),console.log(A),console.log();let O=await d(` ${p.green("save")} / ${p.yellow("edit (opens $EDITOR)")} / ${p.red("scrap")} ? `);if(c?.close(),O==="scrap"||O==="/abort"){console.log(p.dim(" Discarded."));return}if(!e.commit){console.log(p.dim(" --no-commit: not writing.")),console.log(p.dim(` Draft kept at ${k} (prompt only; no article file)`));return}let Me=ke,Ae={title:Se,type:D.type||l,related:D.related?D.related.replace(/^\[|\]$/g,"").split(",").map(T=>T.trim().replace(/^"(.*)"$/,"$1")).filter(Boolean):void 0,tags:D.tags?D.tags.replace(/^\[|\]$/g,"").split(",").map(T=>T.trim().replace(/^"(.*)"$/,"$1")).filter(Boolean):[],owner:e.agent,access:D.access||"public",created:D.created||new Date().toISOString().slice(0,10),lastUpdated:D.last_updated||new Date().toISOString().slice(0,10),sources:[`interview-${new Date().toISOString().slice(0,10)}`]};if(O==="edit"){let T=process.env.EDITOR||"vi",fe=N(x,`draft-${t().slice(0,8)}.md`);ko(fe,A);try{bo(`${T} '${fe}'`,{stdio:"inherit"});let Qt=it(fe,"utf-8").match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);if(Qt){let qe={};for(let Ho of Qt[1].split(`
239
+ `)){let Xt=Ho.match(/^(\w+):\s*(.+)$/);Xt&&(qe[Xt[1]]=Xt[2].trim().replace(/^"(.*)"$/,"$1"))}Me=Qt[2],Ae={...Ae,title:qe.title||Ae.title,type:qe.type||Ae.type,access:qe.access||Ae.access,related:qe.related?qe.related.replace(/^\[|\]$/g,"").split(",").map(Ho=>Ho.trim().replace(/^"(.*)"$/,"$1")).filter(Boolean):Ae.related,tags:qe.tags?qe.tags.replace(/^\[|\]$/g,"").split(",").map(Ho=>Ho.trim().replace(/^"(.*)"$/,"$1")).filter(Boolean):Ae.tags}}}catch(Oe){console.log(p.red(` editor failed: ${Oe.message}`));return}}if(!a.writeArticle(Ie,Ae,Me,e.agent)){console.log(p.red(" write failed (permission?)"));return}a.rebuildIndex(),console.log(p.green(` \u2713 ${Ie} saved.`));let Vt=a.extractWikilinks(Me),zo=new Map;for(let T of a.listArticles(e.agent))zo.set(T.meta.title.toLowerCase(),!0);let ns=Vt.filter(T=>!zo.has(T.toLowerCase()));if(ns.length){console.log(),console.log(p.bold(" Gaps referenced but not yet covered:"));for(let T of ns.slice(0,8))console.log(p.dim(` - ${T}`));console.log(p.dim(` Run \`agentx wiki interview --agent ${e.agent} --topic "<title>"\` for any of these.`))}});ae.command("quiz").description("reverse interview \u2014 ask the wiki questions and patch cited articles with corrections, additions, or links").option("--dir <path>","wiki directory").option("--agent <id>","which agent's wiki to quiz (required)").option("--selector-model <m>","candidate-selection model","haiku").option("--synth-model <m>","answer synthesis model","sonnet").option("--patch-model <m>","article patch model","sonnet").option("--rounds <n>","stop after N rounds","20").option("--script <path>","non-interactive: question line + /verdict line per entry, entries separated by blank lines").option("--no-commit","show proposed patches but don't write").option("--out <path>","write a session transcript as markdown").action(async e=>{let o=await import("node:readline/promises"),{randomUUID:t}=await import("node:crypto"),{agenticQuery:n}=await import("./query-HZYYRCLU.js");if(!e.agent){console.log(p.red(" --agent <id> is required."));return}let s=ve(e.dir),i;try{i=s.getAgentWiki(e.agent)}catch(m){console.log(p.red(` can't open wiki for agent "${e.agent}": ${m.message}`));return}let r=null;if(e.script){let f=it(e.script,"utf-8").split(/\n\s*\n/).map(y=>y.trim()).filter(Boolean);r=[];for(let y of f){let $=y.split(/\r?\n/).filter(S=>S.trim());if($.length<2)continue;let v=$[0].replace(/^Q:\s*/i,"").trim(),b=$.slice(1).join(" ").trim();v&&b&&r.push({q:v,verdict:b})}}let a=r?null:o.createInterface({input:process.stdin,output:process.stdout}),c=async m=>a?(await a.question(m)).trim():"",d=Math.min(parseInt(e.rounds)||20,r?.length??999),g=e.commit!==!1;console.log(),console.log(p.bold(" Wiki Quiz")),console.log(p.dim(` Agent: ${e.agent} \xB7 Rounds: ${d} \xB7 ${g?"commit":"dry-run"} \xB7 ${r?`scripted (${r.length})`:"interactive"}`)),console.log(p.dim(" Verdicts: /ok /correct <note> /add <note> /link <url> /skip /done")),console.log();let l=[],u=0;for(let m=0;m<d;m++){let f,y;if(r){let he=r[m];if(!he)break;f=he.q,y=he.verdict,console.log(p.cyan(` [${m+1}] Q: `)+f)}else{if(f=await c(p.cyan(` [${m+1}] Q: `)),!f||f==="/done"||f==="/abort")break;y=""}process.stdout.write(p.dim(" querying... "));let $;try{$=await n(f,i,e.agent,{selectorModel:e.selectorModel,synthModel:e.synthModel})}catch(he){console.log(p.red(`failed: ${he.message?.slice(0,120)}`));continue}if(console.log(p.dim("done")),$.status!=="ok")console.log(p.yellow(` (status: ${$.status}${$.error?" \u2014 "+$.error:""})`));else{let he=$.citations.map(Ie=>`${Ie.title} [${Ie.type||"?"}]`).join(" \xB7 ");console.log(),console.log(p.dim(" A: ")+$.answer.replace(/\n/g,`
240
+ `)),console.log(p.dim(" citations: "+he))}r||(y=await c(p.cyan(" verdict > "))),console.log(p.dim(" verdict: ")+y);let v={q:f,answer:$.answer,citations:$.citations,verdict:y},b=y.match(/^(\/ok|\/correct|\/add|\/link|\/skip)(?:\s+(.+))?$/i);if(!b){console.log(p.yellow(" (verdict not recognised \u2014 treating as /skip)")),l.push(v);continue}let S=b[1].toLowerCase(),x=(b[2]||"").trim();if(l.push(v),S==="/ok"||S==="/skip")continue;if(!x){console.log(p.yellow(` ${S} without note \u2014 skipping.`));continue}let k=$.citations[0];if(!k){console.log(p.yellow(" no cited article to patch."));continue}let A=i.readArticle(k.path);if(!A){console.log(p.red(` can't read ${k.path} to patch.`));continue}console.log(p.dim(` patching ${k.title} (${k.path})...`));let I=Oc(A.content,S,x,A.meta.title,A.meta.type),J=N(i.baseDir,"_tmp");Je(J,{recursive:!0});let B=N(J,`quiz-patch-${t().slice(0,8)}.txt`);ko(B,I);let D="";try{let he=`cat '${B}' | claude -p - --output-format json --max-turns 1 --model ${e.patchModel} --disallowedTools "Bash Read Write Edit Glob Grep Agent WebSearch WebFetch NotebookEdit"`,Ie=bo(he,{encoding:"utf-8",timeout:12e4,maxBuffer:4*1024*1024});try{let Me=JSON.parse(Ie);D=String(Me.result||Me.content||"")}catch{D=Ie}let O=D.trim().match(/```(?:markdown|md)?\s*\n([\s\S]*?)\n```\s*$/);O&&(D=O[1].trim()),D=D.trim()}catch(he){console.log(p.red(` patch failed: ${he.message?.slice(0,120)}`));continue}if(!D||D.length<20){console.log(p.yellow(" patch returned nothing usable."));continue}let ke=A.content.split(`
241
+ `).length,Se=D.split(`
242
+ `).length;if(console.log(p.dim(` diff: ${ke} \u2192 ${Se} lines (${Se-ke>=0?"+":""}${Se-ke})`)),!g){console.log(p.dim(" (--no-commit: patch not written)")),v.patched=k.path+" (dry)";continue}if(!i.writeArticle(k.path,{...A.meta,lastUpdated:new Date().toISOString().slice(0,10)},D,e.agent)){console.log(p.red(" write failed (permission?)"));continue}u++,v.patched=k.path,console.log(p.green(` \u2713 patched ${k.path}`))}if(a&&a.close(),u>0&&g&&i.rebuildIndex(),console.log(),console.log(p.bold(` Session done: ${l.length} rounds, ${u} patches applied.`)),e.out){let m=[`# Wiki Quiz \u2014 ${e.agent}`,"",`_Generated ${new Date().toISOString()} \xB7 ${l.length} rounds \xB7 ${u} patches_`,""];for(let f=0;f<l.length;f++){let y=l[f];m.push(`## ${f+1}. Q: ${y.q}`,"",`**A:** ${y.answer}`,"",`**Citations:** ${y.citations.map($=>`\`${$.title}\``).join(", ")||"_(none)_"}`,"",`**Verdict:** \`${y.verdict}\``+(y.patched?` \u2192 patched \`${y.patched}\``:""),"")}ko(e.out,m.join(`
243
+ `)),console.log(p.dim(` Transcript written to ${e.out}`))}});ae.command("edit <agent> <titleOrPath>").description("open an article in $EDITOR (resolves by title or path), rebuild catalog on exit").option("--dir <path>","wiki directory").option("--editor <cmd>","override $EDITOR for this run").action((e,o,t)=>{let n=ve(t.dir),s;try{s=n.getAgentWiki(e)}catch(d){console.log(p.red(` can't open wiki for agent "${e}": ${d.message}`));return}let i=Wi(s,o,e);if(!i){console.log(p.yellow(` no article matches "${o}" in ${e}'s wiki.`)),console.log(p.dim(` Tip: ${p.green("agentx wiki status")} lists agents; ${p.green(`agentx wiki search "term" --agent ${e}`)} finds by content.`));return}let r=N(s.baseDir,i),a=t.editor||process.env.EDITOR||"vi";console.log(p.dim(` opening ${i} in ${a}...`));try{bo(`${a} '${r}'`,{stdio:"inherit"})}catch(d){console.log(p.red(` editor exited with error: ${d.message?.slice(0,150)}`));return}let c=s.readArticle(i);if(c){let d=new Set,g=s.extractWikilinks(c.content).filter(m=>d.has(m)?!1:(d.add(m),!0)),l=c.meta.related||[];(g.some(m=>!l.includes(m))||l.some(m=>!g.includes(m)))&&(s.writeArticle(i,{...c.meta,related:g.length?g:void 0,lastUpdated:new Date().toISOString().slice(0,10)},c.content,e),console.log(p.dim(` (synced frontmatter.related from body: ${g.length} wikilink${g.length===1?"":"s"})`)))}try{s.rebuildIndex(),console.log(p.green(` \u2713 ${i} saved. Catalog rebuilt.`))}catch(d){console.log(p.yellow(` saved, but catalog rebuild failed: ${d.message?.slice(0,100)}`))}});ae.command("patch <agent> <titleOrPath> <instruction>").description("LLM-patch an article from a free-form instruction; shows diff + confirms before writing").option("--dir <path>","wiki directory").option("--patch-model <m>","patch model","sonnet").option("--yes","skip confirmation and write immediately").option("--no-commit","show the patched body but don't write").action(async(e,o,t,n)=>{let s=await import("node:readline/promises"),{randomUUID:i}=await import("node:crypto"),r=ve(n.dir),a;try{a=r.getAgentWiki(e)}catch(k){console.log(p.red(` can't open wiki for agent "${e}": ${k.message}`));return}let c=Wi(a,o,e);if(!c){console.log(p.yellow(` no article matches "${o}" in ${e}'s wiki.`));return}let d=a.readArticle(c);if(!d){console.log(p.red(` can't read ${c}.`));return}let g=Ac(d.content,d.meta.title,d.meta.type,t),l=N(a.baseDir,"_tmp");Je(l,{recursive:!0});let u=N(l,`patch-${i().slice(0,8)}.txt`);ko(u,g),console.log(),console.log(p.dim(` Target: ${d.meta.title} (${c})`)),console.log(p.dim(` Patching with ${n.patchModel}...`));let m="";try{let k=`cat '${u}' | claude -p - --output-format json --max-turns 1 --model ${n.patchModel} --disallowedTools "Bash Read Write Edit Glob Grep Agent WebSearch WebFetch NotebookEdit"`,A=bo(k,{encoding:"utf-8",timeout:12e4,maxBuffer:4*1024*1024});try{let J=JSON.parse(A);m=String(J.result||J.content||"")}catch{m=A}let I=m.trim().match(/```(?:markdown|md)?\s*\n([\s\S]*?)\n```\s*$/);I&&(m=I[1].trim()),m=m.trim()}catch(k){console.log(p.red(` patch failed: ${k.message?.slice(0,150)}`));return}if(!m||m.length<20){console.log(p.yellow(" patch returned nothing usable."));return}let f=d.content.split(`
244
+ `).length,y=m.split(`
245
+ `).length;if(console.log(),console.log(p.bold(" === Patched preview ===")),console.log(p.dim(` diff: ${f} \u2192 ${y} lines (${y-f>=0?"+":""}${y-f})`)),console.log(),console.log(m),console.log(),!n.commit){console.log(p.dim(" --no-commit: preview only, not written."));return}let $=!!n.yes;if(!$){let k=s.createInterface({input:process.stdin,output:process.stdout}),A=(await k.question(p.cyan(" apply? [y/N] "))).trim().toLowerCase();k.close(),$=A==="y"||A==="yes"}if(!$){console.log(p.dim(" aborted."));return}let v=new Set,b=a.extractWikilinks(m).filter(k=>v.has(k)?!1:(v.add(k),!0));if(!a.writeArticle(c,{...d.meta,related:b.length?b:void 0,lastUpdated:new Date().toISOString().slice(0,10)},m,e)){console.log(p.red(" write failed (permission?)"));return}a.rebuildIndex();let x=(d.meta.related||[]).filter(k=>!b.includes(k));x.length&&console.log(p.dim(` (pruned stale related: ${x.slice(0,3).join(", ")}${x.length>3?", \u2026":""})`)),console.log(p.green(` \u2713 ${c} patched.`))});function Wi(e,o,t){let n=o.trim(),s=e.listArticles(t),i=n.toLowerCase(),r=s.find(d=>d.meta.title.toLowerCase()===i);if(r)return r.path;if(n.includes("/")||n.endsWith(".md")){let d=n.endsWith(".md")?n:`${n}.md`,g=s.find(l=>l.path===d||l.path===n);if(g)return g.path}let a=n.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,""),c=s.find(d=>{let g=d.meta.title.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"");return g===a||g.includes(a)||d.path.toLowerCase().includes(a)});return c?c.path:null}function Ac(e,o,t,n){return["You are editing a single wiki article to incorporate an operator instruction.","",`## Article: "${o}" (type: ${t||"untyped"})`,"","<article-body>",e,"</article-body>","","## Operator instruction",n,"","## Rules","- Output ONLY the modified article body (content between the --- frontmatter markers).","- Do NOT output frontmatter (title, type, tags, etc.) \u2014 the serializer handles that.","- Do NOT wrap the output in code fences.",`- Do NOT prepend any preamble like "Here's the updated article:".`,"- Preserve existing wikilinks, markdown formatting, and section structure unless the instruction requires changing them.","- Make the MINIMUM edit that satisfies the instruction \u2014 don't rewrite untouched sections.","- If the instruction is ambiguous or can't be applied, return the original body unchanged."].join(`
246
+ `)}function Oc(e,o,t,n,s){let i={"/correct":`The operator says the article contains an error:
247
+
248
+ ${t}
249
+
250
+ Edit the article to correct ONLY this mistake. Do not rewrite untouched sections. Keep the existing prose style. If the correction invalidates a larger passage, revise the minimum that must change.`,"/add":`The operator wants to add this detail:
251
+
252
+ ${t}
253
+
254
+ Incorporate it into the most relevant existing section, or create a short new section if nothing fits. Keep the prose encyclopedic and under 100 additional words.`,"/link":`The operator wants to add this resource:
255
+
256
+ ${t}
257
+
258
+ Add it as a Markdown link in the most relevant section, or append a "## References" section at the end if none exists. If it looks like a URL, use it as-is; otherwise treat it as a wikilink target and use [[${t}]].`};return["You are editing a single wiki article to incorporate operator feedback.","",`## Article: "${n}" (type: ${s||"untyped"})`,"","<article-body>",e,"</article-body>","","## Edit instruction",i[o]||`Make a minimal edit per: ${t}`,"","## Rules","- Output ONLY the modified article body (content between the --- frontmatter markers).","- Do NOT output frontmatter (title, type, tags, etc.) \u2014 the serializer handles that.","- Do NOT wrap the output in code fences.",`- Do NOT prepend any preamble like "Here's the updated article:".`,"- Preserve existing wikilinks, markdown formatting, and section structure unless the edit requires changing them.","- If no meaningful edit can be made from the operator's feedback, return the original body unchanged."].join(`
259
+ `)}ae.command("prune").description("collapse legacy flat/unified mode dirs into graph/ (dedup by title; losers archived)").option("--dir <path>","wiki directory").option("--agent <id>","prune only this agent's wiki").option("--commit","execute moves + archives (default: dry-run)").action(async e=>{let o=ve(e.dir),t=e.agent?[e.agent]:o.listAgents(),n=!!e.commit,s=e.dir?N(e.dir):N(process.cwd(),".agentx/wiki"),i="graph",r=["flat","unified"];console.log(),console.log(p.bold(n?" Pruning (commit)":" Pruning (dry-run)")),console.log(p.dim(` Canonical: ${i}/ \xB7 Legacy to collapse: ${r.join("/, ")}/`));let a=0,c=0,d=0,g=0;for(let l of t){let u=N(s,"agents",l);if(!Re(u))continue;let m=N(u,i),f=r.map(b=>({mode:b,dir:N(u,b)})).filter(b=>Re(b.dir));if(f.length===0)continue;let y=new Map;Nn(m,b=>{let S=Pi(m,b);if(S.startsWith("_")||S.startsWith("raw/")||S.includes("_tmp/"))return;let x=Li(it(b,"utf-8"));x?.meta.title&&y.set(x.meta.title.trim().toLowerCase(),{relPath:S,type:x.meta.type,lastUpdated:x.meta.lastUpdated||""})});let $=[];for(let b of f)Nn(b.dir,S=>{let x=Pi(b.dir,S);if(x.startsWith("_")||x.startsWith("raw/")||x.includes("_tmp/"))return;let k=Li(it(S,"utf-8"));k?.meta.title&&$.push({mode:b.mode,relPath:x,absPath:S,title:k.meta.title.trim(),type:k.meta.type,lastUpdated:k.meta.lastUpdated||""})});if(a+=$.length,$.length===0)continue;console.log();let v=f.map(b=>b.mode).join("+");console.log(p.bold(` ${p.cyan(l)}: ${$.length} legacy article(s) across ${v}`));for(let b of $){let S=b.title.toLowerCase(),x=y.get(S),k;x?!x.type&&b.type||!!x.type==!!b.type&&b.lastUpdated&&x.lastUpdated&&b.lastUpdated>x.lastUpdated?k="upgrade":k="archive":k="promote";let A=b.type?p.magenta(`[${b.type}]`):p.yellow("[untyped]"),I=k==="promote"?p.green("\u2192 promote "):k==="upgrade"?p.cyan("\u27F3 upgrade "):p.dim("\u2717 archive "),J=b.relPath.length>46?b.relPath.slice(0,43)+"...":b.relPath.padEnd(46);if(console.log(` ${I} ${A} ${p.dim(b.mode+"/")}${J}`),k==="upgrade"&&console.log(p.dim(` (replaces ${x.relPath}, old version kept under _versions/)`)),!n){k==="promote"?c++:k==="upgrade"?d++:g++;continue}try{if(k==="promote"){let B=N(m,b.relPath);Re(B)?(console.log(p.yellow(` ! target path already exists, archiving instead: ${b.relPath}`)),Ui(u,b),g++):(Je(Mi(B),{recursive:!0}),Pt(b.absPath,B),y.set(S,{relPath:b.relPath,type:b.type,lastUpdated:b.lastUpdated}),c++)}else if(k==="upgrade"){let B=N(m,x.relPath);if(Re(B)){let D=N(u,"_versions",x.relPath.replace(/\.md$/,""));Je(D,{recursive:!0});let ke=new Date().toISOString().replace(/[:.]/g,"-");Pt(B,N(D,`${ke}.md`))}Je(Mi(B),{recursive:!0}),Pt(b.absPath,B),y.set(S,{relPath:x.relPath,type:b.type,lastUpdated:b.lastUpdated}),d++}else Ui(u,b),g++}catch(B){console.log(p.red(` ! ${k} failed: ${B.message?.slice(0,120)}`))}}if(n){for(let b of f)try{Ec(b.dir,{recursive:!0,force:!0})}catch{}try{o.getAgentWiki(l).rebuildIndex()}catch(b){console.log(p.dim(` (catalog rebuild skipped: ${b.message?.slice(0,80)})`))}}}console.log(),console.log(p.dim(` Legacy scanned: ${a}`)),n?(console.log(p.green(` Promoted: ${c} \xB7 Upgraded canonical: ${d} \xB7 Archived: ${g}`)),console.log(p.dim(" Archived losers preserved under <agent>/_versions/. Legacy mode dirs deleted."))):(console.log(p.dim(` Would: promote ${c} \xB7 upgrade ${d} \xB7 archive ${g}`)),console.log(p.dim(" Dry-run \u2014 add --commit to execute"))),console.log()});function Nn(e,o){if(Re(e))for(let t of jc(e,{withFileTypes:!0})){let n=N(e,t.name);t.isDirectory()?Nn(n,o):t.isFile()&&t.name.endsWith(".md")&&o(n)}}function Li(e){let o=e.match(/^---\n([\s\S]*?)\n---/);if(!o)return null;let t={};for(let n of o[1].split(`
260
+ `)){let s=n.match(/^(\w+):\s*(.+)$/);if(!s)continue;let i=s[1]==="last_updated"?"lastUpdated":s[1];t[i]=s[2].trim().replace(/^"(.*)"$/,"$1")}return{meta:t}}function Ui(e,o){let t=N(e,"_versions",`legacy-${o.mode}`,o.relPath.replace(/\.md$/,""));Je(t,{recursive:!0});let n=new Date().toISOString().replace(/[:.]/g,"-");Pt(o.absPath,N(t,`${n}.md`))}ae.command("migrate").description("backfill type + related on legacy articles (one-shot)").option("--dir <path>","wiki directory").option("--agent <id>","migrate only this agent's articles").option("--commit","write changes (default: dry-run, reports what would change)").option("--batch <n>","articles per LLM call","10").option("--max <n>","cap articles this run (for spot-checks)").option("--model <m>","classifier model","sonnet").action(async e=>{let o=ve(e.dir),t=e.agent?[e.agent]:o.listAgents(),n=Math.max(1,parseInt(e.batch)||10),s=e.max?parseInt(e.max):1/0,i=!!e.commit;console.log(),console.log(p.bold(i?" Migrating (commit)":" Migrating (dry-run)")),console.log(p.dim(` Batch size: ${n} \xB7 Model: ${e.model}`)),console.log();let r=0,a=0,c=0;for(let d of t){let g=o.getAgentWiki(d),l=g.listArticles(d),u=l.filter(v=>!v.meta.type);if(r+=l.length,a+=u.length,u.length===0){console.log(` ${p.cyan(d)}: ${p.green("all typed")} (${l.length} articles)`);continue}let m=Math.max(0,s-c),f=Math.min(u.length,m);if(f===0){console.log(p.dim(` ${d}: skipped (--max reached)`));continue}console.log(),console.log(p.bold(` ${p.cyan(d)}: ${f}/${u.length} untyped articles`));let y=g.baseDir,$=N(y,"_tmp");Je($,{recursive:!0});for(let v=0;v<f;v+=n){let b=u.slice(v,Math.min(v+n,f)),S=Rc(b),x=N($,"migrate-prompt.txt");ko(x,S);let k;try{let A=`cat '${x}' | claude -p - --output-format json --max-turns 1 --model ${e.model} --disallowedTools "Bash Read Write Edit Glob Grep Agent WebSearch WebFetch NotebookEdit"`,I=bo(A,{encoding:"utf-8",timeout:12e4,maxBuffer:4*1024*1024}),J=JSON.parse(I),D=String(J.result||J.content||"").match(/\[[\s\S]*\]/);if(!D)throw new Error("no JSON array in classifier response");k=JSON.parse(D[0])}catch(A){console.log(p.red(` LLM classify failed: ${A.message?.slice(0,150)}`));continue}for(let A of b){let I=k.find(Fe=>Fe.path===A.path),J=Cc(I?.type);if(!I||!J){console.log(p.yellow(` ? ${A.path}: unclassified${I?` (got "${I.type}")`:""}`));continue}let B=g.extractWikilinks(A.content),D={...A.meta,type:J.type,related:B.length?B:void 0,lastUpdated:new Date().toISOString().slice(0,10)},ke=B.length?p.dim(` \u2192 ${B.slice(0,3).join(", ")}${B.length>3?", \u2026":""}`):"",Se=J.alias?p.dim(` (from "${J.alias}")`):"";console.log(` ${p.green("+")} ${p.magenta("["+J.type+"]")}${Se} ${A.path}${ke}`),i&&(g.writeArticle(A.path,D,A.content,d),c++)}}i&&g.rebuildIndex()}console.log(),console.log(p.dim(` Scanned ${r} articles; ${a} needed migration`)),console.log(i?p.green(` Applied ${c} patches.`):p.dim(" Dry-run \u2014 add --commit to write changes.")),console.log()});function Cc(e){if(!e)return null;let o=String(e).trim().toLowerCase().replace(/^[\s"'`]+|[\s"'`]+$/g,"");if(!o)return null;let n={person:"person",project:"project",place:"place",concept:"concept",event:"event",decision:"decision",pattern:"pattern",people:"person",projects:"project",places:"place",concepts:"concept",events:"event",decisions:"decision",patterns:"pattern",agent:"project",bot:"project",service:"project",product:"project",tool:"project",repo:"project",repository:"project",app:"project",application:"project",team:"project",organization:"project",company:"project",issue:"event",mr:"event","merge-request":"event",merge_request:"event",ticket:"event",incident:"event",deploy:"event",deployment:"event",launch:"event",release:"event",process:"pattern",procedure:"pattern",workflow:"pattern",runbook:"pattern",recipe:"pattern",template:"pattern",sop:"pattern",infrastructure:"place",infra:"place",server:"place",environment:"place",env:"place",location:"place",host:"place",decisionrecord:"decision",adr:"decision","decision-record":"decision"}[o];return n?n===o?{type:n}:{type:n,alias:o}:null}function Rc(e){let o=e.map((t,n)=>{let s=t.content.replace(/\s+/g," ").slice(0,400),i=(t.meta.tags||[]).slice(0,8).join(", ");return`${n+1}. path: "${t.path}"
261
+ title: "${t.meta.title}"
262
+ tags: [${i}]
263
+ body: ${s}${t.content.length>400?"\u2026":""}`}).join(`
264
+
265
+ `);return`Classify each of these ${e.length} wiki articles into EXACTLY ONE type from this closed set (no synonyms, no plurals, no new values):
266
+
267
+ person | project | place | concept | event | decision | pattern
268
+
269
+ What each type means:
270
+ - person: an individual human (team member, stakeholder, contact)
271
+ - project: a named initiative, repo, product, service, bot, agent, team, or org
272
+ - place: a physical or logical location (office, server, environment, infrastructure)
273
+ - concept: a recurring idea, philosophy, methodology, or thinking pattern
274
+ - event: a specific dated thing that happened (incident, deploy, launch, GitLab issue, MR)
275
+ - decision: a specific choice made and why (architecture decision, policy, ADR)
276
+ - pattern: a reusable workflow, template, recipe, procedure, runbook, or SOP
277
+
278
+ Explicit mappings (you WILL be tempted to volunteer these \u2014 don't; use the right-hand value):
279
+ - "agent" / "bot" / "service" / "repo" / "team" \u2192 project
280
+ - "issue" / "MR" / "ticket" / "incident" / "deploy" \u2192 event
281
+ - "process" / "procedure" / "runbook" / "workflow" \u2192 pattern
282
+ - "infrastructure" / "server" / "environment" \u2192 place
283
+ - "ADR" / "decision record" \u2192 decision
284
+
285
+ Rules:
286
+ - Return EXACTLY one type per article, by path, from the 7 allowed values above.
287
+ - Do not invent new types. Do not return plurals. Do not return synonyms.
288
+ - If the title is a person's name, it's person. If it's a repo/product/bot name, it's project. If it's dated + past tense, it's event.
289
+ - If you genuinely cannot decide, pick "concept" as the fallback.
290
+ - Output ONLY valid JSON, no markdown fencing, no prose.
291
+
292
+ Articles:
293
+
294
+ ${o}
295
+
296
+ Output (JSON array, one entry per input article, same order):
297
+ [
298
+ {"path": "path/to/article.md", "type": "project"},
299
+ ...
300
+ ]`}ae.command("entries").description("list raw entries").option("--dir <path>","wiki directory").option("--agent <id>","filter by agent").action(e=>{let n=ve(e.dir).getSharedStore().listEntries();e.agent&&(n=n.filter(s=>s.agentId===e.agent)),console.log(),console.log(p.bold(` ${n.length} raw entries`)),console.log();for(let s of n.slice(-20))console.log(` ${p.dim(s.date)} ${p.cyan(s.agentId)} via ${s.source}: ${s.content.slice(0,80)}...`);n.length>20&&console.log(p.dim(` ... showing last 20 of ${n.length}`)),console.log()});ae.command("serve").description("start a local web server to browse agent wikis (local + mesh)").option("--dir <path>","wiki directory").option("--mode <mode>","graph (default, canonical) | unified | flat (legacy, back-compat)","graph").option("--agent <id>","serve only this agent's wiki").option("--port <n>","port number","4200").option("--peer <urls...>","mesh peer URLs to federate").action(async e=>{let o=e.dir||N(process.cwd(),".agentx/wiki"),t=parseInt(e.port),n=e.peer||[];if(n.length===0)try{let{loadDaemonConfig:s}=await import("./config-7FSZFRTQ.js");n=(s().mesh?.peers||[]).map(r=>r.url)}catch{}console.log(),console.log(p.bold(" AgentX Wiki Server")),console.log(),console.log(` ${p.green(">")} http://localhost:${t}`),e.agent?console.log(` Agent: ${p.cyan(e.agent)}`):console.log(` Mode: ${p.cyan("Hub")} (all agents)`),n.length>0&&console.log(` Mesh: ${p.cyan(n.length+" peer(s)")} \u2014 ${n.join(", ")}`),console.log(p.dim(` Wiki: ${o}`)),console.log(p.dim(" Press Ctrl+C to stop")),console.log(),Ys(o,t,e.agent,n,e.mode)});ae.command("query <question>").description("agentic wiki query \u2014 walks the catalog + wikilink graph, synthesizes an answer").option("--dir <path>","wiki directory").option("--agent <id>","which agent's wiki to query (default: first one with a catalog)").option("--selector-model <m>","candidate-selection model","haiku").option("--synth-model <m>","synthesis model","sonnet").option("--max-candidates <n>","candidates from selector","3").option("--max-hops <n>","wikilink hops from candidates","2").option("--max-articles <n>","cap on total articles walked","8").option("--json","emit full result as JSON (for A/B harnesses)").option("--trace","print selector + walk trace").action(async(e,o)=>{let{agenticQuery:t}=await import("./query-HZYYRCLU.js"),n=ve(o.dir),s=o.agent?[o.agent]:n.listAgents(),i=null;for(let c of s){let d=n.getAgentWiki(c),g=N(d.baseDir,"_index.md");try{if((await import("fs")).existsSync(g)){i=c;break}}catch{}}if(!i){console.log(p.yellow(" No agent has a catalog yet. Run `agentx wiki status` or migrate first."));return}let r=n.getAgentWiki(i),a=await t(e,r,i,{selectorModel:o.selectorModel,synthModel:o.synthModel,maxCandidates:parseInt(o.maxCandidates),maxHops:parseInt(o.maxHops),maxArticles:parseInt(o.maxArticles)});if(o.json){console.log(JSON.stringify(a,null,2));return}if(console.log(),console.log(p.bold(` Q: ${e}`)),console.log(p.dim(` agent: ${i} \xB7 status: ${a.status} \xB7 walked: ${a.walked.length}`)),console.log(),a.status!=="ok"){console.log(p.yellow(` (no answer) ${a.error||a.status}`));return}if(console.log(a.answer),console.log(),a.citations.length){console.log(p.dim(" Citations:"));for(let c of a.citations){let d=c.type?p.magenta(` [${c.type}]`):"";console.log(p.dim(` - ${c.title}${d} (${c.path})`))}console.log()}if(o.trace&&a.trace){if(console.log(p.dim(` selector: ${a.trace.selectorMs}ms synthesis: ${a.trace.synthesisMs}ms`)),console.log(p.dim(` candidates: ${a.candidates.map(c=>c.title).join(" | ")||"(none)"}`)),a.walked.length>a.candidates.length){let c=a.walked.filter(d=>!a.candidates.some(g=>g.path===d.path));console.log(p.dim(` followed: ${c.map(d=>`${d.title}@h${d.hop}`).join(" | ")}`))}console.log()}});ae.command("search <query>").description("search wiki articles").option("--dir <path>","wiki directory").option("--mode <mode>","graph (default, canonical) | unified | flat (legacy, back-compat)","graph").option("--agent <id>","search specific agent's wiki").action((e,o)=>{let t=ve(o.dir,o.mode),n=o.agent?[o.agent]:t.listAgents();console.log();let s=0;for(let i of n){let a=t.getAgentWiki(i).findRelevant(e,void 0,10);if(a.length>0){console.log(p.bold(` ${p.cyan(i)}:`));for(let c of a)console.log(` ${c.meta.title} [${(c.meta.tags||[]).slice(0,3).join(", ")}]`),console.log(p.dim(` ${c.path} \u2014 ${c.content.slice(0,100)}...`));s+=a.length}}s===0&&console.log(p.dim(" No matching articles")),console.log()});ae.command("backfill-graphpath").description("populate graphPath on existing articles by looking up source-entry classifications").option("--dir <path>","wiki directory").option("--agent <id>","backfill only this agent").option("--dry-run","preview without writing",!1).action(e=>{let o=ve(e.dir),t=e.agent?[e.agent]:o.listAgents(),n=N(process.cwd(),".agentx/graph");if(!Re(n)){console.log(p.red(` No graph store at ${n} \u2014 nothing to backfill against.`));return}let s=new ao({baseDir:n}),i=s.loadIndex(),r=new Map;for(let[l,u]of Object.entries(i.entries))r.set(l,u.path);if(r.size===0){console.log(p.yellow(" Graph index is empty \u2014 nothing to look up."));return}console.log(p.dim(` Loaded ${r.size} fingerprint -> path entries from .agentx/graph/index.json`));let a=0,c=0,d=0,g=0;for(let l of t){let u=o.getAgentWiki(l),m=u.listArticles(l),f=o.getSharedStore().listEntries(),y=new Map(f.map(S=>[S.id,S])),$=0,v=0,b=0;for(let S of m){if(a++,S.meta.graphPath?.length||!S.meta.sources?.length)continue;$++,c++;let x=new Map;for(let A of S.meta.sources){let I=y.get(A);if(!I)continue;let J=String(I.meta?.sender??""),B=s.fingerprint({text:I.content,channel:I.source,sender:J}),D=r.get(B);if(!D?.length)continue;let ke=D.join("/"),Se=x.get(ke);Se?Se.n++:x.set(ke,{path:D,n:1})}if(x.size===0)continue;v++,d++;let k;for(let A of x.values())(!k||A.n>k.n)&&(k=A);k&&(e.dryRun||(u.writeArticle(S.path,{...S.meta,graphPath:k.path},S.content,l),b++,g++))}console.log(` ${p.cyan(l)}: ${p.bold(m.length)} articles \xB7 ${$} eligible \xB7 ${v} matched \xB7 ${p.green(b)} written`)}console.log(),console.log(p.bold(` Total: ${a} articles, ${c} missing graphPath, ${d} matched (${Math.round(d/Math.max(1,c)*100)}%), ${g} written`)),e.dryRun&&console.log(p.dim(" Dry-run \u2014 re-run without --dry-run to commit.")),console.log()});ae.command("sync").description("pull raw entries from mesh peers into local wiki").option("--dir <path>","wiki directory").option("--peer <url>","sync from a specific peer URL (e.g., http://100.67.108.119:19900)").option("--dry-run","show what would be synced without writing").action(async e=>{let t=ve(e.dir).getSharedStore(),n=[];if(e.peer)n=[e.peer];else try{let{loadDaemonConfig:r}=await import("./config-7FSZFRTQ.js");n=(r().mesh?.peers||[]).map(c=>c.url)}catch{console.log(p.red(" No --peer specified and no daemon config found")),console.log(p.dim(" Usage: agentx wiki sync --peer http://100.67.108.119:19900"));return}if(n.length===0){console.log(p.dim(" No mesh peers configured"));return}let s=new Set(t.listEntries().map(r=>r.id)),i=0;for(let r of n){console.log(),console.log(p.bold(` Syncing from ${r}...`));try{let a=await fetch(`${r}/wiki/entries`,{signal:AbortSignal.timeout(12e4)});if(!a.ok){console.log(p.red(` HTTP ${a.status}`));continue}let c=await a.json(),d=c.entries||[],g=c.nodeId||"unknown";console.log(p.dim(` Node: ${g} \u2014 ${d.length} entries`));let l=0;for(let u of d)s.has(u.id)||(e.dryRun||(t.addEntry({id:u.id,date:u.date,agentId:u.agentId,source:`${u.source}@${g}`,sourceContext:u.sourceContext,content:u.content}),s.add(u.id)),l++);l>0?console.log(p.green(` ${l} new entries${e.dryRun?" (dry run)":" synced"}`)):console.log(p.dim(" Already up to date")),i+=l;try{let u=await fetch(`${r}/wiki/agents`,{signal:AbortSignal.timeout(5e3)});if(u.ok){let m=await u.json();for(let f of m.agents||[])f.totalEntries>0&&console.log(p.dim(` ${f.agentId}: ${f.totalEntries} entries, ${f.totalArticles} articles`))}}catch{}}catch(a){a.cause?.code==="ECONNREFUSED"?console.log(p.red(" Connection refused")):console.log(p.red(` ${a.message}`))}}console.log(),i>0&&!e.dryRun?console.log(p.green(` ${i} entries synced.`)):i>0?console.log(p.dim(` ${i} entries would be synced (dry run)`)):console.log(p.dim(" All peers up to date")),console.log()});ae.command("compare").description("compare all wiki compilation modes for an agent").option("--dir <path>","wiki directory").requiredOption("--agent <id>","agent to compare").action(e=>{let o=e.dir||N(process.cwd(),".agentx/wiki"),t=e.agent,s=["flat","graph","unified"].map(c=>{let d=new Zt(o,void 0,c),l=d.getAgentWiki(t).rebuildIndex(),u=d.getAgentEntries(t),m=d.getUnabsorbedEntries(t),f=new Set,y=0,$=new Set;for(let b of l.articles){for(let S of b.tags||[])f.add(S);y+=b.tags?.length||0;for(let S of b.sources||[])$.add(S)}let v=new Set(l.articles.map(b=>b.path.includes("/")?b.path.split("/")[0]:"/"));return{mode:c,label:_n(c),articles:l.articles.length,entries:u.length,unabsorbed:m.length,uniqueTags:f.size,avgTags:l.articles.length>0?(y/l.articles.length).toFixed(1):"0",dirs:v.size,coverage:$.size,articleList:l.articles}});console.log(),console.log(p.bold(` Wiki Compare: ${t}`)),console.log(` Raw entries: ${s[0].entries}`),console.log();let i=22;console.log(` ${"".padEnd(20)} ${s.map(c=>c.label.padEnd(i)).join(" ")}`),console.log(` ${"\u2500".repeat(20)} ${s.map(()=>"\u2500".repeat(i)).join(" ")}`);let r=[["Articles",c=>String(c.articles)],["Unabsorbed",c=>String(c.unabsorbed)],["Unique tags",c=>String(c.uniqueTags)],["Avg tags/article",c=>c.avgTags],["Directories",c=>String(c.dirs)],["Entry coverage",c=>`${c.coverage}/${c.entries}`]];for(let[c,d]of r){let g=s.map(l=>d(l).padEnd(i));console.log(` ${c.padEnd(20)} ${g.join(" ")}`)}console.log();let a=(c,d)=>{let g=Math.max(...s.map(u=>u[c])),l=s.filter(u=>u[c]===g).map(u=>u.label);g>0&&console.log(p.dim(` Best ${d}: ${l.join(", ")} (${g})`))};a("uniqueTags","tag richness"),a("articles","article count"),a("coverage","entry coverage"),console.log()});ae.command("export [output]").description("export the entire wiki tree to a .tar.gz").option("--dir <path>","wiki directory to archive (default: .agentx/wiki)").option("--include-raw","also include raw entries (.agentx/wiki/raw/) \u2014 bigger archive").action(async(e,o)=>{let t=o.dir?N(process.cwd(),o.dir):N(process.cwd(),".agentx/wiki");Re(t)||(console.log(p.red(` Wiki dir not found: ${t}`)),process.exit(1));let n=new Date().toISOString().slice(0,10),s=e?N(process.cwd(),e):N(process.cwd(),`agentx-wiki-${n}.tar.gz`),i=N(t,".."),r=t.split("/").pop()||"wiki",a=["-C",i,"-czf",s];o.includeRaw||a.push("--exclude="+r+"/raw"),a.push(r),console.log(p.dim(` tar ${a.join(" ")}`));let{spawnSync:c}=await import("child_process"),d=c("tar",a,{stdio:"inherit"});d.status!==0&&(console.log(p.red(` tar exited with ${d.status}`)),process.exit(d.status||1));let{statSync:g}=await import("fs"),l=Re(s)?g(s).size:0;console.log(),console.log(p.green(` \u2713 wrote ${s} (${Tc(l)})`)),console.log(p.dim(` Restore on another node: agentx wiki import ${s}`)),console.log()});ae.command("import <archive>").description("restore a wiki archive (created with `agentx wiki export`)").option("--dir <path>","where to restore (default: .agentx/wiki)").option("--force","overwrite existing wiki without prompting").action(async(e,o)=>{let t=N(process.cwd(),e);Re(t)||(console.log(p.red(` archive not found: ${t}`)),process.exit(1));let n=o.dir?N(process.cwd(),o.dir):N(process.cwd(),".agentx/wiki");Re(n)&&!o.force&&(console.log(p.yellow(` ${n} already exists.`)),console.log(p.yellow(" Re-run with --force to overwrite (existing files where the archive has them; others stay).")),process.exit(1));let s=N(n,".."),{mkdirSync:i}=await import("fs");i(s,{recursive:!0});let r=["-C",s,"-xzf",t];console.log(p.dim(` tar ${r.join(" ")}`));let{spawnSync:a}=await import("child_process"),c=a("tar",r,{stdio:"inherit"});c.status!==0&&(console.log(p.red(` tar exited with ${c.status}`)),process.exit(c.status||1)),console.log(),console.log(p.green(` \u2713 restored to ${n}`)),console.log(p.dim(" Restart the daemon (or POST /reload) for the wiki hub to pick up the new content.")),console.log()});function Tc(e){return e<1024?e+"B":e<1024*1024?(e/1024).toFixed(1)+"K":e<1024*1024*1024?(e/1024/1024).toFixed(1)+"M":(e/1024/1024/1024).toFixed(2)+"G"}import{Command as Dc}from"commander";import P from"chalk";import{resolve as Pn}from"path";import{existsSync as Nc}from"fs";var rt=new Dc().name("graph").description("intent knowledge graph \u2014 review, inspect, manage");rt.command("review").description("triage pending classifications via the configured review agent (the agent uses `wiki query` for context before deciding)").option("--agent <id>","override the review agent (defaults to graph.reviewAgent or graph.draftAgent)").option("--max <n>","cap reviews this run","20").option("--dry-run","show the agent's decision but don't apply approvals/rejections").option("--daemon-url <url>","daemon URL (default: http://localhost:18800)").action(async e=>{let o=L();if(!o.graph?.enabled){console.log(P.yellow(" graph.enabled is false \u2014 nothing to review"));return}let t=e.agent||o.graph.reviewAgent||o.graph.draftAgent;if(!t){console.log(P.red(" no review agent. Set graph.reviewAgent in agentx.json or pass --agent <id>."));return}if(!o.agents?.[t]){console.log(P.red(` agent "${t}" not in config`));return}let n=(e.daemonUrl||o.dashboard?.daemonUrl||"http://localhost:18800").replace(/\/+$/,""),s=Math.max(1,parseInt(e.max)||20),i=new ao({baseDir:Pn(process.cwd(),o.graph.baseDir)}),r=i.listPendingClassifications(s);if(r.length===0){console.log(P.dim(" no pending classifications \u2014 nothing to review"));return}let a=i.loadSchema(),c=i.loadNodes().nodes;console.log(),console.log(P.bold(` Graph review \u2014 ${r.length} pending`)),console.log(P.dim(` Reviewer: ${t} \xB7 daemon: ${n} \xB7 ${e.dryRun?"dry-run":"commit"}`)),console.log();let d=0,g=0,l=0,u=0;for(let f=0;f<r.length;f++){let y=r[f],$=is(y.path,c)||y.path.join(" \u203A ");process.stdout.write(` [${f+1}/${r.length}] ${P.cyan(y.msgHash.slice(0,8))} ${P.dim($)} ... `);let v=_c(y,a,c),b=null;try{b=await Pc(n,t,v,o.dashboard?.token)}catch(k){u++,console.log(P.red(`ERR ${k.message?.slice(0,80)}`));continue}if(!b||!b.decision){u++,console.log(P.red("ERR no decision"));continue}let S=String(b.decision).toLowerCase().trim(),x=(b.reason||"").toString().slice(0,180);if(S==="approve")if(console.log(P.green("APPROVE")+" "+P.dim(x)),e.dryRun)d++;else try{Mc(i,y,a,c,t),i.updateClassificationStatus(y.msgHash,{status:"approved",reviewer:t,reviewReason:x}),d++}catch(k){console.log(P.yellow(` (commit failed, staying pending: ${k.message?.slice(0,100)})`)),u++}else S==="reject"?(console.log(P.red("REJECT")+" "+P.dim(x)),e.dryRun||i.updateClassificationStatus(y.msgHash,{status:"rejected",reviewer:t,reviewReason:x}),g++):(console.log(P.yellow("SKIP")+" "+P.dim(x||S)),l++)}console.log();let m=e.dryRun?"would":"did";console.log(P.dim(` Summary: ${m} approve ${d}, ${m} reject ${g}, ${l} skipped, ${u} errors.`)),e.dryRun&&(d>0||g>0)&&console.log(P.dim(" Dry-run \u2014 omit --dry-run to apply.")),console.log()});rt.command("migrate").description("remap past classifications from v1 (scope/location/org/unit/activity) to v2 (category/verb) via keyword heuristics. No LLM calls. Idempotent.").option("--dry-run","report what would change, don't write").action(async e=>{let o=L();if(!o.graph){console.log(P.yellow(" graph block missing from agentx.json \u2014 nothing to migrate"));return}let t=Pn(process.cwd(),o.graph.baseDir);if(!Nc(t)){console.log(P.yellow(` ${t} does not exist \u2014 nothing to migrate`));return}let{migrateV2:n}=await import("./migrate-v2-QO7SLKIM.js"),s=new ao({baseDir:t,log:()=>{}}),i=n(s,{dryRun:!!e.dryRun,log:(...a)=>console.log(P.dim(" "),...a)});console.log(),console.log(P.bold(" Pattern hits:"));let r=Object.entries(i.patternHits).sort((a,c)=>c[1]-a[1]);r.length===0&&console.log(P.dim(" (none)"));for(let[a,c]of r)console.log(` ${a.padEnd(28)} ${c}`);console.log(),e.dryRun?console.log(P.dim(" Dry-run \u2014 omit --dry-run to apply.")):console.log(P.green(` Done. Backups: ${i.backups.length} file(s).`)),console.log()});rt.command("pull").description("pull schema + nodes + approved classifications from a peer's graph").requiredOption("--from <url>","peer daemon URL (e.g. http://clawd.noqta.tn:19900)").option("--token <t>","bearer token for the peer (if it requires auth)").option("--limit <n>","max approved classifications to pull","500").option("--dry-run","show what would change, don't write").action(async e=>{let o=L();if(!o.graph?.enabled){console.log(P.yellow(" graph.enabled is false on this node \u2014 nothing to sync INTO."));return}let t=String(e.from).replace(/\/+$/,""),n=Math.max(1,parseInt(e.limit)||500),s={Accept:"application/json"};e.token&&(s.Authorization=`Bearer ${e.token}`);let i=new ao({baseDir:Pn(process.cwd(),o.graph.baseDir)}),r=i.loadSchema(),a=i.loadNodes().nodes,c=new Set(a.map(I=>I.id));console.log(),console.log(P.bold(` graph pull \u2190 ${t}`));let d,g,l;try{let I=await fetch(`${t}/graph/schema`,{headers:s});if(!I.ok)throw new Error(`schema HTTP ${I.status}`);d=(await I.json()).schema;let B=await fetch(`${t}/graph/nodes`,{headers:s});if(!B.ok)throw new Error(`nodes HTTP ${B.status}`);g=(await B.json()).nodes||[];let D=await fetch(`${t}/graph/classifications?status=approved&limit=${n}`,{headers:s});if(!D.ok)throw new Error(`classifications HTTP ${D.status}`);l=(await D.json()).classifications||[]}catch(I){console.log(P.red(` fetch failed: ${I.message}`));return}let u=r.levels.map(I=>I.id).join(","),m=d?.levels?.map(I=>I.id).join(","),f=u===m;f?console.log(P.dim(` schema matches (${r.levels.length} levels)`)):(console.log(P.yellow(" \u26A0 schema levels differ:")),console.log(P.dim(` local: ${u}`)),console.log(P.dim(` peer: ${m}`)),a.length===0&&!e.dryRun?(console.log(P.dim(" local is empty \u2014 adopting peer schema")),i.saveSchema(d)):console.log(P.yellow(" keeping local schema; node import may fail if levels disagree")));let y=f?r:d,$=new Map;y.levels.forEach((I,J)=>$.set(I.id,J));let v=[...g].sort((I,J)=>($.get(I.level)??99)-($.get(J.level)??99)),b=0,S=0,x=0;for(let I of v){if(c.has(I.id)){S++;continue}if(e.dryRun){b++,c.add(I.id);continue}try{i.addNode({...I,createdBy:I.createdBy?`${I.createdBy} (sync)`:`sync:${t}`}),c.add(I.id),b++}catch(J){x++,console.log(P.yellow(` skip node ${I.id}: ${J.message?.slice(0,100)}`))}}console.log(P.dim(` nodes: +${b} added \xB7 ${S} already-present \xB7 ${x} failed`));let k=0,A=0;for(let I of l)if(!(!I.msgHash||!Array.isArray(I.path))){if(i.getFingerprint(I.msgHash)){A++;continue}if(e.dryRun){k++;continue}i.setFingerprint(I.msgHash,{path:I.path,leaf:I.leaf||{}}),k++}console.log(P.dim(` fingerprints: +${k} cached \xB7 ${A} already-present`)),console.log(),e.dryRun?console.log(P.dim(" Dry-run \u2014 omit --dry-run to apply.")):console.log(P.green(` \u2713 pulled from ${t}`)),console.log()});function _c(e,o,t){let n={};for(let r of t)(n[r.level]||=[]).push(`${r.id}${r.axes?.name?` (${r.axes.name})`:""}`);let s=o.levels.map(r=>` ${r.id}: [${(n[r.id]||[]).slice(0,10).join(", ")||"(none yet)"}]`).join(`
301
+ `),i=JSON.stringify(e.proposedAxes||{},null,2);return["You are reviewing a pending intent classification. Decide whether it is reasonable.","","--- MESSAGE (first 500 chars) ---",(e.preview||"").slice(0,500),`channel: ${e.channel||"?"} \xB7 sender: ${e.sender||"?"}`,"","--- PROPOSED PATH ---",e.path.join(" \u203A "),"","--- PROPOSED AXES (per new node) ---",i,"","--- CURRENT NODES BY LEVEL (first 10 per level) ---",s,"","--- YOUR JOB ---","Decide one of:"," approve \u2014 the path is right and the axes make sense for the message"," reject \u2014 the path is wrong (e.g. the message is NOT about the proposed org/topic, or the axes are bogus)"," skip \u2014 you can't confidently decide; leave it for a human","",'BEFORE you decide, you MAY call `agentx wiki query` (via Bash) to check institutional context \u2014 e.g. "is there an article for this project" or "who is this person". This is optional; don\'t call it for obvious cases.',"","Bias toward reject over approve when uncertain. A wrong approval pollutes the graph; a wrong rejection just keeps the entry pending.","","Return EXACTLY one JSON object on one line, no prose, no fences:",' {"decision":"approve"|"reject"|"skip","reason":"one sentence"}'].join(`
302
+ `)}async function Pc(e,o,t,n){let s={"Content-Type":"application/json"};n&&(s.Authorization=`Bearer ${n}`);let i=new AbortController,r=setTimeout(()=>i.abort(),12e4);try{let a=await fetch(`${e}/task`,{method:"POST",headers:s,body:JSON.stringify({agent:o,message:t,context:{channel:"a2a",sender:"graph-review"}}),signal:i.signal});if(!a.ok)throw new Error(`daemon /task HTTP ${a.status}`);let c=await a.json();if(c?.error)throw new Error(String(c.error));return Lc((c?.content||"").toString())}finally{clearTimeout(r)}}function Mc(e,o,t,n,s){e.commitNodesAlongPath(o.path,o.proposedAxes||{},t,s),e.setFingerprint(o.msgHash,{path:o.path,leaf:o.leaf||{}})}function Lc(e){if(!e)return null;try{return JSON.parse(e)}catch{}let o=e.match(/```(?:json)?\s*([\s\S]*?)```/);if(o)try{return JSON.parse(o[1])}catch{}let t=e.indexOf("{");if(t<0)return null;let n=0;for(let s=t;s<e.length;s++)if(e[s]==="{")n++;else if(e[s]==="}"&&(n--,n===0))try{return JSON.parse(e.slice(t,s+1))}catch{return null}return null}import{Command as qc}from"commander";import W from"chalk";import{Command as Bi}from"commander";import V from"chalk";import{existsSync as Uc}from"fs";import{resolve as Wc}from"path";import Jc from"better-sqlite3";function Bc(e){if(!e)return;let o=/^(\d+)\s*([smhd])$/.exec(e.trim());if(o){let n=Number(o[1]),s=o[2];return Date.now()-n*(s==="s"?1e3:s==="m"?6e4:s==="h"?36e5:864e5)}let t=Number(e);if(Number.isFinite(t)&&t>0)return t;console.log(V.red(` Invalid --since "${e}". Use "1h", "30m", "7d", or an ms epoch.`)),process.exit(1)}function Fc(e){let o=Wc(process.cwd(),e);return Uc(o)||(console.log(V.red(` No db at ${o}`)),process.exit(1)),new Jc(o,{readonly:!0})}var Fi=new Bi().name("extract").description("mine recurring user activity into procedure drafts (dry-run by default)").option("--path <path>","SQLite db path",".agentx/db.sqlite").option("--since <duration>","activity window, e.g. 24h, 7d, or ms epoch","7d").option("--agent <id>","only mine one agent's activity").option("--min-occurrences <n>","recurrences before a pattern becomes a draft",String(Yo)).option("--max <n>","max clusters distilled per run",String($t)).option("--via <agentId>","route the LLM call through a running agent \u2014 uses the agent's own session").option("--model <model>","direct Anthropic API model \u2014 needs ANTHROPIC_API_KEY").option("--daemon <url>","daemon API base URL for --via","http://127.0.0.1:18800").option("--commit","write drafts and persist recurrence counts",!1).action(async e=>{e.commit&&!e.via&&!e.model&&(console.log(V.red(" --commit needs an LLM: pass --via <agentId> or --model <model>")),process.exit(1));let o=Fc(e.path),t=await Cs(o,{since:Bc(e.since),agentId:e.agent,sessions:new ss(process.cwd()),minOccurrences:Number(e.minOccurrences)||Yo,max:Number(e.max)||$t,commit:!!e.commit,llm:{viaAgent:e.via,model:e.model,daemonUrl:e.daemon,chatId:"procedure-miner"},log:n=>console.log(V.dim(` ${n}`))});console.log(),console.log(` ${t.episodes} episode(s) \u2192 ${t.clusters} cluster(s), ${t.ready.length} at threshold`);for(let n of t.ready)console.log(` ${V.cyan(n.key)} ${V.bold(`\xD7${n.count}`)} ${V.dim(n.status)}`),n.sampleMessage&&console.log(V.dim(` "${n.sampleMessage.slice(0,100)}"`));if(t.nearMisses.length){console.log(V.dim(`
303
+ near-misses (same intent, different activity shape \u2014 tuning signal):`));for(let n of t.nearMisses.slice(0,5))console.log(V.dim(` ${n.intent} \u2192 ${n.keys.length} variants`))}for(let n of t.drafted)console.log(V.green(` \u2713 draft written: ${n.id}`)+V.dim(` (${n.count} occurrences)`));for(let n of t.skipped)console.log(V.yellow(` skipped ${n.cluster}: ${n.reason}`));for(let n of t.warnings)console.log(V.yellow(` \u26A0 ${n}`));t.dryRun?console.log(V.dim(`
304
+ dry run only; pass --commit --via <agentId> to write drafts`)):t.drafted.length&&console.log(V.dim(`
305
+ review with: agentx procedure list --drafts \xB7 then promote/reject <id>`))}),Ji={hourly:"0 * * * *",daily:"0 3 * * *"},qi=new Bi().name("watch").description("set the extraction cadence: registers the cron + config the daemon runs on").option("--hourly","extract every hour").option("--daily","extract daily at 03:00").option("--cron <expr>","custom 5-field cron expression").option("--on-task","also count patterns live after each completed task").option("--off","disable scheduled extraction").option("--via <agentId>","agent that runs the extraction (LLM distillation goes through its session)").option("--min-occurrences <n>","recurrences before a pattern becomes a draft",String(Yo)).option("--timezone <tz>","IANA timezone","Africa/Tunis").option("-c, --config <path>","path to agentx.json").option("--dry-run","print what would be written without writing").action(async e=>{let o=e.off?void 0:e.cron??(e.hourly?Ji.hourly:e.daily?Ji.daily:void 0);!e.off&&!o&&!e.onTask&&(console.log(V.red(" pick a cadence: --hourly, --daily, --cron <expr>, and/or --on-task (or --off)")),process.exit(1)),!e.off&&!e.via&&(console.log(V.red(" --via <agentId> is required: drafts are distilled through that agent's session")),process.exit(1));let t=Number(e.minOccurrences)||Yo,n={enabled:!e.off,onTaskCompletion:!e.off&&!!e.onTask,minOccurrences:t,via:e.off?void 0:e.via},s=["Run this shell command EXACTLY as written. Do NOT substitute, rewrite, shorten, or fall back to alternative commands. If it exits non-zero, report the verbatim exit code and stderr and STOP.","",`1) cd ${process.cwd()} && node dist/cli.js procedure extract --since 7d --max ${$t} --min-occurrences ${t} --via ${e.via} --commit`,"","Report a one-line summary of drafted/skipped counts."].join(`
306
+ `),i=await K(a=>{X(a,"procedures.extraction",n),e.off?a.crons?.["procedure-extract"]&&X(a,"crons.procedure-extract.enabled",!1):o&&X(a,"crons.procedure-extract",{enabled:!0,schedule:o,timezone:e.timezone,agent:e.via,prompt:s,timeout:900,onError:"log"})},{configPath:e.config,dryRun:!!e.dryRun});i.success||(console.log(V.red(` \u2717 ${i.error}`)),process.exit(1));let r=e.dryRun?"would set":"Set";e.off?console.log(V.green(` \u2713 ${r} procedure extraction: off`)):(console.log(V.green(` \u2713 ${r} procedure extraction cadence`)),o&&console.log(V.dim(` cron: ${o} (${e.timezone}) via ${e.via}`)),e.onTask&&console.log(V.dim(" on-task counting: enabled")),console.log(V.dim(` threshold: ${t} occurrences`))),console.log(V.yellow(" \u26A0 restart the daemon to activate \u2014 hot-reload does not pick up new config blocks"))});var Te=new qc().name("procedure").description("procedures (SOPs) \u2014 extract from activity, review drafts, match, schedule");Te.addCommand(Fi);Te.addCommand(qi);Te.command("list").description("list procedures with their trigger line").option("--drafts","list unreviewed drafts instead",!1).option("--all","list active procedures and drafts",!1).action(e=>{let o=new He,t=[...e.drafts?[]:o.list(),...e.drafts||e.all?o.listDrafts():[]];if(!t.length){console.log(W.dim(e.drafts?" no procedure drafts. Mine some with: agentx procedure extract":" no procedures yet. Try: agentx procedure extract \xB7 or: agentx procedure add"));return}console.log();for(let n of t){let s=n.meta.status==="active"?W.green(n.meta.status):n.meta.status==="draft"?W.yellow(n.meta.status):W.dim(n.meta.status);console.log(` ${W.cyan(n.meta.id)} ${W.bold(n.meta.title)} ${s}`),console.log(` ${W.dim("trigger:")} ${n.meta.trigger}`),n.meta.source&&console.log(` ${W.dim("source:")} ${W.dim(n.meta.source)}`),n.meta.kpis?.length&&console.log(` ${W.dim("kpis:")} ${n.meta.kpis.join(", ")}`)}console.log(),console.log(W.dim(` ${t.length} procedure${t.length===1?"":"s"}.`))});Te.command("add").description("add a new procedure (non-interactive; pass all fields as flags)").requiredOption("--id <id>","procedure id (lower-kebab, e.g. deploy-clawd)").requiredOption("--title <t>","human-readable title").requiredOption("--trigger <t>","when this procedure applies (one sentence)").option("--input <i...>","required input (repeatable)",(e,o=[])=>[...o,e]).option("--expected <t>","expected output / success criterion").option("--kpi <k...>","KPI (repeatable)",(e,o=[])=>[...o,e]).option("--owner <id>","owning agent or person").option("--tag <t...>","tag (repeatable)",(e,o=[])=>[...o,e]).option("--related <r...>","related procedure id or wiki article title (repeatable)",(e,o=[])=>[...o,e]).option("--steps <md>","markdown body \u2014 usually numbered steps").action(e=>{let o=new He,t=e.steps?String(e.steps):`## Steps
307
+
308
+ 1. (fill in)
309
+
310
+ ## Notes
311
+
312
+ `;try{let n=o.add({id:e.id,title:e.title,trigger:e.trigger,inputs:e.input||[],expected:e.expected,kpis:e.kpi||[],owner:e.owner,tags:e.tag||[],related:e.related||[]},t);console.log(W.green(` \u2713 procedure added: ${n.meta.id}`)),console.log(W.dim(` ${o.baseDir}/${n.path}`))}catch(n){console.log(W.red(` add failed: ${n.message}`)),process.exit(1)}});Te.command("show <id>").description("show a single procedure (active or draft)").action(e=>{let o=new He,t=o.get(e)??o.getDraft(e);t||(console.log(W.yellow(` no procedure matches "${e}". Try: agentx procedure list --all`)),process.exit(1)),console.log(),console.log(W.bold(` ${t.meta.title}`)+W.dim(` (${t.meta.id}, ${t.meta.status})`)),console.log(W.dim(` trigger: ${t.meta.trigger}`)),t.meta.inputs?.length&&console.log(W.dim(` inputs: ${t.meta.inputs.join(", ")}`)),t.meta.expected&&console.log(W.dim(` expected: ${t.meta.expected}`)),t.meta.kpis?.length&&console.log(W.dim(` kpis: ${t.meta.kpis.join(", ")}`)),t.meta.owner&&console.log(W.dim(` owner: ${t.meta.owner}`)),t.meta.source&&console.log(W.dim(` source: ${t.meta.source}`)),t.meta.related?.length&&console.log(W.dim(` related: ${t.meta.related.join(", ")}`)),console.log(),console.log(t.body),console.log()});Te.command("promote <id>").description("promote a draft into the active procedure set").action(e=>{let o=new He;try{let t=o.promoteDraft(e);console.log(W.green(` \u2713 promoted: ${t.meta.id}`)),console.log(W.dim(` agents will now follow it when a task matches: "${t.meta.trigger}"`))}catch(t){console.log(W.red(` ${t.message}`)),process.exit(1)}});Te.command("reject <id>").description("reject a draft (kept under _drafts/_rejected/ so it is never re-mined)").action(e=>{let o=new He;try{o.rejectDraft(e),console.log(W.green(` \u2713 rejected: ${e}`))}catch(t){console.log(W.red(` ${t.message}`)),process.exit(1)}});Te.command("deprecate <id>").description("retire an active procedure (kept, but never surfaced to agents)").action(e=>{let o=new He;try{o.deprecate(e),console.log(W.green(` \u2713 deprecated: ${e}`))}catch(t){console.log(W.red(` ${t.message}`)),process.exit(1)}});Te.command("match <text>").description("find active procedures matching a task description").option("--limit <n>","max matches","3").option("--min-score <s>","minimum score 0..1","0.2").action((e,o)=>{let t=new He,n=Es(e,t.list(),{limit:Number(o.limit)||3,minScore:Number(o.minScore)||.2});if(!n.length){console.log(W.dim(" no matching procedures"));return}for(let s of n)console.log(` ${W.cyan(s.procedure.meta.id)} ${W.bold(s.score.toFixed(2))} ${W.dim(s.reasons.join(", "))}`),console.log(W.dim(` ${s.procedure.meta.trigger}`))});import{Command as Gc}from"commander";import w from"chalk";import{resolve as to,basename as Yc}from"path";import{existsSync as Mt,mkdirSync as Kc,readFileSync as Mn,writeFileSync as ct}from"fs";import Vc from"better-sqlite3";import{readFileSync as zc}from"fs";import{resolve as Mo,dirname as zi}from"path";import{fileURLToPath as Hc}from"url";var at=[{name:"linear",title:"Linear",description:"Trigger \u2192 Agent \u2192 End. Smallest workflow that runs."},{name:"branching",title:"Branching",description:"Classify, then route on the result via a branch node."},{name:"extract",title:"Structured extraction",description:"Pull typed fields from free-form text via extract.structured."},{name:"retry",title:"Retry + fallback",description:"Per-node retry policy with a branch fallback path."}];function Hi(e){let o=zi(Hc(import.meta.url)),t=process.argv[1]?zi(process.argv[1]):"",n=[Mo(o,`${e}.yaml`),Mo(o,"workflows/templates",`${e}.yaml`),Mo(o,"..","workflows/templates",`${e}.yaml`),Mo(o,"../..","workflows/templates",`${e}.yaml`),t?Mo(t,"workflows/templates",`${e}.yaml`):"",t?Mo(t,"..","workflows/templates",`${e}.yaml`):""].filter(Boolean);for(let s of n)try{return zc(s,"utf-8")}catch{}throw new Error(`workflow template "${e}" not found in any of:
313
+ ${n.join(`
314
+ `)}`)}var de=new Gc().name("workflow").description("workflow definitions \u2014 list, show, validate, manage runs");function Qc(e){let o=/^(\d+)\s*([smhd])$/.exec(e.trim());if(!o)return null;let t=Number(o[1]),n=o[2];return t*(n==="s"?1e3:n==="m"?6e4:n==="h"?36e5:864e5)}function Xc(e){if(!e)return;let o=Qc(e);if(o===null){let t=Number(e);if(Number.isFinite(t)&&t>0)return t;console.log(w.red(` Invalid --since "${e}". Use "1h", "30m", "7d", or an ms epoch.`)),process.exit(1)}return Date.now()-o}function Ln(e=".agentx/db.sqlite"){let o=to(process.cwd(),e);return Mt(o)||(console.log(w.red(` No db at ${o}`)),process.exit(1)),new Vc(o,{readonly:!0})}function Ki(e,o){console.log(o==="json"?JSON.stringify(e,null,2):ht(e))}function Gi(e){let o=Array.from(new Set(e.map(i=>i.model).filter(Boolean).map(String))),t=e.reduce((i,r)=>i+(Number(r.durationMs)||0),0),n=e.reduce((i,r)=>i+(Number(r.totalTokens)||0),0),s=e.at(-1);return{durationMs:t,totalTokens:n,models:o,status:e.some(i=>i.status==="error")?"error":s?.status||"ok",result:String(s?.outputSummary||s?.error||"").slice(0,160)}}function Zc(e,o){let t=o.durationMs-e.durationMs,n=o.totalTokens-e.totalTokens;console.log(),console.log(w.bold(" replay comparison")),console.log(` original: ${e.status} ${e.totalTokens||0}t ${e.durationMs||0}ms model=${e.models.join(",")||"\u2014"}`),console.log(` replay: ${o.status} ${o.totalTokens||0}t ${o.durationMs||0}ms model=${o.models.join(",")||"\u2014"}`),console.log(` delta: ${n>=0?"+":""}${n}t ${t>=0?"+":""}${t}ms`),o.result&&console.log(` result: ${o.result}`)}de.command("list").description("list all workflows with their trigger + state count").action(()=>{let o=new Ue().list();if(!o.length){console.log(w.dim(" no workflows yet. Add one by writing .agentx/workflows/<id>.json"));return}console.log();for(let t of o){let s=t.nodes.find(a=>a.type.startsWith("trigger."))?.config??{},i=[s.filter?.project,s.filter?.repo,s.filter?.chat].filter(Boolean).join(" / "),r=t.state==="active"?w.green("dispatch: active"):w.yellow(`dispatch: ${t.state}`);console.log(` ${w.cyan(t.id)} ${w.bold(t.title)} ${w.dim(`v${t.version} \xB7`)} ${r} ${w.dim(`\xB7 review: ${t.status}`)}`),console.log(` ${w.dim("trigger:")} ${s.source??"?"}${i?` ${w.dim(i)}`:""}`),console.log(` ${w.dim("nodes: ")} ${t.nodes.length} (${t.nodes.map(a=>a.type).join(", ")})`)}console.log(),console.log(w.dim(` ${o.length} workflow${o.length===1?"":"s"}.`))});de.command("show <id>").description("show a single workflow's full definition").option("--format <fmt>","output format: json (default) or yaml","json").action((e,o)=>{let n=new Ue().get(e);n||(console.log(w.yellow(` no workflow matches "${e}". Try: agentx workflow list`)),process.exit(1));let s=o.format.toLowerCase();s==="yaml"||s==="yml"?console.log(ht(n)):s==="json"?console.log(JSON.stringify(n,null,2)):(console.log(w.red(` unknown format "${o.format}" \u2014 use json or yaml`)),process.exit(1))});de.command("validate [file]").description("validate all workflows in .agentx/workflows (or a single file)").action(e=>{if(e){let i=to(process.cwd(),e);Mt(i)||(console.log(w.red(` file not found: ${i}`)),process.exit(1));try{let r=Mn(i,"utf-8"),c=/\.(ya?ml)$/i.test(e)?jo(r,{filePath:e}):JSON.parse(r),d=ze.safeParse(c);if(!d.success){console.log(w.red(` \u2717 ${i}`));for(let l of d.error.issues)console.log(` ${l.path.join(".")||"<root>"}: ${l.message}`);process.exit(1)}let g=ut(d.data);if(g.length){console.log(w.yellow(` ! ${i} (lint)`));for(let l of g)console.log(` ${l}`);process.exit(1)}console.log(w.green(` \u2713 ${d.data.id} \u2014 valid`))}catch(r){let a=r instanceof ft?r.message:`parse error: ${r?.message??r}`;console.log(w.red(` ${a}`)),process.exit(1)}return}let t=new Ue().validateAll(),n=0,s=0;for(let i of t)if(i.isValid)console.log(w.green(` \u2713 ${i.workflow.id}`)),n++;else{let r=i.workflow?.id||i.id;console.log(w.red(` \u2717 ${r}`));for(let a of i.issues)console.log(` ${a}`);s++}console.log(),console.log(w.dim(` ${n} valid, ${s} invalid.`)),s>0&&process.exit(1)});de.command("init <id>").description("scaffold a new workflow YAML from a template (linear by default)").option("--template <name>",`which template to use (${at.map(e=>e.name).join(" | ")})`,"linear").option("--agent <id>","fill the agent placeholder with this agent id","default").option("--reviewer <id>","fill the reviewer placeholder for human-in-the-loop templates","alice").option("--title <text>","workflow title","").option("--json","scaffold as .json instead of .yaml",!1).option("--force","overwrite an existing workflow with the same id",!1).action((e,o)=>{let t=String(o.template||"linear");at.some(a=>a.name===t)||(console.log(w.red(` unknown template "${o.template}"`)),console.log(w.dim(` available: ${at.map(a=>a.name).join(", ")}`)),process.exit(1)),/^[a-z0-9][a-z0-9_-]*$/.test(e)||(console.log(w.red(` workflow id must be lower-kebab (got "${e}")`)),process.exit(1));let n=new Ue;!o.force&&n.get(e)&&(console.log(w.red(` workflow "${e}" already exists. Use --force to overwrite or pick a new id.`)),process.exit(1));let s=Hi(t);s=s.replace(/__ID__/g,e).replace(/__TITLE__/g,o.title?String(o.title):`${t} workflow ${e}`).replace(/__AGENT__/g,String(o.agent)).replace(/__REVIEWER__/g,String(o.reviewer));try{let a=jo(s,{filePath:`${e}.yaml`}),c=ze.safeParse(a);if(!c.success){console.log(w.red(` template "${t}" failed schema validation after substitution:`));for(let d of c.error.issues)console.log(` ${d.path.join(".")||"<root>"}: ${d.message}`);process.exit(1)}}catch(a){console.log(w.red(` template parse failed: ${a?.message??a}`)),process.exit(1)}let i=o.json?"json":"yaml",r=to(process.cwd(),".agentx/workflows",`${e}.${i}`);if(o.json){let a=jo(s,{filePath:`${e}.yaml`}),c=ze.parse(a);ct(r,JSON.stringify(c,null,2)+`
315
+ `)}else ct(r,s);console.log(),console.log(w.green(` \u2713 scaffolded ${r}`)),console.log(w.dim(` template: ${t}`)),console.log(),console.log(w.dim(" Next:")),console.log(w.dim(` 1. edit ${r}`)),console.log(w.dim(` 2. agentx workflow validate ${r}`)),console.log(w.dim(` 3. agentx workflow run ${e} --watch`)),console.log()});de.command("templates").description("list the available workflow templates for `init`").action(()=>{console.log();for(let e of at)console.log(` ${w.cyan(e.name.padEnd(20))} ${w.bold(e.title)}`),console.log(` ${" ".repeat(20)} ${w.dim(e.description)}`);console.log(),console.log(w.dim(" Use: agentx workflow init <id> --template <name>")),console.log()});de.command("add <file>").description("import a YAML/JSON workflow file into .agentx/workflows and hot-reload").option("--daemon <url>","daemon API base URL","http://127.0.0.1:18800").option("--no-reload","skip the POST /reload after writing").action(async(e,o)=>{let t=to(process.cwd(),e);Mt(t)||(console.log(w.red(` file not found: ${t}`)),process.exit(1));let n=Mn(t,"utf-8"),s=/\.(ya?ml)$/i.test(e),i;try{i=s?jo(n,{filePath:e}):JSON.parse(n)}catch(u){let m=u instanceof ft?u.message:`parse error: ${u?.message??u}`;console.log(w.red(` ${m}`)),process.exit(1)}let r=ze.safeParse(i);if(!r.success){console.log(w.red(" \u2717 schema:"));for(let u of r.error.issues)console.log(` ${u.path.join(".")||"<root>"}: ${u.message}`);process.exit(1)}let a=ut(r.data);if(a.length){console.log(w.red(" \u2717 lint:"));for(let u of a)console.log(` ${u}`);process.exit(1)}let c=new Ue,d=s?Yc(e).match(/\.yml$/i)?"yml":"yaml":"json",g=to(c.baseDir,`${r.data.id}.${d}`);if(ct(g,s?n:JSON.stringify(r.data,null,2)+`
316
+ `),console.log(w.green(` \u2713 ${r.data.id} \u2192 ${g}`)),o.reload===!1){console.log(w.dim(" --no-reload: skipped daemon hot-reload; restart manually if running."));return}let l=`${String(o.daemon).replace(/\/$/,"")}/reload`;try{let u=await fetch(l,{method:"POST"});u.ok?console.log(w.dim(" \u2713 daemon reloaded")):console.log(w.dim(` daemon /reload returned ${u.status} \u2014 restart manually if needed`))}catch{console.log(w.dim(` daemon not reachable at ${o.daemon} \u2014 start it or restart to pick up the new workflow`))}});de.command("runs [id]").description("list recent runs (optionally filtered to a single workflow)").option("--limit <n>","max runs to show","20").option("--node <id>",'home-node id for this daemon (defaults to WF_NODE_ID env or "local")').action((e,o)=>{let t=o.node||process.env.WF_NODE_ID||"local",n=new Go({nodeId:t}).list({workflowId:e,limit:Number(o.limit)});if(!n.length){console.log(w.dim(` no runs yet${e?` for "${e}"`:""}.`));return}console.log();for(let s of n){let i=s.history.at(-1),r=s.pending[0]??(s.status==="completed"?"\u2205":"\u2014"),a=s.status==="running"?w.cyan:s.status==="failed"?w.red:w.green;console.log(` ${w.dim(s.id.slice(0,8))} ${w.bold(s.workflowId)} ${a(r)} ${w.dim(s.status)}`),console.log(` ${w.dim("entity: ")} ${s.entityRef.backend}:${s.entityRef.id}`),console.log(` ${w.dim("home: ")} ${s.homeNode}`),console.log(` ${w.dim("updated: ")} ${s.updatedAt}`),i&&console.log(` ${w.dim("last: ")} ${i.nodeId} (${i.status})`)}console.log(),console.log(w.dim(` ${n.length} run${n.length===1?"":"s"}.`))});de.command("draft-from-task <taskId>").description("generate a reviewable workflow draft from a successful task trace").option("--path <path>","SQLite db path",".agentx/db.sqlite").option("--format <fmt>","yaml (default) or json","yaml").option("--commit","write to .agentx/workflows/_drafts",!1).option("--print","print the generated draft",!1).option("--allow-failed","allow generating from non-ok traces",!1).option("--model <model>","direct Anthropic API path (e.g. claude-sonnet-4-6) \u2014 needs ANTHROPIC_API_KEY in daemon env").option("--via <agentId>","route the LLM call through an agent (e.g. coo-agent) \u2014 uses the agent's own session, no raw API key needed").option("--daemon <url>","daemon API base URL","http://127.0.0.1:18800").action(async(e,o)=>{let t=Ln(o.path),n=Io(t,e);n||(console.log(w.red(` task trace not found: ${e}`)),process.exit(1)),n.task.status!=="ok"&&!o.allowFailed&&(console.log(w.red(` task ${e} status=${n.task.status}; pass --allow-failed to draft anyway`)),process.exit(1));let s=await ws(n.task,n.steps,{model:o.model,viaAgent:o.via,daemonUrl:o.daemon,log:c=>console.log(w.dim(` ${c}`))}),i=s.workflow;s.usedLlm?console.log(w.dim(` \u2726 LLM-architected via ${s.via||o.model||o.via}`)):(o.model||o.via)&&console.log(w.yellow(" \u26A0 LLM architect failed, used deterministic fallback"));let r=wt(i);if(r.length){console.log(w.red(" generated draft failed validation:"));for(let c of r)console.log(` ${c}`);process.exit(1)}let a=String(o.format).toLowerCase()==="json"?"json":"yaml";if(o.commit){let c=tn(i,{format:a});console.log(w.green(` \u2713 draft written: ${c}`))}(o.print||!o.commit)&&Ki(i,a)});de.command("absorb").description("distill executable DAG drafts from traces (advanced \u2014 for user-facing pattern mining see `agentx procedure extract`)").option("--path <path>","SQLite db path",".agentx/db.sqlite").option("--since <duration>","trace window, e.g. 24h, 7d, or ms epoch","24h").option("--agent <id>","only absorb traces for one agent").option("--min-cluster-size <n>","minimum similar traces per draft","3").option("--min-message-length <n>","minimum messagePreview chars to consider a trace (default 30)","30").option("--max <n>","maximum drafts to generate","10").option("--dry-run","preview candidates without writing",!1).option("--commit","write generated drafts",!1).option("--model <model>","direct Anthropic API architect (e.g. claude-sonnet-4-6) \u2014 needs ANTHROPIC_API_KEY").option("--via <agentId>","route the LLM call through an agent (e.g. coo-agent) \u2014 uses the agent's own session").option("--daemon <url>","daemon API base URL for --via","http://127.0.0.1:18800").action(async e=>{let o=Ln(e.path),t=xs(o,{since:Xc(e.since),agentId:e.agent,limit:1e3,minMessageLength:Number(e.minMessageLength)||30});if(t.length===0){console.log(w.dim(" no successful free-form traces found in window"));return}let n=Ss(t,{minClusterSize:Number(e.minClusterSize)||3,max:Number(e.max)||10});if(n.length===0){console.log(w.dim(` ${t.length} trace(s), but no cluster met min size ${e.minClusterSize}`));return}let s=await js(o,n,{model:e.model,viaAgent:e.via,daemonUrl:e.daemon,log:i=>console.log(w.dim(` ${i}`))});for(let i of s){let r=i.usedLlm?w.magenta("\u2726 llm"):w.dim("det.");console.log(` ${w.cyan(i.id)} ${w.bold(`${i.sourceTaskIds.length} traces`)} confidence=${i.confidence.toFixed(2)} ${r}`),console.log(w.dim(` ${i.reason}`));let a=wt(i.workflow);if(a.length){console.log(w.red(` validation failed: ${a.join("; ")}`));continue}if(!$s(i.workflow)){console.log(w.yellow(` skipped: draft has no agent/action/branch step (${i.workflow.nodes.length} nodes \u2014 likely a notification trace)`));continue}if(e.commit&&!e.dryRun)try{let c=tn(i.workflow,{format:"yaml"});console.log(w.green(` written: ${c}`))}catch(c){console.log(w.yellow(` skipped: ${c.message}`))}}(!e.commit||e.dryRun)&&console.log(w.dim(" dry run only; pass --commit to write drafts"))});de.command("drafts").description("list workflow drafts pending review").option("--json","emit JSON",!1).action(e=>{let o=nn();if(e.json){console.log(JSON.stringify(o.map(t=>({id:t.id,path:t.path,workflow:t.workflow})),null,2));return}if(o.length===0){console.log(w.dim(" no workflow drafts"));return}for(let t of o){let n=t.workflow;console.log(` ${w.cyan(t.id)} ${w.bold(n.title)} ${w.dim(`status=${n.status} state=${n.state}`)}`),n.sourceTaskIds.length&&console.log(w.dim(` sources: ${n.sourceTaskIds.slice(0,5).join(", ")}${n.sourceTaskIds.length>5?"...":""}`)),n.confidence!=null&&console.log(w.dim(` confidence: ${n.confidence.toFixed(2)} path: ${t.path}`))}});de.command("promote <draftId>").description("promote a workflow draft into the active workflow store").option("--replace","replace an existing workflow file with the same id",!1).option("--format <fmt>","yaml (default) or json","yaml").option("--daemon <url>","daemon API base URL","http://127.0.0.1:18800").action(async(e,o)=>{try{let t=String(o.format).toLowerCase()==="json"?"json":"yaml",n=ks(e,{replace:!!o.replace,format:t});console.log(w.green(` \u2713 promoted ${e}`)),console.log(w.dim(` ${n.from} -> ${n.to}`));try{await fetch(`${String(o.daemon).replace(/\/$/,"")}/reload`,{method:"POST"})}catch{}}catch(t){console.log(w.red(` ${t.message}`)),process.exit(1)}});de.command("reject <draftId>").description("archive a workflow draft without activating it").action(e=>{try{let o=vs(e);console.log(w.green(` \u2713 rejected ${e}`)),console.log(w.dim(` archived: ${o}`))}catch(o){console.log(w.red(` ${o.message}`)),process.exit(1)}});de.command("replay-task <taskId>").description("replay a task through its generated workflow draft or an ad-hoc draft").option("--path <path>","SQLite db path",".agentx/db.sqlite").option("--workflow <id>","workflow or draft id to replay instead of auto-discovery").option("--input <json>","override workflow trigger input").option("--agent <id>","override agent id for generated draft agent nodes").option("--model <id>","override model id for generated draft agent nodes").option("--timeout <minutes>","override timeout hint in trigger input").option("--validate-only","validate the replay workflow without running it",!1).option("--dry-run","print the replay workflow and input without running",!1).option("--watch","tail workflow traces while the replay runs",!1).option("--daemon <url>","daemon API base URL","http://127.0.0.1:18800").action(async(e,o)=>{let t=Ln(o.path),n=Io(t,e);n||(console.log(w.red(` task trace not found: ${e}`)),process.exit(1));let s=o.workflow?bs(o.workflow)?.workflow:void 0;!s&&o.workflow&&(s=new Ue().get(o.workflow)||void 0),s||(s=nn().find(y=>y.workflow.sourceTaskIds.includes(e))?.workflow),s||(s=ys(n.task,n.steps,{id:`_adhoc-replay-${e.toLowerCase().slice(0,8)}`})),(o.agent||o.model)&&(s=ze.parse({...s,nodes:s.nodes.map(y=>y.type==="agent"?{...y,config:{...y.config,...o.agent?{agentId:String(o.agent)}:{},...o.model?{model:String(o.model)}:{}}}:y)}));let i=wt(s);if(i.length){console.log(w.red(" replay workflow failed validation:"));for(let y of i)console.log(` ${y}`);process.exit(1)}let r={input:{message:n.task.messagePreview||"",taskId:e,originalAgentId:n.task.agentId,timeoutMinutes:o.timeout?Number(o.timeout):void 0}};if(o.input)try{r=JSON.parse(o.input)}catch{console.log(w.red(" --input must be valid JSON")),process.exit(1)}if(o.validateOnly||o.dryRun){Ki(s,"yaml"),console.log(w.dim(" input:")),console.log(JSON.stringify(r,null,2));return}let a=new Ue;Kc(a.baseDir,{recursive:!0});let c=s.id.startsWith("_adhoc-")?s.id:`_adhoc-replay-${s.id}-${Date.now().toString(36)}`,d=ze.parse({...s,id:c,state:"active",status:"review"}),g=to(a.baseDir,`${c}.yaml`);ct(g,ht(d));let l=String(o.daemon).replace(/\/$/,"");try{await fetch(`${l}/reload`,{method:"POST"})}catch{}let u=await fetch(`${l}/workflows/${encodeURIComponent(c)}/run`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({payload:r,force:!0})});u.ok||(console.log(w.red(` replay failed (${u.status}): ${(await u.text()).slice(0,300)}`)),process.exit(1));let m=await u.json();console.log(w.green(` \u2713 replay started: ${m.runId||"(unknown run)"}`)),console.log(w.dim(` workflow: ${c}`));let f=Gi(n.steps);if(console.log(w.dim(` original: ${f.totalTokens||0}t ${f.durationMs||0}ms model=${f.models.join(",")||n.task.model||"\u2014"}`)),m.runId){let y=await Vi({daemonUrl:l,runId:m.runId,quiet:!o.watch});Zc(f,Gi(y))}});de.command("run <id-or-file>").description("manually trigger a workflow by id, or load + register + run a YAML/JSON file").option("--input <json>","JSON object merged into the trigger event payload").option("--force","fire even if the trigger isn't `trigger.manual` (uses a synthesized event)",!1).option("--watch","tail per-step traces while the run executes",!1).option("--daemon <url>","daemon API base URL","http://127.0.0.1:18800").action(async(e,o)=>{let t={};if(o.input)try{t=JSON.parse(o.input)}catch{console.log(w.red(" --input must be valid JSON")),process.exit(1)}let n=e,s=to(process.cwd(),e);if((e.includes("/")||/\.(ya?ml|json)$/i.test(e))&&Mt(s)){let c=Mn(s,"utf-8"),d=/\.(ya?ml)$/i.test(e),g;try{g=d?jo(c,{filePath:e}):JSON.parse(c)}catch(b){let S=b instanceof ft?b.message:`parse error: ${b?.message??b}`;console.log(w.red(` ${S}`)),process.exit(1)}let l=ze.safeParse(g);if(!l.success){console.log(w.red(" \u2717 schema:"));for(let b of l.error.issues)console.log(` ${b.path.join(".")||"<root>"}: ${b.message}`);process.exit(1)}let u=ut(l.data);if(u.length){console.log(w.red(" \u2717 lint:"));for(let b of u)console.log(` ${b}`);process.exit(1)}let m=new Ue,f=l.data.id.startsWith("_adhoc-")?l.data.id:`_adhoc-${l.data.id}-${Date.now().toString(36)}`,y=d?"yaml":"json",$=to(m.baseDir,`${f}.${y}`),v=d?c.replace(new RegExp(`^id:\\s*${l.data.id}\\b`,"m"),`id: ${f}`):JSON.stringify({...l.data,id:f},null,2)+`
317
+ `;ct($,v),n=f,console.log(w.dim(` ad-hoc registered as "${f}" \u2192 ${$}`));try{await fetch(`${String(o.daemon).replace(/\/$/,"")}/reload`,{method:"POST"})}catch{}}let r=`${String(o.daemon).replace(/\/$/,"")}/workflows/${encodeURIComponent(n)}/run`,a;try{let c=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({payload:t,force:!!o.force})});if(!c.ok){let g=await c.text();console.log(w.red(` run failed (${c.status}): ${g.slice(0,300)}`)),c.status===409&&console.log(w.dim(" hint: pass --force to fire non-manual workflows for testing")),process.exit(1)}let d=await c.json();a=d.runId,console.log(w.green(` \u2713 run started: ${a||"(unknown id)"}${d.force?w.dim(` (forced, source=${d.source})`):""}`))}catch(c){console.log(w.red(` request to daemon failed: ${c.message}`)),process.exit(1)}o.watch&&a&&await Vi({daemonUrl:String(o.daemon).replace(/\/$/,""),runId:a})});async function Vi(e){let o=new Set,t=new Map,n=Date.now(),s=15*60*1e3;for(;Date.now()-n<s;){let i=[];try{let r=await fetch(`${e.daemonUrl}/traces?workflowRunId=${encodeURIComponent(e.runId)}&limit=200`);r.ok&&(i=(await r.json()).traces||[])}catch{}for(let r of i){if(!r.taskId||o.has(r.taskId)||(o.add(r.taskId),t.set(r.taskId,r),e.quiet))continue;let a=r.status||"running",c=a==="ok"?w.green:a==="error"?w.red:w.cyan,d=r.totalTokens!=null?w.dim(`${r.totalTokens}t`):"",g=r.durationMs!=null?w.dim(`${r.durationMs}ms`):"";console.log(` ${c(a.padEnd(5))} ${w.bold(r.nodeId||r.label||"(node)")} ${d} ${g}`),r.error&&console.log(` ${w.red(String(r.error).slice(0,200))}`)}try{let r=await fetch(`${e.daemonUrl}/api/workflows/runs/${encodeURIComponent(e.runId)}`);if(r.ok){let c=(await r.json()).run;if(c&&c.status&&c.status!=="running")return e.quiet||(console.log(),console.log(` ${w.dim("run finished:")} ${c.status}`),console.log(w.dim(` full traces: ${e.daemonUrl}/traces?workflowRunId=${e.runId}`))),Array.from(t.values())}}catch{}await new Promise(r=>setTimeout(r,500))}return e.quiet||console.log(w.yellow(" watch timed out after 15min. Run is still active \u2014 check /traces")),Array.from(t.values())}de.command("trace <id>").description("pretty-print a task's execution trace (taskId or runId)").option("--daemon <url>","daemon API base URL","http://127.0.0.1:18800").option("--json","raw JSON output",!1).action(async(e,o)=>{let t=String(o.daemon).replace(/\/$/,"");try{let i=await fetch(`${t}/traces/${encodeURIComponent(e)}`);if(i.ok){let r=await i.json();if(o.json){console.log(JSON.stringify(r,null,2));return}Yi(r);return}}catch{}let n;try{let i=await fetch(`${t}/traces?workflowRunId=${encodeURIComponent(e)}&limit=200`);i.ok||(console.log(w.red(` /traces returned ${i.status}`)),process.exit(1)),n=await i.json()}catch(i){console.log(w.red(` daemon request failed: ${i.message}`)),process.exit(1)}let s=n.traces||[];if(s.length===0){console.log(w.yellow(` no traces for "${e}"`)),console.log(w.dim(" (sqlite must be opened \u2014 check 'agentx doctor' if the daemon logged 'sqlite: not opened')"));return}if(o.json){console.log(JSON.stringify(s,null,2));return}console.log(),console.log(w.bold(` ${s.length} step${s.length===1?"":"s"} for run ${e}:`)),console.log();for(let i of s)Yi(i)});function Yi(e){let o=e.status||"ok",t=o==="ok"?w.green:o==="error"?w.red:w.cyan,n=e.totalTokens!=null?`${e.totalTokens}t`:"\u2014",s=e.durationMs!=null?`${e.durationMs}ms`:"\u2014",i=e.model||"\u2014";console.log(` ${t(o.padEnd(5))} ${w.bold(String(e.nodeId||e.label||"(node)"))}`),console.log(` ${w.dim("taskId:")} ${e.taskId||"\u2014"}`),console.log(` ${w.dim("model:")} ${i}`),console.log(` ${w.dim("tokens:")} ${n} ${w.dim("duration:")} ${s}`),e.error&&console.log(` ${w.dim("error:")} ${w.red(String(e.error).slice(0,300))}`),console.log()}de.command("pause <runId>").description("pause an active run").option("--node <id>","home-node id").action((e,o)=>{let t=o.node||process.env.WF_NODE_ID||"local";new Go({nodeId:t}).setStatus(e,"paused")||(console.log(w.yellow(` no such run: ${e}`)),process.exit(1)),console.log(w.green(` \u2713 paused ${e}`))});de.command("resume <runId>").description("resume a paused run").option("--node <id>","home-node id").action((e,o)=>{let t=o.node||process.env.WF_NODE_ID||"local";new Go({nodeId:t}).setStatus(e,"running")||(console.log(w.yellow(` no such run: ${e}`)),process.exit(1)),console.log(w.green(` \u2713 resumed ${e}`))});de.command("cancel <runId>").description("cancel an active run").option("--node <id>","home-node id").action((e,o)=>{let t=o.node||process.env.WF_NODE_ID||"local";new Go({nodeId:t}).setStatus(e,"canceled")||(console.log(w.yellow(` no such run: ${e}`)),process.exit(1)),console.log(w.green(` \u2713 canceled ${e}`))});import{Command as Qi}from"commander";import G from"chalk";import vo from"prompts";import{readFileSync as el,existsSync as ol}from"fs";import{resolve as tl}from"path";var lt=["gitlab","github","sentry","stripe","vercel","odoo","hubspot","discord","slack","custom"],Un=new Set(["gitlab","github","sentry","stripe","vercel","odoo","hubspot"]),Xi={gitlab:"GitLab \u2192 Settings \u2192 Webhooks (tick Push/Issue/Pipeline events)",github:"GitHub \u2192 Repo Settings \u2192 Webhooks",sentry:"Sentry \u2192 Project Settings \u2192 Alerts \u2192 Webhooks",stripe:"Stripe Dashboard \u2192 Developers \u2192 Webhooks",vercel:"Vercel \u2192 Project Settings \u2192 Webhooks",odoo:"Odoo \u2192 Settings \u2192 Technical \u2192 Automation \u2192 Webhooks (or your installed `webhook` module)",hubspot:"HubSpot \u2192 Settings \u2192 Integrations \u2192 Private Apps \u2192 Webhooks subscription",discord:"Discord channel \u2192 Integrations \u2192 Webhooks (inbound-only, no on:* events)",slack:"Slack Outgoing Webhooks / Events API (inbound-only, no on:* events)",custom:"Any service that POSTs JSON \u2014 Linear, Calendly, Notion, etc. (inbound-only)"};function Zi(){let e=tl(process.cwd(),"agentx.json");if(!ol(e))throw new Error("No agentx.json found. Run: agentx init");return JSON.parse(el(e,"utf-8"))}function nl(e){return e.enabled?e.source!=="custom"&&!e.secretEnv?G.yellow("\u26A0 no signing secret"):G.green("\u2713 active"):G.dim("disabled")}function sl(e,o){let t=o?`${o.replace(/\/+$/,"")}/webhook/${encodeURIComponent(e.agentId)}/${encodeURIComponent(e.source)}`:`/webhook/${encodeURIComponent(e.agentId)}/${encodeURIComponent(e.source)}`,n=Un.has(e.source)?G.dim(" \xB7 emits on:"+e.source+"-* hook events"):"";console.log(` ${G.cyan(e.id)} ${nl(e)}`),console.log(` ${G.dim(e.source)} \u2192 ${G.bold(e.agentId)}${n}`),console.log(` POST ${G.dim(t)}`),e.secretEnv&&console.log(` secret: ${G.dim("$"+e.secretEnv)}`),e.description&&console.log(` ${G.dim(e.description)}`);let s=e.triggers&&typeof e.triggers=="object"?e.triggers:{},i=Object.keys(s);if(i.length){console.log(` ${G.dim("triggers:")}`);for(let r of i)console.log(` ${r} \u2192 ${s[r]}`)}e.defaultWorkflow&&console.log(` ${G.dim("default workflow:")} ${e.defaultWorkflow}`),console.log()}var Be=new Qi().name("webhook").description("manage webhook entries (gitlab, github, sentry, stripe, vercel, custom)");Be.command("list").alias("ls").description("list webhook entries").option("--json","emit JSON").action(e=>{let o=Zi(),t=Array.isArray(o.webhooks)?o.webhooks:[];if(e.json){console.log(JSON.stringify(t,null,2));return}if(!t.length){console.log(G.dim(`
318
+ No webhooks registered. Add one with: agentx webhook add
319
+ `));return}console.log();for(let n of t)sl(n)});Be.command("add [id]").description("register a new webhook entry").option("--source <source>",`one of: ${lt.join(", ")}`).option("--agent <agentId>","agent that receives the webhook").option("--secret-env <name>","env var holding the signing secret (recommended)").option("--description <text>","free-text description").option("--node <peer>","forward to a mesh peer (skips local agent check; validates against mesh.peers)").option("--no-prompt","fail instead of prompting for missing values").action(async(e,o)=>{let t=Zi(),n=Object.keys(t.agents||{}),s=(t.mesh?.peers||[]).map(m=>m.name).filter(Boolean),i=String(o.node||"").trim();if(!n.length&&!i)throw new Error("No local agents configured. Add one with: agentx agent add \u2014 or use --node <peer> to forward to a mesh peer.");let r=(e||"").trim(),a=String(o.source||"").trim(),c=String(o.agent||"").trim(),d=String(o.secretEnv||"").trim(),g=String(o.description||"").trim(),l=o.prompt!==!1;if(!r&&l&&(r=((await vo({type:"text",name:"id",message:"Webhook id (lowercase, e.g. mtgl-gitlab)",validate:f=>/^[a-z0-9][a-z0-9_-]*$/.test(f)||"Use lowercase letters, digits, -, _"})).id||"").trim()),!r)throw new Error("Webhook id is required.");if(!/^[a-z0-9][a-z0-9_-]*$/.test(r))throw new Error("Webhook id must be lowercase (letters, digits, -, _).");if(!a&&l&&(a=(await vo({type:"select",name:"source",message:"Source",choices:lt.map(f=>({title:`${f}${Un.has(f)?" \u2713 wired":""}`,value:f,description:Xi[f]}))})).source),!lt.includes(a))throw new Error(`Source must be one of: ${lt.join(", ")}`);if(!c&&l&&(i?c=((await vo({type:"text",name:"agentId",message:`Agent on remote peer "${i}"`})).agentId||"").trim():c=(await vo({type:"select",name:"agentId",message:"Agent",choices:n.map(f=>({title:f,value:f}))})).agentId),!c)throw new Error("Agent is required.");if(i){if(!s.includes(i))throw new Error(`Mesh peer "${i}" not found in mesh.peers. Add the peer first or omit --node.`)}else if(!t.agents?.[c])throw new Error(`Unknown agent "${c}". Add it locally, or use --node <peer> to forward to a mesh peer.`);!d&&l&&a!=="custom"&&(d=((await vo({type:"text",name:"secretEnv",message:`Signing secret env-var (recommended for ${a})`,initial:`${a.toUpperCase()}_WEBHOOK_SECRET`})).secretEnv||"").trim()),!g&&l&&(g=((await vo({type:"text",name:"description",message:"Description (optional)"})).description||"").trim());let u=await K(m=>{if(m.webhooks=Array.isArray(m.webhooks)?m.webhooks:[],m.webhooks.find(y=>y.id===r))throw new Error(`Webhook "${r}" already exists.`);let f={id:r,source:a,agentId:c,enabled:!0};d&&(f.secretEnv=d),g&&(f.description=g),i&&(f.node=i),m.webhooks.push(f)});if(!u.success)throw new Error(u.error);console.log(G.green(`\u2713 added webhook "${r}" (${a} \u2192 ${c}${i?` @ ${i}`:""})`)),u.reloaded&&console.log(G.dim(" daemon hot-reloaded")),!d&&a!=="custom"&&console.log(G.yellow(" \u26A0 no signing secret set \u2014 payloads will be accepted unsigned. Add one with --secret-env"))});Be.command("remove <id>").alias("rm").description("delete a webhook entry").option("--yes","skip confirmation").action(async(e,o)=>{if(!o.yes&&!(await vo({type:"confirm",name:"ok",message:`Delete webhook "${e}"?`,initial:!1})).ok)return;let t=await K(n=>{let s=(n.webhooks||[]).length;if(n.webhooks=(n.webhooks||[]).filter(i=>i.id!==e),n.webhooks.length===s)throw new Error(`Webhook "${e}" not found.`)});if(!t.success)throw new Error(t.error);console.log(G.green(`\u2713 removed webhook "${e}"`))});function er(e){return async o=>{let t=await K(n=>{let s=(n.webhooks||[]).find(i=>i.id===o);if(!s)throw new Error(`Webhook "${o}" not found.`);s.enabled=e});if(!t.success)throw new Error(t.error);console.log(G.green(`\u2713 ${e?"enabled":"disabled"} webhook "${o}"`))}}Be.command("enable <id>").description("enable a webhook entry").action(er(!0));Be.command("disable <id>").description("disable a webhook entry").action(er(!1));var Lt=new Qi().name("triggers").description("manage event-type \u2192 workflow mappings on a webhook");Lt.command("set <id> <eventType> <workflowId>").description("map an event-type to a workflow id").action(async(e,o,t)=>{let n=await K(s=>{let i=(s.webhooks||[]).find(r=>r.id===e);if(!i)throw new Error(`Webhook "${e}" not found.`);i.triggers=i.triggers&&typeof i.triggers=="object"?i.triggers:{},i.triggers[o]=t});if(!n.success)throw new Error(n.error);console.log(G.green(`\u2713 ${e}: ${o} \u2192 ${t}`))});Lt.command("remove <id> <eventType>").alias("rm").description("remove an event-type \u2192 workflow mapping").action(async(e,o)=>{let t=await K(n=>{let s=(n.webhooks||[]).find(i=>i.id===e);if(!s)throw new Error(`Webhook "${e}" not found.`);s.triggers&&typeof s.triggers=="object"&&delete s.triggers[o]});if(!t.success)throw new Error(t.error);console.log(G.green(`\u2713 removed trigger ${o} from "${e}"`))});Lt.command("default <id> <workflowId>").description("set the default workflow when no event-type matches (use '-' to clear)").action(async(e,o)=>{let t=o==="-"||o==="",n=await K(s=>{let i=(s.webhooks||[]).find(r=>r.id===e);if(!i)throw new Error(`Webhook "${e}" not found.`);t?delete i.defaultWorkflow:i.defaultWorkflow=o});if(!n.success)throw new Error(n.error);console.log(G.green(`\u2713 ${e}: defaultWorkflow ${t?"cleared":`\u2192 ${o}`}`))});Be.addCommand(Lt);Be.command("sources").description("list known webhook source types").option("--json","emit JSON").action(e=>{let o=lt.map(t=>({source:t,wired:Un.has(t),hint:Xi[t]}));if(e.json){console.log(JSON.stringify(o,null,2));return}console.log();for(let t of o){let n=t.wired?G.green("\u2713 wired"):G.dim("inbound-only");console.log(` ${G.cyan(t.source.padEnd(8))} ${n} ${G.dim(t.hint)}`)}console.log(),console.log(G.dim(" Wired sources emit on:<source>-* hook events that workflows can subscribe to via trigger.hook.")),console.log(G.dim(" Use 'custom' for any service not in the list (Odoo, HubSpot, Linear, Calendly, etc.).")),console.log()});import{Command as il}from"commander";import Y from"chalk";function Lo(e){let{summary:o,backupPath:t}=Ao(n=>e(n));console.log(Y.green(`
320
+ \u2713 ${o}`)),t&&console.log(Y.dim(` Backup: ${t}`)),console.log(Y.dim(` Restart the daemon (or POST /reload) for the change to take effect.
321
+ `))}function Ut(){try{return L().business||{}}catch{return{}}}var Uo=new il().name("business").description("manage the business layer in agentx.json \u2014 orgChart, projects, contactMap");Uo.command("show").description("print the current business config (orgChart, projects, contactMap)").option("--json","machine-readable JSON").action(e=>{let o=Ut();if(e.json){console.log(JSON.stringify(o,null,2));return}if(!o||!o.enabled){console.log(),console.log(Y.yellow(" business block not enabled (or missing).")),console.log(Y.dim(" Add a `business` key to agentx.json to opt in.")),console.log();return}let t=o.orgChart||{},n=o.projects||[],s=o.contactMap||[];console.log(),console.log(Y.bold(" Org chart"));let i=Object.entries(t);if(i.length===0)console.log(Y.dim(" (empty)"));else for(let[r,a]of i){let c=a.reportsTo?Y.dim(` \u2192 reports to ${a.reportsTo}`):"",d=a.schedule?Y.dim(` \xB7 ${(a.schedule.days||[]).join(",")} ${a.schedule.start}\u2013${a.schedule.end}`):"";console.log(` ${Y.cyan(r)} ${a.role}${c}${d}`)}if(console.log(),console.log(Y.bold(` Projects (${n.length})`)),n.length===0)console.log(Y.dim(" (empty)"));else for(let r of n){let a=r.pm?Y.dim(` \xB7 pm=${r.pm}`):"",c=r.client?Y.dim(` \xB7 client=${r.client}`):"";console.log(` ${Y.cyan(r.id)}${a}${c}`)}if(console.log(),console.log(Y.bold(` Contact map (${s.length})`)),s.length===0)console.log(Y.dim(" (empty)"));else for(let r of s){let a=r.chatId?`chatId=${r.chatId}`:r.username?`username=${r.username}`:r.senderId?`senderId=${r.senderId}`:"?",c=r.channel?`${r.channel}/`:"";console.log(` ${Y.cyan(c+a)} \u2192 ${r.client}${r.project?`/${r.project}`:""}${r.displayName?Y.dim(` (${r.displayName})`):""}`)}console.log()});var Wn=Uo.command("orgchart").description("manage business.orgChart \u2014 agentId \u2192 { role, reportsTo, schedule }");Wn.command("list").description("list orgChart entries").option("--json","JSON output").action(e=>{let o=Ut(),t=Object.entries(o.orgChart||{});if(e.json){console.log(JSON.stringify(Object.fromEntries(t),null,2));return}if(t.length===0){console.log(Y.dim(" no entries"));return}for(let[n,s]of t)console.log(` ${Y.cyan(n)} ${s.role}${s.reportsTo?Y.dim(` \u2192 ${s.reportsTo}`):""}`)});Wn.command("add <agentId>").description("add or update an orgChart entry for an agent").requiredOption("--role <role>","role title (e.g. 'PM', 'Coder', 'DevOps')").option("--reports-to <agentId>","manager's agentId").option("--start <hh:mm>","schedule start","09:00").option("--end <hh:mm>","schedule end","17:00").option("--days <csv>","working days (mon,tue,wed,thu,fri)","mon,tue,wed,thu,fri").option("--utilization <0..1>","target utilization","0.8").action((e,o)=>{let t=String(o.days).split(",").map(n=>n.trim()).filter(Boolean);Lo(n=>(n.business=n.business||{},n.business.orgChart=n.business.orgChart||{},n.business.orgChart[e]={role:o.role,...o.reportsTo?{reportsTo:o.reportsTo}:{},schedule:{days:t,start:o.start,end:o.end},utilizationTarget:parseFloat(o.utilization)||.8},`orgChart entry "${e}" upserted (role=${o.role}${o.reportsTo?`, reports to ${o.reportsTo}`:""})`))});Wn.command("remove <agentId>").alias("rm").description("remove an orgChart entry").action(e=>{Lo(o=>{if(!o.business?.orgChart||!(e in o.business.orgChart))throw new Error(`no orgChart entry for "${e}"`);return delete o.business.orgChart[e],`orgChart entry "${e}" removed`})});var Jn=Uo.command("project").description("manage business.projects \u2014 id, pm, client");Jn.command("list").description("list projects").option("--json","JSON output").action(e=>{let t=Ut().projects||[];if(e.json){console.log(JSON.stringify(t,null,2));return}if(t.length===0){console.log(Y.dim(" no projects"));return}for(let n of t)console.log(` ${Y.cyan(n.id)} ${n.pm?`pm=${n.pm}`:""} ${n.client?`client=${n.client}`:""}`.trimEnd())});Jn.command("add <id>").description("add or update a project entry").option("--pm <agentId>","PM responsible for this project (drives PM gate)").option("--client <name>","client this project belongs to (drives activity-graph attribution)").action((e,o)=>{Lo(t=>{t.business=t.business||{},t.business.projects=t.business.projects||[];let n=t.business.projects.findIndex(i=>i.id===e),s={id:e,...o.pm?{pm:o.pm}:{},...o.client?{client:o.client}:{}};return n>=0?t.business.projects[n]={...t.business.projects[n],...s}:t.business.projects.push(s),n>=0?`project "${e}" updated`:`project "${e}" added`})});Jn.command("remove <id>").alias("rm").description("remove a project entry").action(e=>{Lo(o=>{let t=o.business?.projects||[],n=t.length;if(o.business.projects=t.filter(s=>s.id!==e),o.business.projects.length===n)throw new Error(`no project "${e}"`);return`project "${e}" removed`})});var Bn=Uo.command("contact").description("manage business.contactMap \u2014 chatId/username/senderId \u2192 client/project");Bn.command("list").description("list contact map entries").option("--json","JSON output").action(e=>{let t=Ut().contactMap||[];if(e.json){console.log(JSON.stringify(t,null,2));return}if(t.length===0){console.log(Y.dim(" no entries"));return}for(let n of t){let s=n.chatId?`chatId=${n.chatId}`:n.username?`username=${n.username}`:n.senderId?`senderId=${n.senderId}`:"?";console.log(` ${n.channel?n.channel+"/":""}${Y.cyan(s)} \u2192 ${n.client}${n.project?"/"+n.project:""}`)}});Bn.command("add").description("add a contactMap entry").requiredOption("--client <name>","client this contact belongs to").option("--channel <name>","telegram | whatsapp | slack | discord").option("--chat-id <id>","native chat id (e.g. -100\u2026, JID)").option("--username <handle>","sender username/handle").option("--sender-id <id>","numeric sender id (when username is unstable)").option("--project <id>","project this traffic should attribute to").option("--display-name <name>","display-name override for the initiator pill").action(e=>{!e.chatId&&!e.username&&!e.senderId&&(console.log(Y.red(" one of --chat-id / --username / --sender-id is required")),process.exit(1)),Lo(o=>{o.business=o.business||{},o.business.contactMap=o.business.contactMap||[];let t={client:e.client};e.channel&&(t.channel=e.channel),e.chatId&&(t.chatId=e.chatId),e.username&&(t.username=e.username),e.senderId&&(t.senderId=e.senderId),e.project&&(t.project=e.project),e.displayName&&(t.displayName=e.displayName),o.business.contactMap.push(t);let n=e.chatId||e.username||e.senderId;return`contact ${e.channel?e.channel+"/":""}${n} \u2192 ${e.client}${e.project?"/"+e.project:""} added`})});Bn.command("remove").alias("rm").description("remove a contactMap entry by matching field(s)").option("--channel <name>").option("--chat-id <id>").option("--username <handle>").option("--sender-id <id>").action(e=>{Lo(o=>{let t=o.business?.contactMap||[],n=t.length;if(o.business.contactMap=t.filter(s=>!!(e.channel&&s.channel!==e.channel||e.chatId&&s.chatId!==e.chatId||e.username&&s.username!==e.username||e.senderId&&s.senderId!==e.senderId)),o.business.contactMap.length===n)throw new Error("no matching contact entry");return`${n-o.business.contactMap.length} contact entry/entries removed`})});import{Command as rl}from"commander";import $e from"chalk";var al={today:"day",day:"day",week:"week","this-week":"week",month:"month","this-month":"month"};function Fn(e){let o=al[e.toLowerCase()];if(!o)throw new Error(`Unknown plan tier "${e}". Use: today | week | month.`);return o}function Wt(){let o=L().business?.standup?.plansDir||".agentx/plans";return new Ws(o)}var Wo=new rl().name("plan").description("set / read / list day-week-month plans that drive the standup-tick");Wo.command("set <tier>").description("write a plan for today | week | month (priorities-as-bullets)").option("-p, --priority <text...>","bullet line \u2014 repeat for multiple priorities").option("-m, --markdown <md>","raw markdown body (overrides --priority)").option("-f, --file <path>","read markdown from a file").action(async(e,o)=>{let t=Fn(e),n=Wt(),s;if(o.file){let{readFileSync:a}=await import("fs");s=a(o.file,"utf-8")}else o.markdown?s=o.markdown:o.priority?.length?s=o.priority.map(a=>`- ${a}`).join(`
322
+ `):(console.error($e.red(" Provide --priority, --markdown, or --file.")),process.exit(1));let i=n.write(t,s);console.log($e.green(` \u2713 ${t} plan saved \u2192 ${i.path}`));let r=an(s);if(r.length){console.log($e.dim(` ${r.length} bullet${r.length===1?"":"s"}:`));for(let a of r)console.log($e.dim(` \u2022 ${a}`))}});Wo.command("show [tier]").description("print the plan that the next standup will use (default: resolve day\u2192week\u2192month)").action(async e=>{let o=Wt();if(e){let n=Fn(e),s=o.read(n);if(!s){console.log($e.dim(` No ${n} plan set.`));return}or(s.tier,s.date,s.content);return}let t=o.resolve();if(!t){console.log($e.yellow(" No plan set at any tier.")),console.log($e.dim(" The next standup-tick will post a 'no plan today' notice and dispatch no agent.")),console.log($e.dim(' Set one with: agentx plan set today --priority "\u2026"'));return}or(t.tier,t.date,t.content)});Wo.command("list").description("recent plans across all tiers").option("-n, --limit <n>","max rows","20").action(async e=>{let t=Wt().list(parseInt(e.limit,10)||20);if(!t.length){console.log($e.dim(" No plans yet."));return}for(let n of t)console.log(` ${$e.cyan(n.tier.padEnd(5))} ${$e.bold(n.date.padEnd(11))} ${$e.dim(n.path)}`)});Wo.command("clear <tier>").description("remove the plan for today | week | month").action(async e=>{let o=Fn(e),n=Wt().read(o);if(!n){console.log($e.dim(` No ${o} plan to clear.`));return}let{unlinkSync:s}=await import("fs");s(n.path),console.log($e.green(` \u2713 ${o} plan cleared (${n.path})`))});function or(e,o,t){console.log($e.cyan(` ${e} plan \u2014 ${o}`)),console.log();for(let s of t.split(`
323
+ `))console.log(` ${s}`);console.log();let n=an(t);n.length&&console.log($e.dim(` ${n.length} bullet${n.length===1?"":"s"} extracted as priorities.`))}import{Command as cl}from"commander";import je from"chalk";function ll(){try{return L().notifications||{}}catch{return{}}}function Jt(e){let{summary:o,backupPath:t}=Ao(n=>(n.notifications=n.notifications||{},e(n.notifications)));console.log(je.green(`
324
+ \u2713 ${o}`)),t&&console.log(je.dim(` Backup: ${t}`)),console.log(je.dim(` Restart the daemon (or POST /reload) for the change to take effect.
325
+ `))}var Jo=new cl().name("notifications").description("manage notifications routing \u2014 destination, event toggles, long-task threshold");Jo.command("show").description("print current notifications config").option("--json","JSON output").action(e=>{let o=ll();if(e.json){console.log(JSON.stringify(o,null,2));return}if(console.log(),o.destination){let n=o.destination.accountId?` \xB7 account=${o.destination.accountId}`:"";console.log(` destination ${je.cyan(o.destination.channel+":"+o.destination.chatId)}${je.dim(n)}`)}else console.log(` destination ${je.dim("(unset \u2014 notifications go to the daemon log only)")}`);console.log(` longTaskThreshold ${o.longTaskThreshold??30}s ${je.dim("(0 disables long-task pings)")}`);let t=o.on||{};console.log(` on.taskComplete ${t.taskComplete===!1?je.dim("off"):"on"}`),console.log(` on.taskError ${t.taskError===!1?je.dim("off"):"on"}`),console.log(` on.taskQueued ${t.taskQueued?"on":je.dim("off")}`),console.log()});Jo.command("route").description("set the destination channel/chatId for notifications").requiredOption("--channel <name>","telegram | whatsapp | slack | discord").requiredOption("--chat-id <id>","native chat id (e.g. -100\u2026, JID, channel id)").option("--account-id <id>","channel account id when the channel is multi-account (telegram with multiple bots)").option("--clear","clear the destination instead of setting it").action(e=>{if(e.clear){Jt(o=>(delete o.destination,"notifications destination cleared"));return}Jt(o=>(o.destination={channel:e.channel,chatId:e.chatId,...e.accountId?{accountId:e.accountId}:{}},`notifications destination \u2192 ${e.channel}:${e.chatId}${e.accountId?` (account=${e.accountId})`:""}`))});Jo.command("event <name> <state>").description("toggle an event: name in {taskComplete, taskError, taskQueued}; state in {on, off}").action((e,o)=>{let t=["taskComplete","taskError","taskQueued"];t.includes(e)||(console.log(je.red(` unknown event "${e}". Pick from: ${t.join(", ")}`)),process.exit(1));let n=o.toLowerCase();["on","off","true","false","1","0"].includes(n)||(console.log(je.red(" state must be on|off")),process.exit(1));let s=n==="on"||n==="true"||n==="1";Jt(i=>(i.on=i.on||{},i.on[e]=s,`notifications.on.${e} = ${s}`))});Jo.command("threshold <seconds>").description("set the long-task threshold in seconds (0 disables long-task pings)").action(e=>{let o=parseInt(e,10);(!Number.isFinite(o)||o<0)&&(console.log(je.red(" threshold must be a non-negative integer")),process.exit(1)),Jt(t=>(t.longTaskThreshold=o,`longTaskThreshold = ${o}s`))});import{Command as dl}from"commander";import no from"chalk";import{readdirSync as qn,statSync as tr,unlinkSync as gl,rmSync as ml,existsSync as nr}from"fs";import{resolve as sr,join as ir}from"path";var rr=[{dir:".agentx/sessions",label:"Per-day session JSON"},{dir:".agentx/drift",label:"Drift snapshots",recursive:!0},{dir:".agentx/router",label:"Router-decision logs",recursive:!0},{dir:".agentx/patterns",label:"Self-improving pattern store",recursive:!0}];function zn(e,o){if(!nr(e))return[];let t=[];for(let n of qn(e,{withFileTypes:!0})){let s=ir(e,n.name);n.isFile()?t.push(s):n.isDirectory()&&o&&t.push(...zn(s,!0))}return t}var Ft=new dl().name("retention").description("prune old workspace state (sessions, drift, router traces, pattern store)");Ft.command("show",{isDefault:!0}).description("show how much each retention target currently weighs").action(()=>{console.log();let e=0;for(let o of rr){let t=sr(process.cwd(),o.dir),n=zn(t,!!o.recursive),s=0,i=null;for(let a of n)try{let c=tr(a);s+=c.size,(i===null||c.mtimeMs<i)&&(i=c.mtimeMs)}catch{}e+=s;let r=i!==null?`${Math.round((Date.now()-i)/864e5)}d`:"\u2014";console.log(` ${no.cyan(o.dir.padEnd(22))} ${String(n.length).padStart(5)} files ${Bt(s).padStart(10)} oldest=${r}`)}console.log(no.dim(` ${"".padEnd(22)} ${"".padStart(5)} ${Bt(e).padStart(10)} total`)),console.log(),console.log(no.dim(" Run `agentx retention prune --days 30` to delete files older than 30 days.")),console.log()});Ft.command("prune").description("delete files older than --days from each retention target").option("--days <n>","max age in days; everything older is deleted","30").option("--dry-run","list candidates, don't delete").option("--target <name>","limit to one target dir (e.g. .agentx/sessions)").action(e=>{let o=parseInt(e.days,10);(!Number.isFinite(o)||o<1)&&(console.log(no.red(" --days must be a positive integer")),process.exit(1));let t=Date.now()-o*864e5,n=e.target,s=0,i=0;console.log(),console.log(no.bold(` Pruning files older than ${o}d (${new Date(t).toISOString().slice(0,10)})${e.dryRun?no.yellow(" \u2014 DRY RUN"):""}`)),console.log();for(let r of rr){if(n&&r.dir!==n)continue;let a=sr(process.cwd(),r.dir),c=zn(a,!!r.recursive),d=0,g=0;for(let u of c)try{let m=tr(u);if(m.mtimeMs<t&&(g+=m.size,d++,!e.dryRun))try{gl(u)}catch{}}catch{}if(r.recursive&&!e.dryRun)try{ar(a)}catch{}let l=e.dryRun?"would remove":"removed";console.log(` ${no.cyan(r.dir.padEnd(22))} ${l} ${String(d).padStart(5)} files (${Bt(g)})`),s+=d,i+=g}console.log(),console.log(no.green(` \u2713 ${e.dryRun?"(dry-run)":""} ${s} files, ${Bt(i)} ${e.dryRun?"would be reclaimed":"reclaimed"}`)),console.log()});function ar(e){if(nr(e))for(let o of qn(e,{withFileTypes:!0})){if(!o.isDirectory())continue;let t=ir(e,o.name);ar(t);try{qn(t).length===0&&ml(t,{recursive:!1})}catch{}}}function Bt(e){return e<1024?e+"B":e<1024*1024?(e/1024).toFixed(1)+"K":e<1024*1024*1024?(e/1024/1024).toFixed(1)+"M":(e/1024/1024/1024).toFixed(2)+"G"}import{Command as pl}from"commander";import R from"chalk";var so=new pl().name("actions").description("manage the action registry \u2014 reusable shell/http invocations");so.command("list").description("list registered actions").option("--json","JSON output").action(e=>{let t=new Eo().list();if(e.json){console.log(JSON.stringify(t,null,2));return}if(t.length===0){console.log(),console.log(R.dim(" No actions yet. Add one with `agentx actions add` or `agentx actions create`.")),console.log();return}console.log();for(let n of t){let s=n.inputs?.length?R.dim(` (${n.inputs.length} input${n.inputs.length===1?"":"s"})`):"",i=n.kind==="shell"?`shell: ${n.command.slice(0,50)}`:`${n.method} ${n.url.slice(0,50)}`;console.log(` ${R.cyan(n.id.padEnd(24))} ${n.title}${s}`),console.log(` ${" ".repeat(24)} ${R.dim(i)}`)}console.log()});so.command("show <id>").description("show an action's full definition").option("--json","JSON output").action((e,o)=>{let n=new Eo().get(e);if(n||(console.log(R.red(` no action "${e}"`)),process.exit(1)),o.json){console.log(JSON.stringify(n,null,2));return}if(console.log(),console.log(R.bold(` ${n.title}`)),n.description&&console.log(R.dim(` ${n.description}`)),console.log(),console.log(` id ${n.id}`),console.log(` kind ${n.kind}`),console.log(` timeout ${n.timeoutMs}ms`),n.kind==="shell"?(console.log(` command ${n.command}`),n.cwd&&console.log(` cwd ${n.cwd}`),n.env&&console.log(` env ${JSON.stringify(n.env)}`)):(console.log(` ${n.method.padEnd(9)} ${n.url}`),n.headers&&console.log(` headers ${JSON.stringify(n.headers)}`),n.body&&console.log(` body ${n.body.slice(0,200)}`)),n.inputs.length){console.log(),console.log(R.bold(" Inputs:"));for(let s of n.inputs){let i=s.required?R.red("*"):" ",r=s.defaultValue!==void 0?R.dim(` default=${JSON.stringify(s.defaultValue)}`):"",a=s.description?R.dim(` \u2014 ${s.description}`):"";console.log(` ${i} ${R.cyan(s.name)} (${s.type})${r}${a}`)}}console.log()});so.command("add <id>").description("add (or replace) an action non-interactively").requiredOption("--title <text>").requiredOption("--kind <kind>","shell | http").option("--description <text>").option("--command <text>","[shell] templated shell command").option("--cwd <path>","[shell] working directory").option("--url <url>","[http] URL").option("--method <m>","[http] GET|POST|PUT|PATCH|DELETE","POST").option("--headers <json>","[http] headers as JSON object").option("--body <text>","[http] body template").option("--inputs <csv>","comma-separated list of name:type[!] (e.g. amount:number!,note:string)").option("--timeout <ms>","timeout in ms (default 30000)","30000").action((e,o)=>{let t=ul(o.inputs),n={id:e,title:o.title,description:o.description,inputs:t,timeoutMs:parseInt(o.timeout,10)||3e4,kind:o.kind};if(o.kind==="shell")o.command||(console.log(R.red(" --command required for kind=shell")),process.exit(1)),n.command=o.command,o.cwd&&(n.cwd=o.cwd);else if(o.kind==="http"){if(o.url||(console.log(R.red(" --url required for kind=http")),process.exit(1)),n.url=o.url,n.method=o.method,o.headers)try{n.headers=JSON.parse(o.headers)}catch{console.log(R.red(" --headers must be valid JSON")),process.exit(1)}o.body&&(n.body=o.body)}else console.log(R.red(" --kind must be shell|http")),process.exit(1);let s=cs.safeParse(n);if(!s.success){console.log(R.red(" validation failed:"));for(let a of s.error.issues)console.log(R.red(` ${a.path.join(".")}: ${a.message}`));process.exit(1)}let r=new Eo().save(s.data);console.log(R.green(`
326
+ \u2713 action "${r.id}" saved (${r.kind}).`)),console.log(R.dim(` Test: agentx actions run ${r.id}
327
+ `))});so.command("remove <id>").alias("rm").description("delete an action").action(e=>{new Eo().delete(e)||(console.log(R.red(` no action "${e}"`)),process.exit(1)),console.log(R.green(` \u2713 action "${e}" removed`))});so.command("run <id>").description("invoke an action and print its output").option("--input <kv...>","input as key=value (repeat for each)").option("--json","JSON output (full ActionRunResult)").action(async(e,o)=>{let n=new Eo().get(e);n||(console.log(R.red(` no action "${e}"`)),process.exit(1));let s={};for(let i of o.input||[]){let r=i.indexOf("=");r<0&&(console.log(R.red(` --input "${i}" must be key=value`)),process.exit(1)),s[i.slice(0,r)]=i.slice(r+1)}try{let i=await ls(n,s);if(o.json){console.log(JSON.stringify(i,null,2));return}console.log(),console.log(` ${i.ok?R.green("\u2713 ok"):R.red("\u2717 failed")} status=${i.status} ${i.durationMs}ms`),console.log(),i.output&&(console.log(R.dim(" --- output ---")),console.log(i.output)),i.errors&&(console.log(R.dim(" --- errors ---")),console.log(R.red(i.errors))),console.log(),i.ok||process.exit(1)}catch(i){console.log(R.red(` ${i.message}`)),process.exit(1)}});so.command("builtin [name]").description("list registered built-in actions, or run one with --input").option("--input <json>","JSON input matching the action's inputSchema").option("--daemon <url>","daemon API base URL","http://127.0.0.1:18800").option("--json","raw JSON output (omit chrome)").option("--schema","show the action's input/output schema instead of running it").action(async(e,o)=>{let t=String(o.daemon).replace(/\/+$/,"");if(!e){try{let s=await fetch(`${t}/api/actions/builtin`);s.ok||(console.log(R.red(` list failed (${s.status}): ${(await s.text()).slice(0,200)}`)),process.exit(1));let r=(await s.json()).actions||[];if(o.json){console.log(JSON.stringify(r,null,2));return}if(r.length===0){console.log(R.dim(" No built-in actions registered. Daemon may not be running, or registerAllBuiltins() didn't fire at boot."));return}console.log(),console.log(R.bold(" Built-in typed actions")),console.log(R.dim(" Invoke: agentx actions builtin <name> --input '<json>'")),console.log();for(let a of r){let c=a.timeoutMs?R.dim(` (timeout=${Math.round(a.timeoutMs/1e3)}s)`):"";console.log(` ${R.cyan(a.name.padEnd(22))} ${a.description||""}${c}`)}console.log()}catch(s){console.log(R.red(` daemon unreachable: ${s.message||s}`)),process.exit(1)}return}if(o.schema){try{let r=((await(await fetch(`${t}/api/actions/builtin`)).json()).actions||[]).find(a=>a.name===e);if(r||(console.log(R.red(` unknown action: ${e}`)),process.exit(1)),o.json){console.log(JSON.stringify(r,null,2));return}console.log(),console.log(R.bold(` ${r.name}`)),r.description&&console.log(R.dim(` ${r.description}`)),console.log(),console.log(R.dim(" --- input schema ---")),console.log(JSON.stringify(r.inputSchema??{},null,2)),r.outputSchema&&(console.log(),console.log(R.dim(" --- output schema ---")),console.log(JSON.stringify(r.outputSchema,null,2))),console.log()}catch(s){console.log(R.red(` schema fetch failed: ${s.message||s}`)),process.exit(1)}return}let n={};if(o.input)try{n=JSON.parse(o.input)}catch(s){console.log(R.red(` --input must be valid JSON: ${s.message}`)),process.exit(1)}try{let s=await fetch(`${t}/api/actions/builtin/${encodeURIComponent(e)}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)}),i=await s.json().catch(()=>({}));if(s.ok||(o.json&&(console.log(JSON.stringify(i,null,2)),process.exit(1)),console.log(R.red(` action failed (${s.status}): ${i.error||JSON.stringify(i).slice(0,300)}`)),process.exit(1)),o.json){console.log(JSON.stringify(i,null,2));return}console.log(JSON.stringify(i,null,2))}catch(s){console.log(R.red(` daemon unreachable: ${s.message||s}`)),process.exit(1)}});function ul(e){if(!e)return[];let o=[];for(let t of e.split(",").map(n=>n.trim()).filter(Boolean)){let n=t.endsWith("!"),s=n?t.slice(0,-1):t,i=s.indexOf(":"),r=i>=0?s.slice(0,i):s,a=i>=0?s.slice(i+1):"string";o.push({name:r,type:a,required:n})}return o}import{Command as fl}from"commander";import z from"chalk";var cr=new fl().name("watch").description("stream live daemon events \u2014 workflow runs, user tasks, signals, mesh health").option("-c, --config <path>","daemon config file").option("-t, --type <kinds>","comma-separated event kinds (run,task,signal,mesh,channel,status)").option("-w, --workflow <id>","only events for this workflow id").option("-a, --actor <id>","only task events involving this actor id").option("-r, --run <id>","only events for this run id").option("--channel <name>","only channel events on this channel (telegram/whatsapp/\u2026)").option("--node <url>","daemon URL (defaults to dashboard.daemonUrl from config)").option("--token <token>","bearer token (defaults to dashboard.token from config)").action(async e=>{let o=typeof e.node=="string"?e.node:"",t=typeof e.token=="string"?e.token:"";if(!o||!t)try{let r=L(e.config);o||(o=r.dashboard?.daemonUrl||""),t||(t=r.dashboard?.token||"")}catch{}o||(o="http://localhost:18800");let n=[];typeof e.type=="string"&&n.push(`type=${encodeURIComponent(e.type)}`),typeof e.workflow=="string"&&n.push(`workflow=${encodeURIComponent(e.workflow)}`),typeof e.actor=="string"&&n.push(`actor=${encodeURIComponent(e.actor)}`),typeof e.run=="string"&&n.push(`run=${encodeURIComponent(e.run)}`),typeof e.channel=="string"&&n.push(`channel=${encodeURIComponent(e.channel)}`);let s=`${o.replace(/\/+$/,"")}/events${n.length?`?${n.join("&")}`:""}`;console.log(z.dim(` connecting to ${s}`));let i={Accept:"text/event-stream"};t&&(i.Authorization=`Bearer ${t}`);try{let r=await fetch(s,{headers:i});(!r.ok||!r.body)&&(console.error(z.red(` connect failed: HTTP ${r.status}`)),process.exit(1)),console.log(z.green(" connected \u2014 Ctrl-C to stop")),console.log("");let a=r.body.getReader(),c=new TextDecoder,d="";for(;;){let{done:g,value:l}=await a.read();if(g)break;d+=c.decode(l,{stream:!0});let u;for(;(u=d.indexOf(`
328
+
329
+ `))>=0;){let m=d.slice(0,u);d=d.slice(u+2),hl(m)}}}catch(r){console.error(z.red(` disconnected: ${r.message||r}`)),process.exit(1)}});function hl(e){let o="message",t="";for(let i of e.split(`
330
+ `))i.startsWith("event: ")?o=i.slice(7):i.startsWith("data: ")&&(t=i.slice(6));if(!t)return;let n={};try{n=JSON.parse(t)}catch{console.log(`${o}: ${t}`);return}let s=n.at?z.dim(new Date(n.at).toLocaleTimeString()):"";switch(o){case"run":{let i=z.cyan(n.phase?.padEnd(9)??"run"),r=z.bold(n.workflowId??""),a=z.dim((n.runId??"").slice(0,8)),c=n.nodeId?z.yellow(n.nodeId):"",d=n.note?z.dim(`\u2014 ${n.note}`):"";console.log(`${s} ${i} ${r} ${a} ${c} ${d}`);break}case"task":{let i=z.magenta(`task.${n.phase}`.padEnd(14)),r=z.bold(n.workflowId??""),a=Array.isArray(n.assignedTo)?n.assignedTo.join(","):"",c=n.title?z.dim(`"${n.title}"`):"";console.log(`${s} ${i} ${r} ${z.cyan(a)} ${c}`);break}case"signal":{let i=z.green("signal".padEnd(9)),r=z.bold(n.name),a=z.dim(`(${n.scope})`),c=n.workflowId?z.dim(n.workflowId):"";console.log(`${s} ${i} ${r} ${a} ${c}`);break}case"mesh":{let i=z.blue("mesh".padEnd(9)),r=z.bold(n.peer),a=n.healthy?z.green("\u2713 healthy"):z.red("\u2717 lost");console.log(`${s} ${i} ${r} ${a} ${z.dim(n.delta??"")}`);break}case"channel":{let i=z.yellow("channel".padEnd(9)),r=n.direction==="in"?z.cyan("\u2190"):z.magenta("\u2192"),a=z.bold(`${n.channel}:${n.chatId??""}`),c=n.textPreview?z.dim(n.textPreview):"";console.log(`${s} ${i} ${r} ${a} ${c}`);break}case"status":{console.log(z.dim(`${s} status ${n.node??""} agents=${n.agents??0} active=${(n.active??[]).length} peers=${(n.mesh??[]).length}`));break}default:console.log(`${s} ${o} ${JSON.stringify(n)}`)}}import{Command as yl}from"commander";import wl from"chalk";import qt from"chalk";function zt(e){console.error(),console.error(qt.yellow(` \u26A0 ${e.what} is deprecated.`)),console.error(qt.dim(` ${e.why}`)),console.error(),console.error(` Use ${qt.cyan(e.instead)} instead.`),e.note&&console.error(qt.dim(` ${e.note}`)),console.error()}var Ht={why:"No recorded use on any node since 2026-07-03.",instead:"agentx attach <agent>",note:"Wears the identity in the Claude Code session you already have open \u2014 see docs/reference/attach.md"};var lr=new yl().name("tui").description("[deprecated \u2014 see agentx attach] interactive terminal mission control").option("-c, --config <path>","daemon config file").option("--node <url>","daemon URL (defaults to dashboard.daemonUrl from config)").option("--token <token>","bearer token (defaults to dashboard.token from config)").option("--poll <ms>","snapshot poll interval in ms","3000").action(async e=>{zt({what:"agentx tui",...Ht}),process.stdout.isTTY||(console.error(wl.red("agentx tui requires an interactive terminal")),process.exit(1));let{resolveConn:o}=await import("./client-GGVVLOOF.js"),{App:t}=await import("./App-6Y7B3YQH.js"),{render:n}=await import("ink"),s=(await import("react")).default,i=o({node:e.node,token:e.token,config:e.config}),r=Math.max(500,Number(e.poll)||3e3);await n(s.createElement(t,{conn:i,pollMs:r})).waitUntilExit()});import{Command as $l}from"commander";import F from"chalk";import{randomUUID as dr}from"crypto";import{createInterface as bl}from"node:readline/promises";import kl from"ora";var gr=new $l().name("chat").description("[deprecated \u2014 see agentx attach] interactive chat with a daemon-registered agent").argument("[agent]","agent id to talk to (with or without leading @)").option("-c, --config <path>","daemon config file").option("--node <url>","daemon URL (defaults to dashboard.daemonUrl from config)").option("--token <token>","bearer token (defaults to dashboard.token from config)").option("--channel <name>","logical channel name passed in context","chat-cli").option("--chat-id <id>","resume an existing chatId (otherwise a fresh one is generated)").action(async(e,o)=>{zt({what:"agentx chat",...Ht});let t=Ks({node:o.node,token:o.token,config:o.config}),n=(e??"").replace(/^@/,"").trim(),s=o.channel,i=o.chatId||`chat-cli:${dr().slice(0,12)}`,r=[];try{r=await kt(t)}catch(g){console.error(F.red(` cannot reach daemon at ${t.baseUrl}: ${g?.message||g}`)),console.error(F.dim(" is the daemon running? try: agentx daemon status")),process.exit(1)}if(r.length===0&&(console.error(F.red(" no agents registered with the daemon")),process.exit(1)),n?r.find(g=>g.id===n)||(console.error(F.red(` unknown agent: @${n}`)),console.error(F.dim(` registered: ${r.map(g=>`@${g.id}`).join(", ")}`)),process.exit(1)):(n=r[0].id,console.log(F.dim(` no agent given \u2014 defaulting to @${n}`))),process.stdout.isTTY&&process.stdin.isTTY){let{render:g}=await import("ink"),{ChatApp:l}=await import("./ChatApp-KIEUJ2DS.js"),u=(await import("react")).default;await g(u.createElement(l,{conn:t,agentId:n,channel:s,chatId:i,agents:r})).waitUntilExit();return}xl(t.baseUrl,n,i,s);let a=bl({input:process.stdin,output:process.stdout}),c=!1,d=!1;for(a.on("close",()=>{if(c){d=!0;return}console.log(F.dim(`
331
+ bye`)),process.exit(0)});;){let g;try{g=(await a.question(F.cyan("you \u203A "))).trim()}catch{a.close();return}if(!g)continue;if(g==="/exit"||g==="/quit"){a.close();return}if(g==="/who"){console.log(F.dim(` @${n} chatId=${i} channel=${s}`));continue}if(g==="/clear"){i=`chat-cli:${dr().slice(0,12)}`,console.log(F.dim(` fresh chatId=${i} (warm process dropped)`));continue}if(g==="/agents"){try{let f=await kt(t);for(let y of f){let $=y.id===n?F.green("\u25CF"):F.dim("\u25CB");console.log(` ${$} @${y.id} ${F.dim(y.tier)} ${y.model??""}`)}}catch(f){console.error(F.red(` failed to list agents: ${f?.message||f}`))}continue}if(g.startsWith("/agent ")){let f=g.slice(7).replace(/^@/,"").trim();if(!f){console.log(F.red(" usage: /agent <id>"));continue}try{if(!(await kt(t)).find($=>$.id===f)){console.log(F.red(` unknown agent: @${f}`));continue}n=f,console.log(F.dim(` now talking to @${n}`))}catch(y){console.error(F.red(` failed to switch agent: ${y?.message||y}`))}continue}if(g.startsWith("/")){console.log(F.red(` unknown command: ${g.split(/\s+/)[0]}`)),console.log(F.dim(" available: /exit /quit /clear /agent <id> /who /agents"));continue}c=!0;let l=kl({text:`@${n}\u2026`,color:"cyan"}).start(),u=Date.now(),m=vl(n);try{let f=await Vs(t,n,g,{channel:s,chatId:i,onText:$=>{l.stop(),m.text($)},onThinking:()=>{},onTool:$=>{l.stop(),m.tool($)}});l.stop();let y=Math.round((Date.now()-u)/100)/10;f.error?m.finishError(f.error):m.finish(y,f.usage)}catch(f){l.stop(),m.finishError(f?.message||String(f))}finally{c=!1}if(d){console.log(F.dim(" bye"));return}}});function vl(e){let o=!1,t=!0,n=!1,s=new Set,i=()=>{o||(process.stdout.write(`${F.green(`@${e}`)}
332
+ `),o=!0)};return{text(r){if(r){i(),n=!0;for(let a of r)t&&a!==`
333
+ `&&(process.stdout.write(" "),t=!1),process.stdout.write(a),a===`
334
+ `&&(t=!0)}},tool(r){if(i(),r.status==="start"&&r.name){let a=r.id||`${r.name}:${s.size}`;if(s.has(a))return;s.add(a),t||(process.stdout.write(`
335
+ `),t=!0);let c=r.arg?F.dim(`(${r.arg})`):"";process.stdout.write(` ${F.green("\u25CF")} ${F.bold(r.name)}${c}
336
+ `)}else r.status==="result"&&r.error&&(t||(process.stdout.write(`
337
+ `),t=!0),process.stdout.write(` ${F.red("\u25CF")} ${F.dim(`${r.name??"tool"} failed`)}
338
+ `))},finish(r,a){if(!o){process.stdout.write(`${F.green(`@${e}`)} ${F.dim("\xB7 (empty reply)")}
339
+
340
+ `);return}t||process.stdout.write(`
341
+ `);let c=a?.outputTokens!=null?`, ${a.outputTokens} tok`:"";process.stdout.write(F.dim(` \xB7 ${r}s${c}
342
+
343
+ `))},finishError(r){o&&!t&&process.stdout.write(`
344
+ `),process.stdout.write(F.red(` \u2717 ${r}
345
+
346
+ `))}}}function xl(e,o,t,n){console.log(),console.log(F.bold(" agentx chat")),console.log(F.dim(` daemon: ${e} \xB7 channel: ${n} \xB7 chatId: ${t}`)),console.log(F.dim(` agent: @${o} \xB7 /exit to quit \xB7 /agent <id> to switch`)),console.log()}import{Command as Sl}from"commander";import be from"chalk";import{readFileSync as jl}from"fs";var Gt=["user","feedback","project","reference"],xo=new Sl().name("memory").description("audit + edit agent-memory (per-agent experiential notes)");xo.command("add").description("add a memory for an agent").requiredOption("--agent <id>","agent id").requiredOption("--type <type>","one of: user, feedback, project, reference").requiredOption("--name <slug>","short slug, unique per agent (e.g. deep-backend)").requiredOption("--description <line>","one-line hook shown in MEMORY.md").option("--body <text>","memory body (markdown); repeat --body or use --file").option("--file <path>","read body from a file (use '-' for stdin)").action(async e=>{if(!Gt.includes(e.type)){console.error(be.red(` --type must be one of: ${Gt.join(", ")}`)),process.exitCode=1;return}let o=e.body??"";if(e.file)if(e.file==="-")o=await El();else try{o=jl(e.file,"utf-8")}catch(s){console.error(be.red(` could not read --file: ${s.message}`)),process.exitCode=1;return}if(!o.trim()){console.error(be.red(" memory body is required (use --body or --file)")),process.exitCode=1;return}let n=new So().save({agentId:e.agent,type:e.type,name:e.name,description:e.description,body:o});console.log(be.green(` \u2713 memory "${n.name}" saved (${n.type}) for agent ${e.agent}`))});xo.command("list").description("list memories for an agent").requiredOption("--agent <id>","agent id").option("--type <type>","filter by memory type").action(e=>{let t=new So().list(e.agent);if(e.type){if(!Gt.includes(e.type)){console.error(be.red(` --type must be one of: ${Gt.join(", ")}`)),process.exitCode=1;return}t=t.filter(s=>s.type===e.type)}if(!t.length){console.log(be.dim(` no memories for agent "${e.agent}"${e.type?` of type "${e.type}"`:""}`));return}console.log();let n=null;for(let s of t)s.type!==n&&(console.log(be.bold(` ${s.type}`)),n=s.type),console.log(` ${be.cyan(s.name.padEnd(28))} ${s.description}`);console.log(),console.log(be.dim(` ${t.length} memor${t.length===1?"y":"ies"} for ${e.agent}`))});xo.command("show").description("print the full body of a memory").requiredOption("--agent <id>","agent id").argument("<name>","memory name (slug)").action((e,o)=>{let n=new So().get(o.agent,e);if(!n){console.error(be.red(` no memory "${e}" for agent "${o.agent}"`)),process.exitCode=1;return}console.log(be.bold(`${n.type}/${n.name}`)+be.dim(` (${n.description})`)),console.log(be.dim(` updated ${n.updatedAt}`)),console.log(),console.log(n.body)});xo.command("remove").alias("rm").description("remove a memory").requiredOption("--agent <id>","agent id").argument("<name>","memory name (slug)").action((e,o)=>{if(!new So().remove(o.agent,e)){console.error(be.red(` no memory "${e}" for agent "${o.agent}"`)),process.exitCode=1;return}console.log(be.green(` \u2713 memory "${e}" removed`))});xo.command("index").description("print the MEMORY.md index content an agent sees in its prompt").requiredOption("--agent <id>","agent id").action(e=>{let t=new So().indexMarkdown(e.agent);if(!t){console.log(be.dim(` (no memories \u2014 MEMORY.md is empty for agent "${e.agent}")`));return}console.log(t)});async function El(){let e=[];for await(let o of process.stdin)e.push(o);return Buffer.concat(e).toString("utf-8")}import Hn from"chalk";import{Command as Il}from"commander";import{resolve as Al}from"path";var mr=new Il().name("serve").description("run agentx as an MCP server for AI editors (Claude Code, Cursor, Windsurf, etc.)").option("--stdio","use stdio transport (default)",!0).option("-c, --cwd <cwd>","working directory",process.cwd()).action(async e=>{try{process.chdir(Al(e.cwd)),e.stdio!==!1?await Is():(Le.error("Only stdio transport is currently supported."),Le.info(`Usage: ${Hn.green("agentx serve --stdio")} or configure in your MCP client.`),Le.break(),Le.info("Add to Claude Code:"),Le.info(Hn.dim(" claude mcp add agentx -- npx agentx serve --stdio")),Le.break(),Le.info("Add to Cursor/MCP config:"),Le.info(Hn.dim(JSON.stringify({mcpServers:{agentx:{command:"npx",args:["agentx","serve","--stdio"]}}},null,2))))}catch(o){Js(o)}});import{Command as Ol}from"commander";import te from"chalk";import{spawn as Gn}from"child_process";import{mkdirSync as Cl,writeFileSync as Yn,rmSync as pr,openSync as ur,existsSync as fr}from"fs";import{resolve as Rl,join as Ee}from"path";import{randomBytes as Tl}from"crypto";var hr="[demo] Customer reports checkout is broken and CI is red on demo/shop. Handle it.",Dl="CI is red on demo/shop \u2014 checkout.test.ts failing on Node 22. Diagnose, fix, and report back.";function Nl(e,o){return[{dir:Ee(e,"node-a"),id:"demo-laptop",name:"laptop-paris",port:o,agentId:"cx",agentName:"CX",persona:"You are CX, the customer-facing coordinator. You triage inbound issues and delegate technical work to @builder on the vps node.",script:{steps:[{match:"checkout is broken",thinking:"Checkout failure + red CI \u2014 this is a build problem, not a support question. @builder on vps-nyc owns demo/shop.",reply:"Checkout failure traced to the red pipeline on demo/shop. This needs a code fix \u2014 delegating to @builder on the vps-nyc node over the mesh. I'll report back on this thread.",delayMs:900},{match:"Builder reports",thinking:"Fix confirmed and pipeline green \u2014 close the loop with the customer.",reply:"Resolved \u2705 \u2014 builder patched checkout.test.ts (Node 22 crypto import), MR !47 merged, pipeline green. Customer thread updated. Every hop of this run is in the ledger: `agentx ledger`.",delayMs:800}],fallback:{reply:"Demo mode \u2014 I answer from a script. Try the scripted scenario, or run `agentx setup` to connect a real model."}}},{dir:Ee(e,"node-b"),id:"demo-vps",name:"vps-nyc",port:o+1,agentId:"builder",agentName:"Builder",persona:"You are Builder, the engineering agent on the vps node. You fix code, open MRs, and report results tersely.",script:{steps:[{match:"checkout\\.test\\.ts|CI is red",thinking:"Reproducing on Node 22\u2026 crypto.webcrypto import moved. Patching the test setup, rerunning the suite.",reply:"Fixed. checkout.test.ts assumed the legacy crypto.webcrypto import \u2014 patched for Node 22, suite green locally. Opened MR !47 on demo/shop; pipeline is green. Handing back to @cx.",delayMs:1400,chunkMs:40}],fallback:{reply:"Builder here (demo script). Send me a failing pipeline."}}},{dir:Ee(e,"node-c"),id:"demo-pi",name:"pi-office",port:o+2,agentId:"scout",agentName:"Scout",persona:"You are Scout, the monitoring agent on the office Raspberry Pi. You watch schedules and report status.",script:{fallback:{reply:"Scout here (demo script) \u2014 all monitors green on pi-office."},steps:[]}}]}function _l(e,o,t){let n=Ee(e.dir,"workspaces",e.agentId);Cl(n,{recursive:!0}),Yn(Ee(n,"CLAUDE.md"),`# ${e.agentName}
347
+
348
+ ${e.persona}
349
+ `),Yn(Ee(e.dir,"demo-script.json"),JSON.stringify(e.script,null,2));let s=o.filter(r=>r.id!==e.id).map(r=>({name:r.name,url:`http://127.0.0.1:${r.port}`,token:t})),i={node:{id:e.id,name:e.name,bind:`127.0.0.1:${e.port}`,defaultAgent:e.agentId},dashboard:{enabled:!0,port:e.port+10,bind:"127.0.0.1",daemonUrl:`http://127.0.0.1:${e.port}`},providers:{demo:{apiKey:"demo-mode"}},agents:{[e.agentId]:{name:e.agentName,description:e.persona,workspace:"./workspaces/"+e.agentId,tier:"orchestrator",provider:"demo",mentions:[`@${e.agentId}`,e.agentId],maxConcurrent:2,systemPrompt:e.persona}},mesh:{enabled:!0,peers:s,discovery:"static",healthCheck:{interval:3,timeout:5}}};Yn(Ee(e.dir,"agentx.json"),JSON.stringify(i,null,2))}async function Kn(e,o,t){let n=Date.now();for(;Date.now()-n<t;){try{if(await o())return}catch{}await new Promise(s=>setTimeout(s,500))}throw new Error(`Timed out waiting for ${e} (${Math.round(t/1e3)}s)`)}async function Vn(e,o,t){let n=await fetch(`http://127.0.0.1:${e}${o}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),s=await n.json().catch(()=>({}));if(!n.ok||s.error)throw new Error(s.error||`${o} HTTP ${n.status}`);return s}function Yt(e,o,t){console.log(),console.log(t(` ${e}`)),console.log(` ${o.split(`
350
+ `).join(`
351
+ `)}`)}var yr=new Ol().name("demo").description("zero-key demo: three daemons, a real A2A mesh, a scripted scenario").option("--base-port <port>","first of three consecutive loopback ports","18921").option("--once","play the scenario once and exit (default: keep daemons up until Ctrl-C)").option("--keep","keep the .agentx-demo directory on exit").option("--no-open","don't open the dashboard in a browser").action(async e=>{let o=parseInt(e.basePort,10),t=Rl(process.cwd(),".agentx-demo"),n=Nl(t,o),s=Tl(24).toString("hex"),i=[],r=process.argv[1];(!r||!fr(r))&&(console.error(te.red("Cannot resolve the agentx CLI entrypoint \u2014 run via the installed `agentx` binary.")),process.exit(1)),fr(t)&&pr(t,{recursive:!0,force:!0});for(let g of n)_l(g,n,s);console.log(),console.log(te.bold(" agentx demo \u2014 one message, three machines (simulated on loopback)")),console.log(te.yellow(" Canned model responses. Real daemons, real A2A mesh, real ledger.")),console.log(te.dim(" Run `agentx setup` to wire real agents.\n"));let a=!1,c=g=>{if(!a){a=!0;for(let l of i)try{l.kill("SIGTERM")}catch{}setTimeout(()=>{if(!e.keep)try{pr(t,{recursive:!0,force:!0})}catch{}process.exit(g)},800)}};process.on("SIGINT",()=>c(0)),process.on("SIGTERM",()=>c(0));let d={...process.env};delete d.ANTHROPIC_API_KEY,delete d.ANTHROPIC_API_KEY_OLD,delete d.OPENAI_API_KEY,delete d.DEEPSEEK_API_KEY;try{for(let m of n){let f=ur(Ee(m.dir,"daemon.log"),"a"),y=Gn(process.execPath,[r,"daemon","start","-c",Ee(m.dir,"agentx.json")],{cwd:m.dir,env:{...d,MESH_TOKEN:s,AGENTX_DEMO_SCRIPT:Ee(m.dir,"demo-script.json")},stdio:["ignore",f,f]});i.push(y),console.log(te.dim(` \u25B8 ${m.name} starting on 127.0.0.1:${m.port} (log: ${Ee(m.dir,"daemon.log")})`))}for(let m of n)await Kn(`${m.name} /health`,async()=>(await fetch(`http://127.0.0.1:${m.port}/health`)).ok,3e4);console.log(te.green(" \u2713 three daemons up"));let g=n[0].port+10;{let m=ur(Ee(n[0].dir,"board.log"),"a"),f=Gn(process.execPath,[r,"board","serve"],{cwd:n[0].dir,env:{...d,MESH_TOKEN:s},stdio:["ignore",m,m]});i.push(f)}await Kn("dashboard /live",async()=>(await fetch(`http://127.0.0.1:${g}/live`)).ok,3e4),console.log(te.green(" \u2713 dashboard up")),await Kn("mesh discovery (laptop sees both peers)",async()=>((await(await fetch(`http://127.0.0.1:${n[0].port}/health`)).json()).mesh||[]).filter($=>$.healthy&&$.skills?.length).length>=2,3e4),console.log(te.green(" \u2713 A2A mesh healthy \u2014 agent cards exchanged across three nodes"));let l=`http://127.0.0.1:${g}/live`;if(console.log(),console.log(` Dashboard: ${te.cyan(l)} (all three nodes via the mesh)`),console.log(te.dim(` Daemon APIs: ${n.map(m=>`127.0.0.1:${m.port}`).join(" \xB7 ")}`)),e.open!==!1){let m=process.platform==="darwin"?"open":process.platform==="win32"?"start":"xdg-open";try{Gn(m,[l],{stdio:"ignore",detached:!0}).unref()}catch{}}let u=async()=>{console.log(),console.log(te.bold(" \u2500\u2500 Scenario: red pipeline, cross-node fix \u2500\u2500")),Yt("You \u2192 @cx (laptop-paris)",hr,te.cyan);let m=await Vn(n[0].port,"/task",{agent:"cx",message:hr});Yt("@cx (laptop-paris)",m.content||"(no content)",te.green),console.log(),console.log(te.magenta(" \u21C4 mesh hop: laptop-paris \u2192 vps-nyc (A2A /task)"));let f=await Vn(n[0].port,"/send/agent",{agentId:"builder",senderAgentId:"cx",text:Dl}),y=typeof f.messageId=="string"?f.messageId:f.content||"(no reply)";Yt("@builder (vps-nyc)",y,te.yellow);let $=await Vn(n[0].port,"/task",{agent:"cx",message:`[demo] Builder reports: ${y}`});Yt("@cx (laptop-paris)",$.content||"(no content)",te.green),console.log(),console.log(te.dim(` Inspect the run: ${l} \xB7 ledger rows on each node record every dispatch`)),console.log(te.dim(` Worth your time? A star helps others find it: ${te.cyan("https://github.com/anis-marrouchi/agentx")}`))};if(await u(),e.once){console.log(),console.log(te.dim(" --once: shutting down.")),c(0);return}console.log(),console.log(te.bold(" Daemons stay up \u2014 browse the dashboards. Press Enter to replay, Ctrl-C to exit.")),process.stdin.setEncoding("utf8"),process.stdin.on("data",()=>{u().catch(m=>console.error(te.red(` Replay failed: ${m.message}`)))}),process.stdin.resume()}catch(g){console.error(),console.error(te.red(` Demo failed: ${g.message}`)),console.error(te.dim(` Node logs: ${n.map(l=>Ee(l.dir,"daemon.log")).join(", ")}`)),c(1)}});import{Command as Pl}from"commander";import pe from"chalk";var io=new Pl().name("whatsapp").description("list WhatsApp chats/contacts and ingest them into the wiki as a data source");io.command("list-chats").description("list chats observed by the WhatsApp socket (cached, no live fetch)").option("--format <format>","output format: table | json","table").option("--group","groups only").option("--dm","DMs only").action(async e=>{try{let t=(await dt("/whatsapp/chats")).chats??[];if(e.group&&(t=t.filter(n=>n.isGroup)),e.dm&&(t=t.filter(n=>!n.isGroup)),e.format==="json"){console.log(JSON.stringify(t,null,2));return}if(t.length===0){console.log(pe.dim(" no chats cached yet"));return}console.log(),console.log(pe.bold(` ${t.length} chat${t.length===1?"":"s"}`)),console.log();for(let n of t){let s=n.isGroup?pe.cyan("group"):pe.dim("dm"),i=n.lastMessageAt?new Date(n.lastMessageAt*1e3).toISOString().slice(0,16).replace("T"," "):"\u2014";console.log(` ${s} ${n.name.padEnd(30)} ${pe.dim(n.jid.padEnd(40))} ${pe.dim(i)}`)}console.log()}catch(o){Bo(o)}});io.command("list-contacts").description("list contacts observed by the WhatsApp socket (cached, no live fetch)").option("--format <format>","output format: table | json","table").action(async e=>{try{let t=(await dt("/whatsapp/contacts")).contacts??[];if(e.format==="json"){console.log(JSON.stringify(t,null,2));return}if(t.length===0){console.log(pe.dim(" no contacts cached yet"));return}console.log(),console.log(pe.bold(` ${t.length} contact${t.length===1?"":"s"}`)),console.log();for(let n of t){let s=(n.name||n.pushName||n.phone||n.jid).padEnd(30),i=n.phone?`+${n.phone}`.padEnd(16):"\u2014".padEnd(16);console.log(` ${s} ${pe.dim(i)} ${pe.dim(n.jid)}`)}console.log()}catch(o){Bo(o)}});io.command("ingest-all").description("run a full ingest sweep against the configured allowlist (writes raw wiki entries)").option("--dry-run","compute entries without writing them \u2014 review before enabling").option("--agent <id>","owner agent for the entries (defaults to channels.whatsapp.defaultAgent)").option("--force","bypass the channels.whatsapp.ingest.enabled guard").action(async e=>{try{let o={dryRun:!!e.dryRun,force:!!e.force};e.agent&&(o.agent=e.agent);let t=await Xn("/whatsapp/ingest",o);Qn(t,!!e.dryRun)}catch(o){Bo(o)}});io.command("ingest-contact <jid>").description("ingest one contact (skips scope allowlist for this JID only)").option("--dry-run","compute entries without writing them").option("--agent <id>","owner agent for the entries").action(async(e,o)=>{try{let t={dryRun:!!o.dryRun,force:!0,onlyJid:e,onlyKind:"contact"};o.agent&&(t.agent=o.agent);let n=await Xn("/whatsapp/ingest",t);Qn(n,!!o.dryRun)}catch(t){Bo(t)}});io.command("ingest-chat <jid>").description("ingest one chat (DM or group) including message window if `--messages` is set").option("--dry-run","compute entries without writing them").option("--messages","include the bounded message window for this chat (overrides config mode)").option("--agent <id>","owner agent for the entries").action(async(e,o)=>{try{let t={dryRun:!!o.dryRun,force:!0,onlyJid:e};o.agent&&(t.agent=o.agent),o.messages&&(t.forceMode="messages");let n=await Xn("/whatsapp/ingest",t);Qn(n,!!o.dryRun)}catch(t){Bo(t)}});io.command("status").description("show WhatsApp channel + ingest status").action(async()=>{try{let e=await dt("/whatsapp/state"),o=await dt("/whatsapp/chats"),t=await dt("/whatsapp/contacts");console.log(),console.log(pe.bold(" WhatsApp status")),console.log(),console.log(` connection: ${e.connection||"\u2014"}`),console.log(` cached chats: ${o.chats?.length??0}`),console.log(` cached contacts: ${t.contacts?.length??0}`),console.log()}catch(e){Bo(e)}});function Qn(e,o){if(console.log(),console.log(pe.bold(o?" Ingest (dry-run)":" Ingest report")),console.log(),console.log(` scanned: contacts=${e.scannedContacts}, groups=${e.scannedGroups}`),console.log(` wrote: contacts=${e.wroteContacts}, groups=${e.wroteGroups}, dm-windows=${e.wroteDmWindows}, group-windows=${e.wroteGroupWindows}`),e.skippedUnchanged&&console.log(` skipped: ${e.skippedUnchanged} unchanged`),e.errors?.length){console.log(pe.yellow(` errors: ${e.errors.length}`));for(let t of e.errors.slice(0,5))console.log(pe.yellow(` - [${t.kind}] ${t.jid}: ${t.message}`))}if(o&&e.dryRunEntries?.length){console.log(),console.log(pe.dim(" would-be entries:"));for(let t of e.dryRunEntries.slice(0,10))console.log(pe.dim(` - ${t.id} (${t.source}, ${t.content.length} bytes)`));e.dryRunEntries.length>10&&console.log(pe.dim(` \u2026and ${e.dryRunEntries.length-10} more`))}console.log()}function wr(){try{let e=L(),[o,t]=e.node.bind.split(":");return`http://${o||"127.0.0.1"}:${t||"19900"}`}catch{return"http://127.0.0.1:19900"}}async function dt(e){let o=await fetch(`${wr()}${e}`,{signal:AbortSignal.timeout(1e4)}),t=await o.json().catch(()=>({}));if(!o.ok)throw new Error(t.error||`HTTP ${o.status}`);return t}async function Xn(e,o){let t=await fetch(`${wr()}${e}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o),signal:AbortSignal.timeout(6e5)}),n=await t.json().catch(()=>({}));if(!t.ok)throw new Error(n.error||`HTTP ${t.status}`);return n}function Bo(e){console.error(pe.red(` ${e.message||e}`)),process.exit(1)}import{Command as Ml}from"commander";import Q from"chalk";import{existsSync as Ll,mkdirSync as $r,writeFileSync as gt}from"fs";import{join as Fo,resolve as Ul}from"path";var mt=new Ml().name("plugin").description("manage agentx plugins (Move B)");mt.command("list",{isDefault:!0}).description("list plugins configured in agentx.json").action(()=>{try{let o=L().plugins??[];if(console.log(),o.length===0){console.log(Q.dim(" No plugins configured.")),console.log(Q.dim(' Add a plugin: edit agentx.json\'s `plugins: ["your-package"]` and pnpm install it.')),console.log();return}console.log(Q.bold(` Plugins configured: ${o.length}`)),console.log();for(let t of o)console.log(` ${Q.cyan(t)}`);console.log(),console.log(Q.dim(" Run `agentx plugin doctor` to verify each plugin loads.")),console.log()}catch(e){console.log(Q.red(` ${e.message}`)),process.exitCode=1}});mt.command("init <name>").description("scaffold a new plugin package (npm-publishable, with manifest + setup hook)").option("--cwd <cwd>","directory to create the package under",process.cwd()).option("--description <text>","short package description").option("--force","overwrite an existing directory").action((e,o)=>{/^(@[a-z0-9-]+\/)?[a-z0-9][a-z0-9._-]*$/i.test(e)||(console.log(Q.red(` invalid package name "${e}". Use lowercase letters, digits, hyphens; optional @scope/ prefix.`)),process.exit(1));let t=e.startsWith("@")?e.split("/")[1]:e,n=Ul(o.cwd,t);Ll(n)&&!o.force&&(console.log(Q.red(` ${n} already exists. Use --force to overwrite.`)),process.exit(1)),$r(n,{recursive:!0}),$r(Fo(n,"src"),{recursive:!0});let s=o.description||`AgentX plugin: ${e}`;gt(Fo(n,"package.json"),Fl(e,s)),gt(Fo(n,"tsconfig.json"),ql()),gt(Fo(n,"src/index.ts"),zl(e)),gt(Fo(n,"README.md"),Hl(e,s)),gt(Fo(n,".gitignore"),Gl()),console.log(),console.log(Q.green(` \u2713 scaffolded plugin at ${n}`)),console.log(),console.log(Q.bold(" Next steps:")),console.log(Q.dim(` cd ${t}`)),console.log(Q.dim(" pnpm install")),console.log(Q.dim(" pnpm build")),console.log(Q.dim(" pnpm link --global # for local testing")),console.log(Q.dim(" # then in your agentx project:")),console.log(Q.dim(` pnpm link --global ${e}`)),console.log(Q.dim(` agentx config set plugins[0] "${e}"`)),console.log(Q.dim(" agentx plugin doctor")),console.log()});mt.command("doctor").description("dynamic-import each configured plugin and report status").action(async()=>{try{let o=L().plugins??[];if(console.log(),o.length===0){console.log(Q.dim(" No plugins configured. Nothing to check.")),console.log();return}let t=0;for(let n of o){let s=await Wl(n);s.kind==="ok"?console.log(` ${Q.green("\u2713")} ${Q.cyan(n)} \u2014 ${s.manifestName} v${s.manifestVersion}`):(t++,console.log(` ${Q.red("\u2717")} ${Q.cyan(n)} \u2014 ${s.reason}`))}console.log(),t>0&&(console.log(Q.red(` ${t}/${o.length} plugin(s) failed`)),process.exitCode=1)}catch(e){console.log(Q.red(` ${e.message}`)),process.exitCode=1}});async function Wl(e){let o;try{o=await import(e)}catch(s){return{kind:"fail",reason:`import failed: ${s?.message??s}`}}let t=Jl(o);if(!Bl(t))return{kind:"fail",reason:"no default AgentXPlugin export (manifest/setup pair not found)"};let n=Rs.safeParse(t.manifest);return n.success?{kind:"ok",manifestName:n.data.name,manifestVersion:n.data.version}:{kind:"fail",reason:`manifest invalid: ${n.error.issues.map(i=>i.message).join("; ")}`}}function Jl(e){if(e==null||typeof e!="object")return e;let o=e;if(o.default&&typeof o.default=="object"){let t=o.default;if(t.manifest&&typeof t.setup=="function")return t;if(t.default&&typeof t.default=="object"){let n=t.default;if(n.manifest&&typeof n.setup=="function")return n}}return o.manifest&&typeof o.setup=="function"?o:o.default??o}function Bl(e){return!!e&&typeof e=="object"&&"manifest"in e&&"setup"in e&&typeof e.setup=="function"}function Fl(e,o){return JSON.stringify({name:e,version:"0.1.0",description:o,main:"dist/index.js",types:"dist/index.d.ts",files:["dist"],scripts:{build:"tsc",prepublishOnly:"pnpm build"},keywords:["agentx","agentx-plugin"],license:"MIT",devDependencies:{typescript:"^5.4.0","@types/node":"^20.0.0"}},null,2)+`
352
+ `}function ql(){return JSON.stringify({compilerOptions:{target:"es2020",module:"commonjs",lib:["es2020"],declaration:!0,outDir:"dist",strict:!0,esModuleInterop:!0,skipLibCheck:!0,moduleResolution:"node"},include:["src/**/*.ts"]},null,2)+`
353
+ `}function zl(e){return`// ${e} \u2014 AgentX plugin
354
+ //
355
+ // A plugin's default export is { manifest, setup }. The daemon calls
356
+ // setup(ctx) once at boot. ctx.on() subscribes to the bus; ctx.addChannel()
357
+ // registers a custom channel adapter; ctx.log() namespaces output.
358
+
359
+ const plugin = {
360
+ manifest: {
361
+ name: ${JSON.stringify(e)},
362
+ version: "0.1.0",
363
+ // agentxRange: "0.x", // optional \u2014 gate against the daemon's major.minor
364
+ },
365
+
366
+ setup(ctx: any) {
367
+ ctx.log("hello from", ${JSON.stringify(e)})
368
+
369
+ // Example: log every completed task.
370
+ ctx.on("task:completed", (payload: any) => {
371
+ ctx.log("task done:", payload.agentId, payload.durationMs + "ms")
372
+ })
373
+
374
+ // Example: register a no-op channel adapter.
375
+ // ctx.addChannel({
376
+ // name: "my-channel",
377
+ // start: async () => {},
378
+ // stop: async () => {},
379
+ // send: async (out) => { ctx.log("send:", out) },
380
+ // })
381
+ },
382
+
383
+ // Optional \u2014 only needed for resources you own (timers, sockets).
384
+ // Bus subscriptions are removed automatically.
385
+ // teardown() {},
386
+ }
387
+
388
+ export default plugin
389
+ `}function Hl(e,o){return`# ${e}
390
+
391
+ ${o}
392
+
393
+ ## Install
394
+
395
+ \`\`\`bash
396
+ pnpm install ${e}
397
+ \`\`\`
398
+
399
+ ## Use
400
+
401
+ Add to \`agentx.json\`:
402
+
403
+ \`\`\`json
404
+ {
405
+ "plugins": ["${e}"]
406
+ }
407
+ \`\`\`
408
+
409
+ Then verify the load:
410
+
411
+ \`\`\`bash
412
+ agentx plugin doctor
413
+ \`\`\`
414
+
415
+ Restart the daemon to activate.
416
+ `}function Gl(){return`node_modules/
417
+ dist/
418
+ *.log
419
+ .env
420
+ `}import{Command as Yl}from"commander";import ue from"chalk";import{writeFileSync as Kl,mkdirSync as Vl,existsSync as Kt,readFileSync as br,appendFileSync as kr}from"fs";import{resolve as qo,dirname as Sr}from"path";import{homedir as es}from"os";var jr=new Yl().name("completion").description("generate a shell completion script for agentx").option("-s, --shell <shell>","shell to generate completion for: zsh|bash|fish","zsh").option("-i, --install","install the completion script to a default path for the shell").option("-o, --output <path>","write the script to this path (overrides default install path)").option("-y, --yes","skip confirmation prompts (non-interactive)").action(async e=>{let{buildProgram:o}=await import("./program-WVAVJFND.js"),t=await o(),n=String(e.shell||"zsh").toLowerCase(),s;switch(n){case"zsh":s=Zl(t);break;case"bash":s=sd(t);break;case"fish":s=id(t);break;default:console.error(ue.red(`unsupported shell: ${n}`)),console.error(ue.dim("supported: zsh, bash, fish")),process.exit(1)}if(!e.install&&!e.output){process.stdout.write(s);return}let i=e.output?qo(String(e.output)):Ql(n);Vl(Sr(i),{recursive:!0}),Kl(i,s),console.log(ue.green(`\u2713 Wrote ${n} completion to:`)),console.log(` ${i}`),console.log(),Xl(n,i,!!e.yes)});function Ql(e){let o=es();switch(e){case"zsh":return qo(o,".zsh","completions","_agentx");case"bash":return qo(o,".bash_completion.d","agentx.bash");case"fish":return qo(o,".config","fish","completions","agentx.fish");default:throw new Error(`unsupported shell: ${e}`)}}function Xl(e,o,t){if(e==="zsh"){let n=qo(es(),".zshrc"),s=Sr(o),i=`fpath=(${s} $fpath)`,r="autoload -U compinit && compinit",a=Kt(n)?br(n,"utf-8"):"",c=!a.includes(s),d=!/autoload\s+-U\s+compinit/.test(a);if(!c&&!d){console.log(ue.dim("~/.zshrc already has fpath + compinit wired up. Run `exec zsh` to pick up the new script."));return}if(t&&Kt(n)){let g=["","# agentx completion"];c&&g.push(i),d&&g.push(r),kr(n,g.join(`
421
+ `)+`
422
+ `),console.log(ue.green("\u2713 Patched ~/.zshrc")),console.log(ue.dim("Reload your shell: exec zsh"));return}console.log(ue.dim("Add this to ~/.zshrc if not already present:")),c&&console.log(ue.cyan(` ${i}`)),d&&console.log(ue.cyan(` ${r}`)),console.log(),console.log(ue.dim("Then reload: exec zsh")),console.log(ue.dim("Or rerun with --yes to auto-patch ~/.zshrc"));return}if(e==="bash"){let n=qo(es(),".bashrc"),s=`[ -f "${o}" ] && source "${o}"`;if((Kt(n)?br(n,"utf-8"):"").includes(o)){console.log(ue.dim("~/.bashrc already sources this file. Start a new shell to pick it up."));return}if(t&&Kt(n)){kr(n,`
423
+ # agentx completion
424
+ ${s}
425
+ `),console.log(ue.green("\u2713 Patched ~/.bashrc")),console.log(ue.dim("Reload your shell: exec bash"));return}console.log(ue.dim("Add this to ~/.bashrc:")),console.log(ue.cyan(` ${s}`)),console.log(),console.log(ue.dim("Or rerun with --yes to auto-patch ~/.bashrc"));return}if(e==="fish"){console.log(ue.dim("Fish auto-loads completions from this directory \u2014 start a new shell to pick it up."));return}}function xe(e){return e._name||e.name()||""}function ro(e){return e.commands.filter(o=>{let t=xe(o);return!(!t||t==="help")})}function pt(e){return e.options.filter(o=>!o.hidden)}function Zl(e){let o=[];return o.push("#compdef agentx"),o.push(""),Er(e,[],o),o.push("compdef _agentx_root_completion agentx"),o.push(""),o.join(`
426
+ `)}function vr(e){return e.length===0?"_agentx_root_completion":`_agentx_${e.join("_").replace(/[^a-z0-9_]/gi,"_")}`}function Er(e,o,t){let n=ro(e),i=pt(e).map(td).filter(c=>!!c),r=vr(o);if(t.push(`${r}() {`),t.push(" local -a commands"),t.push(" local -a options"),t.push(" "),i.length===0&&n.length===0){t.push(" return 0"),t.push("}"),t.push("");return}let a=[];for(let c of i)a.push(c);if(n.length>0){let c=n.map(nd).join(" ");a.push(` "1: :_values 'command' ${c}"`),a.push(' "*::arg:->args"')}t.push(" _arguments -C \\");for(let c=0;c<a.length;c++){let d=c===a.length-1;t.push(`${a[c]}${d?"":" \\"}`)}if(n.length>0){t.push(""),t.push(" case $state in"),t.push(" (args)"),t.push(" case $line[1] in");for(let c of n){let d=vr([...o,xe(c)]);t.push(` (${xe(c)}) ${d} ;;`)}t.push(" esac"),t.push(" ;;"),t.push(" esac")}t.push("}"),t.push("");for(let c of n)Er(c,[...o,xe(c)],t)}function ed(e){return String(e||"").replace(/\r?\n/g," ").replace(/"/g,'\\"').replace(/\[/g,"\\[").replace(/\]/g,"\\]")}function od(e){return String(e||"").replace(/\r?\n/g," ").replace(/'/g,"'\\''").replace(/\[/g,"\\[").replace(/\]/g,"\\]")}function td(e){let o=e.short||void 0,t=e.long||void 0;if(!o&&!t)return null;let n=ed(e.description||"");return o&&t?` "(${t} ${o})"{${t},${o}}"[${n}]"`:` "${t||o}[${n}]"`}function nd(e){let o=od(e.description()||"");return`'${xe(e)}[${o}]'`}function sd(e){let o=ro(e),t=[];t.push("# bash completion for agentx"),t.push("_agentx_complete() {"),t.push(" local cur prev words cword"),t.push(" COMPREPLY=()"),t.push(' cur="${COMP_WORDS[COMP_CWORD]}"'),t.push(' prev="${COMP_WORDS[COMP_CWORD-1]}"'),t.push(""),t.push(` local top="${o.map(xe).join(" ")}"`),t.push(""),t.push(" if [[ $COMP_CWORD -eq 1 ]]; then"),t.push(' COMPREPLY=( $(compgen -W "$top" -- "$cur") )'),t.push(" return 0"),t.push(" fi"),t.push(""),t.push(' local cmd="${COMP_WORDS[1]}"'),t.push(' local sub2="${COMP_WORDS[2]:-}"'),t.push(' case "$cmd" in');for(let n of o){let s=ro(n).map(xe),i=xr(n);if(t.push(` ${xe(n)})`),s.length>0){t.push(" if [[ $COMP_CWORD -eq 2 ]]; then"),t.push(` COMPREPLY=( $(compgen -W "${s.join(" ")} ${i}" -- "$cur") )`),t.push(" return 0"),t.push(" fi"),t.push(' case "$sub2" in');for(let r of ro(n)){let a=xr(r);t.push(` ${xe(r)})`),t.push(` COMPREPLY=( $(compgen -W "${a}" -- "$cur") )`),t.push(" return 0"),t.push(" ;;")}t.push(" esac")}else t.push(` COMPREPLY=( $(compgen -W "${i}" -- "$cur") )`);t.push(" ;;")}return t.push(" esac"),t.push("}"),t.push(""),t.push("complete -F _agentx_complete agentx"),t.push(""),t.join(`
427
+ `)}function xr(e){let o=[];for(let t of pt(e))t.long&&o.push(t.long),t.short&&o.push(t.short);return o.join(" ")}function id(e){let o=[];o.push("# fish completion for agentx"),o.push(""),o.push("complete -c agentx -f"),o.push(""),o.push("# top-level commands");for(let t of ro(e)){let n=os(t.description()||"");o.push(`complete -c agentx -n "__fish_use_subcommand" -a "${xe(t)}" -d "${n}"`)}o.push("");for(let t of pt(e)){let n=Zn(t,["__fish_use_subcommand"]);n&&o.push(n)}o.push("");for(let t of ro(e)){let n=xe(t);o.push(`# ${n}`);for(let s of pt(t)){let i=Zn(s,[`__fish_seen_subcommand_from ${n}`]);i&&o.push(i)}for(let s of ro(t)){let i=os(s.description()||"");o.push(`complete -c agentx -n "__fish_seen_subcommand_from ${n}; and not __fish_seen_subcommand_from ${ro(t).map(xe).join(" ")}" -a "${xe(s)}" -d "${i}"`);for(let r of pt(s)){let a=Zn(r,[`__fish_seen_subcommand_from ${xe(s)}`]);a&&o.push(a)}}o.push("")}return o.join(`
428
+ `)}function os(e){return String(e||"").replace(/\r?\n/g," ").replace(/\\/g,"\\\\").replace(/"/g,'\\"')}function Zn(e,o){let t=e.short?e.short.replace(/^-/,""):"",n=e.long?e.long.replace(/^--/,""):"";if(!t&&!n)return null;let s=os(e.description||""),r=["complete -c agentx",o.map(a=>`-n "${a}"`).join(" ")];return t&&r.push(`-s ${t}`),n&&r.push(`-l ${n}`),r.push(`-d "${s}"`),r.join(" ")}async function gh(){let e=await Bs(),o=new rd().name("agentx").description("self-hosted multi-agent orchestrator \u2014 Telegram, WhatsApp, crons, A2A mesh, wiki knowledge").version(e.version||"1.0.0","-v, --version","display the version number");o.hook("preAction",(t,n)=>{let s=Hs(n);Gs(s)&&zs("cli",s)});for(let t of[ni,ei,$o,De,qs,go,vt,eo,fo,Ze,nt,mr,Vo,Ke,jr])o.addCommand(t);for(let t of Ir)o.addCommand(t,{hidden:!0});return o.addHelpText("after",()=>{let t=Ir.map(n=>n.name()).sort();return["",`Advanced (${t.length}, hidden \u2014 run \`agentx <command> --help\` for any of them):`,ad(t,74," "),""].join(`
429
+ `)}),o}var Ir=[ae,rt,xo,Te,Xo,_o,de,Be,oo,ho,Uo,Wo,Xe,Zo,Rt,cr,We,To,Do,mt,mn,so,Ro,pn,Ft,Jo,io,yr,gr,lr];function ad(e,o,t){let n=[],s="";for(let i of e)s&&s.length+i.length+2>o&&(n.push(t+s),s=""),s+=(s?", ":"")+i;return s&&n.push(t+s),n.join(`
430
+ `)}export{gh as a};
431
+ //# sourceMappingURL=chunk-KUCCW2VG.js.map