agentix-cli 0.6.0 → 0.9.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 (58) hide show
  1. package/README.md +337 -116
  2. package/dist/agent-QYQVGKLM.js +2 -0
  3. package/dist/chunk-34UAFPK4.js +10 -0
  4. package/dist/chunk-34UAFPK4.js.map +1 -0
  5. package/dist/{chunk-X7UN6JAA.js → chunk-6MKAXEE5.js} +2 -2
  6. package/dist/chunk-BAIQJHQF.js +64 -0
  7. package/dist/chunk-BAIQJHQF.js.map +1 -0
  8. package/dist/chunk-BHDLKX3G.js +6 -0
  9. package/dist/chunk-BHDLKX3G.js.map +1 -0
  10. package/dist/chunk-CJ45Y2IR.js +12 -0
  11. package/dist/chunk-CJ45Y2IR.js.map +1 -0
  12. package/dist/chunk-DSYKYZMT.js +2 -0
  13. package/dist/chunk-DSYKYZMT.js.map +1 -0
  14. package/dist/chunk-IVVVYXPH.js +136 -0
  15. package/dist/chunk-IVVVYXPH.js.map +1 -0
  16. package/dist/{chunk-MGMZNJCE.js → chunk-KXZMYLHQ.js} +27 -27
  17. package/dist/chunk-KXZMYLHQ.js.map +1 -0
  18. package/dist/chunk-SBUX74OU.js +108 -0
  19. package/dist/chunk-SBUX74OU.js.map +1 -0
  20. package/dist/chunk-X32247GM.js +2 -0
  21. package/dist/chunk-X32247GM.js.map +1 -0
  22. package/dist/chunk-ZRYBSI5G.js +23 -0
  23. package/dist/chunk-ZRYBSI5G.js.map +1 -0
  24. package/dist/cli.js +289 -9
  25. package/dist/cli.js.map +1 -1
  26. package/dist/compaction-E3MQRLTL.js +28 -0
  27. package/dist/compaction-E3MQRLTL.js.map +1 -0
  28. package/dist/config-MSWKC466.js +2 -0
  29. package/dist/debug-N3B5NVJU.js +2 -0
  30. package/dist/heal-OTGT5HHJ.js +2 -0
  31. package/dist/heal-OTGT5HHJ.js.map +1 -0
  32. package/dist/index.d.ts +707 -164
  33. package/dist/index.js +2 -2
  34. package/dist/index.js.map +1 -1
  35. package/dist/memory-extract-RGPUPMKU.js +2 -0
  36. package/dist/memory-extract-RGPUPMKU.js.map +1 -0
  37. package/dist/{providers-AVYG63KK.js → providers-G3WZ4RVJ.js} +2 -2
  38. package/dist/providers-G3WZ4RVJ.js.map +1 -0
  39. package/dist/registry-FP6FUOXF.js +2 -0
  40. package/dist/registry-FP6FUOXF.js.map +1 -0
  41. package/dist/subagent-WV7QKQ3L.js +3 -0
  42. package/dist/subagent-WV7QKQ3L.js.map +1 -0
  43. package/dist/usage-dashboard-2TJQBFSH.js +404 -0
  44. package/dist/usage-dashboard-2TJQBFSH.js.map +1 -0
  45. package/package.json +4 -1
  46. package/dist/agent-K2YOEOJ5.js +0 -2
  47. package/dist/chunk-F73GPYCO.js +0 -106
  48. package/dist/chunk-F73GPYCO.js.map +0 -1
  49. package/dist/chunk-MGMZNJCE.js.map +0 -1
  50. package/dist/chunk-WT2UJMBC.js +0 -68
  51. package/dist/chunk-WT2UJMBC.js.map +0 -1
  52. package/dist/chunk-Z4GC5D6D.js +0 -12
  53. package/dist/chunk-Z4GC5D6D.js.map +0 -1
  54. package/dist/heal-UV5A6B5T.js +0 -2
  55. /package/dist/{agent-K2YOEOJ5.js.map → agent-QYQVGKLM.js.map} +0 -0
  56. /package/dist/{chunk-X7UN6JAA.js.map → chunk-6MKAXEE5.js.map} +0 -0
  57. /package/dist/{heal-UV5A6B5T.js.map → config-MSWKC466.js.map} +0 -0
  58. /package/dist/{providers-AVYG63KK.js.map → debug-N3B5NVJU.js.map} +0 -0
package/dist/cli.js CHANGED
@@ -1,11 +1,291 @@
1
1
  #!/usr/bin/env node
2
- import{d as j,e as R,p as G,q as z}from"./chunk-WT2UJMBC.js";import"./chunk-Z4GC5D6D.js";import"./chunk-M7HKBG3V.js";import{a as H}from"./chunk-4YCH6IZV.js";import{Command as ne}from"commander";import{Command as V}from"commander";import r from"chalk";var v=new V().name("daemon").description("manage the agentx daemon \u2014 start, stop, status, logs");v.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:n}=await import("child_process"),a=["dist/cli.js","daemon","start"];e.config&&a.push("-c",e.config),e.port&&a.push("--port",e.port);let t=n("node",a,{detached:!0,stdio:["ignore","ignore","ignore"],cwd:process.cwd()});t.unref();let{writeFileSync:i}=await import("fs");i("/tmp/agentx-daemon.pid",String(t.pid)),console.log(r.green(`Daemon started in background (PID: ${t.pid})`)),console.log(r.dim(" Logs: tail -f /tmp/agentx-daemon.log")),console.log(r.dim(" Stop: agentx daemon stop"));return}await new G(e.config).start()});v.command("stop").description("stop the running daemon").action(async()=>{let{readFileSync:e,existsSync:o,unlinkSync:n}=await import("fs"),{resolve:a}=await import("path"),t=[a(process.cwd(),".agentx/daemon.pid"),"/tmp/agentx-daemon.pid"];for(let c of t)if(o(c))try{let d=parseInt(e(c,"utf-8").trim(),10);process.kill(d,"SIGTERM"),await new Promise(l=>setTimeout(l,2e3));try{n(c)}catch{}console.log(r.green(`Daemon stopped (PID: ${d})`));return}catch{try{n(c)}catch{}}let{execSync:i}=await import("child_process");try{i("pkill -f 'node dist/cli.js daemon'",{stdio:"ignore"}),console.log(r.green("Daemon stopped"))}catch{console.log(r.yellow("No daemon process found"))}});v.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=j(e.config),[n,a]=o.node.bind.split(":"),t=`http://${n||"127.0.0.1"}:${a}/health`,c=await(await fetch(t,{signal:AbortSignal.timeout(3e3)})).json();if(e.json){console.log(JSON.stringify(c,null,2));return}console.log(),console.log(r.bold(` ${c.node.name}`)+r.dim(` (${c.node.id})`)),console.log(r.green(" Status: running")+r.dim(` \u2014 uptime ${Z(c.uptime)}`)),console.log(),console.log(r.bold(" Agents:"));for(let l of c.agents){let u=l.active>0?r.yellow(` [${l.active} active]`):"",k=l.errors>0?r.red(` (${l.errors} errors)`):"";console.log(` ${r.cyan(l.name)} ${r.dim(`(${l.tier})`)}${u}${k}`),console.log(r.dim(` ${l.workspace}`))}let d=c.crons.filter(l=>l.enabled);if(c.crons.length){console.log(),console.log(r.bold(` Crons: ${d.length} enabled / ${c.crons.length} total`));for(let l of c.crons){let u=l.enabled?r.green("\u25CF"):r.dim("\u25CB"),k=l.nextRun?r.dim(` next: ${new Date(l.nextRun).toLocaleTimeString()}`):"";console.log(` ${u} ${l.id}${k}`)}}if(c.mesh?.length){console.log(),console.log(r.bold(" Mesh:"));for(let l of c.mesh){let u=l.healthy?r.green("\u25CF"):r.red("\u25CF"),k=l.skills?.length?r.dim(` (${l.skills.length} agents)`):"";console.log(` ${u} ${l.peer} ${r.dim(l.peerUrl)}${k}`)}}console.log()}catch(o){if(o.cause?.code==="ECONNREFUSED"||o.name==="TimeoutError")console.log(r.red(" Daemon is not running")),console.log(r.dim(" Start with: agentx daemon start"));else try{let n=j(e.config),a=R(n);if(console.log(),console.log(r.bold(` ${n.node.name}`)+r.dim(` (${n.node.id})`)),console.log(r.yellow(" Status: stopped")),console.log(r.dim(` ${Object.keys(n.agents).length} agents, ${Object.keys(n.crons).length} crons configured`)),a.length){console.log();for(let t of a)console.log(r.yellow(` \u26A0 ${t}`))}console.log()}catch(n){console.log(r.red(` ${n.message}`))}}});v.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:n}=await import("child_process"),a="/tmp/agentx-daemon.log",{existsSync:t}=await import("fs");if(!t(a)){console.log(r.yellow("No log file found at /tmp/agentx-daemon.log"));return}if(e.follow){let i=n("tail",["-f","-n",e.lines,a],{stdio:"inherit"});process.on("SIGINT",()=>{i.kill(),process.exit(0)})}else o(`tail -n ${e.lines} ${a}`,{stdio:"inherit"})});v.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,n)=>{let a=o.join(" ");try{let t=j(n.config),[i,c]=t.node.bind.split(":"),d=`http://${i||"127.0.0.1"}:${c}`,l,u;n.peer?(l=`${d}/mesh/task`,u={peer:n.peer,message:a,agent:e}):(l=`${d}/task`,u={agent:e,message:a}),console.log(r.dim(` Sending to ${n.peer?`${n.peer}/${e}`:e}...`));let y=await(await fetch(l,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(u)})).json();n.json?console.log(JSON.stringify(y,null,2)):y.error?console.log(r.red(` Error: ${y.error}`)):(console.log(),console.log(y.content||y.response||"No response"),y.duration&&console.log(r.dim(`
3
- (${(y.duration/1e3).toFixed(1)}s)`)))}catch(t){console.log(r.red(` Failed: ${t.message}`)),console.log(r.dim(" Is the daemon running? Try: agentx daemon status"))}});v.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","clawd").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:n}=await import("child_process"),a=o.identity?`-e "ssh -i ${o.identity}"`:"",t=`${o.user}@${e}:${o.path}/dist/`;if(!o.skipChecks){try{console.log(r.dim(" Running tests...")),n("npx vitest run --reporter=dot",{stdio:"pipe",cwd:process.cwd()}),console.log(r.green(" \u2713 Tests passed"))}catch{console.log(r.red(" \u2717 Tests failed \u2014 aborting deploy")),console.log(r.dim(" Use --skip-checks to deploy anyway"));return}let{existsSync:i,statSync:c}=await import("fs"),{resolve:d}=await import("path"),l=d(process.cwd(),"dist/cli.js");if(!i(l)){console.log(r.red(" \u2717 dist/ not found \u2014 run build first"));return}Date.now()-c(l).mtimeMs>3e5&&(console.log(r.yellow(" \u26A0 dist/ is older than 5 minutes \u2014 rebuilding...")),n("npx tsup --no-dts",{stdio:"pipe",cwd:process.cwd()}),console.log(r.green(" \u2713 Build complete")))}console.log(r.dim(` Deploying to ${o.user}@${e}:${o.path}...`));try{if(n(`rsync -avzL --delete ${a} dist/ ${t}`,{stdio:"inherit",cwd:process.cwd()}),console.log(r.green(" \u2713 Deploy complete")),o.restart){console.log(r.dim(" Restarting remote daemon..."));let i=o.identity?`ssh -i ${o.identity}`:"ssh";n(`${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(r.green(" Remote daemon restarted"))}}catch(i){console.log(r.red(` Deploy failed: ${i.message}`))}});v.action(async e=>{await new G(e.config).start()});function Z(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 ee}from"commander";import $ from"chalk";import{existsSync as O,readFileSync as oe,writeFileSync as _,mkdirSync as q}from"fs";import{resolve as C}from"path";var X=new ee().name("init").description("initialize agentx configuration in the current directory").option("--force","overwrite existing config").action(async e=>{let o=process.cwd(),n=C(o,"agentx.json"),a=C(o,".env"),t=C(o,".agentx/sessions");if(console.log(),console.log($.bold(" agentx init")),console.log(),O(n)&&!e.force){console.log($.yellow(" agentx.json already exists. Use --force to overwrite.")),console.log();return}let i=[C(o,"agentx.example.json"),C(o,"node_modules/agentix-cli/agentx.example.json")],c;for(let l of i)if(O(l)){c=oe(l,"utf-8");break}c||(c=JSON.stringify({node:{id:H("os").hostname().toLowerCase().replace(/[^a-z0-9-]/g,"-"),name:H("os").hostname(),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)),_(n,c),console.log($.green(" \u2713 Created agentx.json")),O(a)||(_(a,["# AgentX environment variables","# ANTHROPIC_API_KEY=sk-ant-...","# TG_BOT_TOKEN=123456:ABC...","# MESH_TOKEN=your-mesh-secret",""].join(`
4
- `)),console.log($.green(" \u2713 Created .env template"))),O(t)||(q(t,{recursive:!0}),console.log($.green(" \u2713 Created .agentx/sessions/")));let d=C(o,".claude");O(d)||(q(d,{recursive:!0}),console.log($.green(" \u2713 Created .claude/"))),console.log(),console.log($.bold(" Next steps:")),console.log(` 1. Edit ${$.cyan("agentx.json")} \u2014 configure agents, channels, crons`),console.log(` 2. Edit ${$.cyan(".env")} \u2014 add API keys and bot tokens`),console.log(` 3. Run ${$.cyan("agentx daemon start")} \u2014 start the daemon`),console.log(` 4. Run ${$.cyan("agentx daemon status")} \u2014 verify everything`),console.log()});import{Command as S}from"commander";import s from"chalk";import w from"prompts";import{readFileSync as M,writeFileSync as E,existsSync as h,mkdirSync as A,cpSync as F,readdirSync as Y}from"fs";import{resolve as g,join as L}from"path";function p(){let e=g(process.cwd(),"agentx.json");if(!h(e))throw new Error("No agentx.json found. Run: agentx init");return JSON.parse(M(e,"utf-8"))}function b(e){E(g(process.cwd(),"agentx.json"),JSON.stringify(e,null,2))}var I=new S().name("agent").description("manage agents \u2014 add, list, remove");I.command("list").alias("ls").description("list configured agents").action(()=>{let e=p();console.log();for(let[o,n]of Object.entries(e.agents||{})){let a=n.mentions?.join(", ")||"";console.log(` ${s.cyan(o)} \u2014 ${n.name} (${n.tier})`),console.log(s.dim(` workspace: ${n.workspace}`)),a&&console.log(s.dim(` mentions: ${a}`))}console.log()});I.command("add").description("add a new agent interactively").action(async()=>{let e=p(),o=await w([{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:g(process.cwd(),"agents/")},{type:"select",name:"tier",message:"Execution tier",choices:[{title:"claude-code (subscription, full features)",value:"claude-code"},{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 n=o.workspace.endsWith(o.id)?o.workspace:L(o.workspace,o.id);A(g(n,".claude/skills"),{recursive:!0});let a=`# ${o.name}
5
-
6
- ${o.systemPrompt}
7
- `;E(g(n,"CLAUDE.md"),a),E(g(n,".claude/settings.json"),JSON.stringify({permissions:{allow:["Bash(git *)","Bash(curl *)","Bash(node *)","Read","Write","Edit","Glob","Grep","WebFetch","WebSearch"]}},null,2));let t=g(process.cwd(),"src/wiki/SKILL.md"),i=g(__dirname,"../wiki/SKILL.md"),c=h(t)?t:i;h(c)&&(A(g(n,".claude/skills/wiki"),{recursive:!0}),F(c,g(n,".claude/skills/wiki/SKILL.md"))),e.agents=e.agents||{},e.agents[o.id]={name:o.name,workspace:n,tier:o.tier,model:o.model,mentions:o.mentions?.map(d=>d.trim()).filter(Boolean)||[],maxConcurrent:o.maxConcurrent,systemPrompt:o.systemPrompt,permissionMode:"default"},b(e),console.log(s.green(`
8
- Agent "${o.id}" added`)),console.log(s.dim(` Workspace: ${n}`)),console.log(s.dim(" Wiki skill installed")),console.log(s.dim(" CLAUDE.md + settings.json created")),console.log()});I.command("remove <id>").alias("rm").description("remove an agent from config (keeps workspace)").action(e=>{let o=p();if(!o.agents?.[e]){console.log(s.red(` Agent "${e}" not found`));return}delete o.agents[e],b(o),console.log(s.green(` Agent "${e}" removed from config (workspace preserved)`))});var B=new S().name("channel").description("manage channels \u2014 add telegram/whatsapp/discord, list");B.command("list").alias("ls").description("list configured channels and bindings").action(()=>{let e=p();console.log();let o=e.channels?.telegram;if(o?.enabled){console.log(s.bold(" Telegram:"));for(let[i,c]of Object.entries(o.accounts||{}))console.log(` ${s.cyan(i)} -> agent: ${c.agentBinding}`)}else console.log(s.dim(" Telegram: disabled"));let n=e.channels?.whatsapp;if(n?.enabled){console.log(s.bold(" WhatsApp:")),console.log(` default agent: ${n.defaultAgent||"(none)"}`),console.log(` session: ${n.sessionDir||".agentx/whatsapp-sessions"}`);for(let i of n.routes||[]){let c=i.contact?`contact ${i.contact}`:`group "${i.group}"`;console.log(` ${c} -> agent: ${i.agent}`)}}else console.log(s.dim(" WhatsApp: disabled"));let a=e.channels?.discord;a?.enabled?(console.log(s.bold(" Discord:")),console.log(` agent: ${a.agentBinding||"(none)"}`)):console.log(s.dim(" Discord: disabled"));let t=e.channels?.gitlab;if(t?.enabled){console.log(s.bold(" GitLab:")),console.log(` host: ${t.host}`),console.log(` webhook: :${t.webhookPort}`);for(let i of t.routes||[])console.log(` ${i.project} -> agent: ${i.agent}`)}else console.log(s.dim(" GitLab: disabled"));console.log()});B.command("add").description("add a channel (telegram, whatsapp, or discord)").action(async()=>{let e=p(),o=Object.keys(e.agents||{});if(!o.length){console.log(s.red(" No agents configured. Run: agentx agent add"));return}let n=o.map(t=>({title:`${t} (${e.agents[t].name})`,value:t})),{channelType:a}=await w({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(a){if(e.channels=e.channels||{},a==="telegram"){let t=await w([{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:n}]);if(!t.accountName||!t.token)return;try{let c=await(await fetch(`https://api.telegram.org/bot${t.token}/getMe`)).json();if(!c.ok)throw new Error(c.description);console.log(s.green(` Bot verified: @${c.result.username}`));let d=`@${c.result.username}`,l=e.agents[t.agentBinding].mentions||[];l.includes(d)||(l.push(d,c.result.username),e.agents[t.agentBinding].mentions=l)}catch(i){console.log(s.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[t.accountName]={token:t.token,agentBinding:t.agentBinding},b(e),console.log(s.green(` Telegram "${t.accountName}" added -> ${t.agentBinding}`))}if(a==="whatsapp"){let t=await w([{type:"select",name:"defaultAgent",message:"Default agent for WhatsApp messages",choices:n},{type:"text",name:"sessionDir",message:"Session directory",initial:".agentx/whatsapp-sessions"},{type:"confirm",name:"addRoute",message:"Add a contact/group route?",initial:!0}]);if(!t.defaultAgent)return;let i=[],c=t.addRoute;for(;c;){let d=await w([{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:l=>l==="contact"?"Phone number (e.g. +21624309128)":"Group name (partial match)"},{type:"select",name:"agent",message:"Route to agent",choices:n},{type:"confirm",name:"more",message:"Add another route?",initial:!1}]);d.value&&d.agent&&i.push({[d.type]:d.value,agent:d.agent}),c=d.more}e.channels.whatsapp={enabled:!0,sessionDir:t.sessionDir,defaultAgent:t.defaultAgent,routes:i},b(e),A(g(process.cwd(),t.sessionDir),{recursive:!0}),console.log(s.green(` WhatsApp enabled (${i.length} routes, default: ${t.defaultAgent})`)),console.log(s.dim(" Start daemon to scan QR code: agentx daemon start"))}if(a==="discord"){let t=await w([{type:"text",name:"token",message:"Discord bot token (from developer portal)"},{type:"select",name:"agentBinding",message:"Bind to agent",choices:n}]);if(!t.token)return;e.channels.discord={enabled:!0,token:t.token,agentBinding:t.agentBinding},b(e),console.log(s.green(` Discord added -> ${t.agentBinding}`))}if(a==="gitlab"){let t=await w([{type:"text",name:"host",message:"GitLab instance URL",initial:"https://gitlab.noqta.tn"},{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(!t.token)return;let i=[],c=t.addRoute;for(;c;){let d=await w([{type:"text",name:"project",message:"GitLab project path (e.g. noqta/mtgl-v2, or * for default)"},{type:"select",name:"agent",message:"Route to agent",choices:n},{type:"confirm",name:"more",message:"Add another route?",initial:!1}]);d.project&&d.agent&&i.push({project:d.project,agent:d.agent}),c=d.more}e.channels.gitlab={enabled:!0,host:t.host,token:t.token,webhookPort:t.webhookPort,webhookSecret:t.webhookSecret||void 0,routes:i},b(e),console.log(s.green(` GitLab enabled (${i.length} routes, webhook :${t.webhookPort})`)),console.log(s.dim(` Add webhook in GitLab: ${t.host}/<project>/-/hooks`)),console.log(s.dim(` URL: http://your-server:${t.webhookPort}/`)),t.webhookSecret&&console.log(s.dim(` Secret token: ${t.webhookSecret}`)),console.log(s.dim(" Events: Comments, Issues, Merge requests, Pipeline"))}console.log(s.dim(" Restart daemon to apply: agentx daemon stop && agentx daemon start")),console.log()}});var P=new S().name("cron").description("manage cron jobs \u2014 add, list, enable, disable");P.command("list").alias("ls").description("list cron jobs").action(()=>{let e=p();console.log();for(let[o,n]of Object.entries(e.crons||{})){let a=n.enabled?s.green("\u25CF"):s.dim("\u25CB");console.log(` ${a} ${s.cyan(o)} \u2014 ${n.schedule} (${n.timezone})`),console.log(s.dim(` agent: ${n.agent} | timeout: ${n.timeout}s`))}Object.keys(e.crons||{}).length||console.log(s.dim(" No cron jobs configured")),console.log()});P.command("add").description("add a cron job").action(async()=>{let e=p(),o=Object.keys(e.agents||{}),n=await w([{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(a=>({title:a,value:a}))},{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}]);!n.id||!n.schedule||(e.crons=e.crons||{},e.crons[n.id]={enabled:n.enabled,schedule:n.schedule,timezone:n.timezone,agent:n.agent,prompt:n.prompt,timeout:n.timeout,onError:"log"},b(e),console.log(s.green(` Cron "${n.id}" added (${n.enabled?"enabled":"disabled"})`)),console.log())});P.command("enable <id>").description("enable a cron job").action(e=>{let o=p();if(!o.crons?.[e]){console.log(s.red(` Cron "${e}" not found`));return}o.crons[e].enabled=!0,b(o),console.log(s.green(` Cron "${e}" enabled`))});P.command("disable <id>").description("disable a cron job").action(e=>{let o=p();if(!o.crons?.[e]){console.log(s.red(` Cron "${e}" not found`));return}o.crons[e].enabled=!1,b(o),console.log(s.green(` Cron "${e}" disabled`))});var T=new S().name("mesh").description("manage mesh peers \u2014 add, list, remove");T.command("list").alias("ls").description("list mesh peers").action(async()=>{let e=p();if(console.log(),!e.mesh?.enabled)console.log(s.dim(" Mesh: disabled"));else{console.log(s.bold(" Mesh peers:"));for(let o of e.mesh.peers||[]){let n=s.red("unreachable");try{let t=await(await fetch(`${o.url}/health`,{signal:AbortSignal.timeout(3e3)})).json();n=s.green(`healthy \u2014 ${t.agents?.length||0} agents`)}catch{}console.log(` ${s.cyan(o.name)} ${s.dim(o.url)} ${n}`)}}console.log()});T.command("add").description("add a mesh peer (another agentx server)").action(async()=>{let e=p(),o=await w([{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 a=await(await fetch(`${o.url}/.well-known/agent-card.json`,{signal:AbortSignal.timeout(5e3)})).json();console.log(s.green(` Connected: ${a.name} (${a.skills?.length||0} agents)`))}catch{console.log(s.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}:{}}),b(e),console.log(s.green(` Peer "${o.name}" added`)),console.log()}});T.command("remove <name>").alias("rm").description("remove a mesh peer").action(e=>{let o=p();o.mesh?.peers&&(o.mesh.peers=o.mesh.peers.filter(n=>n.name!==e),o.mesh.peers.length===0&&(o.mesh.enabled=!1),b(o),console.log(s.green(` Peer "${e}" removed`)))});var W=new S().name("skill").description("manage skills \u2014 add to agent(s), list");W.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 n=p(),a=g(e);if(!h(a)){console.log(s.red(` Skill not found: ${a}`));return}let t=h(L(a,"SKILL.md"))?a.split("/").pop():a.replace(/\/SKILL\.md$/,"").split("/").pop(),i;if(o.all)i=Object.keys(n.agents||{});else if(o.agent)i=o.agent;else{let c=Object.keys(n.agents||{});i=(await w({type:"multiselect",name:"agents",message:"Install to which agents?",choices:c.map(l=>({title:`${l} (${n.agents[l].name})`,value:l}))})).agents||[]}for(let c of i){let d=n.agents[c];if(!d){console.log(s.yellow(` Agent "${c}" not found, skipping`));continue}let l=g(d.workspace,".claude/skills",t);A(l,{recursive:!0}),h(L(a,"SKILL.md"))?F(a,l,{recursive:!0}):a.endsWith(".md")&&F(a,L(l,"SKILL.md")),console.log(s.green(` ${t} -> ${c} (${d.workspace})`))}console.log()});W.command("list").alias("ls").description("list skills per agent").action(()=>{let e=p();console.log();for(let[o,n]of Object.entries(e.agents||{})){let a=g(n.workspace,".claude/skills"),t=h(a)?Y(a).filter(i=>!i.startsWith(".")):[];console.log(` ${s.cyan(o)}: ${t.length?t.join(", "):s.dim("(none)")}`)}console.log()});var J=new S().name("hook").description("manage hooks \u2014 add to agent workspace");J.command("add <agent>").description("add a hook to an agent's workspace settings").action(async e=>{let n=p().agents?.[e];if(!n){console.log(s.red(` Agent "${e}" not found`));return}let a=g(n.workspace,".claude/settings.json"),t=h(a)?JSON.parse(M(a,"utf-8")):{},i=await w([{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:l=>l==="command"?"Shell command":"URL"},{type:"text",name:"matcher",message:"Matcher regex (optional, e.g. 'Bash' to match tool)"}]);if(!i.event||!i.value)return;t.hooks=t.hooks||{},t.hooks[i.event]=t.hooks[i.event]||[];let d={hooks:[{type:i.type,...i.type==="command"?{command:i.value}:{url:i.value}}]};i.matcher&&(d.matcher=i.matcher),t.hooks[i.event].push(d),A(g(n.workspace,".claude"),{recursive:!0}),E(a,JSON.stringify(t,null,2)),console.log(s.green(` Hook added to ${e}: ${i.event} -> ${i.type}`)),console.log()});var K=new S().name("migrate").description("import configuration from OpenClaw or other tools");K.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 n=[e,g(process.env.HOME||"",".openclaw/openclaw.json"),g(process.env.HOME||"",".openclaw/clawdbot.json")].filter(Boolean),a;for(let m of n)if(m&&h(m)){a=m;break}if(!a){console.log(s.red(" OpenClaw config not found. Pass the path: agentx migrate openclaw /path/to/config.json"));return}console.log(s.dim(` Reading: ${a}`));let t=JSON.parse(M(a,"utf-8")),i=h(g(process.cwd(),"agentx.json"))?p():{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:[]}},c=t.agents?.list||[],d=t.bindings||[],l={};for(let m of d)m.match?.channel==="telegram"&&(l[m.match.accountId]=m.agentId);c.find(m=>m.id==="main"||m.default)&&(l.default="main");let u=0;for(let m of c){if(m.workspace?.startsWith("/data"))continue;let x=m.id.replace(/-agent$/,"").replace(/^main$/,"atlas"),f=m.groupChat?.mentionPatterns||[];f.some(D=>D===x)||f.push(x),i.agents[x]={name:m.name,workspace:m.workspace,tier:"claude-code",model:"claude-sonnet-4-6",mentions:f,maxConcurrent:m.id==="main"?2:1,systemPrompt:`You are ${m.name} on AgentX.`,permissionMode:"bypassPermissions"},u++,o.dryRun||A(g(m.workspace,".claude/skills"),{recursive:!0})}console.log(` ${s.green(u)} agents imported`);let k=t.channels?.telegram?.accounts||{},y=0;for(let[m,x]of Object.entries(k)){if(!x.botToken)continue;let D=l[m]?.replace(/-agent$/,"").replace(/^main$/,"atlas")||"atlas";i.channels.telegram.accounts[m]={token:x.botToken,agentBinding:D},i.channels.telegram.enabled=!0,y++}console.log(` ${s.green(y)} Telegram accounts imported`);let N=g(a,"../cron/jobs.json");if(h(N)){let m=JSON.parse(M(N,"utf-8")),x=0;for(let f of m.jobs||[]){let D=f.name||f.id,Q=f.agentId?.replace(/-agent$/,"").replace(/^main$/,"atlas")||"atlas";i.crons[D]={enabled:f.enabled||!1,schedule:f.schedule?.expr||"0 9 * * *",timezone:f.schedule?.tz||"UTC",agent:Q,prompt:f.payload?.message||"",timeout:f.payload?.timeoutSeconds||600,onError:"log"},x++}console.log(` ${s.green(x)} cron jobs imported`)}o.dryRun?(console.log(s.yellow(`
9
- Dry run \u2014 nothing written`)),console.log(JSON.stringify(i,null,2))):(b(i),console.log(s.green(`
10
- Migration complete! Review agentx.json and run: agentx daemon start`))),console.log()});var U=new S().name("config").description("validate and inspect configuration");U.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=j(e.config);console.log(s.green(" \u2713 Config valid")),console.log(s.dim(` Node: ${o.node.name} (${o.node.id})`)),console.log(s.dim(` Bind: ${o.node.bind}`)),console.log(s.dim(` Agents: ${Object.keys(o.agents).length}`)),console.log(s.dim(` Crons: ${Object.keys(o.crons).length}`)),console.log(s.dim(` Mesh peers: ${o.mesh.peers.length}`));let n=Object.keys(o.channels.telegram.accounts).length;console.log(s.dim(` Telegram: ${o.channels.telegram.enabled?`${n} accounts`:"disabled"}`)),console.log(s.dim(` WhatsApp: ${o.channels.whatsapp.enabled?`${o.channels.whatsapp.routes.length} routes`:"disabled"}`)),console.log(s.dim(` Discord: ${o.channels.discord?.enabled?"enabled":"disabled"}`));let a=R(o);if(a.length){console.log(),console.log(s.yellow(" Warnings:"));for(let t of a)console.log(s.yellow(` \u26A0 ${t}`))}else console.log(s.green(" \u2713 All workspaces valid"));console.log();for(let[t,i]of Object.entries(o.agents)){let c=i.workspace,d=h(g(c,".claude")),l=h(g(c,"CLAUDE.md")),u=h(g(c,".claude/settings.json")),k=h(g(c,".claude/skills"))?Y(g(c,".claude/skills")).filter(N=>!N.startsWith(".")).length:0,y=[d?s.green("\u2713"):s.red("\u2717"),".claude",l?s.green("\u2713"):s.yellow("\u25CB"),"CLAUDE.md",u?s.green("\u2713"):s.yellow("\u25CB"),"settings",`${k} skills`];console.log(` ${s.cyan(t)}: ${y.join(" ")}`)}}catch(o){console.log(s.red(` \u2717 ${o.message}`))}console.log()});U.command("show").description("print the resolved configuration").option("-c, --config <path>","path to agentx.json").action(e=>{try{let o=j(e.config);console.log(JSON.stringify(o,null,2))}catch(o){console.log(s.red(o.message))}});process.on("SIGINT",()=>process.exit(0));process.on("SIGTERM",()=>process.exit(0));async function te(){let e=await z(),o=new ne().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");if(o.addCommand(v).addCommand(X).addCommand(I).addCommand(B).addCommand(P).addCommand(T).addCommand(W).addCommand(J).addCommand(K).addCommand(U),process.argv.slice(2).length===0){o.outputHelp();return}o.parse()}te();
2
+ import{g as ge,h as $e}from"./chunk-BAIQJHQF.js";import{f as K}from"./chunk-IVVVYXPH.js";import"./chunk-CJ45Y2IR.js";import"./chunk-X32247GM.js";import{b as M,c as ne}from"./chunk-BHDLKX3G.js";import"./chunk-34UAFPK4.js";import"./chunk-M7HKBG3V.js";import"./chunk-DSYKYZMT.js";import"./chunk-ZRYBSI5G.js";import{a as ye}from"./chunk-4YCH6IZV.js";import{Command as Fe}from"commander";import{Command as Ee}from"commander";import m from"chalk";var L=new Ee().name("daemon").description("manage the agentx daemon \u2014 start, stop, status, logs");L.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"),h=["dist/cli.js","daemon","start"];e.config&&h.push("-c",e.config),e.port&&h.push("--port",e.port);let p=d("node",h,{detached:!0,stdio:["ignore","ignore","ignore"],cwd:process.cwd()});p.unref();let{writeFileSync:g}=await import("fs");g("/tmp/agentx-daemon.pid",String(p.pid)),console.log(m.green(`Daemon started in background (PID: ${p.pid})`)),console.log(m.dim(" Logs: tail -f /tmp/agentx-daemon.log")),console.log(m.dim(" Stop: agentx daemon stop"));return}let{readFileSync:t,writeFileSync:s,mkdirSync:o,existsSync:n}=await import("fs"),{resolve:i}=await import("path"),r=i(process.cwd(),".agentx");n(r)||o(r,{recursive:!0});let c=i(r,"daemon.pid");if(n(c)){let d=parseInt(t(c,"utf-8").trim(),10);if(d&&d!==process.pid)try{process.kill(d,0),console.error(`Another agentx daemon is already running (PID ${d}). Exiting.`),process.exit(0)}catch{}}s(c,String(process.pid)),await new ge(e.config).start()});L.command("stop").description("stop the running daemon").action(async()=>{let{readFileSync:e,existsSync:t,unlinkSync:s}=await import("fs"),{resolve:o}=await import("path"),n=[o(process.cwd(),".agentx/daemon.pid"),"/tmp/agentx-daemon.pid"];for(let r of n)if(t(r))try{let c=parseInt(e(r,"utf-8").trim(),10);process.kill(c,"SIGTERM"),await new Promise(a=>setTimeout(a,2e3));try{s(r)}catch{}console.log(m.green(`Daemon stopped (PID: ${c})`));return}catch{try{s(r)}catch{}}let{execSync:i}=await import("child_process");try{i("pkill -f 'node dist/cli.js daemon'",{stdio:"ignore"}),console.log(m.green("Daemon stopped"))}catch{console.log(m.yellow("No daemon process found"))}});L.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 t=M(e.config),[s,o]=t.node.bind.split(":"),n=`http://${s||"127.0.0.1"}:${o}/health`,r=await(await fetch(n,{signal:AbortSignal.timeout(3e3)})).json();if(e.json){console.log(JSON.stringify(r,null,2));return}console.log(),console.log(m.bold(` ${r.node.name}`)+m.dim(` (${r.node.id})`)),console.log(m.green(" Status: running")+m.dim(` \u2014 uptime ${je(r.uptime)}`)),console.log(),console.log(m.bold(" Agents:"));for(let a of r.agents){let d=a.active>0?m.yellow(` [${a.active} active]`):"",h=a.errors>0?m.red(` (${a.errors} errors)`):"";console.log(` ${m.cyan(a.name)} ${m.dim(`(${a.tier})`)}${d}${h}`),console.log(m.dim(` ${a.workspace}`))}let c=r.crons.filter(a=>a.enabled);if(r.crons.length){console.log(),console.log(m.bold(` Crons: ${c.length} enabled / ${r.crons.length} total`));for(let a of r.crons){let d=a.enabled?m.green("\u25CF"):m.dim("\u25CB"),h=a.nextRun?m.dim(` next: ${new Date(a.nextRun).toLocaleTimeString()}`):"";console.log(` ${d} ${a.id}${h}`)}}if(r.mesh?.length){console.log(),console.log(m.bold(" Mesh:"));for(let a of r.mesh){let d=a.healthy?m.green("\u25CF"):m.red("\u25CF"),h=a.skills?.length?m.dim(` (${a.skills.length} agents)`):"";console.log(` ${d} ${a.peer} ${m.dim(a.peerUrl)}${h}`)}}console.log()}catch(t){if(t.cause?.code==="ECONNREFUSED"||t.name==="TimeoutError")console.log(m.red(" Daemon is not running")),console.log(m.dim(" Start with: agentx daemon start"));else try{let s=M(e.config),o=ne(s);if(console.log(),console.log(m.bold(` ${s.node.name}`)+m.dim(` (${s.node.id})`)),console.log(m.yellow(" Status: stopped")),console.log(m.dim(` ${Object.keys(s.agents).length} agents, ${Object.keys(s.crons).length} crons configured`)),o.length){console.log();for(let n of o)console.log(m.yellow(` \u26A0 ${n}`))}console.log()}catch(s){console.log(m.red(` ${s.message}`))}}});L.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:t,spawn:s}=await import("child_process"),o="/tmp/agentx-daemon.log",{existsSync:n}=await import("fs");if(!n(o)){console.log(m.yellow("No log file found at /tmp/agentx-daemon.log"));return}if(e.follow){let i=s("tail",["-f","-n",e.lines,o],{stdio:"inherit"});process.on("SIGINT",()=>{i.kill(),process.exit(0)})}else t(`tail -n ${e.lines} ${o}`,{stdio:"inherit"})});L.command("watch").description("stream live agent activity (SSE)").option("-c, --config <path>","path to agentx.json").action(async e=>{let o=`http://${(M(e.config).node.bind||"127.0.0.1:18800").replace("0.0.0.0","127.0.0.1")}/events`;console.log(m.dim(` Connecting to ${o}...`)),console.log(m.dim(` Press Ctrl+C to stop
3
+ `));try{let n=await fetch(o);if(!n.ok||!n.body){console.log(m.red(` Failed: ${n.status}`));return}let i=n.body.getReader(),r=new TextDecoder,c="";for(;;){let{done:a,value:d}=await i.read();if(a)break;c+=r.decode(d,{stream:!0});let h=c.split(`
4
+ `);c=h.pop()||"";for(let p of h)if(p.startsWith("data: "))try{let g=JSON.parse(p.slice(6)),y=new Date(g.time).toLocaleTimeString(),f=g.message?.replace(/^\[agentx\]\s*/,"")||"";f.includes("executing")?console.log(`${m.dim(y)} ${m.cyan("\u25B6")} ${f}`):f.includes("completed")?console.log(`${m.dim(y)} ${m.green("\u2713")} ${f}`):f.includes("error")||f.includes("Error")?console.log(`${m.dim(y)} ${m.red("\u2717")} ${f}`):f.includes("Routing")?console.log(`${m.dim(y)} ${m.yellow("\u2192")} ${f}`):f.includes("mention")?console.log(`${m.dim(y)} ${m.magenta("@")} ${f}`):f.includes("Skipping")?console.log(`${m.dim(y)} ${m.dim("\u2298")} ${m.dim(f)}`):console.log(`${m.dim(y)} ${f}`)}catch{}else p.startsWith("event: status")}}catch(n){console.log(m.red(` Connection failed: ${n.message}`)),console.log(m.dim(" Is the daemon running? Try: agentx daemon status"))}});L.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,t,s)=>{let o=t.join(" ");try{let n=M(s.config),[i,r]=n.node.bind.split(":"),c=`http://${i||"127.0.0.1"}:${r}`,a,d;s.peer?(a=`${c}/mesh/task`,d={peer:s.peer,message:o,agent:e}):(a=`${c}/task`,d={agent:e,message:o}),console.log(m.dim(` Sending to ${s.peer?`${s.peer}/${e}`:e}...`));let p=await(await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(d)})).json();s.json?console.log(JSON.stringify(p,null,2)):p.error?console.log(m.red(` Error: ${p.error}`)):(console.log(),console.log(p.content||p.response||"No response"),p.duration&&console.log(m.dim(`
5
+ (${(p.duration/1e3).toFixed(1)}s)`)))}catch(n){console.log(m.red(` Failed: ${n.message}`)),console.log(m.dim(" Is the daemon running? Try: agentx daemon status"))}});L.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,t)=>{let{execSync:s}=await import("child_process"),o=t.identity?`-e "ssh -i ${t.identity}"`:"",n=`${t.user}@${e}:${t.path}/dist/`;if(!t.skipChecks){try{console.log(m.dim(" Running tests...")),s("npx vitest run --reporter=dot",{stdio:"pipe",cwd:process.cwd()}),console.log(m.green(" \u2713 Tests passed"))}catch{console.log(m.red(" \u2717 Tests failed \u2014 aborting deploy")),console.log(m.dim(" Use --skip-checks to deploy anyway"));return}let{existsSync:i,statSync:r}=await import("fs"),{resolve:c}=await import("path"),a=c(process.cwd(),"dist/cli.js");if(!i(a)){console.log(m.red(" \u2717 dist/ not found \u2014 run build first"));return}Date.now()-r(a).mtimeMs>3e5&&(console.log(m.yellow(" \u26A0 dist/ is older than 5 minutes \u2014 rebuilding...")),s("npx tsup --no-dts",{stdio:"pipe",cwd:process.cwd()}),console.log(m.green(" \u2713 Build complete")))}console.log(m.dim(` Deploying to ${t.user}@${e}:${t.path}...`));try{if(s(`rsync -avzL --delete ${o} dist/ ${n}`,{stdio:"inherit",cwd:process.cwd()}),console.log(m.green(" \u2713 Deploy complete")),t.restart){console.log(m.dim(" Restarting remote daemon..."));let i=t.identity?`ssh -i ${t.identity}`:"ssh";s(`${i} ${t.user}@${e} "pkill -f 'node dist/cli.js daemon' 2>/dev/null; sleep 2; cd ${t.path} && nohup node dist/cli.js daemon start > /tmp/agentx-daemon.log 2>&1 &"`,{stdio:"inherit"}),console.log(m.green(" Remote daemon restarted"))}}catch(i){console.log(m.red(` Deploy failed: ${i.message}`))}});L.action(async e=>{await new ge(e.config).start()});function je(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 Re}from"commander";import D from"chalk";import{existsSync as q,readFileSync as Te,writeFileSync as we,mkdirSync as ke}from"fs";import{resolve as J}from"path";var ve=new Re().name("init").description("initialize agentx configuration in the current directory").option("--force","overwrite existing config").action(async e=>{let t=process.cwd(),s=J(t,"agentx.json"),o=J(t,".env"),n=J(t,".agentx/sessions");if(console.log(),console.log(D.bold(" agentx init")),console.log(),q(s)&&!e.force){console.log(D.yellow(" agentx.json already exists. Use --force to overwrite.")),console.log();return}let i=[J(t,"agentx.example.json"),J(t,"node_modules/agentix-cli/agentx.example.json")],r;for(let a of i)if(q(a)){r=Te(a,"utf-8");break}r||(r=JSON.stringify({node:{id:ye("os").hostname().toLowerCase().replace(/[^a-z0-9-]/g,"-"),name:ye("os").hostname(),bind:"127.0.0.1:18800"},providers:{claude:{apiKey:"${ANTHROPIC_API_KEY}"}},agents:{assistant:{name:"Assistant",workspace:t,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)),we(s,r),console.log(D.green(" \u2713 Created agentx.json")),q(o)||(we(o,["# AgentX environment variables","# ANTHROPIC_API_KEY=sk-ant-...","# TG_BOT_TOKEN=123456:ABC...","# MESH_TOKEN=your-mesh-secret",""].join(`
6
+ `)),console.log(D.green(" \u2713 Created .env template"))),q(n)||(ke(n,{recursive:!0}),console.log(D.green(" \u2713 Created .agentx/sessions/")));let c=J(t,".claude");q(c)||(ke(c,{recursive:!0}),console.log(D.green(" \u2713 Created .claude/"))),console.log(),console.log(D.bold(" Next steps:")),console.log(` 1. Edit ${D.cyan("agentx.json")} \u2014 configure agents, channels, crons`),console.log(` 2. Edit ${D.cyan(".env")} \u2014 add API keys and bot tokens`),console.log(` 3. Run ${D.cyan("agentx daemon start")} \u2014 start the daemon`),console.log(` 4. Run ${D.cyan("agentx daemon status")} \u2014 verify everything`),console.log()});import{Command as B}from"commander";import l from"chalk";import O from"prompts";import{readFileSync as se,writeFileSync as ae,existsSync as R,mkdirSync as _,cpSync as pe,readdirSync as xe}from"fs";import{resolve as I,join as oe}from"path";function j(){let e=I(process.cwd(),"agentx.json");if(!R(e))throw new Error("No agentx.json found. Run: agentx init");return JSON.parse(se(e,"utf-8"))}function N(e){ae(I(process.cwd(),"agentx.json"),JSON.stringify(e,null,2))}var X=new B().name("agent").description("manage agents \u2014 add, list, remove");X.command("list").alias("ls").description("list configured agents").action(()=>{let e=j();console.log();for(let[t,s]of Object.entries(e.agents||{})){let o=s.mentions?.join(", ")||"";console.log(` ${l.cyan(t)} \u2014 ${s.name} (${s.tier})`),console.log(l.dim(` workspace: ${s.workspace}`)),o&&console.log(l.dim(` mentions: ${o}`))}console.log()});X.command("add").description("add a new agent interactively").action(async()=>{let e=j(),t=await O([{type:"text",name:"id",message:"Agent ID (kebab-case)",validate:c=>/^[a-z0-9-]+$/.test(c)||"Use lowercase, numbers, hyphens"},{type:"text",name:"name",message:"Display name"},{type:"text",name:"workspace",message:"Workspace path",initial:I(process.cwd(),"agents/")},{type:"select",name:"tier",message:"Execution tier",choices:[{title:"claude-code (subscription, full features)",value:"claude-code"},{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(!t.id)return;let s=t.workspace.endsWith(t.id)?t.workspace:oe(t.workspace,t.id);_(I(s,".claude/skills"),{recursive:!0});let o=`# ${t.name}
7
+
8
+ ${t.systemPrompt}
9
+ `;ae(I(s,"CLAUDE.md"),o),ae(I(s,".claude/settings.json"),JSON.stringify({permissions:{allow:["Bash(git *)","Bash(curl *)","Bash(node *)","Read","Write","Edit","Glob","Grep","WebFetch","WebSearch"]}},null,2));let n=I(process.cwd(),"src/wiki/SKILL.md"),i=I(__dirname,"../wiki/SKILL.md"),r=R(n)?n:i;R(r)&&(_(I(s,".claude/skills/wiki"),{recursive:!0}),pe(r,I(s,".claude/skills/wiki/SKILL.md"))),e.agents=e.agents||{},e.agents[t.id]={name:t.name,workspace:s,tier:t.tier,model:t.model,mentions:t.mentions?.map(c=>c.trim()).filter(Boolean)||[],maxConcurrent:t.maxConcurrent,systemPrompt:t.systemPrompt,permissionMode:"default"},N(e),console.log(l.green(`
10
+ Agent "${t.id}" added`)),console.log(l.dim(` Workspace: ${s}`)),console.log(l.dim(" Wiki skill installed")),console.log(l.dim(" CLAUDE.md + settings.json created")),console.log()});X.command("remove <id>").alias("rm").description("remove an agent from config (keeps workspace)").action(e=>{let t=j();if(!t.agents?.[e]){console.log(l.red(` Agent "${e}" not found`));return}delete t.agents[e],N(t),console.log(l.green(` Agent "${e}" removed from config (workspace preserved)`))});var ie=new B().name("channel").description("manage channels \u2014 add telegram/whatsapp/discord, list");ie.command("list").alias("ls").description("list configured channels and bindings").action(()=>{let e=j();console.log();let t=e.channels?.telegram;if(t?.enabled){console.log(l.bold(" Telegram:"));for(let[i,r]of Object.entries(t.accounts||{}))console.log(` ${l.cyan(i)} -> agent: ${r.agentBinding}`)}else console.log(l.dim(" Telegram: disabled"));let s=e.channels?.whatsapp;if(s?.enabled){console.log(l.bold(" WhatsApp:")),console.log(` default agent: ${s.defaultAgent||"(none)"}`),console.log(` session: ${s.sessionDir||".agentx/whatsapp-sessions"}`);for(let i of s.routes||[]){let r=i.contact?`contact ${i.contact}`:`group "${i.group}"`;console.log(` ${r} -> agent: ${i.agent}`)}}else console.log(l.dim(" WhatsApp: disabled"));let o=e.channels?.discord;o?.enabled?(console.log(l.bold(" Discord:")),console.log(` agent: ${o.agentBinding||"(none)"}`)):console.log(l.dim(" Discord: disabled"));let n=e.channels?.gitlab;if(n?.enabled){console.log(l.bold(" GitLab:")),console.log(` host: ${n.host}`),console.log(` webhook: :${n.webhookPort}`);for(let i of n.routes||[])console.log(` ${i.project} -> agent: ${i.agent}`)}else console.log(l.dim(" GitLab: disabled"));console.log()});ie.command("add").description("add a channel (telegram, whatsapp, or discord)").action(async()=>{let e=j(),t=Object.keys(e.agents||{});if(!t.length){console.log(l.red(" No agents configured. Run: agentx agent add"));return}let s=t.map(n=>({title:`${n} (${e.agents[n].name})`,value:n})),{channelType:o}=await O({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(o){if(e.channels=e.channels||{},o==="telegram"){let n=await O([{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:s}]);if(!n.accountName||!n.token)return;try{let r=await(await fetch(`https://api.telegram.org/bot${n.token}/getMe`)).json();if(!r.ok)throw new Error(r.description);console.log(l.green(` Bot verified: @${r.result.username}`));let c=`@${r.result.username}`,a=e.agents[n.agentBinding].mentions||[];a.includes(c)||(a.push(c,r.result.username),e.agents[n.agentBinding].mentions=a)}catch(i){console.log(l.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[n.accountName]={token:n.token,agentBinding:n.agentBinding},N(e),console.log(l.green(` Telegram "${n.accountName}" added -> ${n.agentBinding}`))}if(o==="whatsapp"){let n=await O([{type:"select",name:"defaultAgent",message:"Default agent for WhatsApp messages",choices:s},{type:"text",name:"sessionDir",message:"Session directory",initial:".agentx/whatsapp-sessions"},{type:"confirm",name:"addRoute",message:"Add a contact/group route?",initial:!0}]);if(!n.defaultAgent)return;let i=[],r=n.addRoute;for(;r;){let c=await O([{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:a=>a==="contact"?"Phone number (e.g. +21624309128)":"Group name (partial match)"},{type:"select",name:"agent",message:"Route to agent",choices:s},{type:"confirm",name:"more",message:"Add another route?",initial:!1}]);c.value&&c.agent&&i.push({[c.type]:c.value,agent:c.agent}),r=c.more}e.channels.whatsapp={enabled:!0,sessionDir:n.sessionDir,defaultAgent:n.defaultAgent,routes:i},N(e),_(I(process.cwd(),n.sessionDir),{recursive:!0}),console.log(l.green(` WhatsApp enabled (${i.length} routes, default: ${n.defaultAgent})`)),console.log(l.dim(" Start daemon to scan QR code: agentx daemon start"))}if(o==="discord"){let n=await O([{type:"text",name:"token",message:"Discord bot token (from developer portal)"},{type:"select",name:"agentBinding",message:"Bind to agent",choices:s}]);if(!n.token)return;e.channels.discord={enabled:!0,token:n.token,agentBinding:n.agentBinding},N(e),console.log(l.green(` Discord added -> ${n.agentBinding}`))}if(o==="gitlab"){let n=await O([{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(!n.token)return;let i=[],r=n.addRoute;for(;r;){let c=await O([{type:"text",name:"project",message:"GitLab project path (e.g. group/project, or * for default)"},{type:"select",name:"agent",message:"Route to agent",choices:s},{type:"confirm",name:"more",message:"Add another route?",initial:!1}]);c.project&&c.agent&&i.push({project:c.project,agent:c.agent}),r=c.more}e.channels.gitlab={enabled:!0,host:n.host,token:n.token,webhookPort:n.webhookPort,webhookSecret:n.webhookSecret||void 0,routes:i},N(e),console.log(l.green(` GitLab enabled (${i.length} routes, webhook :${n.webhookPort})`)),console.log(l.dim(` Add webhook in GitLab: ${n.host}/<project>/-/hooks`)),console.log(l.dim(` URL: http://your-server:${n.webhookPort}/`)),n.webhookSecret&&console.log(l.dim(` Secret token: ${n.webhookSecret}`)),console.log(l.dim(" Events: Comments, Issues, Merge requests, Pipeline"))}console.log(l.dim(" Restart daemon to apply: agentx daemon stop && agentx daemon start")),console.log()}});var Y=new B().name("cron").description("manage cron jobs \u2014 add, list, enable, disable");Y.command("list").alias("ls").description("list cron jobs").action(()=>{let e=j();console.log();for(let[t,s]of Object.entries(e.crons||{})){let o=s.enabled?l.green("\u25CF"):l.dim("\u25CB");console.log(` ${o} ${l.cyan(t)} \u2014 ${s.schedule} (${s.timezone})`),console.log(l.dim(` agent: ${s.agent} | timeout: ${s.timeout}s`))}Object.keys(e.crons||{}).length||console.log(l.dim(" No cron jobs configured")),console.log()});Y.command("add").description("add a cron job").action(async()=>{let e=j(),t=Object.keys(e.agents||{}),s=await O([{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:t.map(o=>({title:o,value:o}))},{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}]);!s.id||!s.schedule||(e.crons=e.crons||{},e.crons[s.id]={enabled:s.enabled,schedule:s.schedule,timezone:s.timezone,agent:s.agent,prompt:s.prompt,timeout:s.timeout,onError:"log"},N(e),console.log(l.green(` Cron "${s.id}" added (${s.enabled?"enabled":"disabled"})`)),console.log())});Y.command("enable <id>").description("enable a cron job").action(e=>{let t=j();if(!t.crons?.[e]){console.log(l.red(` Cron "${e}" not found`));return}t.crons[e].enabled=!0,N(t),console.log(l.green(` Cron "${e}" enabled`))});Y.command("disable <id>").description("disable a cron job").action(e=>{let t=j();if(!t.crons?.[e]){console.log(l.red(` Cron "${e}" not found`));return}t.crons[e].enabled=!1,N(t),console.log(l.green(` Cron "${e}" disabled`))});var V=new B().name("mesh").description("manage mesh peers \u2014 add, list, remove");V.command("list").alias("ls").description("list mesh peers").action(async()=>{let e=j();if(console.log(),!e.mesh?.enabled)console.log(l.dim(" Mesh: disabled"));else{console.log(l.bold(" Mesh peers:"));for(let t of e.mesh.peers||[]){let s=l.red("unreachable");try{let n=await(await fetch(`${t.url}/health`,{signal:AbortSignal.timeout(3e3)})).json();s=l.green(`healthy \u2014 ${n.agents?.length||0} agents`)}catch{}console.log(` ${l.cyan(t.name)} ${l.dim(t.url)} ${s}`)}}console.log()});V.command("add").description("add a mesh peer (another agentx server)").action(async()=>{let e=j(),t=await O([{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(!(!t.name||!t.url)){try{let o=await(await fetch(`${t.url}/.well-known/agent-card.json`,{signal:AbortSignal.timeout(5e3)})).json();console.log(l.green(` Connected: ${o.name} (${o.skills?.length||0} agents)`))}catch{console.log(l.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:t.url,name:t.name,...t.token?{token:t.token}:{}}),N(e),console.log(l.green(` Peer "${t.name}" added`)),console.log()}});V.command("remove <name>").alias("rm").description("remove a mesh peer").action(e=>{let t=j();t.mesh?.peers&&(t.mesh.peers=t.mesh.peers.filter(s=>s.name!==e),t.mesh.peers.length===0&&(t.mesh.enabled=!1),N(t),console.log(l.green(` Peer "${e}" removed`)))});var re=new B().name("skill").description("manage skills \u2014 add to agent(s), list");re.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,t)=>{let s=j(),o=I(e);if(!R(o)){console.log(l.red(` Skill not found: ${o}`));return}let n=R(oe(o,"SKILL.md"))?o.split("/").pop():o.replace(/\/SKILL\.md$/,"").split("/").pop(),i;if(t.all)i=Object.keys(s.agents||{});else if(t.agent)i=t.agent;else{let r=Object.keys(s.agents||{});i=(await O({type:"multiselect",name:"agents",message:"Install to which agents?",choices:r.map(a=>({title:`${a} (${s.agents[a].name})`,value:a}))})).agents||[]}for(let r of i){let c=s.agents[r];if(!c){console.log(l.yellow(` Agent "${r}" not found, skipping`));continue}let a=I(c.workspace,".claude/skills",n);_(a,{recursive:!0}),R(oe(o,"SKILL.md"))?pe(o,a,{recursive:!0}):o.endsWith(".md")&&pe(o,oe(a,"SKILL.md")),console.log(l.green(` ${n} -> ${r} (${c.workspace})`))}console.log()});re.command("list").alias("ls").description("list skills per agent").action(()=>{let e=j();console.log();for(let[t,s]of Object.entries(e.agents||{})){let o=I(s.workspace,".claude/skills"),n=R(o)?xe(o).filter(i=>!i.startsWith(".")):[];console.log(` ${l.cyan(t)}: ${n.length?n.join(", "):l.dim("(none)")}`)}console.log()});var me=new B().name("hook").description("manage hooks \u2014 add to agent workspace");me.command("add <agent>").description("add a hook to an agent's workspace settings").action(async e=>{let s=j().agents?.[e];if(!s){console.log(l.red(` Agent "${e}" not found`));return}let o=I(s.workspace,".claude/settings.json"),n=R(o)?JSON.parse(se(o,"utf-8")):{},i=await O([{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:a=>a==="command"?"Shell command":"URL"},{type:"text",name:"matcher",message:"Matcher regex (optional, e.g. 'Bash' to match tool)"}]);if(!i.event||!i.value)return;n.hooks=n.hooks||{},n.hooks[i.event]=n.hooks[i.event]||[];let c={hooks:[{type:i.type,...i.type==="command"?{command:i.value}:{url:i.value}}]};i.matcher&&(c.matcher=i.matcher),n.hooks[i.event].push(c),_(I(s.workspace,".claude"),{recursive:!0}),ae(o,JSON.stringify(n,null,2)),console.log(l.green(` Hook added to ${e}: ${i.event} -> ${i.type}`)),console.log()});var he=new B().name("migrate").description("import configuration from OpenClaw or other tools");he.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,t)=>{let s=[e,I(process.env.HOME||"",".openclaw/openclaw.json"),I(process.env.HOME||"",".openclaw/clawdbot.json")].filter(Boolean),o;for(let y of s)if(y&&R(y)){o=y;break}if(!o){console.log(l.red(" OpenClaw config not found. Pass the path: agentx migrate openclaw /path/to/config.json"));return}console.log(l.dim(` Reading: ${o}`));let n=JSON.parse(se(o,"utf-8")),i=R(I(process.cwd(),"agentx.json"))?j():{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=n.agents?.list||[],c=n.bindings||[],a={};for(let y of c)y.match?.channel==="telegram"&&(a[y.match.accountId]=y.agentId);r.find(y=>y.id==="main"||y.default)&&(a.default="main");let d=0;for(let y of r){if(y.workspace?.startsWith("/data"))continue;let f=y.id.replace(/-agent$/,"").replace(/^main$/,"main-agent"),b=y.groupChat?.mentionPatterns||[];b.some(w=>w===f)||b.push(f),i.agents[f]={name:y.name,workspace:y.workspace,tier:"claude-code",model:"claude-sonnet-4-6",mentions:b,maxConcurrent:y.id==="main"?2:1,systemPrompt:`You are ${y.name} on AgentX.`,permissionMode:"bypassPermissions"},d++,t.dryRun||_(I(y.workspace,".claude/skills"),{recursive:!0})}console.log(` ${l.green(d)} agents imported`);let h=n.channels?.telegram?.accounts||{},p=0;for(let[y,f]of Object.entries(h)){if(!f.botToken)continue;let w=a[y]?.replace(/-agent$/,"").replace(/^main$/,"main-agent")||"default";i.channels.telegram.accounts[y]={token:f.botToken,agentBinding:w},i.channels.telegram.enabled=!0,p++}console.log(` ${l.green(p)} Telegram accounts imported`);let g=I(o,"../cron/jobs.json");if(R(g)){let y=JSON.parse(se(g,"utf-8")),f=0;for(let b of y.jobs||[]){let w=b.name||b.id,k=b.agentId?.replace(/-agent$/,"").replace(/^main$/,"main-agent")||"default";i.crons[w]={enabled:b.enabled||!1,schedule:b.schedule?.expr||"0 9 * * *",timezone:b.schedule?.tz||"UTC",agent:k,prompt:b.payload?.message||"",timeout:b.payload?.timeoutSeconds||600,onError:"log"},f++}console.log(` ${l.green(f)} cron jobs imported`)}t.dryRun?(console.log(l.yellow(`
11
+ Dry run \u2014 nothing written`)),console.log(JSON.stringify(i,null,2))):(N(i),console.log(l.green(`
12
+ Migration complete! Review agentx.json and run: agentx daemon start`))),console.log()});var ce=new B().name("config").description("validate and inspect configuration");ce.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 t=M(e.config);console.log(l.green(" \u2713 Config valid")),console.log(l.dim(` Node: ${t.node.name} (${t.node.id})`)),console.log(l.dim(` Bind: ${t.node.bind}`)),console.log(l.dim(` Agents: ${Object.keys(t.agents).length}`)),console.log(l.dim(` Crons: ${Object.keys(t.crons).length}`)),console.log(l.dim(` Mesh peers: ${t.mesh.peers.length}`));let s=Object.keys(t.channels.telegram.accounts).length;console.log(l.dim(` Telegram: ${t.channels.telegram.enabled?`${s} accounts`:"disabled"}`)),console.log(l.dim(` WhatsApp: ${t.channels.whatsapp.enabled?`${t.channels.whatsapp.routes.length} routes`:"disabled"}`)),console.log(l.dim(` Discord: ${t.channels.discord?.enabled?"enabled":"disabled"}`));let o=ne(t);if(o.length){console.log(),console.log(l.yellow(" Warnings:"));for(let n of o)console.log(l.yellow(` \u26A0 ${n}`))}else console.log(l.green(" \u2713 All workspaces valid"));console.log();for(let[n,i]of Object.entries(t.agents)){let r=i.workspace,c=R(I(r,".claude")),a=R(I(r,"CLAUDE.md")),d=R(I(r,".claude/settings.json")),h=R(I(r,".claude/skills"))?xe(I(r,".claude/skills")).filter(g=>!g.startsWith(".")).length:0,p=[c?l.green("\u2713"):l.red("\u2717"),".claude",a?l.green("\u2713"):l.yellow("\u25CB"),"CLAUDE.md",d?l.green("\u2713"):l.yellow("\u25CB"),"settings",`${h} skills`];console.log(` ${l.cyan(n)}: ${p.join(" ")}`)}}catch(t){console.log(l.red(` \u2717 ${t.message}`))}console.log()});ce.command("show").description("print the resolved configuration").option("-c, --config <path>","path to agentx.json").action(e=>{try{let t=M(e.config);console.log(JSON.stringify(t,null,2))}catch(t){console.log(l.red(t.message))}});import{Command as We}from"commander";import U from"chalk";var Q=new We().name("usage").description("token usage analysis and reporting");Q.command("today",{isDefault:!0}).description("show today's token usage").action(async()=>{try{let e=M(),[t,s]=e.node.bind.split(":"),n=await(await fetch(`http://${t||"127.0.0.1"}:${s}/usage`,{signal:AbortSignal.timeout(3e3)})).json();if(console.log(),console.log(U.bold(" Token Usage (last 7 days)")),console.log(),n.totalTasks===0){console.log(U.dim(" No tasks recorded yet")),console.log();return}let i=c=>c.toLocaleString();console.log(` Total: ${U.bold(i(n.totalTokens))} tokens across ${n.totalTasks} tasks`),console.log(` Input: ${i(n.totalInput)}`),console.log(` Output: ${i(n.totalOutput)}`),console.log(` Cache read: ${i(n.totalCacheRead)}`),console.log(` Cache create: ${i(n.totalCacheCreate)}`),n.cacheHitRatio>0&&console.log(` Cache hit: ${(n.cacheHitRatio*100).toFixed(1)}%`),n.totalErrors>0&&console.log(` Errors: ${U.red(String(n.totalErrors))}`),console.log(),console.log(U.bold(" By Agent:"));let r=Object.entries(n.byAgent||{});r.sort((c,a)=>a[1].total-c[1].total);for(let[c,a]of r){let d=i(a.total||a.input+a.output+a.cacheRead+a.cacheCreate),h=a.avgDuration?`${(a.avgDuration/1e3).toFixed(1)}s avg`:"";console.log(` ${U.cyan(c)}: ${d} tokens (${a.tasks} tasks) ${U.dim(h)}`)}console.log()}catch(e){e.cause?.code==="ECONNREFUSED"?console.log(U.red(" Daemon not running. Start with: agentx daemon start")):console.log(U.red(` ${e.message}`))}});Q.command("serve").description("start a web dashboard for token usage visualization").option("--port <n>","port number","4201").action(async e=>{let{startUsageDashboard:t}=await import("./usage-dashboard-2TJQBFSH.js");t(parseInt(e.port))});Q.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:t}=await import("child_process"),{existsSync:s}=await import("fs"),{resolve:o}=await import("path"),n=o(process.cwd(),"scripts/token-report.py");if(!s(n)){console.log(U.red(` Script not found: ${n}`)),console.log(U.dim(" The token analyzer script should be at scripts/token-report.py"));return}console.log(U.dim(" Analyzing Claude Code sessions..."));try{t(`SINCE_DAYS=${e.days} python3 "${n}"`,{stdio:"inherit"})}catch{console.log(U.red(" Analysis failed. Is Python 3 installed?"))}});import{Command as Ge}from"commander";import u from"chalk";import{createServer as Pe}from"http";var le=class{peers;cache=new Map;ttl=3e4;constructor(t){this.peers=t.map(s=>({url:s.replace(/\/$/,"")}))}async getRemoteAgents(){let t=[];return await Promise.all(this.peers.map(async s=>{try{let o=await this.fetchJson(`${s.url}/wiki/agents`);s.nodeId=o.nodeId;for(let n of o.agents||[])(n.totalEntries>0||n.totalArticles>0)&&t.push({...n,nodeId:o.nodeId,peerUrl:s.url})}catch{}})),t}async getRemoteArticles(t,s){try{return(await this.fetchJson(`${t}/wiki/articles?agent=${encodeURIComponent(s)}`)).articles||[]}catch{return[]}}async getRemoteArticle(t,s,o){try{let n=await this.fetchJson(`${t}/wiki/article?agent=${encodeURIComponent(s)}&path=${encodeURIComponent(o)}`);return{meta:{title:n.title,tags:n.tags||[],owner:n.owner,access:"public",created:n.created,lastUpdated:n.lastUpdated,sources:n.sources||[]},content:n.content,path:n.path}}catch{return null}}async fetchJson(t){let s=this.cache.get(t);if(s&&Date.now()-s.ts<this.ttl)return s.data;let o=await fetch(t,{signal:AbortSignal.timeout(5e3)});if(!o.ok)throw new Error(`HTTP ${o.status}`);let n=await o.json();return this.cache.set(t,{data:n,ts:Date.now()}),n}};function $(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function de(e,t,s=""){let o=$(e);return o=o.replace(/```(\w*)\n([\s\S]*?)```/g,(n,i,r)=>`<pre><code class="lang-${i}">${r.trim()}</code></pre>`),o=o.replace(/`([^`]+)`/g,"<code>$1</code>"),o=o.replace(/^(\|.+\|)\n(\|[-| :]+\|)\n((?:\|.+\|\n?)*)/gm,(n,i,r,c)=>{let a=i.split("|").filter(h=>h.trim()).map(h=>`<th>${h.trim()}</th>`).join(""),d=c.trim().split(`
13
+ `).map(h=>`<tr>${h.split("|").filter(g=>g.trim()).map(g=>`<td>${g.trim()}</td>`).join("")}</tr>`).join("");return`<table><thead><tr>${a}</tr></thead><tbody>${d}</tbody></table>`}),o=o.replace(/^#### (.+)$/gm,"<h4>$1</h4>"),o=o.replace(/^### (.+)$/gm,"<h3>$1</h3>"),o=o.replace(/^## (.+)$/gm,"<h2>$1</h2>"),o=o.replace(/^# (.+)$/gm,"<h1>$1</h1>"),o=o.replace(/\*\*(.+?)\*\*/g,"<strong>$1</strong>"),o=o.replace(/\*(.+?)\*/g,"<em>$1</em>"),o=o.replace(/\[\[([^\]]+)\]\]/g,(n,i)=>{let r=t.get(i);return r?`<a href="${s}/article/${encodeURIComponent(r)}" class="wikilink">${i}</a>`:`<a href="${s}/search?q=${encodeURIComponent(i)}" class="wikilink broken">${i}</a>`}),o=o.replace(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2" target="_blank">$1</a>'),o=o.replace(/^- (.+)$/gm,"<li>$1</li>"),o=o.replace(/(<li>.*<\/li>\n?)+/g,"<ul>$&</ul>"),o=o.replace(/^---$/gm,"<hr>"),o=o.replace(/\n\n+/g,"</p><p>"),o=`<p>${o}</p>`,o=o.replace(/<p>(<(?:h[1-4]|pre|table|ul|hr|div))/g,"$1"),o=o.replace(/(<\/(?:h[1-4]|pre|table|ul|hr|div)>)<\/p>/g,"$1"),o=o.replace(/<p>\s*<\/p>/g,""),o}var Ue=`
14
+ :root {
15
+ --bg: #f8f9fa; --card: #ffffff; --border: #a2a9b1;
16
+ --link: #0645ad; --link-visited: #0b0080; --link-broken: #ba0000;
17
+ --heading-border: #a2a9b1; --text: #202122; --text-dim: #54595d;
18
+ --accent: #eaecf0; --tag: #eaf3ff;
19
+ }
20
+ * { margin: 0; padding: 0; box-sizing: border-box; }
21
+ body { font-family: -apple-system, 'Segoe UI', Roboto, sans-serif; color: var(--text); background: var(--bg); line-height: 1.6; }
22
+ .layout { display: grid; grid-template-columns: 260px 1fr; min-height: 100vh; }
23
+ .sidebar { background: var(--card); border-right: 1px solid var(--border); padding: 20px 16px; position: sticky; top: 0; height: 100vh; overflow-y: auto; }
24
+ .sidebar h2 { font-size: 13px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-dim); margin: 20px 0 6px; padding-bottom: 4px; border-bottom: 1px solid var(--accent); }
25
+ .sidebar h2:first-child { margin-top: 0; }
26
+ .sidebar a { display: block; padding: 3px 8px; color: var(--link); text-decoration: none; font-size: 14px; border-radius: 3px; }
27
+ .sidebar a:hover { background: var(--accent); }
28
+ .sidebar a.active { background: var(--tag); font-weight: 600; }
29
+ .sidebar .logo { font-size: 20px; font-weight: 700; color: var(--text); text-decoration: none; display: block; margin-bottom: 12px; padding: 4px 8px; }
30
+ .sidebar .search-box { width: 100%; padding: 6px 10px; border: 1px solid var(--border); border-radius: 4px; font-size: 14px; margin-bottom: 12px; }
31
+ .sidebar .agent-badge { display: inline-block; background: #dcedc8; color: #33691e; border-radius: 3px; padding: 1px 6px; font-size: 11px; font-weight: 600; margin-left: 4px; }
32
+ .main { padding: 32px 48px; max-width: 960px; }
33
+ .main h1 { font-size: 28px; font-weight: 400; border-bottom: 1px solid var(--heading-border); padding-bottom: 8px; margin-bottom: 16px; }
34
+ .main h2 { font-size: 22px; font-weight: 400; border-bottom: 1px solid var(--accent); padding-bottom: 4px; margin: 24px 0 12px; }
35
+ .main h3 { font-size: 18px; margin: 20px 0 8px; }
36
+ .main h4 { font-size: 16px; margin: 16px 0 8px; }
37
+ .main p { margin: 8px 0; }
38
+ .main ul { margin: 8px 0 8px 24px; }
39
+ .main li { margin: 2px 0; }
40
+ .main a { color: var(--link); }
41
+ .main a:visited { color: var(--link-visited); }
42
+ .main a.wikilink.broken { color: var(--link-broken); }
43
+ .meta { background: var(--accent); border: 1px solid var(--border); border-radius: 4px; padding: 12px 16px; margin-bottom: 20px; font-size: 13px; display: grid; grid-template-columns: auto 1fr; gap: 4px 16px; }
44
+ .meta dt { font-weight: 600; color: var(--text-dim); }
45
+ .tag { display: inline-block; background: var(--tag); border-radius: 3px; padding: 1px 8px; font-size: 12px; margin: 1px 2px; }
46
+ table { border-collapse: collapse; margin: 12px 0; width: 100%; }
47
+ th, td { border: 1px solid var(--border); padding: 6px 12px; text-align: left; font-size: 14px; }
48
+ th { background: var(--accent); font-weight: 600; }
49
+ tr:hover td { background: #f0f4ff; }
50
+ pre { background: #f5f5f5; border: 1px solid var(--border); border-radius: 4px; padding: 12px; overflow-x: auto; font-size: 13px; margin: 12px 0; }
51
+ code { font-family: 'SF Mono', Menlo, monospace; font-size: 13px; }
52
+ p code { background: #f5f5f5; padding: 1px 4px; border-radius: 2px; }
53
+ .entry-card { background: var(--card); border: 1px solid var(--border); border-radius: 4px; padding: 12px 16px; margin: 8px 0; }
54
+ .entry-card .meta-line { font-size: 12px; color: var(--text-dim); margin-bottom: 4px; }
55
+ .stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; margin: 16px 0; }
56
+ .stat-card { background: var(--card); border: 1px solid var(--border); border-radius: 6px; padding: 16px; text-align: center; }
57
+ .stat-card .number { font-size: 32px; font-weight: 700; color: var(--link); }
58
+ .stat-card .label { font-size: 13px; color: var(--text-dim); margin-top: 4px; }
59
+ .agent-card { background: var(--card); border: 1px solid var(--border); border-radius: 8px; padding: 20px; margin: 12px 0; transition: box-shadow 0.15s; }
60
+ .agent-card:hover { box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
61
+ .agent-card h3 { margin: 0 0 8px; }
62
+ .agent-card h3 a { text-decoration: none; font-size: 20px; }
63
+ .agent-card .agent-stats { display: flex; gap: 16px; font-size: 14px; color: var(--text-dim); }
64
+ .agent-card .agent-stats strong { color: var(--text); }
65
+ .agent-card .article-list { margin-top: 8px; font-size: 13px; }
66
+ .agent-card .article-list a { margin-right: 8px; }
67
+ .backlinks { background: #fffbe6; border: 1px solid #e6d98c; border-radius: 4px; padding: 12px 16px; margin-top: 24px; font-size: 13px; }
68
+ .backlinks h4 { margin-bottom: 4px; }
69
+ .breadcrumb { font-size: 13px; color: var(--text-dim); margin-bottom: 12px; }
70
+ .breadcrumb a { color: var(--link); text-decoration: none; }
71
+ @media (max-width: 768px) { .layout { grid-template-columns: 1fr; } .sidebar { position: static; height: auto; } .main { padding: 20px; } }
72
+ `;function W(e,t,s){return`<!DOCTYPE html>
73
+ <html lang="en">
74
+ <head>
75
+ <meta charset="utf-8">
76
+ <meta name="viewport" content="width=device-width, initial-scale=1">
77
+ <title>${$(e)} \u2014 AgentX Wiki</title>
78
+ <style>${Ue}</style>
79
+ </head>
80
+ <body>
81
+ <div class="layout">
82
+ <nav class="sidebar">${t}</nav>
83
+ <main class="main">${s}</main>
84
+ </div>
85
+ </body>
86
+ </html>`}function Se(e){let t=new Map;for(let s of e){let o=s.path.includes("/")?s.path.split("/").slice(0,-1).join("/"):"/",n=t.get(o)||[];n.push(s),t.set(o,n)}return new Map([...t].sort((s,o)=>s[0].localeCompare(o[0])))}function Ae(e){return e==="/"?"Root":e.split("/").pop().replace(/-/g," ")}function z(e,t){let s='<a href="/" class="logo">AgentX Wiki</a>';s+='<form action="/search" method="get"><input type="text" name="q" class="search-box" placeholder="Search all wikis..."></form>',s+="<h2>Hub</h2>",s+=`<a href="/"${t?"":' class="active"'}>Home</a>`,s+='<a href="/entries">All Entries</a>';for(let o of e){let n=`/agent/${encodeURIComponent(o.agentId)}`;s+=`<h2><a href="${n}" style="color:inherit;text-decoration:none">${$(o.agentId)}</a> <span class="agent-badge">${o.totalArticles}</span></h2>`;let i=Se(o.articles);for(let[r,c]of i){s+=`<div style="font-size:11px;text-transform:uppercase;letter-spacing:0.3px;color:#888;margin:8px 8px 2px;font-weight:600">${Ae(r)}</div>`;for(let a of c){let h=`${o.agentId}/${a.path}`===t;s+=`<a href="${n}/article/${encodeURIComponent(a.path)}"${h?' class="active"':""} style="padding-left:16px;font-size:13px">${$(a.title)}</a>`}}}return s}function Oe(e,t,s){let o=t||e.summary(),i=e.getSharedStore().listEntries().length,r=o.reduce((h,p)=>h+p.totalArticles,0),c=o.reduce((h,p)=>h+p.unabsorbed,0),a=new Set((s||[]).map(h=>h.agentId)),d="<h1>AgentX Wiki Hub</h1>";if(d+='<div class="stats">',d+=`<div class="stat-card"><div class="number">${o.length}</div><div class="label">Agents</div></div>`,d+=`<div class="stat-card"><div class="number">${r}</div><div class="label">Articles</div></div>`,d+=`<div class="stat-card"><div class="number">${i}</div><div class="label">Raw Entries</div></div>`,s&&s.length>0){let h=new Set(s.map(p=>p.nodeId));d+=`<div class="stat-card"><div class="number">${h.size}</div><div class="label">Mesh Nodes</div></div>`}d+="</div>",d+="<h2>Agent Wikis</h2>";for(let h of o){let p=h.unabsorbed>0?`<span style="color: #e6a700;">${h.unabsorbed} unabsorbed</span>`:'<span style="color: green;">up to date</span>',y=a.has(h.agentId)&&!e.listAgents().includes(h.agentId)?' <span class="tag" style="background:#fff3cd;color:#856404">remote</span>':"";if(d+='<div class="agent-card">',d+=`<h3><a href="/agent/${encodeURIComponent(h.agentId)}">${$(h.agentId)}</a>${y}</h3>`,d+=`<div class="agent-stats">
87
+ <span><strong>${h.totalArticles}</strong> articles</span>
88
+ <span><strong>${h.totalEntries}</strong> entries</span>
89
+ <span>${p}</span>
90
+ </div>`,h.articles.length>0){d+='<div class="article-list">';for(let f of h.articles)d+=`<a href="/agent/${encodeURIComponent(h.agentId)}/article/${encodeURIComponent(f.path)}"><span class="tag">${(f.tags||[]).slice(0,3).join(", ")}</span> ${$(f.title)}</a> `;d+="</div>"}d+="</div>"}return W("Hub",z(o),d)}function Ne(e,t,s){let o=s||e.summary(),n=o.find(g=>g.agentId===t);if(!n)return W("Not Found",z(o),"<h1>Agent not found</h1>");let i=n.peerUrl!==void 0,r=e.getAgentEntries(t),c=n.articles||[],a=`<div class="breadcrumb"><a href="/">Hub</a> / ${$(t)}</div>`;if(a+=`<h1>${$(t)}</h1>`,i&&(a+=`<p><span class="tag" style="background:#fff3cd;color:#856404">remote @ ${$(n.nodeId)}</span></p>`),a+='<div class="stats">',a+=`<div class="stat-card"><div class="number">${n.totalArticles}</div><div class="label">Articles</div></div>`,a+=`<div class="stat-card"><div class="number">${n.totalEntries}</div><div class="label">Entries</div></div>`,a+=`<div class="stat-card"><div class="number">${n.unabsorbed}</div><div class="label">Unabsorbed</div></div>`,a+="</div>",c.length>0){a+="<h2>Articles</h2><table>",a+="<tr><th>Title</th><th>Tags</th></tr>";for(let g of c)a+=`<tr>
91
+ <td><a href="/agent/${encodeURIComponent(t)}/article/${encodeURIComponent(g.path)}">${$(g.title)}</a></td>
92
+ <td>${(g.tags||[]).slice(0,4).map(y=>`<span class="tag">${y}</span>`).join(" ")}</td>
93
+ </tr>`;a+="</table>"}let d=r.slice(-5).reverse();if(d.length>0){a+="<h2>Recent Entries</h2>";for(let g of d)a+=`<div class="entry-card">
94
+ <div class="meta-line">${g.date} via ${g.source} &middot; <code>${g.id}</code></div>
95
+ <p>${$(g.content.slice(0,300))}${g.content.length>300?"...":""}</p>
96
+ </div>`}let p=e.getAgentWiki(t).lint();if(p.length>0){a+=`<h2>Health Issues (${p.length})</h2><table>`,a+="<tr><th>Type</th><th>Article</th><th>Issue</th></tr>";for(let g of p)a+=`<tr><td><span class="tag">${g.type}</span></td><td>${$(g.article)}</td><td>${$(g.message)}</td></tr>`;a+="</table>"}return W(t,z(o,t),a)}function De(e,t,s,o){let n=e.getAgentWiki(t),i=n.readArticle(s);if(!i)return null;let r=o||e.summary(),c=n.rebuildIndex(),a=new Map;for(let y of c.articles)a.set(y.title,y.path);let d=`/agent/${encodeURIComponent(t)}`,p=n.buildBacklinks()[i.meta.title]||[],g=`<div class="breadcrumb"><a href="/">Hub</a> / <a href="${d}">${$(t)}</a> / ${$(i.meta.title)}</div>`;if(g+=`<h1>${$(i.meta.title)}</h1>`,g+='<dl class="meta">',g+=`<dt>Tags</dt><dd>${(i.meta.tags||[]).map(y=>`<span class="tag">${y}</span>`).join(" ")}</dd>`,g+=`<dt>Owner</dt><dd>${$(i.meta.owner)}</dd>`,g+=`<dt>Created</dt><dd>${i.meta.created}</dd>`,g+=`<dt>Updated</dt><dd>${i.meta.lastUpdated}</dd>`,i.meta.sources?.length&&(g+=`<dt>Sources</dt><dd>${i.meta.sources.length} entries</dd>`),g+="</dl>",g+=de(i.content,a,d),p.length>0){g+='<div class="backlinks"><h4>Pages that link here:</h4><ul>';for(let y of p){let f=n.readArticle(y);g+=`<li><a href="${d}/article/${encodeURIComponent(y)}">${$(f?.meta.title||y)}</a></li>`}g+="</ul></div>"}return W(i.meta.title,z(r,`${t}/${s}`),g)}function Me(e,t){let s=t||e.summary(),n=e.getSharedStore().listEntries(),i='<div class="breadcrumb"><a href="/">Hub</a> / Entries</div>';i+=`<h1>All Raw Entries (${n.length})</h1>`;let r=new Map;for(let c of n){let a=r.get(c.date)||[];a.push(c),r.set(c.date,a)}for(let[c,a]of[...r].reverse()){i+=`<h2>${c} (${a.length})</h2>`;for(let d of a)i+=`<div class="entry-card">
97
+ <div class="meta-line">
98
+ <a href="/agent/${encodeURIComponent(d.agentId)}">${$(d.agentId)}</a>
99
+ via ${d.source} &middot; <code>${d.id}</code>
100
+ </div>
101
+ <p>${$(d.content.slice(0,400))}${d.content.length>400?"...":""}</p>
102
+ </div>`}return W("All Entries",z(s),i)}function Le(e,t,s){let o=s||e.summary(),n=`<h1>Search: "${$(t)}"</h1>`,i=0;for(let r of o){let a=e.getAgentWiki(r.agentId).findRelevant(t,void 0,10);if(a.length>0){n+=`<h2>${$(r.agentId)} (${a.length})</h2>`;for(let d of a){let h=`/agent/${encodeURIComponent(r.agentId)}`;n+=`<div class="entry-card">
103
+ <div class="meta-line">${(d.meta.tags||[]).slice(0,3).map(p=>`<span class="tag">${p}</span>`).join(" ")}</div>
104
+ <h3><a href="${h}/article/${encodeURIComponent(d.path)}">${$(d.meta.title)}</a></h3>
105
+ <p>${$(d.content.slice(0,200))}...</p>
106
+ </div>`}i+=a.length}}return i===0&&(n+="<p>No matching articles found.</p>"),W(`Search: ${t}`,z(o),n)}function Z(e,t,s){let o=e.rebuildIndex(),n=Se(o.articles.map(r=>({title:r.title,path:r.path,tags:r.tags}))),i=`<a href="/" class="logo">${$(t)}</a>`;i+='<form action="/search" method="get"><input type="text" name="q" class="search-box" placeholder="Search..."></form>',i+="<h2>Navigation</h2>",i+=`<a href="/"${s?"":' class="active"'}>Home</a>`,i+='<a href="/lint">Health Check</a>';for(let[r,c]of n){i+=`<h2>${Ae(r)}</h2>`;for(let a of c){let d=a.path===s;i+=`<a href="/article/${encodeURIComponent(a.path)}"${d?' class="active"':""}>${$(a.title)}</a>`}}return i}function Ie(e,t=4200,s,o=[],n="unified"){let i=new K(e,void 0,n),r=o.length>0?new le(o):null;Pe(async(a,d)=>{let h=new URL(a.url||"/",`http://localhost:${t}`),p=decodeURIComponent(h.pathname),g=null,y=200;try{if(p.startsWith("/remote/")&&r){let f=p.slice(8).split("/article/");if(f.length===2){let[b,w]=f,k=b.indexOf("/"),S=decodeURIComponent(b.slice(0,k)),v=b.slice(k+1),A=await r.getRemoteArticle(S,v,w);if(A){let P=await r.getRemoteAgents(),G=[...i.summary(),...P.map(C=>({...C,agentId:`${C.agentId} @${C.nodeId}`}))],E=`<div class="breadcrumb"><a href="/">Hub</a> / <span class="tag">remote</span> ${$(v)} @ ${$(S)}</div>`;E+=`<h1>${$(A.meta.title)}</h1>`,E+='<dl class="meta">',E+=`<dt>Tags</dt><dd>${(A.meta.tags||[]).map(C=>`<span class="tag">${C}</span>`).join(" ")}</dd>`,E+=`<dt>Owner</dt><dd>${$(A.meta.owner)}</dd>`,E+=`<dt>Source</dt><dd><span class="tag">remote</span> ${$(S)}</dd>`,E+=`<dt>Updated</dt><dd>${A.meta.lastUpdated}</dd>`,E+="</dl>";let T=new Map;E+=de(A.content,T),g=W(A.meta.title,z(G),E)}}}if(!g&&s){let f=i.getAgentWiki(s);if(p==="/"||p===""){let b=f.rebuildIndex(),w=i.getAgentEntries(s),k=`<h1>${$(s)}</h1>`;if(k+='<div class="stats">',k+=`<div class="stat-card"><div class="number">${b.articles.length}</div><div class="label">Articles</div></div>`,k+=`<div class="stat-card"><div class="number">${w.length}</div><div class="label">Entries</div></div>`,k+="</div>",b.articles.length>0){k+="<h2>Articles</h2><table><tr><th>Title</th><th>Type</th><th>Updated</th></tr>";for(let S of b.articles)k+=`<tr><td><a href="/article/${encodeURIComponent(S.path)}">${$(S.title)}</a></td><td><span class="tag">${(S.tags||[]).slice(0,3).join(", ")}</span></td><td>${S.lastUpdated||""}</td></tr>`;k+="</table>"}g=W(s,Z(f,s),k)}else if(p.startsWith("/article/")){let b=p.slice(9),w=f.readArticle(b);if(w){let k=f.rebuildIndex(),S=new Map;for(let A of k.articles)S.set(A.title,A.path);let v=`<h1>${$(w.meta.title)}</h1>`;v+='<dl class="meta">',v+=`<dt>Tags</dt><dd>${(w.meta.tags||[]).map(A=>`<span class="tag">${A}</span>`).join(" ")}</dd>`,v+=`<dt>Updated</dt><dd>${w.meta.lastUpdated}</dd>`,w.meta.sources?.length&&(v+=`<dt>Sources</dt><dd>${w.meta.sources.length} entries</dd>`),v+="</dl>",v+=de(w.content,S),g=W(w.meta.title,Z(f,s,b),v)}}else if(p==="/lint"){let b=f.lint(),w="<h1>Health Check</h1>";if(b.length===0)w+='<p style="color:green;">No issues.</p>';else{w+="<table><tr><th>Type</th><th>Article</th><th>Issue</th></tr>";for(let k of b)w+=`<tr><td><span class="tag">${k.type}</span></td><td>${$(k.article)}</td><td>${$(k.message)}</td></tr>`;w+="</table>"}g=W("Health Check",Z(f,s),w)}else if(p==="/search"){let b=h.searchParams.get("q")||"",w=f.findRelevant(b,void 0,20),k=`<h1>Search: "${$(b)}"</h1>`;for(let S of w)k+=`<div class="entry-card"><h3><a href="/article/${encodeURIComponent(S.path)}">${$(S.meta.title)}</a></h3><p>${$(S.content.slice(0,200))}...</p></div>`;w.length===0&&(k+="<p>No results.</p>"),g=W(`Search: ${b}`,Z(f,s),k)}}else{let f=r?await r.getRemoteAgents():[],b=i.summary(),w=new Map(b.map(v=>[v.agentId,v])),k=[],S=new Set;for(let v of b){let A=f.find(P=>P.agentId===v.agentId);A&&A.totalArticles>v.totalArticles?k.push({...A,totalEntries:Math.max(v.totalEntries,A.totalEntries),unabsorbed:Math.max(v.unabsorbed,A.unabsorbed)}):k.push(v),S.add(v.agentId)}for(let v of f)S.has(v.agentId)||(k.push(v),S.add(v.agentId));if(p==="/"||p==="")g=Oe(i,k,f);else if(p==="/entries")g=Me(i,k);else if(p==="/search")g=Le(i,h.searchParams.get("q")||"",k);else if(p.startsWith("/agent/")){let v=p.slice(7),A=v.indexOf("/article/");if(A===-1)g=Ne(i,v,k);else{let P=v.slice(0,A),G=v.slice(A+9);if(g=De(i,P,G,k),!g&&r){let E=f.find(T=>T.agentId===P);if(E){let T=await r.getRemoteArticle(E.peerUrl,P,G);if(T){let C=`<div class="breadcrumb"><a href="/">Hub</a> / <span class="tag">remote @ ${$(E.nodeId)}</span> / ${$(P)}</div>`;C+=`<h1>${$(T.meta.title)}</h1>`,C+='<dl class="meta">',C+=`<dt>Tags</dt><dd>${(T.meta.tags||[]).map(te=>`<span class="tag">${te}</span>`).join(" ")}</dd>`,C+=`<dt>Source</dt><dd><span class="tag">remote</span> ${$(E.nodeId)}</dd>`,C+=`<dt>Updated</dt><dd>${T.meta.lastUpdated}</dd>`,C+="</dl>",C+=de(T.content,new Map),g=W(T.meta.title,z(k),C)}}}}}}if(!g){y=404;let f=s?Z(i.getAgentWiki(s),s):z(i.summary());g=W("Not Found",f,"<h1>Page not found</h1>")}}catch(f){y=500,g=W("Error","",`<h1>Error</h1><pre>${$(f.message)}</pre>`)}d.writeHead(y,{"Content-Type":"text/html; charset=utf-8"}),d.end(g)}).listen(t)}function Ce(e,t,s,o,n,i){let r=o.length>0?`
107
+ Existing articles:
108
+ ${o.map(a=>`- ${a.title} [${(a.tags||[]).join(", ")}] (${a.path})`).join(`
109
+ `)}
110
+ `:"",c=s?`
111
+ ## Worldview
112
+
113
+ ${s}
114
+ `:"";return e==="flat"?He(t,c,r,n,i):e==="graph"?ze(t,c,r,n,i):Be(t,c,r,n,i)}function He(e,t,s,o,n){return`You are a wiki editor for the "${e}" agent. Compile raw entries into a personal wiki.
115
+
116
+ ## Karpathy Pattern
117
+
118
+ - Plain markdown files. YOU choose the path and structure \u2014 it emerges from the data.
119
+ - Tag AGGRESSIVELY. Tags are the #1 mechanism for context narrowing.
120
+ - Use [[wikilinks]] to cross-reference between articles.
121
+ - Synthesize \u2014 distill conversations into factual wiki articles, don't copy-paste.
122
+ - If an existing article covers the topic, produce an UPDATE with full merged content.
123
+ ${t}${s}
124
+ ## Tagging Rules
125
+
126
+ Tag every article with ALL relevant dimensions:
127
+ - WHO: people, agents, teams (e.g., "alice", "deploy-bot", "cto")
128
+ - WHAT: project, client, topic, tech (e.g., "billing-api", "seo", "gitlab", "deploy")
129
+ - WHEN: dates, periods (e.g., "2026-04-06", "week-14")
130
+ - WHERE: server, environment, channel (e.g., "staging", "telegram", "production")
131
+ - HOW: type of knowledge (e.g., "process", "incident", "report", "decision")
132
+
133
+ Use section tags: \`<!-- tags: runbook, staging -->\`
134
+
135
+ ## Gap Detection
136
+
137
+ After compiling, add a "gaps" array \u2014 topics MENTIONED but not yet covered.
138
+
139
+ ## Output \u2014 ONLY valid JSON, no markdown fencing
140
+
141
+ {
142
+ "articles": [
143
+ {
144
+ "path": "infra/staging-deploy.md",
145
+ "title": "Staging Deployment Process",
146
+ "tags": ["deploy", "staging", "devops", "process", "2026-04-06"],
147
+ "content": "How we deploy to staging...\\n\\n## Steps\\n<!-- tags: runbook, staging -->\\n1. ...",
148
+ "sources": ["entry-id-1", "entry-id-2"]
149
+ }
150
+ ],
151
+ "gaps": ["Production server \u2014 mentioned but no article exists"]
152
+ }
153
+
154
+ ENTRIES (${n}):
155
+
156
+ ${o}`}function ze(e,t,s,o,n){return`You are building a knowledge graph for the "${e}" agent. Compile raw entries into a structured wiki.
157
+
158
+ ## Knowledge Graph Model
159
+
160
+ Every article is a NODE with a kind and position in a hierarchy.
161
+ The path reflects the hierarchy: org/team/agent-name.md
162
+
163
+ ### Node Kinds
164
+
165
+ **Entities** (things that persist):
166
+ person, agent, company, team, client, project, repo, server, service, domain
167
+
168
+ **Occurrences** (things that happen \u2014 always have a date):
169
+ event, incident, deploy, decision
170
+
171
+ **Knowledge** (what we know):
172
+ process, pattern, concept, report
173
+
174
+ ### Rules
175
+
176
+ 1. Ask: "What ENTITY is this about? What HAPPENED? Where in the hierarchy?"
177
+ 2. Entities go under their parent: agent under team, server under project
178
+ 3. Events go under events/YYYY-MM-DD/ and list which entities are involved
179
+ 4. Path = hierarchy: org/<company>/<area>/<entity>.md
180
+ 5. Use [[wikilinks]] to cross-reference between nodes
181
+ 6. Synthesize \u2014 distill conversations into factual wiki content
182
+ 7. If an existing article covers the topic, produce an UPDATE with full merged content
183
+ 8. Tag aggressively too \u2014 tags help narrow context
184
+ ${t}${s}
185
+ ## Gap Detection
186
+
187
+ After compiling, add a "gaps" array \u2014 entities or events MENTIONED but missing.
188
+
189
+ ## Output \u2014 ONLY valid JSON, no markdown fencing
190
+
191
+ {
192
+ "articles": [
193
+ {
194
+ "path": "org/team/marketing-bot.md",
195
+ "title": "Marketing Bot \u2014 Content Agent",
196
+ "tags": ["marketing-bot", "agent", "marketing"],
197
+ "kind": "agent",
198
+ "parent": "org/team",
199
+ "content": "Marketing bot handles content and SEO.",
200
+ "sources": ["entry-id-1"],
201
+ "date": null,
202
+ "involves": null
203
+ },
204
+ {
205
+ "path": "events/2026-04-06/token-expiry.md",
206
+ "title": "GitLab Token Expiry",
207
+ "tags": ["incident", "gitlab", "2026-04-06"],
208
+ "kind": "incident",
209
+ "parent": "events/2026-04-06",
210
+ "content": "The GITLAB_TOKEN expired, blocking deploys.",
211
+ "sources": ["entry-id-2"],
212
+ "date": "2026-04-06",
213
+ "involves": ["org/clients/acme"]
214
+ }
215
+ ],
216
+ "gaps": ["Production server \u2014 entity mentioned but missing"]
217
+ }
218
+
219
+ ENTRIES (${n}):
220
+
221
+ ${o}`}function Be(e,t,s,o,n){return`You are compiling a personal wiki for the "${e}" agent.
222
+
223
+ ## Your Job
224
+
225
+ Read ${n} raw conversation entries. For each, ask:
226
+ 1. **Who** is mentioned? (people, agents, companies, teams)
227
+ 2. **What** is this about? (project, server, tool, concept)
228
+ 3. **What happened?** (deploy, incident, decision, report)
229
+ 4. **What's missing?** (entities mentioned but undocumented)
230
+
231
+ Then compile articles \u2014 one topic per article, synthesized (not copy-pasted).
232
+
233
+ ## How to Organize
234
+
235
+ - YOU choose the file path. Let structure emerge from the data.
236
+ - Use whatever directory names make sense: projects/, incidents/, agents/ \u2014 your call.
237
+ - Use [[wikilinks]] to cross-reference between articles.
238
+ - If an existing article covers the topic, produce an UPDATE with full merged content.
239
+ ${t}${s}
240
+ ## How to Tag (CRITICAL)
241
+
242
+ Tags are how agents find relevant context. Tag EVERY article with ALL dimensions:
243
+
244
+ | Dimension | Examples |
245
+ |-----------|----------|
246
+ | **Who** | agent names, people, team names |
247
+ | **What** | project names, tools, technologies |
248
+ | **Type** | person, agent, server, project, incident, process, report, decision |
249
+ | **When** | 2026-04-06, week-14, q2-2026 |
250
+ | **Where** | staging, production, telegram, server-name |
251
+ | **How** | deploy, runbook, spike, review |
252
+
253
+ Minimum 6 tags per article. More is better.
254
+ Use section-level tags too: \`<!-- tags: runbook, staging -->\`
255
+
256
+ ## How to Find Gaps
257
+
258
+ After compiling, identify MISSING PIECES:
259
+ - People mentioned but no article (e.g., "Alice gives deploy instructions but who is Alice?")
260
+ - Servers referenced but undocumented (e.g., "10.0.1.5 \u2014 what is this?")
261
+ - Processes implied but not written down (e.g., "deploy to staging \u2014 how exactly?")
262
+ - Projects named but no overview (e.g., "Project X \u2014 what is it?")
263
+
264
+ Be specific. "X is undocumented" is useless. "Alice \u2014 stakeholder who issues deploy instructions via Telegram, no profile article" is useful.
265
+
266
+ ## Output \u2014 ONLY valid JSON, no markdown fencing
267
+
268
+ {
269
+ "articles": [
270
+ {
271
+ "path": "agents/marketing-bot.md",
272
+ "title": "Marketing Bot \u2014 Content Agent",
273
+ "tags": ["marketing-bot", "agent", "marketing", "content", "seo"],
274
+ "content": "Marketing bot handles content, marketing, and SEO.\\n\\n## Capabilities\\n<!-- tags: capabilities, tools -->\\n...",
275
+ "sources": ["entry-id-1"]
276
+ }
277
+ ],
278
+ "gaps": [
279
+ "Alice \u2014 stakeholder who issues deploy instructions via Telegram, no profile article",
280
+ "10.0.1.5 \u2014 production server IP referenced but no infrastructure article"
281
+ ]
282
+ }
283
+
284
+ ENTRIES (${n}):
285
+
286
+ ${o}`}import{resolve as ee}from"path";import{execSync as Je}from"child_process";import{writeFileSync as _e,mkdirSync as Ye}from"fs";function F(e,t){return new K(e||ee(process.cwd(),".agentx/wiki"),void 0,t||"graph")}function ue(e){return e==="graph"?"Knowledge Graph":e==="flat"?"Karpathy Flat":"Unified"}var H=new Ge().name("wiki").description("wiki knowledge base management");H.command("status").description("show wiki status per agent").option("--dir <path>","wiki directory").option("--mode <mode>","unified (default), graph, or flat","unified").action(e=>{let t=e.mode,s=F(e.dir,t),o=s.summary(),i=s.getSharedStore().listEntries().length;console.log(),console.log(u.bold(` Wiki Hub Status [${ue(t)}]`)),console.log(),console.log(` Total raw entries: ${i}`),console.log(` Agents: ${o.length}`),console.log();for(let c of o){let a=c.unabsorbed>0?u.yellow(`${c.unabsorbed} unabsorbed`):u.green("up to date");if(console.log(` ${u.cyan(c.agentId)}`),console.log(` Entries: ${c.totalEntries} Articles: ${c.totalArticles} ${a}`),c.articles.length>0){for(let d of c.articles.slice(0,3))console.log(u.dim(` - ${d.title} [${(d.tags||[]).slice(0,3).join(", ")}]`));c.articles.length>3&&console.log(u.dim(` ... and ${c.articles.length-3} more`))}console.log()}o.reduce((c,a)=>c+a.unabsorbed,0)>0&&(console.log(u.dim(" Run 'agentx wiki absorb' to compile all, or 'agentx wiki absorb --agent <id>' for one agent")),console.log())});H.command("lint").description("check wiki for issues per agent").option("--dir <path>","wiki directory").option("--mode <mode>","unified (default), graph, or flat","unified").option("--agent <id>","lint a specific agent's wiki").action(e=>{let t=F(e.dir,e.mode),s=e.agent?[e.agent]:t.listAgents(),o=0;console.log();for(let n of s){let r=t.getAgentWiki(n).lint();if(o+=r.length,r.length===0)console.log(` ${u.cyan(n)}: ${u.green("healthy")}`);else{console.log(` ${u.cyan(n)}: ${u.yellow(`${r.length} issues`)}`);for(let c of r){let a=c.type==="broken-link"?"x":c.type==="orphan"?"?":"!";console.log(` [${a}] ${u.dim(c.type)} ${c.article}: ${c.message}`)}}}console.log(),o===0&&console.log(u.green(" All agent wikis are healthy")),console.log()});H.command("absorb").description("compile unabsorbed entries into per-agent wiki articles").option("--dir <path>","wiki directory").option("--mode <mode>","unified (default), graph, or flat","unified").option("--agent <id>","absorb only this agent").option("--dry-run","preview without running").option("--max <n>","max entries per agent","20").action(async e=>{let t=e.mode,s=F(e.dir,t),o=e.agent?[e.agent]:s.listAgents(),n=parseInt(e.max),i=0;for(let r of o){let c=s.getUnabsorbedEntries(r).slice(0,n);if(c.length===0){console.log(` ${u.cyan(r)}: ${u.green("all absorbed")}`);continue}console.log(),console.log(u.bold(` ${u.cyan(r)}: ${c.length} entries to absorb`));for(let b of c.slice(0,3))console.log(u.dim(` [${b.date} via ${b.source}] ${b.content.slice(0,80)}...`));if(c.length>3&&console.log(u.dim(` ... and ${c.length-3} more`)),e.dryRun)continue;let a=s.getAgentWiki(r),d=a.rebuildIndex(),h=a.getWorldview()||s.getSharedStore().getWorldview()||"",p=c.map(b=>`--- ENTRY ${b.id} [${b.date} ${b.agentId} via ${b.source}] ---
287
+ ${b.content}
288
+ --- END ENTRY ---`).join(`
289
+
290
+ `),g=Ce(t,r,h,d.articles,p,c.length);console.log(u.dim(` Mode: ${ue(t)}`));let y=ee(a.baseDir,"_tmp");Ye(y,{recursive:!0});let f=ee(y,"absorb-prompt.txt");_e(f,g),console.log(u.dim(" Compiling with Claude..."));try{let b;try{b=Je(`cat '${f}' | 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:3e5,maxBuffer:10*1024*1024})}catch(x){if(b=x.stdout||"",!b)throw x}let w=b;try{let x=JSON.parse(b);w=x.result||x.content||"",w||(console.log(u.dim(` Claude envelope keys: ${Object.keys(x).join(", ")}`)),console.log(u.dim(` is_error: ${x.is_error}, stop_reason: ${x.stop_reason}`)))}catch{}let k,S=[],v=w.indexOf("{"),A=w.indexOf("["),P=v>=0&&(A<0||v<A)?v:A;if(P===-1){console.log(u.red(" No JSON found in response")),console.log(u.dim(w.slice(0,500)));continue}let G=w[P],E=G==="{"?"}":"]",T=0,C=-1;for(let x=P;x<w.length;x++)if(w[x]===G)T++;else if(w[x]===E&&(T--,T===0)){C=x+1;break}if(C===-1){console.log(u.red(" Unbalanced JSON"));continue}let te=w.slice(P,C);try{let x=JSON.parse(te);Array.isArray(x)?k=x:(k=x.articles||[],S=x.gaps||[])}catch(x){console.log(u.red(` JSON parse error: ${x.message}`)),console.log(u.dim(` First 300 chars: ${te.slice(0,300)}`));continue}for(let x of k){let fe=new Date().toISOString().slice(0,10);a.writeArticle(x.path,{title:x.title,tags:x.tags||[],owner:r,access:"public",created:fe,lastUpdated:fe,sources:x.sources||[]},x.content,r);let be=(x.tags||[]).slice(0,5).join(", ");console.log(` ${u.green("+")} ${x.path}: ${x.title}`),be&&console.log(u.dim(` [${be}]`)),i++}if(S.length>0){console.log(),console.log(u.yellow(` Gaps detected (${S.length} missing pieces):`));for(let x of S)console.log(u.yellow(` ? ${x}`))}a.rebuildIndex()}catch(b){console.log(u.red(` Absorb failed: ${b.message?.slice(0,200)}`)),b.stderr&&console.log(u.dim(String(b.stderr).slice(0,300))),b.stdout&&console.log(u.dim("stdout: "+String(b.stdout).slice(0,300)))}}console.log(),e.dryRun?console.log(u.dim(" Dry run \u2014 no changes made")):i>0&&console.log(u.green(` ${i} articles compiled across ${o.length} agent(s)`)),console.log()});H.command("entries").description("list raw entries").option("--dir <path>","wiki directory").option("--agent <id>","filter by agent").action(e=>{let o=F(e.dir).getSharedStore().listEntries();e.agent&&(o=o.filter(n=>n.agentId===e.agent)),console.log(),console.log(u.bold(` ${o.length} raw entries`)),console.log();for(let n of o.slice(-20))console.log(` ${u.dim(n.date)} ${u.cyan(n.agentId)} via ${n.source}: ${n.content.slice(0,80)}...`);o.length>20&&console.log(u.dim(` ... showing last 20 of ${o.length}`)),console.log()});H.command("serve").description("start a local web server to browse agent wikis (local + mesh)").option("--dir <path>","wiki directory").option("--mode <mode>","unified (default), graph, or flat","unified").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 t=e.dir||ee(process.cwd(),".agentx/wiki"),s=parseInt(e.port),o=e.peer||[];if(o.length===0)try{let{loadDaemonConfig:n}=await import("./config-MSWKC466.js");o=(n().mesh?.peers||[]).map(r=>r.url)}catch{}console.log(),console.log(u.bold(" AgentX Wiki Server")),console.log(),console.log(` ${u.green(">")} http://localhost:${s}`),e.agent?console.log(` Agent: ${u.cyan(e.agent)}`):console.log(` Mode: ${u.cyan("Hub")} (all agents)`),o.length>0&&console.log(` Mesh: ${u.cyan(o.length+" peer(s)")} \u2014 ${o.join(", ")}`),console.log(u.dim(` Wiki: ${t}`)),console.log(u.dim(" Press Ctrl+C to stop")),console.log(),Ie(t,s,e.agent,o,e.mode)});H.command("search <query>").description("search wiki articles").option("--dir <path>","wiki directory").option("--mode <mode>","unified (default), graph, or flat","unified").option("--agent <id>","search specific agent's wiki").action((e,t)=>{let s=F(t.dir,t.mode),o=t.agent?[t.agent]:s.listAgents();console.log();let n=0;for(let i of o){let c=s.getAgentWiki(i).findRelevant(e,void 0,10);if(c.length>0){console.log(u.bold(` ${u.cyan(i)}:`));for(let a of c)console.log(` ${a.meta.title} [${(a.meta.tags||[]).slice(0,3).join(", ")}]`),console.log(u.dim(` ${a.path} \u2014 ${a.content.slice(0,100)}...`));n+=c.length}}n===0&&console.log(u.dim(" No matching articles")),console.log()});H.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 s=F(e.dir).getSharedStore(),o=[];if(e.peer)o=[e.peer];else try{let{loadDaemonConfig:r}=await import("./config-MSWKC466.js");o=(r().mesh?.peers||[]).map(a=>a.url)}catch{console.log(u.red(" No --peer specified and no daemon config found")),console.log(u.dim(" Usage: agentx wiki sync --peer http://100.67.108.119:19900"));return}if(o.length===0){console.log(u.dim(" No mesh peers configured"));return}let n=new Set(s.listEntries().map(r=>r.id)),i=0;for(let r of o){console.log(),console.log(u.bold(` Syncing from ${r}...`));try{let c=await fetch(`${r}/wiki/entries`,{signal:AbortSignal.timeout(1e4)});if(!c.ok){console.log(u.red(` HTTP ${c.status}`));continue}let a=await c.json(),d=a.entries||[],h=a.nodeId||"unknown";console.log(u.dim(` Node: ${h} \u2014 ${d.length} entries`));let p=0;for(let g of d)n.has(g.id)||(e.dryRun||(s.addEntry({id:g.id,date:g.date,agentId:g.agentId,source:`${g.source}@${h}`,sourceContext:g.sourceContext,content:g.content}),n.add(g.id)),p++);p>0?console.log(u.green(` ${p} new entries${e.dryRun?" (dry run)":" synced"}`)):console.log(u.dim(" Already up to date")),i+=p;try{let g=await fetch(`${r}/wiki/agents`,{signal:AbortSignal.timeout(5e3)});if(g.ok){let y=await g.json();for(let f of y.agents||[])f.totalEntries>0&&console.log(u.dim(` ${f.agentId}: ${f.totalEntries} entries, ${f.totalArticles} articles`))}}catch{}}catch(c){c.cause?.code==="ECONNREFUSED"?console.log(u.red(" Connection refused")):console.log(u.red(` ${c.message}`))}}console.log(),i>0&&!e.dryRun?console.log(u.green(` ${i} entries synced. Run 'agentx wiki absorb' to compile.`)):i>0?console.log(u.dim(` ${i} entries would be synced (dry run)`)):console.log(u.dim(" All peers up to date")),console.log()});H.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 t=e.dir||ee(process.cwd(),".agentx/wiki"),s=e.agent,n=["flat","graph","unified"].map(a=>{let d=new K(t,void 0,a),p=d.getAgentWiki(s).rebuildIndex(),g=d.getAgentEntries(s),y=d.getUnabsorbedEntries(s),f=new Set,b=0,w=new Set;for(let S of p.articles){for(let v of S.tags||[])f.add(v);b+=S.tags?.length||0;for(let v of S.sources||[])w.add(v)}let k=new Set(p.articles.map(S=>S.path.includes("/")?S.path.split("/")[0]:"/"));return{mode:a,label:ue(a),articles:p.articles.length,entries:g.length,unabsorbed:y.length,uniqueTags:f.size,avgTags:p.articles.length>0?(b/p.articles.length).toFixed(1):"0",dirs:k.size,coverage:w.size,articleList:p.articles}});console.log(),console.log(u.bold(` Wiki Compare: ${s}`)),console.log(` Raw entries: ${n[0].entries}`),console.log();let i=22;console.log(` ${"".padEnd(20)} ${n.map(a=>a.label.padEnd(i)).join(" ")}`),console.log(` ${"\u2500".repeat(20)} ${n.map(()=>"\u2500".repeat(i)).join(" ")}`);let r=[["Articles",a=>String(a.articles)],["Unabsorbed",a=>String(a.unabsorbed)],["Unique tags",a=>String(a.uniqueTags)],["Avg tags/article",a=>a.avgTags],["Directories",a=>String(a.dirs)],["Entry coverage",a=>`${a.coverage}/${a.entries}`]];for(let[a,d]of r){let h=n.map(p=>d(p).padEnd(i));console.log(` ${a.padEnd(20)} ${h.join(" ")}`)}console.log();let c=(a,d)=>{let h=Math.max(...n.map(g=>g[a])),p=n.filter(g=>g[a]===h).map(g=>g.label);h>0&&console.log(u.dim(` Best ${d}: ${p.join(", ")} (${h})`))};c("uniqueTags","tag richness"),c("articles","article count"),c("coverage","entry coverage"),console.log()});process.on("SIGINT",()=>process.exit(0));process.on("SIGTERM",()=>process.exit(0));async function Ke(){let e=await $e(),t=new Fe().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");if(t.addCommand(L).addCommand(ve).addCommand(X).addCommand(ie).addCommand(Y).addCommand(V).addCommand(re).addCommand(me).addCommand(he).addCommand(ce).addCommand(Q).addCommand(H),process.argv.slice(2).length===0){t.outputHelp();return}t.parse()}Ke();
11
291
  //# sourceMappingURL=cli.js.map