agentix-cli 0.42.0 → 0.44.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 (116) hide show
  1. package/apps/mac-helper/Resources/AppIcon.png +0 -0
  2. package/apps/mac-helper/Sources/AgentXHelper/Notify.swift +51 -0
  3. package/apps/mac-helper/Sources/AgentXHelper/main.swift +15 -1
  4. package/apps/mac-helper/build.sh +25 -2
  5. package/dist/{App-HSRGG5VA.js → App-QTUGMHG7.js} +2 -2
  6. package/dist/{ChatApp-4FOU6Q3S.js → ChatApp-634FDG3W.js} +2 -2
  7. package/dist/{agent-54NUP2Z3.js → agent-3XDPLWMX.js} +2 -2
  8. package/dist/{board-dashboard-HLTVSSGW.js → board-dashboard-OYGL7Z7X.js} +455 -161
  9. package/dist/board-dashboard-OYGL7Z7X.js.map +1 -0
  10. package/dist/{chunk-H4MIYENA.js → chunk-6HMZ5TTM.js} +2 -2
  11. package/dist/{chunk-SJPLXXCI.js → chunk-7A7RC3R4.js} +2 -2
  12. package/dist/{chunk-FZ3ZDG6T.js → chunk-7HHVZJO4.js} +195 -211
  13. package/dist/chunk-7HHVZJO4.js.map +1 -0
  14. package/dist/chunk-BIFJSQB5.js +123 -0
  15. package/dist/chunk-BIFJSQB5.js.map +1 -0
  16. package/dist/{chunk-WBGROLOX.js → chunk-BTGPZ6B7.js} +2 -2
  17. package/dist/{chunk-X763UOES.js → chunk-ICZPZYYR.js} +2 -2
  18. package/dist/{chunk-RCVGIKC2.js → chunk-MTVRYTLL.js} +2 -2
  19. package/dist/{chunk-B32HVIDN.js → chunk-NSYY5NCR.js} +3 -3
  20. package/dist/chunk-NSYY5NCR.js.map +1 -0
  21. package/dist/chunk-P2Y7DFFP.js +36 -0
  22. package/dist/chunk-P2Y7DFFP.js.map +1 -0
  23. package/dist/{chunk-2IYOKDVI.js → chunk-Q3Q6FTFY.js} +194 -164
  24. package/dist/chunk-Q3Q6FTFY.js.map +1 -0
  25. package/dist/chunk-QJANPP4C.js +15 -0
  26. package/dist/chunk-QJANPP4C.js.map +1 -0
  27. package/dist/{chunk-REPZID5M.js → chunk-SAFAI4AV.js} +43 -43
  28. package/dist/chunk-SAFAI4AV.js.map +1 -0
  29. package/dist/chunk-T76YTN34.js +178 -0
  30. package/dist/chunk-T76YTN34.js.map +1 -0
  31. package/dist/chunk-TCJ3SMOF.js +9 -0
  32. package/dist/chunk-TCJ3SMOF.js.map +1 -0
  33. package/dist/{chunk-RQKUQOFC.js → chunk-TNFOGJWZ.js} +2 -2
  34. package/dist/{chunk-FAJEBVZE.js → chunk-V5FZJUK4.js} +2 -2
  35. package/dist/chunk-WLVQDEOT.js +2 -0
  36. package/dist/chunk-WLVQDEOT.js.map +1 -0
  37. package/dist/{chunk-B5HKRYCU.js → chunk-YQVR2HQS.js} +2 -2
  38. package/dist/cli.js +1 -1
  39. package/dist/cli.js.map +1 -1
  40. package/dist/client-NTC667CA.js +2 -0
  41. package/dist/config-5V444ZFW.js +2 -0
  42. package/dist/config-mutator-E6NQLFWR.js +2 -0
  43. package/dist/doctor-5HDW5F66.js +2 -0
  44. package/dist/{editor-chat-OB7IQOB5.js → editor-chat-MOMQHMO4.js} +2 -2
  45. package/dist/editor-chat-MOMQHMO4.js.map +1 -0
  46. package/dist/index.d.ts +462 -46
  47. package/dist/index.js +1 -1
  48. package/dist/index.js.map +1 -1
  49. package/dist/program-HGI5C2MP.js +2 -0
  50. package/dist/query-6EMTLCHQ.js +2 -0
  51. package/dist/registry-5QOY65FS.js +2 -0
  52. package/dist/{request-planner-326NOKPZ.js → request-planner-KYQCJDAW.js} +2 -2
  53. package/dist/{review-candidates-LEOPEOEG.js → review-candidates-BFAWNDOR.js} +2 -2
  54. package/dist/routing-OC3QJI3W.js +2 -0
  55. package/dist/schedule-tool-VQCNMBJF.js +8 -0
  56. package/dist/schedule-tool-VQCNMBJF.js.map +1 -0
  57. package/dist/scheduler-NRXPVG7P.js +2 -0
  58. package/dist/seat-3WT3VJC2.js +2 -0
  59. package/dist/{select-L4VIYSFJ.js → select-KPQWIC22.js} +2 -2
  60. package/dist/web/activity-graph.global.js.map +1 -1
  61. package/dist/web/workflow-editor.global.js +1 -1
  62. package/dist/web/workflow-editor.global.js.map +1 -1
  63. package/dist/wiki-5JORBO4G.js +2 -0
  64. package/package.json +2 -1
  65. package/dist/board-dashboard-HLTVSSGW.js.map +0 -1
  66. package/dist/chunk-2IYOKDVI.js.map +0 -1
  67. package/dist/chunk-B32HVIDN.js.map +0 -1
  68. package/dist/chunk-DIKZWTL5.js +0 -170
  69. package/dist/chunk-DIKZWTL5.js.map +0 -1
  70. package/dist/chunk-F6CNF7V3.js +0 -25
  71. package/dist/chunk-F6CNF7V3.js.map +0 -1
  72. package/dist/chunk-FZ3ZDG6T.js.map +0 -1
  73. package/dist/chunk-IAG6FI3Q.js +0 -13
  74. package/dist/chunk-IAG6FI3Q.js.map +0 -1
  75. package/dist/chunk-NZ6AQIKJ.js +0 -121
  76. package/dist/chunk-NZ6AQIKJ.js.map +0 -1
  77. package/dist/chunk-P7LNYB53.js +0 -9
  78. package/dist/chunk-P7LNYB53.js.map +0 -1
  79. package/dist/chunk-REPZID5M.js.map +0 -1
  80. package/dist/client-6BGTH2TT.js +0 -2
  81. package/dist/config-SRTXHGSK.js +0 -2
  82. package/dist/config-mutator-KJ5GEMVV.js +0 -2
  83. package/dist/doctor-J4FLV45L.js +0 -2
  84. package/dist/editor-chat-OB7IQOB5.js.map +0 -1
  85. package/dist/program-LAACUZWW.js +0 -2
  86. package/dist/query-ZSUBNWOL.js +0 -2
  87. package/dist/registry-OMUDCBKM.js +0 -2
  88. package/dist/routing-AWIRBYGU.js +0 -2
  89. package/dist/scheduler-PGGYW5AB.js +0 -2
  90. package/dist/seat-XB4EGUKJ.js +0 -2
  91. package/dist/wiki-GBC6I7ZX.js +0 -2
  92. /package/dist/{App-HSRGG5VA.js.map → App-QTUGMHG7.js.map} +0 -0
  93. /package/dist/{ChatApp-4FOU6Q3S.js.map → ChatApp-634FDG3W.js.map} +0 -0
  94. /package/dist/{agent-54NUP2Z3.js.map → agent-3XDPLWMX.js.map} +0 -0
  95. /package/dist/{chunk-H4MIYENA.js.map → chunk-6HMZ5TTM.js.map} +0 -0
  96. /package/dist/{chunk-SJPLXXCI.js.map → chunk-7A7RC3R4.js.map} +0 -0
  97. /package/dist/{chunk-WBGROLOX.js.map → chunk-BTGPZ6B7.js.map} +0 -0
  98. /package/dist/{chunk-X763UOES.js.map → chunk-ICZPZYYR.js.map} +0 -0
  99. /package/dist/{chunk-RCVGIKC2.js.map → chunk-MTVRYTLL.js.map} +0 -0
  100. /package/dist/{chunk-RQKUQOFC.js.map → chunk-TNFOGJWZ.js.map} +0 -0
  101. /package/dist/{chunk-FAJEBVZE.js.map → chunk-V5FZJUK4.js.map} +0 -0
  102. /package/dist/{chunk-B5HKRYCU.js.map → chunk-YQVR2HQS.js.map} +0 -0
  103. /package/dist/{client-6BGTH2TT.js.map → client-NTC667CA.js.map} +0 -0
  104. /package/dist/{config-SRTXHGSK.js.map → config-5V444ZFW.js.map} +0 -0
  105. /package/dist/{config-mutator-KJ5GEMVV.js.map → config-mutator-E6NQLFWR.js.map} +0 -0
  106. /package/dist/{doctor-J4FLV45L.js.map → doctor-5HDW5F66.js.map} +0 -0
  107. /package/dist/{program-LAACUZWW.js.map → program-HGI5C2MP.js.map} +0 -0
  108. /package/dist/{query-ZSUBNWOL.js.map → query-6EMTLCHQ.js.map} +0 -0
  109. /package/dist/{registry-OMUDCBKM.js.map → registry-5QOY65FS.js.map} +0 -0
  110. /package/dist/{request-planner-326NOKPZ.js.map → request-planner-KYQCJDAW.js.map} +0 -0
  111. /package/dist/{review-candidates-LEOPEOEG.js.map → review-candidates-BFAWNDOR.js.map} +0 -0
  112. /package/dist/{routing-AWIRBYGU.js.map → routing-OC3QJI3W.js.map} +0 -0
  113. /package/dist/{scheduler-PGGYW5AB.js.map → scheduler-NRXPVG7P.js.map} +0 -0
  114. /package/dist/{seat-XB4EGUKJ.js.map → seat-3WT3VJC2.js.map} +0 -0
  115. /package/dist/{select-L4VIYSFJ.js.map → select-KPQWIC22.js.map} +0 -0
  116. /package/dist/{wiki-GBC6I7ZX.js.map → wiki-5JORBO4G.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/daemon/config-mutator.ts"],"sourcesContent":["import { readFileSync, writeFileSync, existsSync } from \"fs\"\nimport { resolve } from \"path\"\nimport { daemonConfigSchema, expandEnvVars } from \"./config\"\n\n// --- Config mutator ---\n//\n// Single helper for every \"edit agentx.json\" code path. Handles:\n// 1. Read raw JSON (preserves ${VAR} tokens)\n// 2. Call user-supplied mutator on a deep clone\n// 3. Validate the env-expanded clone through the Zod schema\n// 4. Pretty-print and write back (raw, unexpanded form)\n// 5. Signal the running daemon via POST /reload if reachable\n//\n// Commands that previously called saveConfig() now go through this,\n// so every add-wizard gets pre-save validation and auto-reload for free.\n\nexport interface MutationOptions {\n /** Run the Zod schema against the env-expanded mutated config. Default: true. */\n validate?: boolean\n /** Compute + print the diff but do NOT write. Default: false. */\n dryRun?: boolean\n /** POST /reload to the running daemon if reachable. Default: true. */\n reload?: boolean\n /** Override the config file path. Default: ./agentx.json then .agentx/config.json. */\n configPath?: string\n /** Override daemon URL for the reload signal. Default: inferred from config. */\n daemonUrl?: string\n}\n\nexport interface MutationResult {\n success: boolean\n /** Path that was (or would have been) written. */\n path?: string\n /** The mutated raw config (before write, after mutator fn). */\n after?: unknown\n /** Validation error if the Zod parse failed. */\n error?: string\n /** True if the daemon acknowledged the reload. */\n reloaded?: boolean\n /** Reload failure reason — daemon unreachable, 5xx, etc. Not a hard error. */\n reloadSkipped?: string\n /** True when opts.dryRun was set. */\n dryRun?: boolean\n}\n\nexport function findConfigPath(override?: string): string {\n if (override) return resolve(override)\n const candidates = [\n resolve(process.cwd(), \"agentx.json\"),\n resolve(process.cwd(), \".agentx/config.json\"),\n ]\n for (const c of candidates) if (existsSync(c)) return c\n return candidates[0]\n}\n\nfunction deepClone<T>(v: T): T {\n return JSON.parse(JSON.stringify(v))\n}\n\nasync function signalReload(daemonUrl: string): Promise<{ ok: boolean; reason?: string }> {\n try {\n const res = await fetch(`${daemonUrl.replace(/\\/$/, \"\")}/reload`, {\n method: \"POST\",\n signal: AbortSignal.timeout(3000),\n })\n if (!res.ok) return { ok: false, reason: `HTTP ${res.status}` }\n return { ok: true }\n } catch (e: any) {\n // Daemon not running is fine — user is editing config before start.\n return { ok: false, reason: e?.cause?.code || e?.message || \"unreachable\" }\n }\n}\n\n/**\n * Read current config, apply `mutator(cfg)` in place, validate, write, reload.\n *\n * The mutator receives the raw parsed JSON (with `${VAR}` tokens intact). Mutate\n * the object directly — no need to return anything. Validation runs against an\n * env-expanded clone so schema errors are caught before we overwrite the file.\n */\nexport async function applyConfigMutation(\n mutator: (cfg: any) => void | Promise<void>,\n opts: MutationOptions = {},\n): Promise<MutationResult> {\n const {\n validate = true,\n dryRun = false,\n reload = true,\n configPath,\n daemonUrl,\n } = opts\n\n const path = findConfigPath(configPath)\n\n if (!existsSync(path)) {\n return { success: false, path, error: `Config not found at ${path}. Run \\`agentx init\\` first.` }\n }\n\n let raw: string\n try {\n raw = readFileSync(path, \"utf-8\")\n } catch (e: any) {\n return { success: false, path, error: `Cannot read ${path}: ${e.message}` }\n }\n\n let current: any\n try {\n current = JSON.parse(raw)\n } catch (e: any) {\n return { success: false, path, error: `Invalid JSON in ${path}: ${e.message}` }\n }\n\n const mutated = deepClone(current)\n try {\n await mutator(mutated)\n } catch (e: any) {\n return { success: false, path, error: `Mutator threw: ${e.message}` }\n }\n\n if (validate) {\n const expanded = expandEnvVars(mutated)\n const parsed = daemonConfigSchema.safeParse(expanded)\n if (!parsed.success) {\n const issues = parsed.error.issues\n .map((i) => ` ${i.path.join(\".\") || \"<root>\"}: ${i.message}`)\n .join(\"\\n\")\n return { success: false, path, error: `Validation failed:\\n${issues}`, after: mutated }\n }\n }\n\n if (dryRun) {\n return { success: true, path, after: mutated, dryRun: true }\n }\n\n try {\n writeFileSync(path, JSON.stringify(mutated, null, 2) + \"\\n\")\n } catch (e: any) {\n return { success: false, path, error: `Write failed: ${e.message}`, after: mutated }\n }\n\n let reloaded = false\n let reloadSkipped: string | undefined\n\n if (reload) {\n const url = daemonUrl || inferDaemonUrl(mutated)\n const res = await signalReload(url)\n if (res.ok) reloaded = true\n else reloadSkipped = res.reason\n }\n\n return { success: true, path, after: mutated, reloaded, reloadSkipped }\n}\n\nfunction inferDaemonUrl(cfg: any): string {\n const bind: string | undefined = cfg?.node?.bind\n if (!bind) return \"http://127.0.0.1:18800\"\n // bind is \"host:port\" (host may be 0.0.0.0)\n const [host, port] = bind.split(\":\")\n const reachable = host === \"0.0.0.0\" || host === \"::\" ? \"127.0.0.1\" : host\n return `http://${reachable}:${port}`\n}\n\n/**\n * Convenience wrapper for dot-path set: `agents.devops.model` = value.\n * Creates intermediate objects as needed. Array indices (`crons.foo.onError.0`)\n * supported.\n */\nexport function setAtPath(obj: any, path: string, value: unknown): void {\n const parts = path.split(\".\")\n let cur = obj\n for (let i = 0; i < parts.length - 1; i++) {\n const k = parts[i]\n if (cur[k] === undefined || cur[k] === null) cur[k] = {}\n cur = cur[k]\n }\n cur[parts[parts.length - 1]] = value\n}\n\nexport function getAtPath(obj: any, path: string): unknown {\n const parts = path.split(\".\")\n let cur = obj\n for (const k of parts) {\n if (cur == null) return undefined\n cur = cur[k]\n }\n return cur\n}\n\nexport function unsetAtPath(obj: any, path: string): void {\n const parts = path.split(\".\")\n let cur = obj\n for (let i = 0; i < parts.length - 1; i++) {\n if (cur == null) return\n cur = cur[parts[i]]\n }\n if (cur != null) delete cur[parts[parts.length - 1]]\n}\n"],"mappings":"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","names":["init_config","readFileSync","writeFileSync","existsSync","resolve","findConfigPath","override","candidates","c","deepClone","v","signalReload","daemonUrl","res","applyConfigMutation","mutator","opts","validate","dryRun","reload","configPath","path","raw","e","current","mutated","expanded","expandEnvVars","parsed","daemonConfigSchema","issues","i","reloaded","reloadSkipped","url","inferDaemonUrl","cfg","bind","host","port","setAtPath","obj","value","parts","cur","k","getAtPath","unsetAtPath"]}
@@ -0,0 +1,36 @@
1
+ import{i as ne}from"./chunk-BIFJSQB5.js";import{existsSync as xe,readdirSync as Re,readFileSync as Ie,statSync as ve}from"fs";import{resolve as Ae}from"path";function se(r,e,t=Date.now(),s=7){let n=s*864e5,o=new Map;for(let a of e){let i=o.get(a.workflowId);i?i.push(a):o.set(a.workflowId,[a])}return r.map(a=>{let i=o.get(a.id)??[],l=i.filter(f=>t-f.at<=n),c=i.filter(f=>t-f.at>n&&t-f.at<=n*2),u=l.filter(f=>f.status==="failed").length,m=i.filter(f=>f.status==="paused").length,d=i.length?Math.max(...i.map(f=>f.at)):null,g=i.length?c.length>0&&l.length===0?"dormant":l.length>0&&u*2>=l.length?"failing":l.length>0?"active":"quiet":"never";return{id:a.id,name:a.name||a.id,state:g,lastRunAt:d,recent:l.length,prior:c.length,failed:u,paused:m}}).sort((a,i)=>{let l={dormant:0,failing:1,active:2,quiet:3,never:4};return l[a.state]-l[i.state]||(i.lastRunAt??0)-(a.lastRunAt??0)})}var _=new Map;function bt(r,e=1e3){if(!xe(r))return[];let t=Re(r).filter(o=>o.endsWith(".jsonl")).map(o=>{let a=Ae(r,o);return{path:a,mtime:ve(a).mtimeMs}}).sort((o,a)=>a.mtime-o.mtime).slice(0,e),s=new Set(t.map(o=>o.path));for(let o of _.keys())s.has(o)||_.delete(o);let n=[];for(let{path:o,mtime:a}of t){let i=_.get(o);if(i&&i.mtime===a){i.summary&&n.push(i.summary);continue}let l=je(o,a);_.set(o,{mtime:a,summary:l}),l&&n.push(l)}return n}function je(r,e){try{let t=Ie(r,"utf-8").split(`
2
+ `).filter(Boolean);if(!t.length)return null;let s=JSON.parse(t[0]),n=s.run?.workflowId;if(!n)return null;let o=s.run?.status??"unknown";for(let a=t.length-1;a>=0;a--){let i=JSON.parse(t[a]),l=i.status??i.run?.status;if(l){o=l;break}}return{workflowId:n,status:o,at:e}}catch{return null}}var Te={failStreak:3,disabledDays:30,missedFires:2,graceMs:15*6e4},y={routines:200,summary:160,filters:6,filterValue:60,text:120},_e=(r,e,t)=>{try{return ne(r,e,t)}catch{return null}},re=864e5;function St(r){let e=r.now??Date.now(),t={...Te,...r.thresholds},s=r.nextFire??_e,n=[];for(let i of r.crons){let l=r.cronRuns.get(i.id)??[],c=i.timezone||"UTC",u=l.map(g=>({at:Date.parse(g.startedAt),status:g.status})),m=l[0],d={id:`cron:${i.id}`,source:"cron",kind:"schedule",name:S(i.id,y.text),trigger:{schedule:S(i.schedule,y.text),timezone:c},agent:i.agent||null,enabled:i.enabled,state:i.enabled?"enabled":"disabled",nextRunAt:i.enabled?O(i.nextRun)??O(s(i.schedule,new Date(e),c)):null,lastRun:m?{at:m.startedAt,status:m.status,summary:Oe(m.status==="success"?void 0:m.errorSummary,y.summary)}:null,lastSuccessAt:l.find(g=>g.status==="success")?.startedAt??null,consecutiveFailures:ie(u),sampledRuns:l.length,flags:[],attention:null};d.flags=oe(d,u,i.schedule,c,null,e,t,s),n.push(ae(d))}let o=new Map(se(r.workflows.map(i=>({id:i.id,name:i.title})),r.workflowRuns,e).map(i=>[i.id,i])),a=new Map;for(let i of r.workflowRuns){let l=a.get(i.workflowId);l?l.push(i):a.set(i.workflowId,[i])}for(let i of r.workflows){let l=i.nodes.find(x=>x.type.startsWith("trigger."));if(!l||l.type!=="trigger.cron"&&l.type!=="trigger.hook")continue;let c=l.config??{},u=i.state||"active",m=u==="active",d=(a.get(i.id)??[]).slice().sort((x,D)=>D.at-x.at),g=d.map(x=>({at:x.at,status:Ce(x.status)})),f=g[0],h=l.type==="trigger.cron",b=typeof c.spec=="string"?c.spec:"",P=typeof c.timezone=="string"?c.timezone:"UTC",j={id:`workflow:${i.id}`,source:"workflow",kind:h?"schedule":"event",name:S(i.title||i.id,y.text),trigger:h?{schedule:S(b,y.text),timezone:P}:{event:S(typeof c.event=="string"?c.event:"",y.text),filters:De(c.filter,i)},agent:Pe(i),enabled:m,state:u,nextRunAt:h&&m&&b?O(s(b,new Date(e),P)):null,lastRun:f?{at:new Date(f.at).toISOString(),status:f.status}:null,lastSuccessAt:(()=>{let x=g.find(D=>D.status==="success");return x?new Date(x.at).toISOString():null})(),consecutiveFailures:ie(g),sampledRuns:d.length,flags:[],attention:null},T=Date.parse(i.updated||i.created||"");j.flags=h?oe(j,g,b,P,Number.isFinite(T)?T:null,e,t,s):Le(j,o.get(i.id)?.state==="dormant",Number.isFinite(T)?T:null,e,t),n.push(ae(j))}return n.sort(Ee).slice(0,y.routines)}function ie(r){let e=0;for(let t of r)if(!(t.status==="running"||t.status==="paused"||t.status==="unknown"))if(t.status==="failed"||t.status==="timeout")e++;else break;return e}function oe(r,e,t,s,n,o,a,i){let l=[],c=e.length?e[0].at:null;if(!r.enabled){let d=ce(c,n,o,a);return d&&l.push(d),l}if(r.consecutiveFailures>=a.failStreak&&l.push({reason:"failing",detail:`${r.consecutiveFailures} failed attempts in a row`}),c===null)return l.push({reason:"never-ran",detail:"No persisted attempt"}),l;let u=new Date(c),m=null;for(let d=0;d<a.missedFires&&(m=i(t,u,s),!!m);d++)u=m;return m&&o>m.getTime()+a.graceMs&&l.push({reason:"overdue",detail:`No attempt since ${new Date(c).toISOString()}; ${a.missedFires} scheduled fires missed`}),l}function Le(r,e,t,s,n){let o=[],a=r.lastRun?Date.parse(r.lastRun.at):null;if(!r.enabled){let i=ce(a,t,s,n);return i&&o.push(i),o}return r.consecutiveFailures>=n.failStreak&&o.push({reason:"failing",detail:`${r.consecutiveFailures} failed runs in a row`}),a===null?o.push({reason:"never-ran",detail:"No run recorded for this trigger"}):e&&o.push({reason:"dormant",detail:"Fired in the prior week, silent in the last one"}),o}function ce(r,e,t,s){let n=Math.max(r??0,e??0);return n&&t-n<s.disabledDays*re?null:{reason:"disabled-long",detail:n?`Disabled; no activity for ${Math.floor((t-n)/re)} days`:"Disabled and never ran"}}function ae(r){let e=new Set(r.flags.map(t=>t.reason));return r.attention=e.has("failing")?"critical":e.has("overdue")||e.has("dormant")||e.has("never-ran")?"warning":e.has("disabled-long")?"info":null,r}var le={critical:0,warning:1,info:2};function Ee(r,e){let t=r.attention?le[r.attention]:3,s=e.attention?le[e.attention]:3;return t-s||Number(e.enabled)-Number(r.enabled)||r.name.localeCompare(e.name)}function Ce(r){switch(r){case"completed":case"ok":return"success";case"failed":return"failed";case"timeout":return"timeout";case"canceled":return"canceled";case"running":return"running";case"paused":return"paused";default:return"unknown"}}function Pe(r){if(r.ownerAgent)return S(r.ownerAgent,y.text);let e=r.nodes.find(t=>t.type==="agent"&&typeof t.config?.agentId=="string"&&t.config.agentId);return e?S(String(e.config.agentId),y.text):null}function De(r,e){let t=[],s=e?.project;if(s&&t.push(S(`project=${s}`,y.filterValue)),r&&typeof r=="object"&&!Array.isArray(r))for(let[n,o]of Object.entries(r)){let a=Array.isArray(o)?o.map(String):o==null?[]:[String(o)];a.length&&t.push(S(`${n}=${a.join(",")}`,y.filterValue))}return t.slice(0,y.filters)}function O(r){if(!r)return null;let e=r instanceof Date?r:new Date(r);return Number.isFinite(e.getTime())?e.toISOString():null}function S(r,e){return r.length<=e?r:r.slice(0,e-3)+"..."}function Oe(r,e){return r===void 0?void 0:S(r,e)}import{readFileSync as B,writeFileSync as pe,existsSync as F,readdirSync as Ke,statSync as Ge}from"fs";import{resolve as W,join as qe}from"path";import{existsSync as ue,mkdirSync as Ue,readFileSync as Be,writeFileSync as de}from"fs";import{dirname as Fe,resolve as ge}from"path";import{z as p}from"zod";var We=p.object({type:p.enum(["gitlab","github"]),host:p.string().optional(),project:p.string(),iid:p.number().int().positive(),url:p.string()}),Ne=p.object({id:p.string(),title:p.string(),description:p.string().optional(),assignee:p.string().optional(),labels:p.array(p.string()).default([]),milestone:p.string().optional(),status:p.enum(["todo","doing","blocked","done"]).default("todo"),priority:p.number().int().optional(),estimatedSeconds:p.number().int().optional(),source:We.optional(),importedAt:p.string().optional(),createdAt:p.string(),updatedAt:p.string(),blocker:p.string().optional(),doneNote:p.string().optional()}),Me=p.object({version:p.literal(1),items:p.array(Ne).default([])}),L=class{constructor(e){this.path=e}jsonPath(){return ge(process.cwd(),this.path).replace(/\.md$/i,".json")}mdPath(){return ge(process.cwd(),this.path).replace(/\.json$/i,".md")}exists(){return ue(this.jsonPath())}load(){let e=this.jsonPath();if(!ue(e))return{version:1,items:[]};let t=Be(e,"utf-8");try{return Me.parse(JSON.parse(t))}catch(s){throw new Error(`Backlog file invalid (${e}): ${s.message}`)}}save(e){let t=this.jsonPath();Ue(Fe(t),{recursive:!0}),de(t,JSON.stringify(e,null,2)+`
3
+ `),de(this.mdPath(),this.toMarkdown(e))}list(){return this.load().items}findById(e){return this.list().find(t=>t.id===e)}add(e){let t=this.load();if(t.items.some(o=>o.id===e.id))throw new Error(`backlog item ${e.id} already exists`);let s=new Date().toISOString(),n={...e,labels:e.labels??[],status:e.status??"todo",createdAt:e.createdAt??s,updatedAt:s};return t.items.push(n),this.save(t),n}addMany(e){let t=this.load(),s=new Set(t.items.map(a=>a.id)),n=new Date().toISOString(),o=[];for(let a of e){if(s.has(a.id))continue;let i={...a,labels:a.labels??[],status:a.status??"todo",createdAt:a.createdAt??n,updatedAt:n};t.items.push(i),o.push(i),s.add(i.id)}return o.length&&this.save(t),o}update(e,t){let s=this.load(),n=s.items.findIndex(a=>a.id===e);if(n<0)throw new Error(`backlog item ${e} not found`);let o={...s.items[n],...t,id:s.items[n].id,createdAt:s.items[n].createdAt,updatedAt:new Date().toISOString()};return s.items[n]=o,this.save(s),o}remove(e){let t=this.load(),s=t.items.length;return t.items=t.items.filter(n=>n.id!==e),t.items.length===s?!1:(this.save(t),!0)}toMarkdown(e){let t=["# Backlog","","_Generated from `backlog.json`. Do not edit by hand \u2014 use `agentx backlog` commands._",""],s={doing:[],blocked:[],todo:[],done:[]};for(let o of e.items)s[o.status].push(o);let n=[["doing","Doing"],["blocked","Blocked"],["todo","To Do"],["done","Done"]];for(let[o,a]of n)if(s[o].length){t.push(`## ${a}`,"");for(let i of s[o]){let l=o==="done"?"x":" ",c=i.assignee?`@${i.assignee} `:"",u=i.estimatedSeconds?` [time: ${Math.round(i.estimatedSeconds/60)}m]`:"",m=i.source?` (${i.source.type}: ${i.source.project}#${i.source.iid})`:"";t.push(`- [${l}] ${c}${i.title}${u}${m}`),i.source?.url&&t.push(` - <${i.source.url}>`),i.labels.length&&t.push(` - labels: ${i.labels.join(", ")}`),i.milestone&&t.push(` - milestone: ${i.milestone}`),i.blocker&&t.push(` - BLOCKED: ${i.blocker}`)}t.push("")}return t.join(`
4
+ `)}};function vt(r){return`${r.type}:${r.project}:${r.iid}`}function me(r,e,t){let s=[];if(e.type==="gitlab"){let n=t.channels.gitlab?.agentMappings??[];for(let o of r){let a=n.find(i=>i.agentId===o);a?.gitlabUsernames?.length?s.push(a.gitlabUsernames[0]):s.push(o)}}else if(e.type==="github"){let n=t.channels.github?.agentMappings??[];for(let o of r){let a=n.find(i=>i.agentId===o);a?.githubUsernames?.length?s.push(a.githubUsernames[0]):s.push(o)}}return s}async function U(r,e,t,s=()=>{}){return r.source?r.source.type==="gitlab"?ze(r.source,e,t,s):r.source.type==="github"?He(r.source,e,t,s):{ok:!1,error:`unknown source.type ${r.source.type}`,attempts:[]}:{ok:!0,attempts:[]}}async function ze(r,e,t,s){let n=t.channels.gitlab,o=n?.token,a=n?.host||(r.host?`https://${r.host}`:void 0);if(!o||!a)return{ok:!1,error:"gitlab token/host not configured",attempts:[]};let i=[],l=async(g,f={})=>fetch(`${a}/api/v4${g}`,{...f,headers:{"PRIVATE-TOKEN":o,"Content-Type":"application/json",...f.headers||{}}}),c=encodeURIComponent(r.project),u;e.assigneeUsernames!==void 0&&(u=(await Promise.all(e.assigneeUsernames.map(async f=>{try{let h=await fetch(`${a}/api/v4/users?username=${encodeURIComponent(f)}`,{headers:{"PRIVATE-TOKEN":o}});if(!h.ok)return null;let b=await h.json();return Array.isArray(b)&&b[0]?.id?b[0].id:null}catch{return null}}))).filter(f=>typeof f=="number"));let m;if(e.milestoneTitle!==void 0)if(e.milestoneTitle===null||e.milestoneTitle==="")m=0;else try{let g=await l(`/projects/${c}/milestones?title=${encodeURIComponent(e.milestoneTitle)}`);if(g.ok){let f=await g.json();Array.isArray(f)&&f[0]?.id&&(m=f[0].id)}}catch(g){s(`[backlog-sync] milestone lookup failed: ${g.message}`)}let d={};if(e.title!==void 0&&(d.title=e.title),e.description!==void 0&&(d.description=e.description),e.labels!==void 0&&(d.labels=e.labels.join(",")),e.addLabels?.length&&(d.add_labels=e.addLabels.join(",")),e.removeLabels?.length&&(d.remove_labels=e.removeLabels.join(",")),u!==void 0&&(d.assignee_ids=u),m!==void 0&&(d.milestone_id=m),e.state&&(d.state_event=e.state==="closed"?"close":"reopen"),Object.keys(d).length)try{let g=await l(`/projects/${c}/issues/${r.iid}`,{method:"PUT",body:JSON.stringify(d)});if(i.push({kind:"PUT issue",ok:g.ok,status:g.status}),!g.ok){let f=await g.text().catch(()=>"");return{ok:!1,attempts:i,error:`PUT issue ${g.status}: ${f.slice(0,200)}`,url:r.url}}}catch(g){return i.push({kind:"PUT issue",ok:!1,error:g.message}),{ok:!1,attempts:i,error:g.message,url:r.url}}if(e.note)try{let g=await l(`/projects/${c}/issues/${r.iid}/notes`,{method:"POST",body:JSON.stringify({body:e.note})});i.push({kind:"POST note",ok:g.ok,status:g.status})}catch(g){i.push({kind:"POST note",ok:!1,error:g.message})}return{ok:i.every(g=>g.ok),attempts:i,url:r.url}}async function He(r,e,t,s){let o=t.channels.github?.token;if(!o)return{ok:!1,error:"github token not configured",attempts:[]};let a=[],i=async(d,g={})=>fetch(`https://api.github.com${d}`,{...g,headers:{Authorization:`Bearer ${o}`,Accept:"application/vnd.github+json","X-GitHub-Api-Version":"2022-11-28",...g.headers||{}}}),l=r.project,c;if(e.milestoneTitle!==void 0)if(e.milestoneTitle===null||e.milestoneTitle==="")c=null;else try{let d=await i(`/repos/${l}/milestones?state=all&per_page=100`);if(d.ok){let g=await d.json(),f=Array.isArray(g)?g.find(h=>h.title===e.milestoneTitle):null;f?.number&&(c=f.number)}}catch(d){s(`[backlog-sync] github milestone lookup failed: ${d.message}`)}let u;if(e.labels!==void 0)u=e.labels;else if(e.addLabels?.length||e.removeLabels?.length)try{let d=await i(`/repos/${l}/issues/${r.iid}`);if(d.ok){let g=await d.json(),f=Array.isArray(g.labels)?g.labels.map(b=>typeof b=="string"?b:b.name):[],h=new Set([...f,...e.addLabels??[]]);for(let b of e.removeLabels??[])h.delete(b);u=[...h]}}catch(d){s(`[backlog-sync] github label fetch failed: ${d.message}`)}let m={};if(e.title!==void 0&&(m.title=e.title),e.description!==void 0&&(m.body=e.description),u!==void 0&&(m.labels=u),e.assigneeUsernames!==void 0&&(m.assignees=e.assigneeUsernames),c!==void 0&&(m.milestone=c),e.state&&(m.state=e.state),Object.keys(m).length)try{let d=await i(`/repos/${l}/issues/${r.iid}`,{method:"PATCH",body:JSON.stringify(m)});if(a.push({kind:"PATCH issue",ok:d.ok,status:d.status}),!d.ok){let g=await d.text().catch(()=>"");return{ok:!1,attempts:a,error:`PATCH issue ${d.status}: ${g.slice(0,200)}`,url:r.url}}}catch(d){return a.push({kind:"PATCH issue",ok:!1,error:d.message}),{ok:!1,attempts:a,error:d.message,url:r.url}}if(e.note)try{let d=await i(`/repos/${l}/issues/${r.iid}/comments`,{method:"POST",body:JSON.stringify({body:e.note})});a.push({kind:"POST comment",ok:d.ok,status:d.status})}catch(d){a.push({kind:"POST comment",ok:!1,error:d.message})}return{ok:a.every(d=>d.ok),attempts:a,url:r.url}}var N=/^(\s*)-\s*\[( |x|X)\]\s*(.*)$/,E=/@([a-z0-9_-]+)/i,fe=/\[time:\s*([0-9hm\s]+)\]/i;function Je(r){let e=r.match(/(\d+)\s*h/i),t=r.match(/(\d+)\s*m/i);return(e?parseInt(e[1],10)*3600:0)+(t?parseInt(t[1],10)*60:0)}var M=class{constructor(e,t,s=()=>{}){this.path=e;this.daemon=t;this.log=s;this.store=new L(this.path)}type="backlog";capabilities={listAll:!0,create:!1,transition:!1,labels:!0,search:!1};store;getStore(){return this.store}readMd(){let e=W(process.cwd(),this.path);return F(e)?B(e,"utf-8").split(`
5
+ `):[]}writeMd(e){let t=W(process.cwd(),this.path);pe(t,e.join(`
6
+ `))}async listOpen(e){if(this.store.exists())return this.store.list().filter(o=>o.status!=="done"&&(!o.assignee||o.assignee.toLowerCase()===e.toLowerCase())).map(o=>this.toWorkItem(o));let t=this.readMd(),s=[];for(let n=0;n<t.length;n++){let o=t[n].match(N);if(!o||o[2].toLowerCase()==="x")continue;let i=o[3],l=i.match(E);if(l&&l[1].toLowerCase()!==e.toLowerCase())continue;let c=i.match(fe),u=i.replace(E,"").replace(fe,"").trim();s.push({id:`backlog:${n}`,title:u||`Task at line ${n+1}`,assignee:l?l[1]:void 0,estimatedSeconds:c?Je(c[1]):void 0,priority:n})}return s}async listAll(e={}){return this.store.exists()?this.store.list().map(t=>this.toWorkItem(t)):[]}async claim(e,t){if(!this.store.exists())return;let s=this.store.findById(t);if(!s)return;let n=this.store.update(t,{assignee:s.assignee||e,status:"doing"});if(n.source&&this.daemon){let o=me([n.assignee||e],n.source,this.daemon);U(n,{assigneeUsernames:o,addLabels:["Doing"],removeLabels:["To Do"]},this.daemon,this.log).catch(a=>this.log(`[backlog-sync] claim failed for ${t}: ${a?.message??a}`))}}async report(e,t){if(this.store.exists()&&this.store.findById(e)){let i={};t.status==="done"?i.status="done":t.status==="blocked"?(i.status="blocked",i.blocker=t.blocker||t.note||void 0):t.status==="in-progress"&&(i.status="doing"),t.timeSeconds&&(i.estimatedSeconds=t.timeSeconds);let l=this.store.update(e,i);if(l.source&&this.daemon){let c={};t.status==="done"?(c.addLabels=["Done"],c.removeLabels=["Doing"]):t.status==="blocked"&&(c.addLabels=["Blocked"]),t.note&&(c.note=t.note),Object.keys(c).length&&U(l,c,this.daemon,this.log).catch(u=>this.log(`[backlog-sync] report failed for ${e}: ${u?.message??u}`))}return}if(!e.startsWith("backlog:"))throw new Error(`not a backlog id: ${e}`);let s=parseInt(e.slice(8),10),n=this.readMd(),o=n[s];if(!o)throw new Error(`backlog line ${s} missing`);if(!o.match(N))throw new Error(`backlog line ${s} not a checkbox`);if(t.status==="done"){n[s]=o.replace(/- \[ \]/,"- [x]");let i=t.timeSeconds?` <!-- done by agent, ${Math.round(t.timeSeconds/60)}m -->`:" <!-- done by agent -->";n.splice(s+1,0,i)}else t.status==="blocked"?n.splice(s+1,0,` <!-- BLOCKED: ${t.blocker||t.note||"?"} -->`):t.note&&n.splice(s+1,0,` <!-- ${t.note.replace(/-->/g,"\u2014 >")} -->`);this.writeMd(n)}toWorkItem(e){let t={todo:"todo",doing:"doing",blocked:"onhold",done:"done"}[e.status];return{id:e.id,title:e.title,description:e.description,assignee:e.assignee,estimatedSeconds:e.estimatedSeconds,priority:e.priority,labels:e.labels,stage:t,milestone:e.milestone,url:e.source?.url,updatedAt:e.updatedAt,state:e.status==="done"?"closed":"opened"}}},z=class{constructor(e,t){this.root=e;this.glob=t}type="wiki";capabilities={listAll:!1,create:!1,transition:!1,labels:!1,search:!1};walk(e,t){if(F(e))for(let s of Ke(e)){let n=qe(e,s);Ge(n).isDirectory()?this.walk(n,t):s.endsWith(".md")&&t.push(n)}}async listOpen(e){let t=[];this.walk(W(process.cwd(),this.root),t);let s=[];for(let n of t){let o=B(n,"utf-8").split(`
7
+ `);for(let a=0;a<o.length;a++){let i=o[a].match(N);if(!i||i[2].toLowerCase()==="x")continue;let l=i[3].match(E);!l||l[1].toLowerCase()!==e.toLowerCase()||s.push({id:`wiki:${n}:${a}`,title:i[3].replace(E,"").trim(),assignee:l[1],priority:a})}}return s}async claim(){}async report(e,t){let s=e.split(":"),n=parseInt(s[s.length-1],10),o=s.slice(1,-1).join(":");if(!F(o))return;let a=B(o,"utf-8").split(`
8
+ `);t.status==="done"&&a[n]&&(a[n]=a[n].replace(/- \[ \]/,"- [x]"),pe(o,a.join(`
9
+ `)))}},H=class{constructor(e,t,s,n,o){this.host=e;this.token=t;this.projects=s;this.agentUsernames=n;this.log=o}type="gitlab";capabilities={listAll:!0,create:!0,transition:!0,labels:!0,search:!0};usernameToAgent(e){for(let[t,s]of Object.entries(this.agentUsernames))if(s.some(n=>n.toLowerCase()===e.toLowerCase()))return t}mapIssue(e,t){let s=e.references?.full?.split("#")[0]||t,n=[],o;Array.isArray(e.labels)&&(e.labels.length&&typeof e.labels[0]=="object"?(o=e.labels.map(c=>({name:c.name,color:c.color,text_color:c.text_color})),n=o.map(c=>c.name)):n=e.labels);let i=(Array.isArray(e.assignees)&&e.assignees.length?e.assignees:e.assignee?[e.assignee]:[]).map(c=>({username:c.username,name:c.name,avatarUrl:c.avatar_url})),l=i[0]?.username;return{id:`gitlab:${s}:issue:${e.iid}`,title:e.title,description:e.description||void 0,assignee:l?this.usernameToAgent(l)||l:void 0,assignees:i,url:e.web_url,priority:e.weight??99,estimatedSeconds:e.time_stats?.time_estimate||void 0,labels:n,labelDetails:o,updatedAt:e.updated_at,state:e.state==="closed"?"closed":"opened",milestone:e.milestone?.title||void 0}}async api(e,t={}){let s=await fetch(`${this.host}/api/v4${e}`,{...t,headers:{"PRIVATE-TOKEN":this.token,"Content-Type":"application/json",...t.headers||{}}});if(!s.ok)throw new Error(`GitLab ${s.status}: ${await s.text().catch(()=>"")}`);return s.json()}async listOpen(e){let t=this.agentUsernames[e]||[];if(!t.length)return[];let s=[],n=this.projects.length?this.projects:["__ALL__"];for(let o of t)for(let a of n)try{let i=`assignee_username=${encodeURIComponent(o)}&state=opened&scope=all&per_page=20`,l=a==="__ALL__"?`/issues?${i}`:`/projects/${encodeURIComponent(a)}/issues?${i}`,c=await this.api(l);for(let u of c){let m=this.mapIssue(u,a);m.assignee=e,s.push(m)}}catch(i){this.log(`[business] GitLab listOpen failed for ${o} on ${a}: ${i.message}`)}return s}async claim(e,t){let{project:s,iid:n,type:o}=this.parseId(t),a=`/projects/${encodeURIComponent(s)}/${o==="issue"?"issues":"merge_requests"}/${n}?add_labels=Doing&remove_labels=To Do`;try{await this.api(a,{method:"PUT"})}catch(i){this.log(`[business] claim label update failed: ${i.message}`)}}async report(e,t){let{project:s,iid:n,type:o}=this.parseId(e),a=o==="issue"?"issues":"merge_requests";if(t.timeSeconds&&t.timeSeconds>0){let i=Math.max(1,Math.round(t.timeSeconds/60)),l=i>=60?`${Math.floor(i/60)}h${i%60?` ${i%60}m`:""}`:`${i}m`;try{await this.api(`/projects/${encodeURIComponent(s)}/${a}/${n}/add_spent_time?duration=${encodeURIComponent(l)}`,{method:"POST"})}catch(c){this.log(`[business] add_spent_time failed: ${c.message}`)}}if(t.status==="done")try{await this.api(`/projects/${encodeURIComponent(s)}/${a}/${n}?add_labels=Done&remove_labels=Doing`,{method:"PUT"})}catch(i){this.log(`[business] done label update failed: ${i.message}`)}else if(t.status==="blocked")try{await this.api(`/projects/${encodeURIComponent(s)}/${a}/${n}?add_labels=Blocked`,{method:"PUT"})}catch(i){this.log(`[business] blocked label update failed: ${i.message}`)}if(t.note)try{await this.api(`/projects/${encodeURIComponent(s)}/${a}/${n}/notes`,{method:"POST",body:JSON.stringify({body:t.note})})}catch(i){this.log(`[business] report note failed: ${i.message}`)}}async listAll(e={}){let t=e.sinceDays??30,s=new Date(Date.now()-t*24*36e5).toISOString(),n=new URLSearchParams,o=e.state??"opened";n.set("state",o),n.set("updated_after",s),n.set("scope","all"),n.set("per_page","100"),n.set("with_labels_details","true"),n.set("order_by","updated_at"),n.set("sort","desc"),e.labels?.length&&n.set("labels",e.labels.join(",")),e.milestone&&n.set("milestone",e.milestone),e.search&&n.set("search",e.search);let a=this.projects.length?this.projects:["__ALL__"],i=[];for(let c of a)try{let u=c==="__ALL__"?`/issues?${n.toString()}`:`/projects/${encodeURIComponent(c)}/issues?${n.toString()}`,m=await this.api(u);for(let d of m)i.push(this.mapIssue(d,c))}catch(u){this.log(`[business] GitLab listAll failed on ${c}: ${u.message}`)}let l=i;if(e.notLabels?.length){let c=new Set(e.notLabels);l=l.filter(u=>!(u.labels||[]).some(m=>c.has(m)))}if(e.withoutScopedPrefix){let c=e.withoutScopedPrefix+"::";l=l.filter(u=>!(u.labels||[]).some(m=>m.startsWith(c)))}return l}async setState(e,t){let{project:s,iid:n,type:o}=this.parseId(e),a=o==="issue"?"issues":"merge_requests";await this.api(`/projects/${encodeURIComponent(s)}/${a}/${n}?state_event=${t}`,{method:"PUT"})}async getItem(e){let{project:t,iid:s}=this.parseId(e);try{let n=await this.api(`/projects/${encodeURIComponent(t)}/issues/${s}?with_labels_details=true`);return{...this.mapIssue(n,t),descriptionHtml:n.description_html||void 0,createdAt:n.created_at,author:n.author?{username:n.author.username,name:n.author.name,avatarUrl:n.author.avatar_url}:void 0,allLabels:Array.isArray(n.labels)?typeof n.labels[0]=="object"?n.labels.map(a=>a.name):n.labels:[]}}catch(n){return this.log(`[board] GitLab getItem failed for ${e}: ${n.message}`),null}}async updateItem(e,t){let{project:s,iid:n}=this.parseId(e),o;t.assigneeUsernames&&(o=(await Promise.all(t.assigneeUsernames.map(async u=>{try{let m=await this.api(`/users?username=${encodeURIComponent(u)}`);return Array.isArray(m)&&m[0]?.id?m[0].id:null}catch{return null}}))).filter(u=>typeof u=="number"));let a;if(t.milestoneTitle!==void 0)if(t.milestoneTitle===null||t.milestoneTitle==="")a=0;else try{let c=await this.api(`/projects/${encodeURIComponent(s)}/milestones?title=${encodeURIComponent(t.milestoneTitle)}`);Array.isArray(c)&&c[0]?.id&&(a=c[0].id)}catch(c){this.log(`[board] milestone lookup failed for "${t.milestoneTitle}": ${c.message}`)}let i={};t.title!==void 0&&(i.title=t.title),t.description!==void 0&&(i.description=t.description),t.labels!==void 0&&(i.labels=t.labels.join(",")),t.addLabels?.length&&(i.add_labels=t.addLabels.join(",")),t.removeLabels?.length&&(i.remove_labels=t.removeLabels.join(",")),o!==void 0&&(i.assignee_ids=o),a!==void 0&&(i.milestone_id=a);let l=await this.api(`/projects/${encodeURIComponent(s)}/issues/${n}`,{method:"PUT",body:JSON.stringify(i)});return this.mapIssue(l,s)}async listMembers(){let e=new Map;for(let t of this.projects)try{let s=await this.api(`/projects/${encodeURIComponent(t)}/members/all?per_page=100`);for(let n of s)n.username&&!e.has(n.username)&&e.set(n.username,{username:n.username,name:n.name,avatarUrl:n.avatar_url})}catch(s){this.log(`[board] listMembers failed for ${t}: ${s.message}`)}return[...e.values()].sort((t,s)=>t.username.localeCompare(s.username))}async listMilestones(){let e=new Map;for(let s of this.projects)try{let n=await this.api(`/projects/${encodeURIComponent(s)}/milestones?per_page=100`);for(let o of n)o.title&&!e.has(o.title)&&e.set(o.title,{id:o.id,title:o.title,state:o.state,dueDate:o.due_date})}catch(n){this.log(`[board] listMilestones failed for ${s}: ${n.message}`)}let t=[...e.values()];return t.sort((s,n)=>s.state!==n.state?s.state==="active"?-1:1:s.title.localeCompare(n.title)),t}async create(e){let t=e.projectHint||this.projects[0];if(!t)throw new Error("GitLabWorkSource.create requires projectHint or configured projects");let s,n=e.assigneeUsernames?.length?e.assigneeUsernames:e.assignee?(this.agentUsernames[e.assignee]||[]).slice(0,1):[];n.length&&(s=(await Promise.all(n.map(async c=>{try{let u=await this.api(`/users?username=${encodeURIComponent(c)}`);return Array.isArray(u)&&u[0]?.id?u[0].id:null}catch(u){return this.log(`[board] user lookup failed for ${c}: ${u.message}`),null}}))).filter(c=>typeof c=="number"));let o;if(e.milestoneTitle)try{let l=await this.api(`/projects/${encodeURIComponent(t)}/milestones?title=${encodeURIComponent(e.milestoneTitle)}`);Array.isArray(l)&&l[0]?.id&&(o=l[0].id)}catch(l){this.log(`[board] milestone lookup failed for "${e.milestoneTitle}": ${l.message}`)}let a={title:e.title,description:e.description,labels:(e.labels||[]).join(",")};s?.length&&(a.assignee_ids=s),o!==void 0&&(a.milestone_id=o);let i=await this.api(`/projects/${encodeURIComponent(t)}/issues`,{method:"POST",body:JSON.stringify(a)});return this.mapIssue(i,t)}async transition(e,t,s){await this.transitionMany(e,t?[t]:[],s?[s]:[])}async transitionMany(e,t,s){let{project:n,iid:o,type:a}=this.parseId(e),i=a==="issue"?"issues":"merge_requests",l=new URLSearchParams;t.length&&l.set("add_labels",t.join(",")),s.length&&l.set("remove_labels",s.join(",")),l.toString()&&await this.api(`/projects/${encodeURIComponent(n)}/${i}/${o}?${l.toString()}`,{method:"PUT"})}parseId(e){let s=e.replace(/^gitlab:/,"").split(":"),n=s.pop(),o=s.pop()==="mr"?"mr":"issue";return{project:s.join(":"),type:o,iid:n}}};function Ct(r,e,t){let s=r.workSource;switch(s.type){case"backlog":return new M(s.path,e,t);case"wiki":return new z(s.path,s.glob);case"gitlab":{let n=e.channels.gitlab;if(!n?.token)throw new Error("business.workSource.type=gitlab requires channels.gitlab.token");let o={};for(let i of n.agentMappings)i.gitlabUsernames.length&&(o[i.agentId]=i.gitlabUsernames);let a=s.projects.length?s.projects:n.routes.map(i=>i.project);return new H(n.host,n.token,a,o,t)}}}var K="unmapped";function G(r,e=[]){if(!r)return K;let t=e.find(n=>n.id===r);if(t?.client)return t.client;let s=r.indexOf("/");return s>0?r.slice(0,s):r}function Xe(r){let e=new Map,t=r.projects??[],s=r.orgChart??{};for(let o of t)o.pm&&(e.has(o.pm)||e.set(o.pm,G(o.id,t)));let n=(o,a=new Set)=>{if(e.has(o))return e.get(o);if(a.has(o))return;a.add(o);let i=s[o]?.reportsTo;if(!i)return;let l=n(i,a);return l&&e.set(o,l),l};for(let o of Object.keys(s))n(o);return e}function Ve(r,e,t=[]){if(!t.length)return;let s=t.filter(n=>!n.channel||n.channel===r);return s.find(n=>n.chatId&&e.chatId&&n.chatId===e.chatId)??s.find(n=>n.username&&e.username&&n.username===e.username)??s.find(n=>n.senderId&&e.senderId&&n.senderId===e.senderId)??s.find(n=>n.channel&&!n.chatId&&!n.username&&!n.senderId)}function Dt(r,e){let t=Ve(r.channel,{chatId:r.chatId,username:r.username},e.contactMap);if(t)return t.client;if(r.project)return G(r.project,e.projects);if(r.agentId){let s=Xe(e).get(r.agentId);if(s)return s}return K}function Ot(r,e){let t=String(r||"").split(":");if(t.length<3)return{agentId:e};let[s,n,...o]=t,a=o.join(":"),i={agentId:e||s,channel:n};return n==="gitlab"||n==="github"?i.project=a.split(":")[0]:n==="cron"||n==="api"||(i.chatId=a.split("@")[0]),i}var Ye=r=>r===K?"internal":"client";function Qe(r,e){let t=e.clients?.[r];return{id:r,name:t?.name||r,kind:t?.kind??Ye(r),respondWithinMinutes:Ze(t?.respondWithin),standing:t?.standing??[],declared:!!t}}function Ut(r){let e=new Set;for(let t of r.projects??[])e.add(G(t.id,r.projects));for(let t of r.contactMap??[])e.add(t.client);for(let t of Object.keys(r.clients??{}))e.add(t);return[...e].sort().map(t=>Qe(t,r))}function Ze(r){let e=/^(\d+)\s*([mhd])$/.exec(String(r??"").trim());if(!e)return;let t=Number(e[1]);return e[2]==="m"?t:e[2]==="h"?t*60:t*1440}import{existsSync as et}from"node:fs";import{join as he}from"node:path";var tt="tn.acme.agentx.voice",Nt="AgentX Desktop.app",nt="AgentX Helper.app";function Mt(r,e,t){return r!=="darwin"||e!=="arm64"||Number(t.split(".")[0])<23?"AgentX Desktop currently requires Apple Silicon and macOS 14 or newer.":null}function zt(r,e){if(e&&!Object.hasOwn(r,e))throw new Error(`Unknown agent: ${e}. Run agentx agent list.`);let t=e||Object.keys(r)[0];if(!t)throw new Error("No agents configured. Run agentx setup first.");return t}function q(r){return`'${r.replace(/'/g,"'\\''")}'`}function Ht(r){let e=s=>s.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;"),t={AGENTX_VOICE_AGENT:r.agent,AGENTX_DAEMON_URL:r.url,AGENTX_MAC_HELPER:r.helper,AGENTX_PASTE_COMMAND:`cd ${q(r.cwd)} && ${q(r.node)} ${q(r.cli)} paste`};return`<?xml version="1.0" encoding="UTF-8"?>
10
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
11
+ <plist version="1.0"><dict>
12
+ <key>Label</key><string>${tt}</string>
13
+ <key>ProgramArguments</key><array><string>${e(r.executable)}</string></array>
14
+ <key>WorkingDirectory</key><string>${e(r.cwd)}</string>
15
+ <key>EnvironmentVariables</key><dict>${Object.entries(t).map(([s,n])=>`<key>${s}</key><string>${e(n)}</string>`).join("")}</dict>
16
+ <key>RunAtLoad</key><true/><key>KeepAlive</key><true/>
17
+ <key>ProcessType</key><string>Interactive</string>
18
+ <key>StandardErrorPath</key><string>${e(r.log)}</string>
19
+ </dict></plist>
20
+ `}function Kt(r){return r?["--icon",r]:[]}function st(r){return he(r,"Applications",nt,"Contents/MacOS/agentx-mac-helper")}function be(r,e,t){if(t)return t;let s=st(e);return et(s)?s:he(r,"apps/mac-helper/build/AgentX Helper.app/Contents/MacOS/agentx-mac-helper")}import{execFile as rt}from"child_process";import{existsSync as J}from"fs";import{homedir as ye}from"os";import{isAbsolute as it}from"path";var ot={banner:!0,sound:!0,soundName:"Glass",volume:.4},at=5e3,lt=(r,e)=>new Promise(t=>{try{rt(r,e,{timeout:at},s=>t(!s))}catch{t(!1)}});function Qt(r){return{...ot,...r??{}}}function ke(r){if(!/^[\w -]+$/.test(r))return null;let e=`/System/Library/Sounds/${r}.aiff`;return J(e)?e:null}function ct(){let r=be(process.cwd(),ye());return J(r)?r:null}function Zt(r,e={}){let t=e.run??lt,s=e.platform??process.platform,n=async(o,a)=>{let i=e.helper===void 0?ct():e.helper;i&&await t(i,["notify","--title",o,"--message",a])||await t("/usr/bin/osascript",["-e","on run argv","-e","display notification (item 2 of argv) with title (item 1 of argv)","-e","end run",o,a])};return async(o,a)=>{if(s!=="darwin")return;let i=[];r.banner&&i.push(n(o,a));let l=r.sound?ke(r.soundName):null;if(l){let c=Math.min(1,Math.max(0,r.volume));i.push(t("/usr/bin/afplay",["-v",String(c),l]))}await Promise.all(i)}}function en(r,e,t=process.platform){let s={...r??{}};if("banner"in e&&(s.banner=!!e.banner),"sound"in e&&(s.sound=!!e.sound),"soundName"in e){let n=String(e.soundName).trim();if(!(t==="darwin"?ke(n)!==null:/^[\w -]+$/.test(n)))throw new Error(`no system sound named "${n}" in /System/Library/Sounds`);s.soundName=n}if("icon"in e){let n=String(e.icon??"").trim().replace(/^~(?=\/)/,ye());if(!n)delete s.icon;else{if(!it(n)||!/\.(png|jpe?g|icns)$/i.test(n))throw new Error("icon must be the full path to a .png, .jpg or .icns file");if(t==="darwin"&&!J(n))throw new Error(`no icon file at ${n}`);s.icon=n}}if("volume"in e){let n=Number(e.volume);if(!Number.isFinite(n)||n<0||n>1)throw new Error("volume must be between 0 and 1");s.volume=n}return s}import{existsSync as w,readFileSync as Q,writeFileSync as Z,mkdirSync as C,readdirSync as Y,rmSync as ut,statSync as $}from"fs";import{resolve as we,relative as dt,dirname as ee,join as I,extname as A,basename as te}from"path";var k=200*1024,Se=new Set([".md",".markdown",".txt"]),v=new Set([".md",".markdown",".txt",".json",".jsonc",".yaml",".yml",".toml",".sh",".bash",".zsh",".py",".js",".mjs",".ts",".tsx",".jsx",".css",".html",".lock",".cfg",".ini",".env",".gitignore"]),gt=6,mt=new Set(["CLAUDE.md","SOUL.md","IDENTITY.md","PERSONA.md","AGENT.md","BOOTSTRAP.md"]);function R(r,e){if(!r)throw new Error("Agent workspace is not configured.");let t=we(r);if(w(t)||C(t,{recursive:!0}),!e||e.startsWith("/")||e.match(/^[a-zA-Z]:/))throw new Error(`Path must be relative to the workspace: ${e}`);let s=we(t,e);if(dt(t,s).startsWith(".."))throw new Error(`Path escapes the workspace: ${e}`);return s}function $e(r){let e=A(r).toLowerCase();if(!Se.has(e))throw new Error(`Only text files (.md, .markdown, .txt) can be edited from the admin panel (got ${e}).`)}function X(r,e){let t=r.split(/\r?\n/).find(s=>s.trim().startsWith("#"));return t&&t.replace(/^#+\s*/,"").trim().slice(0,120)||e}function rn(r){let e=R(r,"."),t=[],s=new Set,n=[];try{n=Y(e)}catch{}for(let i of mt){let l=I(e,i);if(w(l)&&$(l).isFile()){let c=$(l).size,u=c>0&&c<k?V(l):"";t.push({path:i,title:X(u,i),size:c,exists:!0}),s.add(i)}else t.push({path:i,title:i,size:0,exists:!1}),s.add(i)}for(let i of n){if(s.has(i))continue;let l=I(e,i);try{let c=$(l);if(!c.isFile()||!Se.has(A(i).toLowerCase()))continue;let u=c.size>0&&c.size<k?V(l):"";t.push({path:i,title:X(u,i),size:c.size,exists:!0})}catch{}}let o=[],a=I(e,".claude","skills");if(w(a))try{for(let i of Y(a)){let l=I(a,i),c;try{c=$(l)}catch{continue}if(!c.isDirectory())continue;let u=I(l,"SKILL.md");if(!w(u))continue;let m=$(u),d=m.size<k?V(u):"";o.push({slug:i,title:X(d,i),size:m.size,path:`.claude/skills/${i}/SKILL.md`})}}catch{}return o.sort((i,l)=>i.slug.localeCompare(l.slug)),{identity:t,skills:o,workspace:e}}function V(r){try{return Q(r,"utf-8")}catch{return""}}function on(r,e){let t=R(r,e);if($e(t),!w(t))return{path:e,content:""};if($(t).size>k)throw new Error(`File is too large to edit in the admin panel (> ${k/1024} KB).`);return{path:e,content:Q(t,"utf-8")}}function an(r,e,t){let s=R(r,e);$e(s);let n=Buffer.from(t,"utf-8");if(n.length>k)throw new Error(`File too large (${n.length} > ${k} bytes).`);return C(ee(s),{recursive:!0}),Z(s,n),{path:e,bytes:n.length}}function ln(r,e,t={}){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error("Skill slug must be lowercase (letters, digits, -, _) and start with a letter or digit.");let s=`.claude/skills/${e}/SKILL.md`,n=R(r,s);if(w(n))throw new Error(`Skill "${e}" already exists.`);C(ee(n),{recursive:!0});let o=t.content?.trim()?t.content:ft(e,t.title);return Z(n,o),{slug:e,path:s}}function cn(r,e){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);let t=R(r,`.claude/skills/${e}`);if(!w(t))throw new Error(`Skill "${e}" not found.`);if(!$(t).isDirectory())throw new Error(`Skill path is not a directory: ${e}`);return ut(t,{recursive:!0,force:!0}),{slug:e}}function un(r,e){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);let t=R(r,`.claude/skills/${e}`);if(!w(t))throw new Error(`Skill "${e}" not found.`);let s=[];return n(t,"",0,s),s.sort((o,a)=>o.kind!==a.kind?o.kind==="dir"?-1:1:o.path.localeCompare(a.path)),s;function n(o,a,i,l){if(i>gt)return;let c=[];try{c=Y(o)}catch{return}for(let u of c){if(u==="node_modules"||u===".git")continue;let m=I(o,u),d=a?`${a}/${u}`:u,g;try{g=$(m)}catch{continue}if(g.isDirectory())l.push({path:`${d}/`,kind:"dir",size:0,editable:!1}),n(m,d,i+1,l);else if(g.isFile()){let f=A(u).toLowerCase(),h=v.has(f)||v.has(u.toLowerCase());l.push({path:d,kind:"file",size:g.size,editable:h&&g.size<k})}}}}function dn(r,e,t){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);if(!t||t.includes(".."))throw new Error(`Invalid file path: ${t}`);let s=R(r,`.claude/skills/${e}/${t}`);if(!w(s)||!$(s).isFile())throw new Error(`File not found: ${t}`);let n=A(s).toLowerCase(),o=te(s).toLowerCase();if(!v.has(n)&&!v.has(o))throw new Error(`File type not editable: ${t}`);if($(s).size>k)throw new Error(`File too large to edit (> ${k/1024} KB): ${t}`);return{path:t,content:Q(s,"utf-8")}}function gn(r,e,t,s){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);if(!t||t.includes(".."))throw new Error(`Invalid file path: ${t}`);let n=R(r,`.claude/skills/${e}/${t}`),o=A(n).toLowerCase(),a=te(n).toLowerCase();if(!v.has(o)&&!v.has(a))throw new Error(`File type not editable: ${t}`);let i=Buffer.from(s,"utf-8");if(i.length>k)throw new Error(`File too large (${i.length} > ${k} bytes).`);return C(ee(n),{recursive:!0}),Z(n,i),{path:t,bytes:i.length}}function mn(r,e){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);let t=R(r,`.claude/skills/${e}`);if(!w(t))throw new Error(`Skill "${e}" not found.`);let s=n=>w(I(t,n));return s("pnpm-lock.yaml")?{manager:"pnpm",file:"pnpm-lock.yaml",command:"pnpm install"}:s("yarn.lock")?{manager:"yarn",file:"yarn.lock",command:"yarn install"}:s("package.json")?{manager:"npm",file:"package.json",command:"npm install"}:s("requirements.txt")?{manager:"pip",file:"requirements.txt",command:"pip install -r requirements.txt"}:s("Gemfile")?{manager:"bundle",file:"Gemfile",command:"bundle install"}:{manager:null,file:null,command:null}}function ft(r,e){return`# ${e?.trim()||r}
21
+
22
+ Describe what this skill does and when the agent should use it. Keep it under ~5 lines \u2014 the agent reads this to decide whether to invoke the skill.
23
+
24
+ ## When to use
25
+ - Trigger condition 1
26
+ - Trigger condition 2
27
+
28
+ ## How to use
29
+ Document the arguments, commands, or files the skill expects. Example:
30
+
31
+ \`\`\`bash
32
+ # Example invocation
33
+ claude-code ${te(r)} --arg value
34
+ \`\`\`
35
+ `}export{se as a,bt as b,G as c,Xe as d,Ve as e,Dt as f,Ot as g,Ut as h,y as i,St as j,tt as k,Nt as l,nt as m,Mt as n,zt as o,Ht as p,Kt as q,st as r,be as s,ot as t,Qt as u,Zt as v,en as w,L as x,vt as y,me as z,U as A,H as B,Ct as C,rn as D,on as E,an as F,ln as G,cn as H,un as I,dn as J,gn as K,mn as L};
36
+ //# sourceMappingURL=chunk-P2Y7DFFP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/daemon/workflow-health.ts","../src/daemon/routines.ts","../src/business/work-pool.ts","../src/business/backlog-store.ts","../src/business/backlog-sync.ts","../src/business/clients.ts","../src/desktop/install.ts","../src/notify/local.ts","../src/daemon/file-ops.ts"],"sourcesContent":["// --- Is the automation still running? -------------------------------------\n//\n// A workflow that stops firing produces nothing: no error, no run, no page\n// entry. On this fleet 16 workflows produced 759 runs and then went quiet for\n// three days without anyone noticing, which is the failure this module exists\n// to make visible. Health is therefore about ABSENCE first and failure second.\n//\n// Cost note: RunStore.get() replays a whole run file. Polling that for every\n// run on every dashboard refresh is not affordable, so we read each file's\n// first line (the snapshot, which carries workflowId) and last line (the\n// terminal status), and cache per file keyed on mtime. Runs are append-only,\n// so after the first scan almost nothing is re-read.\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"fs\"\nimport { resolve } from \"path\"\n\nexport interface RunSummary { workflowId: string; status: string; at: number }\n\n/** Absence first: a workflow that used to fire and no longer does is the\n * headline, because nothing else in the product will ever mention it. */\nexport type WorkflowState = \"never\" | \"failing\" | \"dormant\" | \"active\" | \"quiet\"\n\nexport interface WorkflowHealth {\n id: string\n name: string\n state: WorkflowState\n /** Last run of any status, ms since epoch. Null when it has never run. */\n lastRunAt: number | null\n recent: number\n prior: number\n failed: number\n /** Runs parked at a checkpoint. These are waiting on a human, not broken. */\n paused: number\n}\n\nexport function workflowHealth(\n workflows: Array<{ id: string; name?: string }>,\n runs: RunSummary[],\n now = Date.now(),\n windowDays = 7,\n): WorkflowHealth[] {\n // One day in ms, inlined: workflowHealth is stringified and shipped to the\n // workflows page, where a module-scope constant would be unbound.\n const span = windowDays * 86_400_000\n const byId = new Map<string, RunSummary[]>()\n for (const r of runs) {\n const list = byId.get(r.workflowId)\n if (list) list.push(r)\n else byId.set(r.workflowId, [r])\n }\n return workflows.map(w => {\n const mine = byId.get(w.id) ?? []\n const recent = mine.filter(r => now - r.at <= span)\n const prior = mine.filter(r => now - r.at > span && now - r.at <= span * 2)\n const failed = recent.filter(r => r.status === \"failed\").length\n const paused = mine.filter(r => r.status === \"paused\").length\n const lastRunAt = mine.length ? Math.max(...mine.map(r => r.at)) : null\n // Order matters: a workflow that has stopped firing is reported as dormant\n // even when its last few runs failed — the silence is the bigger signal.\n const state: WorkflowState =\n !mine.length ? \"never\"\n : prior.length > 0 && recent.length === 0 ? \"dormant\"\n : recent.length > 0 && failed * 2 >= recent.length ? \"failing\"\n : recent.length > 0 ? \"active\"\n : \"quiet\"\n return { id: w.id, name: w.name || w.id, state, lastRunAt, recent: recent.length, prior: prior.length, failed, paused }\n }).sort((a, b) => {\n // Inlined rather than a module constant: workflowHealth is stringified\n // and shipped to the workflows page, where module scope does not travel.\n // A hoisted RANK only failed with two or more workflows, because Array\n // .sort never calls the comparator on a single-element list.\n const rank: Record<string, number> = { dormant: 0, failing: 1, active: 2, quiet: 3, never: 4 }\n return rank[a.state] - rank[b.state] || (b.lastRunAt ?? 0) - (a.lastRunAt ?? 0)\n })\n}\n\n\nconst cache = new Map<string, { mtime: number; summary: RunSummary | null }>()\n\n/** Read workflowId + terminal status per run, re-reading only changed files. */\nexport function scanRuns(runsDir: string, limit = 1000): RunSummary[] {\n if (!existsSync(runsDir)) return []\n const files = readdirSync(runsDir)\n .filter(f => f.endsWith(\".jsonl\"))\n .map(f => { const p = resolve(runsDir, f); return { path: p, mtime: statSync(p).mtimeMs } })\n .sort((a, b) => b.mtime - a.mtime)\n .slice(0, limit)\n const live = new Set(files.map(f => f.path))\n for (const key of cache.keys()) if (!live.has(key)) cache.delete(key)\n const out: RunSummary[] = []\n for (const { path, mtime } of files) {\n const hit = cache.get(path)\n if (hit && hit.mtime === mtime) { if (hit.summary) out.push(hit.summary); continue }\n const summary = readSummary(path, mtime)\n cache.set(path, { mtime, summary })\n if (summary) out.push(summary)\n }\n return out\n}\n\nfunction readSummary(path: string, mtime: number): RunSummary | null {\n try {\n const lines = readFileSync(path, \"utf-8\").split(\"\\n\").filter(Boolean)\n if (!lines.length) return null\n const head = JSON.parse(lines[0]) as { run?: { workflowId?: string; status?: string } }\n const workflowId = head.run?.workflowId\n if (!workflowId) return null\n // The last line is either a status-bearing exec entry or another snapshot;\n // fall back to the snapshot's own status when it carries neither.\n let status = head.run?.status ?? \"unknown\"\n for (let i = lines.length - 1; i >= 0; i--) {\n const tail = JSON.parse(lines[i]) as { status?: string; run?: { status?: string } }\n const s = tail.status ?? tail.run?.status\n if (s) { status = s; break }\n }\n return { workflowId, status, at: mtime }\n } catch { return null }\n}\n","// --- Routines: everything that runs on its own, in one list -----------------\n//\n// Automation lives in two places: time-based `crons.*` jobs and workflows\n// whose trigger node is `trigger.cron` or `trigger.hook`. This module is a\n// read model over both — no new schema — merged into one bounded row shape\n// with a staleness verdict, so obsolete or failing automation surfaces on\n// the mesh overview instead of going unnoticed.\n//\n// Pure: callers pass the job list, the persisted cron attempts (the source\n// of truth; scheduler counters reset on reload), the workflow definitions\n// and the workflow run summaries. Nothing here reads prompts or responses,\n// and every string that leaves it is length-capped.\n//\n// Routine ids are stable and namespaced by source — `cron:<jobId>` and\n// `workflow:<workflowId>` — so later per-routine actions can address a row\n// without knowing which subsystem owns it.\n\nimport type { CronRunHistoryItem } from \"@/crons/run-history\"\nimport { getNextCronDate } from \"@/crons/scheduler\"\nimport { workflowHealth, type RunSummary } from \"./workflow-health\"\n\nexport type RoutineKind = \"schedule\" | \"event\"\nexport type RoutineSource = \"cron\" | \"workflow\"\nexport type RoutineOutcome = \"success\" | \"failed\" | \"timeout\" | \"canceled\" | \"running\" | \"paused\" | \"unknown\"\nexport type RoutineFlagReason = \"failing\" | \"overdue\" | \"dormant\" | \"never-ran\" | \"disabled-long\"\nexport type RoutineAttention = \"critical\" | \"warning\" | \"info\"\n\nexport interface RoutineFlag { reason: RoutineFlagReason; detail: string }\n\nexport interface Routine {\n id: string\n source: RoutineSource\n kind: RoutineKind\n name: string\n trigger: {\n /** Cron expression for schedules. */\n schedule?: string\n timezone?: string\n /** `on:*` event for event routines. */\n event?: string\n /** Compact `key=value` renderings of the trigger filters. */\n filters?: string[]\n }\n agent: string | null\n enabled: boolean\n /** Cron: enabled/disabled. Workflow: active/disabled/quarantined. */\n state: string\n nextRunAt: string | null\n lastRun: { at: string; status: RoutineOutcome; summary?: string } | null\n lastSuccessAt: string | null\n /** Terminal failures in a row, newest first, over the sampled runs. */\n consecutiveFailures: number\n /** Runs inspected to reach the verdict (bounded, not a lifetime total). */\n sampledRuns: number\n flags: RoutineFlag[]\n attention: RoutineAttention | null\n}\n\nexport interface RoutineCronJob {\n id: string\n enabled: boolean\n schedule: string\n timezone?: string\n agent: string\n nextRun?: Date | string\n}\n\nexport interface RoutineWorkflow {\n id: string\n title?: string\n state?: string\n ownerAgent?: string\n project?: string\n updated?: string\n created?: string\n nodes: Array<{ id: string; type: string; config?: Record<string, unknown> }>\n}\n\nexport interface RoutineThresholds {\n /** Consecutive failures that flag a routine as failing. */\n failStreak: number\n /** Days a routine may sit disabled before it reads as obsolete. */\n disabledDays: number\n /** Scheduled fires that may pass without an attempt before it is overdue. */\n missedFires: number\n /** Slack added to the overdue deadline for slow starts and restarts. */\n graceMs: number\n}\n\nexport const DEFAULT_ROUTINE_THRESHOLDS: RoutineThresholds = {\n failStreak: 3,\n disabledDays: 30,\n missedFires: 2,\n graceMs: 15 * 60_000,\n}\n\n/** Hard caps: the list is served on an unauthenticated GET and polled. */\nexport const ROUTINE_LIMITS = { routines: 200, summary: 160, filters: 6, filterValue: 60, text: 120 }\n\nexport type NextFire = (expression: string, after: Date, timezone: string) => Date | null\n\nconst defaultNextFire: NextFire = (expression, after, timezone) => {\n try { return getNextCronDate(expression, after, timezone) } catch { return null }\n}\n\nexport interface BuildRoutinesInput {\n crons: RoutineCronJob[]\n /** Newest-first persisted attempts per cron job id. */\n cronRuns: Map<string, CronRunHistoryItem[]>\n workflows: RoutineWorkflow[]\n workflowRuns: RunSummary[]\n now?: number\n thresholds?: Partial<RoutineThresholds>\n nextFire?: NextFire\n}\n\nconst DAY = 86_400_000\n\nexport function buildRoutines(input: BuildRoutinesInput): Routine[] {\n const now = input.now ?? Date.now()\n const t = { ...DEFAULT_ROUTINE_THRESHOLDS, ...input.thresholds }\n const nextFire = input.nextFire ?? defaultNextFire\n const out: Routine[] = []\n\n for (const job of input.crons) {\n const runs = input.cronRuns.get(job.id) ?? []\n const timezone = job.timezone || \"UTC\"\n const outcomes = runs.map((r) => ({ at: Date.parse(r.startedAt), status: r.status as RoutineOutcome }))\n const latest = runs[0]\n const routine: Routine = {\n id: `cron:${job.id}`,\n source: \"cron\",\n kind: \"schedule\",\n name: cap(job.id, ROUTINE_LIMITS.text),\n trigger: { schedule: cap(job.schedule, ROUTINE_LIMITS.text), timezone },\n agent: job.agent || null,\n enabled: job.enabled,\n state: job.enabled ? \"enabled\" : \"disabled\",\n nextRunAt: job.enabled ? isoOrNull(job.nextRun) ?? isoOrNull(nextFire(job.schedule, new Date(now), timezone)) : null,\n lastRun: latest\n ? { at: latest.startedAt, status: latest.status, summary: capOpt(latest.status === \"success\" ? undefined : latest.errorSummary, ROUTINE_LIMITS.summary) }\n : null,\n lastSuccessAt: runs.find((r) => r.status === \"success\")?.startedAt ?? null,\n consecutiveFailures: failStreak(outcomes),\n sampledRuns: runs.length,\n flags: [],\n attention: null,\n }\n routine.flags = scheduleFlags(routine, outcomes, job.schedule, timezone, null, now, t, nextFire)\n out.push(finish(routine))\n }\n\n const health = new Map(workflowHealth(\n input.workflows.map((w) => ({ id: w.id, name: w.title })), input.workflowRuns, now,\n ).map((h) => [h.id, h] as const))\n const runsByWorkflow = new Map<string, RunSummary[]>()\n for (const r of input.workflowRuns) {\n const list = runsByWorkflow.get(r.workflowId)\n if (list) list.push(r)\n else runsByWorkflow.set(r.workflowId, [r])\n }\n\n for (const wf of input.workflows) {\n const trigger = wf.nodes.find((n) => n.type.startsWith(\"trigger.\"))\n if (!trigger || (trigger.type !== \"trigger.cron\" && trigger.type !== \"trigger.hook\")) continue\n const cfg = (trigger.config ?? {}) as Record<string, unknown>\n const state = wf.state || \"active\"\n const enabled = state === \"active\"\n const runs = (runsByWorkflow.get(wf.id) ?? []).slice().sort((a, b) => b.at - a.at)\n const outcomes = runs.map((r) => ({ at: r.at, status: workflowOutcome(r.status) }))\n const latest = outcomes[0]\n const isCron = trigger.type === \"trigger.cron\"\n const spec = typeof cfg.spec === \"string\" ? cfg.spec : \"\"\n const timezone = typeof cfg.timezone === \"string\" ? cfg.timezone : \"UTC\"\n const routine: Routine = {\n id: `workflow:${wf.id}`,\n source: \"workflow\",\n kind: isCron ? \"schedule\" : \"event\",\n name: cap(wf.title || wf.id, ROUTINE_LIMITS.text),\n trigger: isCron\n ? { schedule: cap(spec, ROUTINE_LIMITS.text), timezone }\n : { event: cap(typeof cfg.event === \"string\" ? cfg.event : \"\", ROUTINE_LIMITS.text), filters: describeFilters(cfg.filter, wf) },\n agent: workflowAgent(wf),\n enabled,\n state,\n nextRunAt: isCron && enabled && spec ? isoOrNull(nextFire(spec, new Date(now), timezone)) : null,\n lastRun: latest ? { at: new Date(latest.at).toISOString(), status: latest.status } : null,\n lastSuccessAt: (() => { const s = outcomes.find((o) => o.status === \"success\"); return s ? new Date(s.at).toISOString() : null })(),\n consecutiveFailures: failStreak(outcomes),\n sampledRuns: runs.length,\n flags: [],\n attention: null,\n }\n const changedAt = Date.parse(wf.updated || wf.created || \"\")\n routine.flags = isCron\n ? scheduleFlags(routine, outcomes, spec, timezone, Number.isFinite(changedAt) ? changedAt : null, now, t, nextFire)\n : eventFlags(routine, health.get(wf.id)?.state === \"dormant\", Number.isFinite(changedAt) ? changedAt : null, now, t)\n out.push(finish(routine))\n }\n\n return out.sort(compareRoutines).slice(0, ROUTINE_LIMITS.routines)\n}\n\n/** Terminal failures in a row from the newest run. In-flight and parked\n * runs are skipped; a success or cancel ends the streak. */\nexport function failStreak(outcomes: Array<{ status: RoutineOutcome }>): number {\n let n = 0\n for (const o of outcomes) {\n if (o.status === \"running\" || o.status === \"paused\" || o.status === \"unknown\") continue\n if (o.status === \"failed\" || o.status === \"timeout\") n++\n else break\n }\n return n\n}\n\nfunction scheduleFlags(\n r: Routine,\n outcomes: Array<{ at: number; status: RoutineOutcome }>,\n spec: string,\n timezone: string,\n changedAt: number | null,\n now: number,\n t: RoutineThresholds,\n nextFire: NextFire,\n): RoutineFlag[] {\n const flags: RoutineFlag[] = []\n const lastAt = outcomes.length ? outcomes[0].at : null\n if (!r.enabled) {\n const disabled = disabledFlag(lastAt, changedAt, now, t)\n if (disabled) flags.push(disabled)\n return flags\n }\n if (r.consecutiveFailures >= t.failStreak) {\n flags.push({ reason: \"failing\", detail: `${r.consecutiveFailures} failed attempts in a row` })\n }\n if (lastAt === null) {\n flags.push({ reason: \"never-ran\", detail: \"No persisted attempt\" })\n return flags\n }\n // Walk the schedule forward from the last attempt: if `missedFires`\n // expected fires have already passed (plus grace), the job has gone quiet.\n let cursor = new Date(lastAt)\n let deadline: Date | null = null\n for (let i = 0; i < t.missedFires; i++) {\n deadline = nextFire(spec, cursor, timezone)\n if (!deadline) break\n cursor = deadline\n }\n if (deadline && now > deadline.getTime() + t.graceMs) {\n flags.push({ reason: \"overdue\", detail: `No attempt since ${new Date(lastAt).toISOString()}; ${t.missedFires} scheduled fires missed` })\n }\n return flags\n}\n\nfunction eventFlags(\n r: Routine,\n dormant: boolean,\n changedAt: number | null,\n now: number,\n t: RoutineThresholds,\n): RoutineFlag[] {\n const flags: RoutineFlag[] = []\n const lastAt = r.lastRun ? Date.parse(r.lastRun.at) : null\n if (!r.enabled) {\n const disabled = disabledFlag(lastAt, changedAt, now, t)\n if (disabled) flags.push(disabled)\n return flags\n }\n if (r.consecutiveFailures >= t.failStreak) {\n flags.push({ reason: \"failing\", detail: `${r.consecutiveFailures} failed runs in a row` })\n }\n if (lastAt === null) flags.push({ reason: \"never-ran\", detail: \"No run recorded for this trigger\" })\n else if (dormant) flags.push({ reason: \"dormant\", detail: \"Fired in the prior week, silent in the last one\" })\n return flags\n}\n\nfunction disabledFlag(lastAt: number | null, changedAt: number | null, now: number, t: RoutineThresholds): RoutineFlag | null {\n // Crons carry no \"disabled since\" timestamp, so the last attempt (or the\n // workflow's last edit) is the best available proxy for how long it sat.\n const since = Math.max(lastAt ?? 0, changedAt ?? 0)\n if (since && now - since < t.disabledDays * DAY) return null\n return {\n reason: \"disabled-long\",\n detail: since ? `Disabled; no activity for ${Math.floor((now - since) / DAY)} days` : \"Disabled and never ran\",\n }\n}\n\nfunction finish(r: Routine): Routine {\n const reasons = new Set(r.flags.map((f) => f.reason))\n r.attention = reasons.has(\"failing\") ? \"critical\"\n : reasons.has(\"overdue\") || reasons.has(\"dormant\") || reasons.has(\"never-ran\") ? \"warning\"\n : reasons.has(\"disabled-long\") ? \"info\"\n : null\n return r\n}\n\nconst ATTENTION_RANK: Record<string, number> = { critical: 0, warning: 1, info: 2 }\n\nfunction compareRoutines(a: Routine, b: Routine): number {\n const ra = a.attention ? ATTENTION_RANK[a.attention] : 3\n const rb = b.attention ? ATTENTION_RANK[b.attention] : 3\n return ra - rb || Number(b.enabled) - Number(a.enabled) || a.name.localeCompare(b.name)\n}\n\n/** Map a workflow run's last recorded status onto the routine vocabulary.\n * The tail line may be a node execution entry (`ok`), not a run status. */\nexport function workflowOutcome(status: string): RoutineOutcome {\n switch (status) {\n case \"completed\": case \"ok\": return \"success\"\n case \"failed\": return \"failed\"\n case \"timeout\": return \"timeout\"\n case \"canceled\": return \"canceled\"\n case \"running\": return \"running\"\n case \"paused\": return \"paused\"\n default: return \"unknown\"\n }\n}\n\nfunction workflowAgent(wf: RoutineWorkflow): string | null {\n if (wf.ownerAgent) return cap(wf.ownerAgent, ROUTINE_LIMITS.text)\n const node = wf.nodes.find((n) => n.type === \"agent\" && typeof n.config?.agentId === \"string\" && n.config.agentId)\n return node ? cap(String(node.config!.agentId), ROUTINE_LIMITS.text) : null\n}\n\n/** `filter.action = [\"open\",\"reopen\"]` → `action=open,reopen`. The workflow's\n * project scope is a filter too: it silently drops other projects' events. */\nexport function describeFilters(filter: unknown, wf?: { project?: string }): string[] {\n const out: string[] = []\n const project = wf?.project\n if (project) out.push(cap(`project=${project}`, ROUTINE_LIMITS.filterValue))\n if (filter && typeof filter === \"object\" && !Array.isArray(filter)) {\n for (const [key, value] of Object.entries(filter as Record<string, unknown>)) {\n const values = Array.isArray(value) ? value.map(String) : value == null ? [] : [String(value)]\n if (!values.length) continue\n out.push(cap(`${key}=${values.join(\",\")}`, ROUTINE_LIMITS.filterValue))\n }\n }\n return out.slice(0, ROUTINE_LIMITS.filters)\n}\n\nfunction isoOrNull(v: Date | string | null | undefined): string | null {\n if (!v) return null\n const d = v instanceof Date ? v : new Date(v)\n return Number.isFinite(d.getTime()) ? d.toISOString() : null\n}\n\nfunction cap(value: string, limit: number): string {\n return value.length <= limit ? value : value.slice(0, limit - 3) + \"...\"\n}\n\nfunction capOpt(value: string | undefined, limit: number): string | undefined {\n return value === undefined ? undefined : cap(value, limit)\n}\n","import { readFileSync, writeFileSync, existsSync, readdirSync, statSync } from \"fs\"\nimport { resolve, join } from \"path\"\nimport type { BusinessConfig, BusinessWorkSource } from \"./config\"\nimport type { DaemonConfig } from \"@/daemon/config\"\nimport { BacklogStore } from \"./backlog-store\"\nimport { syncBacklogItemToSource, resolveAssigneesForSource, type BacklogPatch } from \"./backlog-sync\"\n\n// --- Work pool: pluggable source of tasks for the business layer ---\n\nexport interface WorkItem {\n id: string // stable id for claim/report (e.g. \"backlog:line-12\", \"gitlab:proj:issue:42\")\n title: string\n description?: string\n assignee?: string // agentId\n estimatedSeconds?: number\n url?: string\n priority?: number // lower = higher priority\n /** Board stage / kanban column id. Was a closed enum back when boards\n * had a fixed schema; widened to `string` because per-project boards\n * can declare arbitrary column ids. The well-known names below remain\n * the standard set most boards use. */\n stage?: string // typically: \"triage\" | \"todo\" | \"doing\" | \"onhold\" | \"review\" | \"done\"\n /** Raw labels from the source (GitLab). Empty array if source doesn't support labels. */\n labels?: string[]\n /** Label name+color when the source returns colored labels (GitLab with_labels_details). */\n labelDetails?: Array<{ name: string; color: string; text_color?: string }>\n /** ISO timestamp of last update on the source. */\n updatedAt?: string\n /** Open/closed state from the source. */\n state?: \"opened\" | \"closed\"\n /** Optional milestone title (GitLab: issue.milestone.title). */\n milestone?: string\n /** GitLab assignees (0..n). `assignee` above is the agentId alias for assignees[0]. */\n assignees?: Array<{ username: string; name?: string; avatarUrl?: string }>\n}\n\n/** Member of a source (GitLab project members) — used for assignee pickers. */\nexport interface WorkMember {\n username: string\n name?: string\n avatarUrl?: string\n}\n\n/** Milestone on a source — used for milestone pickers. */\nexport interface WorkMilestone {\n id: number | string\n title: string\n state?: \"active\" | \"closed\"\n dueDate?: string\n}\n\n/** Full issue detail — a superset of WorkItem for the detail view. */\nexport interface WorkDetail extends WorkItem {\n /** Raw body, may be markdown. */\n descriptionHtml?: string\n /** ISO timestamp of creation. */\n createdAt?: string\n /** Author (issue reporter). */\n author?: { username: string; name?: string; avatarUrl?: string }\n /** Full labels list (all of them). */\n allLabels?: string[]\n}\n\nexport interface WorkReport {\n status: \"in-progress\" | \"done\" | \"blocked\"\n note?: string\n timeSeconds?: number\n blocker?: string\n}\n\n/** What a WorkSource can do beyond the minimal listOpen/claim/report triple. */\nexport interface WorkSourceCapabilities {\n /** Enumerate items beyond assignee filter — required for a board view. */\n listAll: boolean\n /** Create new items (e.g. POST GitLab issue). */\n create: boolean\n /** Arbitrary column transitions (label add/remove pair). */\n transition: boolean\n /** Surfaces labels[] on returned items. */\n labels: boolean\n /** Server-side text search. */\n search: boolean\n}\n\nexport interface WorkCreateInput {\n title: string\n description?: string\n /** agentId */\n assignee?: string\n /** Raw GitLab usernames — bypass agentId resolution for UI-driven creates. */\n assigneeUsernames?: string[]\n labels?: string[]\n /** Milestone title (resolved to id). */\n milestoneTitle?: string\n /** For sources that span multiple projects (gitlab): \"group/project\". */\n projectHint?: string\n}\n\nexport interface ListAllOpts {\n sinceDays?: number\n labels?: string[]\n /** Exclude items carrying any of these labels (client-side filter). */\n notLabels?: string[]\n /** Exclude items whose labels carry this scoped prefix (e.g. \"Status\"). */\n withoutScopedPrefix?: string\n search?: string\n assignee?: string\n /** GitLab issue state. Defaults to \"opened\" (boards hide closed issues). */\n state?: \"opened\" | \"closed\" | \"all\"\n /** Milestone filter (GitLab \"milestone\" query param). */\n milestone?: string\n}\n\nexport interface WorkSource {\n type: string\n capabilities: WorkSourceCapabilities\n listOpen(agentId: string): Promise<WorkItem[]>\n claim(agentId: string, itemId: string): Promise<void>\n report(itemId: string, update: WorkReport): Promise<void>\n /** Optional — gated by capabilities.listAll. */\n listAll?(opts: ListAllOpts): Promise<WorkItem[]>\n /** Optional — gated by capabilities.create. */\n create?(input: WorkCreateInput): Promise<WorkItem>\n /**\n * Optional — gated by capabilities.transition. Add `toLabel` and remove\n * `fromLabel` (if provided) from the item. Used by the kanban board on drag.\n */\n transition?(itemId: string, toLabel: string, fromLabel?: string): Promise<void>\n}\n\n// ---------- BacklogWorkSource: GFM checklist file ----------\n// Format in .agentx/backlog.md (or configured path):\n//\n// - [ ] @alice Implement login form [time: 2h]\n// - [x] @alice Write unit tests (done)\n// - [ ] @bob Design pricing page [time: 1h30m]\n//\n// Line number is the stable id.\n\nconst CHECK_RE = /^(\\s*)-\\s*\\[( |x|X)\\]\\s*(.*)$/\nconst MENTION_RE = /@([a-z0-9_-]+)/i\nconst TIME_RE = /\\[time:\\s*([0-9hm\\s]+)\\]/i\n\nfunction parseDuration(s: string): number {\n // \"2h30m\" -> 9000 s. \"1h\" -> 3600. \"45m\" -> 2700.\n const hMatch = s.match(/(\\d+)\\s*h/i)\n const mMatch = s.match(/(\\d+)\\s*m/i)\n return (hMatch ? parseInt(hMatch[1], 10) * 3600 : 0) + (mMatch ? parseInt(mMatch[1], 10) * 60 : 0)\n}\n\nexport class BacklogWorkSource implements WorkSource {\n type = \"backlog\"\n capabilities: WorkSourceCapabilities = {\n listAll: true, create: false, transition: false, labels: true, search: false,\n }\n private store: BacklogStore\n\n constructor(\n private path: string,\n private daemon?: DaemonConfig,\n private log: (...args: unknown[]) => void = () => {},\n ) {\n this.store = new BacklogStore(this.path)\n }\n\n /** Expose the store so the CLI and sync-back can share state. */\n getStore(): BacklogStore {\n return this.store\n }\n\n private readMd(): string[] {\n const full = resolve(process.cwd(), this.path)\n if (!existsSync(full)) return []\n return readFileSync(full, \"utf-8\").split(\"\\n\")\n }\n\n private writeMd(lines: string[]): void {\n const full = resolve(process.cwd(), this.path)\n writeFileSync(full, lines.join(\"\\n\"))\n }\n\n async listOpen(agentId: string): Promise<WorkItem[]> {\n if (this.store.exists()) {\n const items = this.store.list().filter((it) =>\n it.status !== \"done\" &&\n (!it.assignee || it.assignee.toLowerCase() === agentId.toLowerCase()),\n )\n return items.map((it) => this.toWorkItem(it))\n }\n // Legacy markdown-checklist fallback (unchanged behavior).\n const lines = this.readMd()\n const items: WorkItem[] = []\n for (let i = 0; i < lines.length; i++) {\n const m = lines[i].match(CHECK_RE)\n if (!m) continue\n const checked = m[2].toLowerCase() === \"x\"\n if (checked) continue\n const body = m[3]\n const mention = body.match(MENTION_RE)\n if (mention && mention[1].toLowerCase() !== agentId.toLowerCase()) continue\n const timeMatch = body.match(TIME_RE)\n const title = body.replace(MENTION_RE, \"\").replace(TIME_RE, \"\").trim()\n items.push({\n id: `backlog:${i}`,\n title: title || `Task at line ${i + 1}`,\n assignee: mention ? mention[1] : undefined,\n estimatedSeconds: timeMatch ? parseDuration(timeMatch[1]) : undefined,\n priority: i,\n })\n }\n return items\n }\n\n async listAll(_opts: ListAllOpts = {}): Promise<WorkItem[]> {\n if (!this.store.exists()) return []\n return this.store.list().map((it) => this.toWorkItem(it))\n }\n\n async claim(agentId: string, itemId: string): Promise<void> {\n if (!this.store.exists()) return\n const item = this.store.findById(itemId)\n if (!item) return\n const updated = this.store.update(itemId, {\n assignee: item.assignee || agentId,\n status: \"doing\",\n })\n if (updated.source && this.daemon) {\n const usernames = resolveAssigneesForSource([updated.assignee || agentId], updated.source, this.daemon)\n void syncBacklogItemToSource(updated, {\n assigneeUsernames: usernames,\n addLabels: [\"Doing\"],\n removeLabels: [\"To Do\"],\n }, this.daemon, this.log).catch((e) =>\n this.log(`[backlog-sync] claim failed for ${itemId}: ${e?.message ?? e}`),\n )\n }\n }\n\n async report(itemId: string, update: WorkReport): Promise<void> {\n // Structured store path.\n if (this.store.exists() && this.store.findById(itemId)) {\n const patch: Partial<import(\"./backlog-store\").BacklogItem> = {}\n if (update.status === \"done\") patch.status = \"done\"\n else if (update.status === \"blocked\") {\n patch.status = \"blocked\"\n patch.blocker = update.blocker || update.note || undefined\n } else if (update.status === \"in-progress\") patch.status = \"doing\"\n if (update.timeSeconds) patch.estimatedSeconds = update.timeSeconds\n const item = this.store.update(itemId, patch)\n\n if (item.source && this.daemon) {\n const sourcePatch: BacklogPatch = {}\n if (update.status === \"done\") {\n sourcePatch.addLabels = [\"Done\"]\n sourcePatch.removeLabels = [\"Doing\"]\n } else if (update.status === \"blocked\") {\n sourcePatch.addLabels = [\"Blocked\"]\n }\n if (update.note) sourcePatch.note = update.note\n if (Object.keys(sourcePatch).length) {\n void syncBacklogItemToSource(item, sourcePatch, this.daemon, this.log).catch((e) =>\n this.log(`[backlog-sync] report failed for ${itemId}: ${e?.message ?? e}`),\n )\n }\n }\n return\n }\n\n // Legacy markdown path (line-number ids).\n if (!itemId.startsWith(\"backlog:\")) throw new Error(`not a backlog id: ${itemId}`)\n const lineNo = parseInt(itemId.slice(\"backlog:\".length), 10)\n const lines = this.readMd()\n const line = lines[lineNo]\n if (!line) throw new Error(`backlog line ${lineNo} missing`)\n const m = line.match(CHECK_RE)\n if (!m) throw new Error(`backlog line ${lineNo} not a checkbox`)\n\n if (update.status === \"done\") {\n lines[lineNo] = line.replace(/- \\[ \\]/, \"- [x]\")\n const note = update.timeSeconds\n ? ` <!-- done by agent, ${Math.round(update.timeSeconds / 60)}m -->`\n : \" <!-- done by agent -->\"\n lines.splice(lineNo + 1, 0, note)\n } else if (update.status === \"blocked\") {\n lines.splice(lineNo + 1, 0, ` <!-- BLOCKED: ${update.blocker || update.note || \"?\"} -->`)\n } else if (update.note) {\n lines.splice(lineNo + 1, 0, ` <!-- ${update.note.replace(/-->/g, \"— >\")} -->`)\n }\n this.writeMd(lines)\n }\n\n private toWorkItem(it: import(\"./backlog-store\").BacklogItem): WorkItem {\n const stage = ({ todo: \"todo\", doing: \"doing\", blocked: \"onhold\", done: \"done\" } as const)[it.status]\n return {\n id: it.id,\n title: it.title,\n description: it.description,\n assignee: it.assignee,\n estimatedSeconds: it.estimatedSeconds,\n priority: it.priority,\n labels: it.labels,\n stage,\n milestone: it.milestone,\n url: it.source?.url,\n updatedAt: it.updatedAt,\n state: it.status === \"done\" ? \"closed\" : \"opened\",\n }\n }\n}\n\n// ---------- WikiWorkSource: scan markdown files for @mention checkboxes ----------\n\nexport class WikiWorkSource implements WorkSource {\n type = \"wiki\"\n capabilities: WorkSourceCapabilities = {\n listAll: false, create: false, transition: false, labels: false, search: false,\n }\n constructor(private root: string, private glob: string) {}\n\n private walk(dir: string, out: string[]): void {\n if (!existsSync(dir)) return\n for (const entry of readdirSync(dir)) {\n const full = join(dir, entry)\n const st = statSync(full)\n if (st.isDirectory()) {\n this.walk(full, out)\n } else if (entry.endsWith(\".md\")) {\n out.push(full)\n }\n }\n }\n\n async listOpen(agentId: string): Promise<WorkItem[]> {\n const files: string[] = []\n this.walk(resolve(process.cwd(), this.root), files)\n const items: WorkItem[] = []\n for (const file of files) {\n const lines = readFileSync(file, \"utf-8\").split(\"\\n\")\n for (let i = 0; i < lines.length; i++) {\n const m = lines[i].match(CHECK_RE)\n if (!m || m[2].toLowerCase() === \"x\") continue\n const mention = m[3].match(MENTION_RE)\n if (!mention || mention[1].toLowerCase() !== agentId.toLowerCase()) continue\n items.push({\n id: `wiki:${file}:${i}`,\n title: m[3].replace(MENTION_RE, \"\").trim(),\n assignee: mention[1],\n priority: i,\n })\n }\n }\n return items\n }\n\n async claim(): Promise<void> { /* no-op */ }\n\n async report(itemId: string, update: WorkReport): Promise<void> {\n // wiki:<file>:<line>\n const parts = itemId.split(\":\")\n const lineNo = parseInt(parts[parts.length - 1], 10)\n const file = parts.slice(1, -1).join(\":\")\n if (!existsSync(file)) return\n const lines = readFileSync(file, \"utf-8\").split(\"\\n\")\n if (update.status === \"done\" && lines[lineNo]) {\n lines[lineNo] = lines[lineNo].replace(/- \\[ \\]/, \"- [x]\")\n writeFileSync(file, lines.join(\"\\n\"))\n }\n }\n}\n\n// ---------- GitLabWorkSource: assignee-filtered issues via GitLab API ----------\n\nexport class GitLabWorkSource implements WorkSource {\n type = \"gitlab\"\n capabilities: WorkSourceCapabilities = {\n listAll: true, create: true, transition: true, labels: true, search: true,\n }\n\n constructor(\n private host: string,\n private token: string,\n private projects: string[],\n /** Map from agentId -> GitLab username(s) */\n private agentUsernames: Record<string, string[]>,\n private log: (...args: unknown[]) => void,\n ) {}\n\n /** Reverse lookup: GitLab username -> agentId. Used when resolving board items\n * back to an agent (for the reconciler and card display). */\n private usernameToAgent(username: string): string | undefined {\n for (const [agentId, names] of Object.entries(this.agentUsernames)) {\n if (names.some((n) => n.toLowerCase() === username.toLowerCase())) return agentId\n }\n return undefined\n }\n\n private mapIssue(issue: any, project: string): WorkItem {\n const ns = issue.references?.full?.split(\"#\")[0] || project\n // `labels` may be a string[] or, with with_labels_details=true, an object[].\n let labels: string[] = []\n let labelDetails: Array<{ name: string; color: string; text_color?: string }> | undefined\n if (Array.isArray(issue.labels)) {\n if (issue.labels.length && typeof issue.labels[0] === \"object\") {\n labelDetails = issue.labels.map((l: any) => ({\n name: l.name, color: l.color, text_color: l.text_color,\n }))\n labels = labelDetails!.map((l) => l.name)\n } else {\n labels = issue.labels\n }\n }\n const rawAssignees: any[] = Array.isArray(issue.assignees) && issue.assignees.length\n ? issue.assignees\n : (issue.assignee ? [issue.assignee] : [])\n const assignees = rawAssignees.map((a) => ({\n username: a.username, name: a.name, avatarUrl: a.avatar_url,\n }))\n const firstUsername = assignees[0]?.username\n return {\n id: `gitlab:${ns}:issue:${issue.iid}`,\n title: issue.title,\n description: issue.description || undefined,\n // Prefer configured agentId mapping; fall back to the raw GitLab username\n // so cards still render an assignee even without agentMappings configured.\n assignee: firstUsername ? (this.usernameToAgent(firstUsername) || firstUsername) : undefined,\n assignees,\n url: issue.web_url,\n priority: issue.weight ?? 99,\n estimatedSeconds: issue.time_stats?.time_estimate || undefined,\n labels,\n labelDetails,\n updatedAt: issue.updated_at,\n state: issue.state === \"closed\" ? \"closed\" : \"opened\",\n milestone: issue.milestone?.title || undefined,\n }\n }\n\n private async api(path: string, init: RequestInit = {}): Promise<any> {\n const res = await fetch(`${this.host}/api/v4${path}`, {\n ...init,\n headers: {\n \"PRIVATE-TOKEN\": this.token,\n \"Content-Type\": \"application/json\",\n ...(init.headers || {}),\n },\n })\n if (!res.ok) throw new Error(`GitLab ${res.status}: ${await res.text().catch(() => \"\")}`)\n return res.json()\n }\n\n async listOpen(agentId: string): Promise<WorkItem[]> {\n const usernames = this.agentUsernames[agentId] || []\n if (!usernames.length) return []\n\n const items: WorkItem[] = []\n const projectList = this.projects.length\n ? this.projects\n : [\"__ALL__\"] // falls back to a global issues query\n\n for (const username of usernames) {\n for (const project of projectList) {\n try {\n const q = `assignee_username=${encodeURIComponent(username)}&state=opened&scope=all&per_page=20`\n const path = project === \"__ALL__\"\n ? `/issues?${q}`\n : `/projects/${encodeURIComponent(project)}/issues?${q}`\n const issues = await this.api(path)\n for (const issue of issues) {\n const item = this.mapIssue(issue, project)\n // Force assignee to the agent we queried for (listOpen is per-agent).\n item.assignee = agentId\n items.push(item)\n }\n } catch (e: any) {\n this.log(`[business] GitLab listOpen failed for ${username} on ${project}: ${e.message}`)\n }\n }\n }\n return items\n }\n\n async claim(agentId: string, itemId: string): Promise<void> {\n // itemId: \"gitlab:<project>:issue:<iid>\"\n const { project, iid, type } = this.parseId(itemId)\n // Transition labels: add \"Doing\", remove \"To Do\"\n const path = `/projects/${encodeURIComponent(project)}/${type === \"issue\" ? \"issues\" : \"merge_requests\"}/${iid}?add_labels=Doing&remove_labels=To Do`\n try {\n await this.api(path, { method: \"PUT\" })\n } catch (e: any) {\n this.log(`[business] claim label update failed: ${e.message}`)\n }\n }\n\n async report(itemId: string, update: WorkReport): Promise<void> {\n const { project, iid, type } = this.parseId(itemId)\n const seg = type === \"issue\" ? \"issues\" : \"merge_requests\"\n\n // Time tracking\n if (update.timeSeconds && update.timeSeconds > 0) {\n const mins = Math.max(1, Math.round(update.timeSeconds / 60))\n const dur = mins >= 60 ? `${Math.floor(mins / 60)}h${mins % 60 ? ` ${mins % 60}m` : \"\"}` : `${mins}m`\n try {\n await this.api(\n `/projects/${encodeURIComponent(project)}/${seg}/${iid}/add_spent_time?duration=${encodeURIComponent(dur)}`,\n { method: \"POST\" },\n )\n } catch (e: any) {\n this.log(`[business] add_spent_time failed: ${e.message}`)\n }\n }\n\n // Status → labels\n if (update.status === \"done\") {\n try {\n await this.api(\n `/projects/${encodeURIComponent(project)}/${seg}/${iid}?add_labels=Done&remove_labels=Doing`,\n { method: \"PUT\" },\n )\n } catch (e: any) {\n this.log(`[business] done label update failed: ${e.message}`)\n }\n } else if (update.status === \"blocked\") {\n try {\n await this.api(\n `/projects/${encodeURIComponent(project)}/${seg}/${iid}?add_labels=Blocked`,\n { method: \"PUT\" },\n )\n } catch (e: any) {\n this.log(`[business] blocked label update failed: ${e.message}`)\n }\n }\n\n // Note/comment\n if (update.note) {\n try {\n await this.api(\n `/projects/${encodeURIComponent(project)}/${seg}/${iid}/notes`,\n { method: \"POST\", body: JSON.stringify({ body: update.note }) },\n )\n } catch (e: any) {\n this.log(`[business] report note failed: ${e.message}`)\n }\n }\n }\n\n async listAll(opts: ListAllOpts = {}): Promise<WorkItem[]> {\n const sinceDays = opts.sinceDays ?? 30\n const updatedAfter = new Date(Date.now() - sinceDays * 24 * 3600_000).toISOString()\n\n const params = new URLSearchParams()\n const state = opts.state ?? \"opened\"\n params.set(\"state\", state)\n // `updated_after` makes sense for the Closed column (bounded window) and for\n // keeping the open list recent; for \"opened\" we pass it too but GitLab treats\n // the window loosely (any issue updated since counts, regardless of open-age).\n params.set(\"updated_after\", updatedAfter)\n params.set(\"scope\", \"all\")\n params.set(\"per_page\", \"100\")\n params.set(\"with_labels_details\", \"true\")\n params.set(\"order_by\", \"updated_at\")\n params.set(\"sort\", \"desc\")\n if (opts.labels?.length) params.set(\"labels\", opts.labels.join(\",\"))\n if (opts.milestone) params.set(\"milestone\", opts.milestone)\n if (opts.search) params.set(\"search\", opts.search)\n\n const projectList = this.projects.length ? this.projects : [\"__ALL__\"]\n const items: WorkItem[] = []\n for (const project of projectList) {\n try {\n const path = project === \"__ALL__\"\n ? `/issues?${params.toString()}`\n : `/projects/${encodeURIComponent(project)}/issues?${params.toString()}`\n const issues = await this.api(path)\n for (const issue of issues) items.push(this.mapIssue(issue, project))\n } catch (e: any) {\n this.log(`[business] GitLab listAll failed on ${project}: ${e.message}`)\n }\n }\n\n // Client-side negative-label filter (GitLab API supports `not[labels]` only\n // via the `not[]` nested query form, which isn't convenient via URLSearchParams).\n let result = items\n if (opts.notLabels?.length) {\n const deny = new Set(opts.notLabels)\n result = result.filter((i) => !(i.labels || []).some((l) => deny.has(l)))\n }\n if (opts.withoutScopedPrefix) {\n const prefix = opts.withoutScopedPrefix + \"::\"\n result = result.filter((i) => !(i.labels || []).some((l) => l.startsWith(prefix)))\n }\n return result\n }\n\n /** Close or reopen an issue via state_event. */\n async setState(itemId: string, to: \"close\" | \"reopen\"): Promise<void> {\n const { project, iid, type } = this.parseId(itemId)\n const seg = type === \"issue\" ? \"issues\" : \"merge_requests\"\n await this.api(\n `/projects/${encodeURIComponent(project)}/${seg}/${iid}?state_event=${to}`,\n { method: \"PUT\" },\n )\n }\n\n /** Fetch a single issue's full detail. */\n async getItem(itemId: string): Promise<WorkDetail | null> {\n const { project, iid } = this.parseId(itemId)\n try {\n const issue = await this.api(\n `/projects/${encodeURIComponent(project)}/issues/${iid}?with_labels_details=true`,\n )\n const base = this.mapIssue(issue, project)\n return {\n ...base,\n descriptionHtml: issue.description_html || undefined,\n createdAt: issue.created_at,\n author: issue.author ? {\n username: issue.author.username, name: issue.author.name, avatarUrl: issue.author.avatar_url,\n } : undefined,\n allLabels: Array.isArray(issue.labels)\n ? (typeof issue.labels[0] === \"object\" ? issue.labels.map((l: any) => l.name) : issue.labels)\n : [],\n }\n } catch (e: any) {\n this.log(`[board] GitLab getItem failed for ${itemId}: ${e.message}`)\n return null\n }\n }\n\n /** Update fields on an issue. Accepts string usernames for assignees. */\n async updateItem(\n itemId: string,\n patch: {\n title?: string\n description?: string\n labels?: string[]\n addLabels?: string[]\n removeLabels?: string[]\n assigneeUsernames?: string[]\n milestoneTitle?: string | null\n },\n ): Promise<WorkItem> {\n const { project, iid } = this.parseId(itemId)\n\n // Resolve assignee usernames → user ids (parallel).\n let assigneeIds: number[] | undefined\n if (patch.assigneeUsernames) {\n const results = await Promise.all(patch.assigneeUsernames.map(async (u) => {\n try {\n const users = await this.api(`/users?username=${encodeURIComponent(u)}`)\n return Array.isArray(users) && users[0]?.id ? users[0].id : null\n } catch { return null }\n }))\n assigneeIds = results.filter((x): x is number => typeof x === \"number\")\n }\n\n // Resolve milestone title → id (null clears the milestone).\n let milestoneId: number | 0 | undefined\n if (patch.milestoneTitle !== undefined) {\n if (patch.milestoneTitle === null || patch.milestoneTitle === \"\") {\n milestoneId = 0\n } else {\n try {\n const ms = await this.api(\n `/projects/${encodeURIComponent(project)}/milestones?title=${encodeURIComponent(patch.milestoneTitle)}`,\n )\n if (Array.isArray(ms) && ms[0]?.id) milestoneId = ms[0].id\n } catch (e: any) {\n this.log(`[board] milestone lookup failed for \"${patch.milestoneTitle}\": ${e.message}`)\n }\n }\n }\n\n const body: Record<string, unknown> = {}\n if (patch.title !== undefined) body.title = patch.title\n if (patch.description !== undefined) body.description = patch.description\n if (patch.labels !== undefined) body.labels = patch.labels.join(\",\")\n if (patch.addLabels?.length) body.add_labels = patch.addLabels.join(\",\")\n if (patch.removeLabels?.length) body.remove_labels = patch.removeLabels.join(\",\")\n if (assigneeIds !== undefined) body.assignee_ids = assigneeIds\n if (milestoneId !== undefined) body.milestone_id = milestoneId\n\n const issue = await this.api(`/projects/${encodeURIComponent(project)}/issues/${iid}`, {\n method: \"PUT\",\n body: JSON.stringify(body),\n })\n return this.mapIssue(issue, project)\n }\n\n /** List project members (direct + inherited). Deduped by username across projects. */\n async listMembers(): Promise<WorkMember[]> {\n const byUsername = new Map<string, WorkMember>()\n for (const project of this.projects) {\n try {\n const members = await this.api(\n `/projects/${encodeURIComponent(project)}/members/all?per_page=100`,\n )\n for (const m of members) {\n if (m.username && !byUsername.has(m.username)) {\n byUsername.set(m.username, {\n username: m.username, name: m.name, avatarUrl: m.avatar_url,\n })\n }\n }\n } catch (e: any) {\n this.log(`[board] listMembers failed for ${project}: ${e.message}`)\n }\n }\n return [...byUsername.values()].sort((a, b) => a.username.localeCompare(b.username))\n }\n\n /** List milestones (active first, then closed). Deduped by title across projects. */\n async listMilestones(): Promise<WorkMilestone[]> {\n const byTitle = new Map<string, WorkMilestone>()\n for (const project of this.projects) {\n try {\n const ms = await this.api(\n `/projects/${encodeURIComponent(project)}/milestones?per_page=100`,\n )\n for (const m of ms) {\n if (m.title && !byTitle.has(m.title)) {\n byTitle.set(m.title, {\n id: m.id, title: m.title, state: m.state, dueDate: m.due_date,\n })\n }\n }\n } catch (e: any) {\n this.log(`[board] listMilestones failed for ${project}: ${e.message}`)\n }\n }\n const all = [...byTitle.values()]\n all.sort((a, b) => {\n if (a.state !== b.state) return a.state === \"active\" ? -1 : 1\n return a.title.localeCompare(b.title)\n })\n return all\n }\n\n async create(input: WorkCreateInput): Promise<WorkItem> {\n const project = input.projectHint || this.projects[0]\n if (!project) throw new Error(\"GitLabWorkSource.create requires projectHint or configured projects\")\n\n // Resolve assignee usernames → user ids. Prefer explicit `assigneeUsernames`,\n // fall back to agentId → configured mapping for legacy callers.\n let assignee_ids: number[] | undefined\n const usernames = input.assigneeUsernames?.length\n ? input.assigneeUsernames\n : (input.assignee ? (this.agentUsernames[input.assignee] || []).slice(0, 1) : [])\n if (usernames.length) {\n const results = await Promise.all(usernames.map(async (u) => {\n try {\n const users = await this.api(`/users?username=${encodeURIComponent(u)}`)\n return Array.isArray(users) && users[0]?.id ? users[0].id : null\n } catch (e: any) {\n this.log(`[board] user lookup failed for ${u}: ${e.message}`)\n return null\n }\n }))\n assignee_ids = results.filter((x): x is number => typeof x === \"number\")\n }\n\n // Resolve milestone title → id.\n let milestone_id: number | undefined\n if (input.milestoneTitle) {\n try {\n const ms = await this.api(\n `/projects/${encodeURIComponent(project)}/milestones?title=${encodeURIComponent(input.milestoneTitle)}`,\n )\n if (Array.isArray(ms) && ms[0]?.id) milestone_id = ms[0].id\n } catch (e: any) {\n this.log(`[board] milestone lookup failed for \"${input.milestoneTitle}\": ${e.message}`)\n }\n }\n\n const body: Record<string, unknown> = {\n title: input.title,\n description: input.description,\n labels: (input.labels || []).join(\",\"),\n }\n if (assignee_ids?.length) body.assignee_ids = assignee_ids\n if (milestone_id !== undefined) body.milestone_id = milestone_id\n\n const issue = await this.api(`/projects/${encodeURIComponent(project)}/issues`, {\n method: \"POST\",\n body: JSON.stringify(body),\n })\n return this.mapIssue(issue, project)\n }\n\n async transition(itemId: string, toLabel: string, fromLabel?: string): Promise<void> {\n // Legacy single add/remove — boards use transitionMany.\n await this.transitionMany(itemId, toLabel ? [toLabel] : [], fromLabel ? [fromLabel] : [])\n }\n\n /** Multi-label transition; used by board drag-drop (scoped labels produce pairs). */\n async transitionMany(itemId: string, add: string[], remove: string[]): Promise<void> {\n const { project, iid, type } = this.parseId(itemId)\n const seg = type === \"issue\" ? \"issues\" : \"merge_requests\"\n const params = new URLSearchParams()\n if (add.length) params.set(\"add_labels\", add.join(\",\"))\n if (remove.length) params.set(\"remove_labels\", remove.join(\",\"))\n if (!params.toString()) return\n await this.api(\n `/projects/${encodeURIComponent(project)}/${seg}/${iid}?${params.toString()}`,\n { method: \"PUT\" },\n )\n }\n\n private parseId(itemId: string): { project: string; type: \"issue\" | \"mr\"; iid: string } {\n // \"gitlab:<project>:issue:<iid>\" (project may contain a slash → it's joined)\n const body = itemId.replace(/^gitlab:/, \"\")\n const parts = body.split(\":\")\n const iid = parts.pop() as string\n const type = parts.pop() === \"mr\" ? \"mr\" : \"issue\"\n const project = parts.join(\":\")\n return { project, type, iid }\n }\n}\n\n// ---------- Factory ----------\n\nexport function createWorkSource(\n business: BusinessConfig,\n daemon: DaemonConfig,\n log: (...args: unknown[]) => void,\n): WorkSource {\n const ws: BusinessWorkSource = business.workSource\n switch (ws.type) {\n case \"backlog\":\n return new BacklogWorkSource(ws.path, daemon, log)\n\n case \"wiki\":\n return new WikiWorkSource(ws.path, ws.glob)\n\n case \"gitlab\": {\n const gl = daemon.channels.gitlab\n if (!gl?.token) {\n throw new Error(\"business.workSource.type=gitlab requires channels.gitlab.token\")\n }\n const agentUsernames: Record<string, string[]> = {}\n for (const mapping of gl.agentMappings) {\n if (mapping.gitlabUsernames.length) {\n agentUsernames[mapping.agentId] = mapping.gitlabUsernames\n }\n }\n const projects = ws.projects.length\n ? ws.projects\n : gl.routes.map((r) => r.project)\n return new GitLabWorkSource(gl.host, gl.token, projects, agentUsernames, log)\n }\n }\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\"\nimport { dirname, resolve } from \"path\"\nimport { randomUUID } from \"crypto\"\nimport { z } from \"zod\"\n\n// --- Structured backlog store (Plan B + import) ---\n//\n// Replaces the legacy `.agentx/backlog.md` GFM checklist as the canonical\n// store when `.agentx/backlog.json` is present. The markdown view is\n// regenerated on every save so humans still get a readable diff.\n//\n// Why structured: importing issues from gitlab/github needs a stable\n// linkage back to the source (project + iid + url) so mutations\n// (assignee, labels, status, milestone) can be pushed upstream by the\n// sync-back layer. Encoding that in markdown comments is brittle.\n\nconst sourceSchema = z.object({\n type: z.enum([\"gitlab\", \"github\"]),\n host: z.string().optional(),\n project: z.string(),\n iid: z.number().int().positive(),\n url: z.string(),\n})\n\nconst itemSchema = z.object({\n id: z.string(),\n title: z.string(),\n description: z.string().optional(),\n assignee: z.string().optional(),\n labels: z.array(z.string()).default([]),\n milestone: z.string().optional(),\n status: z.enum([\"todo\", \"doing\", \"blocked\", \"done\"]).default(\"todo\"),\n priority: z.number().int().optional(),\n estimatedSeconds: z.number().int().optional(),\n source: sourceSchema.optional(),\n importedAt: z.string().optional(),\n createdAt: z.string(),\n updatedAt: z.string(),\n blocker: z.string().optional(),\n doneNote: z.string().optional(),\n})\n\nconst fileSchema = z.object({\n version: z.literal(1),\n items: z.array(itemSchema).default([]),\n})\n\nexport type BacklogSource = z.infer<typeof sourceSchema>\nexport type BacklogItem = z.infer<typeof itemSchema>\nexport type BacklogFile = z.infer<typeof fileSchema>\n\nexport class BacklogStore {\n constructor(private path: string) {}\n\n /** Absolute path of the JSON store. */\n jsonPath(): string {\n return resolve(process.cwd(), this.path).replace(/\\.md$/i, \".json\")\n }\n\n /** Absolute path of the markdown view (alongside the JSON store). */\n mdPath(): string {\n return resolve(process.cwd(), this.path).replace(/\\.json$/i, \".md\")\n }\n\n exists(): boolean {\n return existsSync(this.jsonPath())\n }\n\n load(): BacklogFile {\n const p = this.jsonPath()\n if (!existsSync(p)) return { version: 1, items: [] }\n const raw = readFileSync(p, \"utf-8\")\n try {\n return fileSchema.parse(JSON.parse(raw))\n } catch (e: any) {\n throw new Error(`Backlog file invalid (${p}): ${e.message}`)\n }\n }\n\n save(file: BacklogFile): void {\n const jsonP = this.jsonPath()\n mkdirSync(dirname(jsonP), { recursive: true })\n writeFileSync(jsonP, JSON.stringify(file, null, 2) + \"\\n\")\n writeFileSync(this.mdPath(), this.toMarkdown(file))\n }\n\n list(): BacklogItem[] {\n return this.load().items\n }\n\n findById(id: string): BacklogItem | undefined {\n return this.list().find((i) => i.id === id)\n }\n\n /** Add an item; rejects duplicates by id. Returns the saved item. */\n add(input: Omit<BacklogItem, \"createdAt\" | \"updatedAt\"> & { createdAt?: string }): BacklogItem {\n const file = this.load()\n if (file.items.some((i) => i.id === input.id)) {\n throw new Error(`backlog item ${input.id} already exists`)\n }\n const now = new Date().toISOString()\n const item: BacklogItem = {\n ...input,\n labels: input.labels ?? [],\n status: input.status ?? \"todo\",\n createdAt: input.createdAt ?? now,\n updatedAt: now,\n }\n file.items.push(item)\n this.save(file)\n return item\n }\n\n /** Add many; skips items whose id already exists, returns the inserted set. */\n addMany(inputs: Array<Omit<BacklogItem, \"createdAt\" | \"updatedAt\"> & { createdAt?: string }>): BacklogItem[] {\n const file = this.load()\n const existing = new Set(file.items.map((i) => i.id))\n const now = new Date().toISOString()\n const added: BacklogItem[] = []\n for (const input of inputs) {\n if (existing.has(input.id)) continue\n const item: BacklogItem = {\n ...input,\n labels: input.labels ?? [],\n status: input.status ?? \"todo\",\n createdAt: input.createdAt ?? now,\n updatedAt: now,\n }\n file.items.push(item)\n added.push(item)\n existing.add(item.id)\n }\n if (added.length) this.save(file)\n return added\n }\n\n update(id: string, patch: Partial<BacklogItem>): BacklogItem {\n const file = this.load()\n const idx = file.items.findIndex((i) => i.id === id)\n if (idx < 0) throw new Error(`backlog item ${id} not found`)\n const updated: BacklogItem = {\n ...file.items[idx],\n ...patch,\n id: file.items[idx].id,\n createdAt: file.items[idx].createdAt,\n updatedAt: new Date().toISOString(),\n }\n file.items[idx] = updated\n this.save(file)\n return updated\n }\n\n remove(id: string): boolean {\n const file = this.load()\n const before = file.items.length\n file.items = file.items.filter((i) => i.id !== id)\n if (file.items.length === before) return false\n this.save(file)\n return true\n }\n\n /** Render a human-readable markdown view (generated, not authoritative). */\n toMarkdown(file: BacklogFile): string {\n const lines: string[] = [\n \"# Backlog\",\n \"\",\n \"_Generated from `backlog.json`. Do not edit by hand — use `agentx backlog` commands._\",\n \"\",\n ]\n const buckets: Record<BacklogItem[\"status\"], BacklogItem[]> = {\n doing: [], blocked: [], todo: [], done: [],\n }\n for (const item of file.items) buckets[item.status].push(item)\n const sections: Array<[BacklogItem[\"status\"], string]> = [\n [\"doing\", \"Doing\"], [\"blocked\", \"Blocked\"], [\"todo\", \"To Do\"], [\"done\", \"Done\"],\n ]\n for (const [key, label] of sections) {\n if (!buckets[key].length) continue\n lines.push(`## ${label}`, \"\")\n for (const it of buckets[key]) {\n const check = key === \"done\" ? \"x\" : \" \"\n const mention = it.assignee ? `@${it.assignee} ` : \"\"\n const time = it.estimatedSeconds\n ? ` [time: ${Math.round(it.estimatedSeconds / 60)}m]`\n : \"\"\n const src = it.source ? ` (${it.source.type}: ${it.source.project}#${it.source.iid})` : \"\"\n lines.push(`- [${check}] ${mention}${it.title}${time}${src}`)\n if (it.source?.url) lines.push(` - <${it.source.url}>`)\n if (it.labels.length) lines.push(` - labels: ${it.labels.join(\", \")}`)\n if (it.milestone) lines.push(` - milestone: ${it.milestone}`)\n if (it.blocker) lines.push(` - BLOCKED: ${it.blocker}`)\n }\n lines.push(\"\")\n }\n return lines.join(\"\\n\")\n }\n}\n\n/** Generate a stable BacklogItem.id for a fresh manual item. */\nexport function manualBacklogId(): string {\n return `manual:${randomUUID()}`\n}\n\n/** Build the canonical id for an imported source issue. */\nexport function sourceBacklogId(source: BacklogSource): string {\n return `${source.type}:${source.project}:${source.iid}`\n}\n","import type { BacklogItem, BacklogSource } from \"./backlog-store\"\nimport type { DaemonConfig } from \"@/daemon/config\"\n\n// --- Sync-back layer: backlog item mutations → upstream issue ---\n//\n// When a backlog item carries a `source` ref (gitlab or github), changes\n// applied via the backlog work-source need to flow back to the original\n// issue: assignees, labels, title, description, milestone, open/close.\n// Otherwise the imported view drifts from upstream and humans browsing\n// the source see stale state.\n//\n// This module is intentionally stateless — callers pass the patch they\n// just applied to the local item; we forward the same shape to the\n// matching upstream API.\n\nexport interface BacklogPatch {\n title?: string\n description?: string\n /** Replace labels wholesale. Use addLabels/removeLabels for incremental. */\n labels?: string[]\n addLabels?: string[]\n removeLabels?: string[]\n /** Username on the source (NOT agentId — caller resolves the mapping). */\n assigneeUsernames?: string[]\n /** Title of the milestone, or empty string to clear. */\n milestoneTitle?: string | null\n /** Open/close transition. */\n state?: \"open\" | \"closed\"\n /** Optional comment to post on the upstream issue. */\n note?: string\n}\n\nexport interface SyncBackResult {\n ok: boolean\n url?: string\n error?: string\n /** Each upstream call we attempted, in order. */\n attempts: Array<{ kind: string; ok: boolean; status?: number; error?: string }>\n}\n\n/** Resolve agentIds in a patch to upstream usernames using the daemon mappings. */\nexport function resolveAssigneesForSource(\n agentIds: string[],\n source: BacklogSource,\n daemon: DaemonConfig,\n): string[] {\n const out: string[] = []\n if (source.type === \"gitlab\") {\n const mappings = daemon.channels.gitlab?.agentMappings ?? []\n for (const agentId of agentIds) {\n const m = mappings.find((x) => x.agentId === agentId)\n if (m?.gitlabUsernames?.length) out.push(m.gitlabUsernames[0])\n else out.push(agentId)\n }\n } else if (source.type === \"github\") {\n const mappings = daemon.channels.github?.agentMappings ?? []\n for (const agentId of agentIds) {\n const m = mappings.find((x) => x.agentId === agentId)\n if (m?.githubUsernames?.length) out.push(m.githubUsernames[0])\n else out.push(agentId)\n }\n }\n return out\n}\n\n/** Push a backlog patch to the upstream source referenced by the item. */\nexport async function syncBacklogItemToSource(\n item: BacklogItem,\n patch: BacklogPatch,\n daemon: DaemonConfig,\n log: (...args: unknown[]) => void = () => {},\n): Promise<SyncBackResult> {\n if (!item.source) return { ok: true, attempts: [] }\n if (item.source.type === \"gitlab\") return syncToGitLab(item.source, patch, daemon, log)\n if (item.source.type === \"github\") return syncToGitHub(item.source, patch, daemon, log)\n return { ok: false, error: `unknown source.type ${(item.source as any).type}`, attempts: [] }\n}\n\n// ---------- GitLab ----------\n\nasync function syncToGitLab(\n source: BacklogSource,\n patch: BacklogPatch,\n daemon: DaemonConfig,\n log: (...args: unknown[]) => void,\n): Promise<SyncBackResult> {\n const gl = daemon.channels.gitlab\n const token = gl?.token\n const host = gl?.host || (source.host ? `https://${source.host}` : undefined)\n if (!token || !host) {\n return { ok: false, error: \"gitlab token/host not configured\", attempts: [] }\n }\n const attempts: SyncBackResult[\"attempts\"] = []\n\n const api = async (path: string, init: RequestInit = {}): Promise<Response> => {\n return fetch(`${host}/api/v4${path}`, {\n ...init,\n headers: {\n \"PRIVATE-TOKEN\": token,\n \"Content-Type\": \"application/json\",\n ...(init.headers || {}),\n },\n })\n }\n\n const projectEnc = encodeURIComponent(source.project)\n\n // Resolve assignee usernames → ids (parallel) — only when we need them.\n let assigneeIds: number[] | undefined\n if (patch.assigneeUsernames !== undefined) {\n const ids = await Promise.all(patch.assigneeUsernames.map(async (u) => {\n try {\n const res = await fetch(`${host}/api/v4/users?username=${encodeURIComponent(u)}`, {\n headers: { \"PRIVATE-TOKEN\": token },\n })\n if (!res.ok) return null\n const arr: any = await res.json()\n return Array.isArray(arr) && arr[0]?.id ? arr[0].id : null\n } catch { return null }\n }))\n assigneeIds = ids.filter((x): x is number => typeof x === \"number\")\n }\n\n // Resolve milestone title → id.\n let milestoneId: number | 0 | undefined\n if (patch.milestoneTitle !== undefined) {\n if (patch.milestoneTitle === null || patch.milestoneTitle === \"\") {\n milestoneId = 0\n } else {\n try {\n const res = await api(`/projects/${projectEnc}/milestones?title=${encodeURIComponent(patch.milestoneTitle)}`)\n if (res.ok) {\n const arr: any = await res.json()\n if (Array.isArray(arr) && arr[0]?.id) milestoneId = arr[0].id\n }\n } catch (e: any) {\n log(`[backlog-sync] milestone lookup failed: ${e.message}`)\n }\n }\n }\n\n // Build the PUT body.\n const body: Record<string, unknown> = {}\n if (patch.title !== undefined) body.title = patch.title\n if (patch.description !== undefined) body.description = patch.description\n if (patch.labels !== undefined) body.labels = patch.labels.join(\",\")\n if (patch.addLabels?.length) body.add_labels = patch.addLabels.join(\",\")\n if (patch.removeLabels?.length) body.remove_labels = patch.removeLabels.join(\",\")\n if (assigneeIds !== undefined) body.assignee_ids = assigneeIds\n if (milestoneId !== undefined) body.milestone_id = milestoneId\n if (patch.state) body.state_event = patch.state === \"closed\" ? \"close\" : \"reopen\"\n\n if (Object.keys(body).length) {\n try {\n const res = await api(`/projects/${projectEnc}/issues/${source.iid}`, {\n method: \"PUT\",\n body: JSON.stringify(body),\n })\n attempts.push({ kind: \"PUT issue\", ok: res.ok, status: res.status })\n if (!res.ok) {\n const text = await res.text().catch(() => \"\")\n return { ok: false, attempts, error: `PUT issue ${res.status}: ${text.slice(0, 200)}`, url: source.url }\n }\n } catch (e: any) {\n attempts.push({ kind: \"PUT issue\", ok: false, error: e.message })\n return { ok: false, attempts, error: e.message, url: source.url }\n }\n }\n\n if (patch.note) {\n try {\n const res = await api(`/projects/${projectEnc}/issues/${source.iid}/notes`, {\n method: \"POST\",\n body: JSON.stringify({ body: patch.note }),\n })\n attempts.push({ kind: \"POST note\", ok: res.ok, status: res.status })\n } catch (e: any) {\n attempts.push({ kind: \"POST note\", ok: false, error: e.message })\n }\n }\n\n return { ok: attempts.every((a) => a.ok), attempts, url: source.url }\n}\n\n// ---------- GitHub ----------\n\nasync function syncToGitHub(\n source: BacklogSource,\n patch: BacklogPatch,\n daemon: DaemonConfig,\n log: (...args: unknown[]) => void,\n): Promise<SyncBackResult> {\n const gh = daemon.channels.github\n const token = gh?.token\n if (!token) {\n return { ok: false, error: \"github token not configured\", attempts: [] }\n }\n const attempts: SyncBackResult[\"attempts\"] = []\n\n const api = async (path: string, init: RequestInit = {}): Promise<Response> => {\n return fetch(`https://api.github.com${path}`, {\n ...init,\n headers: {\n \"Authorization\": `Bearer ${token}`,\n \"Accept\": \"application/vnd.github+json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n ...(init.headers || {}),\n },\n })\n }\n\n const repo = source.project // GitHub IDs use \"owner/repo\" verbatim in the URL path.\n\n // Resolve milestone title → number.\n let milestoneNumber: number | null | undefined\n if (patch.milestoneTitle !== undefined) {\n if (patch.milestoneTitle === null || patch.milestoneTitle === \"\") {\n milestoneNumber = null\n } else {\n try {\n const res = await api(`/repos/${repo}/milestones?state=all&per_page=100`)\n if (res.ok) {\n const arr: any = await res.json()\n const found = Array.isArray(arr) ? arr.find((m: any) => m.title === patch.milestoneTitle) : null\n if (found?.number) milestoneNumber = found.number\n }\n } catch (e: any) {\n log(`[backlog-sync] github milestone lookup failed: ${e.message}`)\n }\n }\n }\n\n // GitHub doesn't support add/remove labels in the issue PATCH the way\n // GitLab does — labels is a wholesale set. If only addLabels/removeLabels\n // were provided we read+merge.\n let nextLabels: string[] | undefined\n if (patch.labels !== undefined) {\n nextLabels = patch.labels\n } else if (patch.addLabels?.length || patch.removeLabels?.length) {\n try {\n const res = await api(`/repos/${repo}/issues/${source.iid}`)\n if (res.ok) {\n const issue: any = await res.json()\n const current: string[] = Array.isArray(issue.labels)\n ? issue.labels.map((l: any) => (typeof l === \"string\" ? l : l.name))\n : []\n const adds = new Set([...current, ...(patch.addLabels ?? [])])\n for (const r of patch.removeLabels ?? []) adds.delete(r)\n nextLabels = [...adds]\n }\n } catch (e: any) {\n log(`[backlog-sync] github label fetch failed: ${e.message}`)\n }\n }\n\n const body: Record<string, unknown> = {}\n if (patch.title !== undefined) body.title = patch.title\n if (patch.description !== undefined) body.body = patch.description\n if (nextLabels !== undefined) body.labels = nextLabels\n if (patch.assigneeUsernames !== undefined) body.assignees = patch.assigneeUsernames\n if (milestoneNumber !== undefined) body.milestone = milestoneNumber\n if (patch.state) body.state = patch.state\n\n if (Object.keys(body).length) {\n try {\n const res = await api(`/repos/${repo}/issues/${source.iid}`, {\n method: \"PATCH\",\n body: JSON.stringify(body),\n })\n attempts.push({ kind: \"PATCH issue\", ok: res.ok, status: res.status })\n if (!res.ok) {\n const text = await res.text().catch(() => \"\")\n return { ok: false, attempts, error: `PATCH issue ${res.status}: ${text.slice(0, 200)}`, url: source.url }\n }\n } catch (e: any) {\n attempts.push({ kind: \"PATCH issue\", ok: false, error: e.message })\n return { ok: false, attempts, error: e.message, url: source.url }\n }\n }\n\n if (patch.note) {\n try {\n const res = await api(`/repos/${repo}/issues/${source.iid}/comments`, {\n method: \"POST\",\n body: JSON.stringify({ body: patch.note }),\n })\n attempts.push({ kind: \"POST comment\", ok: res.ok, status: res.status })\n } catch (e: any) {\n attempts.push({ kind: \"POST comment\", ok: false, error: e.message })\n }\n }\n\n return { ok: attempts.every((a) => a.ok), attempts, url: source.url }\n}\n","// --- Who is the work for? -------------------------------------------------\n//\n// The principal behind a piece of work is a CLIENT. That concept already\n// existed, spread across `business.projects[].client`, `business.contactMap[]`\n// and the `orgChart` reportsTo chain, resolved privately inside the activity\n// graph panel. This module is the single implementation both that panel and\n// the session monitor call, plus the policy layer the monitor needs.\n//\n// Clients are DERIVED, never declared: one appears the moment a project or a\n// contact mentions it. `business.clients` only carries overrides — a display\n// name, whether it is a paying client or your own house, how fast a delay\n// costs you, and what agents may do for it without asking. A client with no\n// entry there still resolves; it just gets defaults.\n\nexport interface ContactRule {\n channel?: string\n chatId?: string\n username?: string\n senderId?: string\n client: string\n project?: string\n displayName?: string\n}\nexport interface ProjectRule { id: string; pm?: string; client?: string }\nexport interface ClientPolicy {\n name?: string\n kind?: \"client\" | \"internal\" | \"own\"\n respondWithin?: string\n standing?: string[]\n}\nexport interface BusinessShape {\n projects?: ProjectRule[]\n contactMap?: ContactRule[]\n orgChart?: Record<string, { reportsTo?: string }>\n clients?: Record<string, ClientPolicy>\n}\nexport interface ResolvedClient {\n id: string\n name: string\n kind: \"client\" | \"internal\" | \"own\"\n /** Minutes before a wait starts costing; undefined = no external clock. */\n respondWithinMinutes?: number\n standing: string[]\n /** True when nothing in config mentions this client — derived only. */\n declared: boolean\n}\n\nexport const UNMAPPED = \"unmapped\"\n\n/** Explicit `business.projects[].client` wins; otherwise the leading segment\n * of the id. Works for \"owner/repo\" on any forge and for bare internal ids. */\nexport function clientFromProject(project: string | null | undefined, projects: ProjectRule[] = []): string {\n if (!project) return UNMAPPED\n const hit = projects.find(p => p.id === project)\n if (hit?.client) return hit.client\n const slash = project.indexOf(\"/\")\n return slash > 0 ? project.slice(0, slash) : project\n}\n\n/** agent id -> client, from project PMs then transitively down reportsTo.\n * A mesh dispatch carries no project, so without this it is unattributable. */\nexport function agentClients(business: BusinessShape): Map<string, string> {\n const out = new Map<string, string>()\n const projects = business.projects ?? []\n const orgChart = business.orgChart ?? {}\n for (const p of projects) {\n if (p.pm) if (!out.has(p.pm)) out.set(p.pm, clientFromProject(p.id, projects))\n }\n const climb = (agentId: string, seen = new Set<string>()): string | undefined => {\n if (out.has(agentId)) return out.get(agentId)\n if (seen.has(agentId)) return undefined\n seen.add(agentId)\n const up = orgChart[agentId]?.reportsTo\n if (!up) return undefined\n const c = climb(up, seen)\n if (c) out.set(agentId, c)\n return c\n }\n for (const agentId of Object.keys(orgChart)) climb(agentId)\n return out\n}\n\n/** chatId > username > senderId > channel-default. First match wins. */\nexport function matchContact(\n channel: string | undefined,\n who: { chatId?: string; username?: string; senderId?: string },\n contactMap: ContactRule[] = [],\n): ContactRule | undefined {\n if (!contactMap.length) return undefined\n const pool = contactMap.filter(m => !m.channel || m.channel === channel)\n return pool.find(m => m.chatId && who.chatId && m.chatId === who.chatId)\n ?? pool.find(m => m.username && who.username && m.username === who.username)\n ?? pool.find(m => m.senderId && who.senderId && m.senderId === who.senderId)\n ?? pool.find(m => m.channel && !m.chatId && !m.username && !m.senderId)\n}\n\nexport interface WorkRef { agentId?: string; channel?: string; project?: string; chatId?: string; username?: string }\n\n/** The one entry point. Conversation rules beat project shape, which beats\n * the agent's place in the org, because a rule someone wrote by hand is\n * always more deliberate than a heuristic. */\nexport function resolveClient(ref: WorkRef, business: BusinessShape): string {\n const contact = matchContact(ref.channel, { chatId: ref.chatId, username: ref.username }, business.contactMap)\n if (contact) return contact.client\n if (ref.project) return clientFromProject(ref.project, business.projects)\n if (ref.agentId) {\n const byAgent = agentClients(business).get(ref.agentId)\n if (byAgent) return byAgent\n }\n return UNMAPPED\n}\n\n/** A monitor session id is \"<agent>:<channel>:<target...>\". Forge targets\n * carry the project, chat targets carry the chat id. */\nexport function parseWorkRef(sessionId: string, agentId?: string): WorkRef {\n const parts = String(sessionId || \"\").split(\":\")\n if (parts.length < 3) return { agentId }\n const [agent, channel, ...rest] = parts\n const target = rest.join(\":\")\n const ref: WorkRef = { agentId: agentId || agent, channel }\n if (channel === \"gitlab\" || channel === \"github\") {\n // \"owner/repo:issue:94\" and \"owner/repo:pipeline:10897\" -> \"owner/repo\"\n ref.project = target.split(\":\")[0]\n } else if (channel === \"cron\" || channel === \"api\") {\n // Nothing addressable: fall through to the agent's own client.\n } else {\n ref.chatId = target.split(\"@\")[0]\n }\n return ref\n}\n\nconst KIND_DEFAULT = (id: string): \"client\" | \"internal\" | \"own\" => (id === UNMAPPED ? \"internal\" : \"client\")\n\nexport function clientPolicy(id: string, business: BusinessShape): ResolvedClient {\n const p = business.clients?.[id]\n return {\n id,\n name: p?.name || id,\n kind: p?.kind ?? KIND_DEFAULT(id),\n respondWithinMinutes: parseDuration(p?.respondWithin),\n standing: p?.standing ?? [],\n declared: Boolean(p),\n }\n}\n\n/** Every client the config can produce: from projects, contacts and overrides. */\nexport function listClients(business: BusinessShape): ResolvedClient[] {\n const ids = new Set<string>()\n for (const p of business.projects ?? []) ids.add(clientFromProject(p.id, business.projects))\n for (const c of business.contactMap ?? []) ids.add(c.client)\n for (const id of Object.keys(business.clients ?? {})) ids.add(id)\n return [...ids].sort().map(id => clientPolicy(id, business))\n}\n\n/** \"4h\", \"90m\", \"2d\" -> minutes. Anything else is treated as no clock, so a\n * typo can never silently become an aggressive deadline. */\nexport function parseDuration(v: string | undefined): number | undefined {\n const m = /^(\\d+)\\s*([mhd])$/.exec(String(v ?? \"\").trim())\n if (!m) return undefined\n const n = Number(m[1])\n return m[2] === \"m\" ? n : m[2] === \"h\" ? n * 60 : n * 1440\n}\n\n/** Whether an agent may act unattended for this client. Deny by default, and\n * never for a paying client regardless of what is listed — an outward-facing\n * mistake on someone else's repo is not ours to make on a timer. */\nexport function mayProceedUnattended(client: ResolvedClient, capability: string): boolean {\n if (client.kind === \"client\") return false\n return client.standing.includes(\"*\") || client.standing.includes(capability)\n}\n","import { existsSync } from 'node:fs'\nimport { join } from 'node:path'\n\nexport const DESKTOP_LABEL = 'tn.acme.agentx.voice'\nexport const DESKTOP_APP = 'AgentX Desktop.app'\nexport const HELPER_APP = 'AgentX Helper.app'\nexport function desktopPlatformError(platform: string, arch: string, release: string): string | null {\n if (platform !== 'darwin' || arch !== 'arm64' || Number(release.split('.')[0]) < 23)\n return 'AgentX Desktop currently requires Apple Silicon and macOS 14 or newer.'\n return null\n}\nexport function selectDesktopAgent(agents: Record<string, unknown>, requested?: string): string {\n if (requested && !Object.hasOwn(agents, requested)) throw new Error(`Unknown agent: ${requested}. Run agentx agent list.`)\n const id = requested || Object.keys(agents)[0]\n if (!id) throw new Error('No agents configured. Run agentx setup first.')\n return id\n}\nexport function shellQuote(value: string): string { return `'${value.replace(/'/g, \"'\\\\''\")}'` }\nexport function desktopPlist(opts: { executable: string; cwd: string; agent: string; url: string; helper: string; cli: string; node: string; log: string }): string {\n const xml = (s: string) => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;').replace(/'/g, '&apos;')\n const env = {\n AGENTX_VOICE_AGENT: opts.agent,\n AGENTX_DAEMON_URL: opts.url,\n AGENTX_MAC_HELPER: opts.helper,\n AGENTX_PASTE_COMMAND: `cd ${shellQuote(opts.cwd)} && ${shellQuote(opts.node)} ${shellQuote(opts.cli)} paste`,\n }\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\"><dict>\n<key>Label</key><string>${DESKTOP_LABEL}</string>\n<key>ProgramArguments</key><array><string>${xml(opts.executable)}</string></array>\n<key>WorkingDirectory</key><string>${xml(opts.cwd)}</string>\n<key>EnvironmentVariables</key><dict>${Object.entries(env).map(([k,v]) => `<key>${k}</key><string>${xml(v)}</string>`).join('')}</dict>\n<key>RunAtLoad</key><true/><key>KeepAlive</key><true/>\n<key>ProcessType</key><string>Interactive</string>\n<key>StandardErrorPath</key><string>${xml(opts.log)}</string>\n</dict></plist>\\n`\n}\n/** build.sh arguments for the helper: the banner icon from\n * notifications.local.icon, or none for the bundled AgentX logo. */\nexport function helperBuildArgs(icon?: string): string[] {\n return icon ? ['--icon', icon] : []\n}\nexport function installedHelper(home: string): string {\n return join(home, 'Applications', HELPER_APP, 'Contents/MacOS/agentx-mac-helper')\n}\nexport function resolveHelper(cwd: string, home: string, override?: string): string {\n if (override) return override\n const installed = installedHelper(home)\n return existsSync(installed) ? installed : join(cwd, 'apps/mac-helper/build/AgentX Helper.app/Contents/MacOS/agentx-mac-helper')\n}\n","import { execFile } from \"child_process\"\nimport { existsSync } from \"fs\"\nimport { homedir } from \"os\"\nimport { isAbsolute } from \"path\"\nimport { resolveHelper } from \"@/desktop/install\"\n\n// What happens on THIS machine when a notification is delivered: a banner\n// on screen and a short sound. The push goes to the phone; these are for\n// the person sitting at the Mac that raised the event.\n//\n// Both are awaited rather than fired and forgotten. A caller that exits\n// straight after notifying — a `launchctl submit` job that removes itself,\n// a cron one-liner — used to take the sound down with it before it\n// played. Waiting costs about a second and makes the step reliable.\n//\n// The banner is posted by the AgentX Helper app when it is installed, so\n// it carries the helper's icon (the AgentX logo by default). Without the\n// helper, or before macOS has allowed it to notify, osascript posts it,\n// and macOS shows it as coming from Script Editor.\n//\n// Never fatal: a notification whose banner failed still arrived, and no\n// caller should have to handle an audio error to say a build finished.\n\nexport interface LocalSettings {\n /** Show a desktop banner (macOS only; other platforms skip it). */\n banner: boolean\n /** Play a sound (macOS only). */\n sound: boolean\n /** A macOS system sound, by name: /System/Library/Sounds/<name>.aiff. */\n soundName: string\n /** 0 (silent) … 1 (full). */\n volume: number\n /** Image for the helper's app icon, which macOS puts on its banners.\n * Unset: the AgentX logo. Applied when `agentx desktop install` builds\n * the helper, since macOS reads the icon from the app, not the banner. */\n icon?: string\n}\n\n/** Glass is the gentlest system sound that is still audible over a\n * keyboard — Basso and Sosumi are alerts, Funk and Frog are jokes, and\n * Ping is the one every other app already uses. */\nexport const DEFAULT_LOCAL: LocalSettings = { banner: true, sound: true, soundName: \"Glass\", volume: 0.4 }\n\n/** Shows the banner and plays the sound for one delivered notification. */\nexport type LocalAlert = (title: string, message: string) => Promise<void>\n\n/** Runs a command; resolves true when it exited 0. */\ntype Run = (file: string, args: string[]) => Promise<boolean>\n\n/** Neither step may hold a caller hostage: afplay on a long custom sound,\n * or osascript stuck on a permission prompt, is cut off here. */\nconst STEP_TIMEOUT_MS = 5_000\n\nconst run: Run = (file, args) =>\n new Promise((done) => {\n try {\n execFile(file, args, { timeout: STEP_TIMEOUT_MS }, (err) => done(!err))\n } catch {\n done(false) // no osascript / afplay on this machine — not worth reporting\n }\n })\n\n/** Fill gaps in a partial `notifications.local` block with the defaults. */\nexport function localSettings(cfg: Partial<LocalSettings> | undefined): LocalSettings {\n return { ...DEFAULT_LOCAL, ...(cfg ?? {}) }\n}\n\n/** The system sound file for a name, or null when there is none. Names\n * only — a path in the config must not reach afplay. */\nexport function soundPath(name: string): string | null {\n if (!/^[\\w -]+$/.test(name)) return null\n const path = `/System/Library/Sounds/${name}.aiff`\n return existsSync(path) ? path : null\n}\n\n/** The AgentX Helper binary when one is built or installed, else null. */\nexport function findHelper(): string | null {\n const path = resolveHelper(process.cwd(), homedir())\n return existsSync(path) ? path : null\n}\n\nexport function localAlert(\n settings: LocalSettings,\n deps: { run?: Run; platform?: NodeJS.Platform; helper?: string | null } = {},\n): LocalAlert {\n const exec = deps.run ?? run\n const platform = deps.platform ?? process.platform\n const banner = async (title: string, message: string): Promise<void> => {\n const helper = deps.helper === undefined ? findHelper() : deps.helper\n // A non-zero exit is usually \"not allowed to notify yet\" — the first\n // banner asks — so this one goes through osascript instead.\n if (helper && await exec(helper, [\"notify\", \"--title\", title, \"--message\", message])) return\n // Title and message travel as argv, never spliced into the script,\n // so a quote in either cannot turn into AppleScript.\n await exec(\"/usr/bin/osascript\", [\n \"-e\", \"on run argv\",\n \"-e\", \"display notification (item 2 of argv) with title (item 1 of argv)\",\n \"-e\", \"end run\",\n title, message,\n ])\n }\n return async (title, message) => {\n if (platform !== \"darwin\") return\n const steps: Promise<unknown>[] = []\n if (settings.banner) steps.push(banner(title, message))\n const path = settings.sound ? soundPath(settings.soundName) : null\n if (path) {\n const volume = Math.min(1, Math.max(0, settings.volume))\n steps.push(exec(\"/usr/bin/afplay\", [\"-v\", String(volume), path]))\n }\n await Promise.all(steps)\n }\n}\n\n/**\n * Apply a change to a stored `notifications.local` block, the one rule\n * both the CLI and the dashboard write through. Returns the new block;\n * throws on a value that would not load. Only a Mac has sounds to check a\n * name against — elsewhere the setting is inert, so it need only be\n * well-formed.\n */\nexport function patchLocal(\n current: Partial<LocalSettings> | undefined,\n patch: Record<string, unknown>,\n platform: NodeJS.Platform = process.platform,\n): Partial<LocalSettings> {\n const next = { ...(current ?? {}) }\n if (\"banner\" in patch) next.banner = Boolean(patch.banner)\n if (\"sound\" in patch) next.sound = Boolean(patch.sound)\n if (\"soundName\" in patch) {\n const name = String(patch.soundName).trim()\n const ok = platform === \"darwin\" ? soundPath(name) !== null : /^[\\w -]+$/.test(name)\n if (!ok) throw new Error(`no system sound named \"${name}\" in /System/Library/Sounds`)\n next.soundName = name\n }\n if (\"icon\" in patch) {\n const icon = String(patch.icon ?? \"\").trim().replace(/^~(?=\\/)/, homedir())\n if (!icon) delete next.icon\n else {\n if (!isAbsolute(icon) || !/\\.(png|jpe?g|icns)$/i.test(icon)) throw new Error(\"icon must be the full path to a .png, .jpg or .icns file\")\n if (platform === \"darwin\" && !existsSync(icon)) throw new Error(`no icon file at ${icon}`)\n next.icon = icon\n }\n }\n if (\"volume\" in patch) {\n const v = Number(patch.volume)\n if (!Number.isFinite(v) || v < 0 || v > 1) throw new Error(\"volume must be between 0 and 1\")\n next.volume = v\n }\n return next\n}\n","import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, rmSync, statSync } from \"fs\"\nimport { resolve, relative, dirname, join, extname, basename } from \"path\"\n\n// --- Scoped file operations for the admin panel ---\n//\n// Lets the dashboard read/write identity files (CLAUDE.md / SOUL.md /\n// IDENTITY.md) and skill files (.claude/skills/<slug>/SKILL.md) WITHIN an\n// agent's workspace without ever touching the rest of the filesystem.\n// Every public function resolves the requested path against the agent's\n// workspace and refuses anything that would escape via .. symlinks, etc.\n\nconst MAX_FILE_BYTES = 200 * 1024 // 200 KB — plenty for CLAUDE.md / skills.\nconst ALLOWED_EXTENSIONS = new Set([\".md\", \".markdown\", \".txt\"])\n/** Extensions the skill-tree viewer is allowed to read/write. Broader than\n * identity editing — skills include shell scripts, lockfiles, configs. Kept\n * as an allow-list so a malicious prompt can't touch an arbitrary binary. */\nconst SKILL_EDITABLE_EXTENSIONS = new Set([\n \".md\", \".markdown\", \".txt\", \".json\", \".jsonc\", \".yaml\", \".yml\", \".toml\",\n \".sh\", \".bash\", \".zsh\", \".py\", \".js\", \".mjs\", \".ts\", \".tsx\", \".jsx\",\n \".css\", \".html\", \".lock\", \".cfg\", \".ini\", \".env\", \".gitignore\",\n])\n/** Depth limit when walking a skill dir tree — defensive against loops. */\nconst SKILL_TREE_MAX_DEPTH = 6\nconst IDENTITY_FILENAMES = new Set([\n \"CLAUDE.md\", \"SOUL.md\", \"IDENTITY.md\", \"PERSONA.md\", \"AGENT.md\", \"BOOTSTRAP.md\",\n])\n\nexport interface FileOverview {\n identity: Array<{\n path: string // relative to workspace, e.g. \"CLAUDE.md\"\n title: string\n size: number\n exists: boolean // canonical files can be listed as templates even if missing\n }>\n skills: Array<{\n slug: string // dir name under .claude/skills/\n title: string // first # heading in SKILL.md or fallback to slug\n size: number\n path: string // relative to workspace, e.g. \".claude/skills/foo/SKILL.md\"\n }>\n workspace: string // absolute, for display only\n}\n\n/**\n * Resolve a user-supplied path to an absolute path strictly within `workspace`.\n * Throws if the result would escape. Returns the normalised absolute path.\n */\nfunction resolveInWorkspace(workspace: string, relPath: string): string {\n if (!workspace) throw new Error(\"Agent workspace is not configured.\")\n const wsAbs = resolve(workspace)\n if (!existsSync(wsAbs)) {\n // Create the workspace lazily — a fresh agent scaffolded via the wizard\n // may have an empty directory created but not yet with file structure.\n mkdirSync(wsAbs, { recursive: true })\n }\n // Reject absolute-path attempts outright.\n if (!relPath || relPath.startsWith(\"/\") || relPath.match(/^[a-zA-Z]:/)) {\n throw new Error(`Path must be relative to the workspace: ${relPath}`)\n }\n const target = resolve(wsAbs, relPath)\n const rel = relative(wsAbs, target)\n // rel === \"\" means target IS the workspace dir — fine for list ops.\n // rel starts with \"..\" iff target is outside the workspace.\n if (rel.startsWith(\"..\")) {\n throw new Error(`Path escapes the workspace: ${relPath}`)\n }\n return target\n}\n\nfunction assertEditable(absPath: string): void {\n const ext = extname(absPath).toLowerCase()\n if (!ALLOWED_EXTENSIONS.has(ext)) {\n throw new Error(`Only text files (.md, .markdown, .txt) can be edited from the admin panel (got ${ext}).`)\n }\n}\n\nfunction extractTitle(markdown: string, fallback: string): string {\n const line = markdown.split(/\\r?\\n/).find((l) => l.trim().startsWith(\"#\"))\n if (!line) return fallback\n return line.replace(/^#+\\s*/, \"\").trim().slice(0, 120) || fallback\n}\n\n/**\n * List the identity files + skills for an agent. Identity \"canonical\" names\n * that don't exist are still reported (exists: false) so the UI can offer to\n * create them with a sensible default.\n */\nexport function listAgentFiles(workspace: string): FileOverview {\n const wsAbs = resolveInWorkspace(workspace, \".\")\n const identity: FileOverview[\"identity\"] = []\n const seen = new Set<string>()\n\n // Every .md in the workspace root is identity material; canonical names\n // sort first.\n let rootEntries: string[] = []\n try { rootEntries = readdirSync(wsAbs) } catch { /* empty */ }\n for (const name of IDENTITY_FILENAMES) {\n const abs = join(wsAbs, name)\n if (existsSync(abs) && statSync(abs).isFile()) {\n const size = statSync(abs).size\n const content = size > 0 && size < MAX_FILE_BYTES ? safeRead(abs) : \"\"\n identity.push({ path: name, title: extractTitle(content, name), size, exists: true })\n seen.add(name)\n } else {\n identity.push({ path: name, title: name, size: 0, exists: false })\n seen.add(name)\n }\n }\n for (const name of rootEntries) {\n if (seen.has(name)) continue\n const abs = join(wsAbs, name)\n try {\n const st = statSync(abs)\n if (!st.isFile()) continue\n if (!ALLOWED_EXTENSIONS.has(extname(name).toLowerCase())) continue\n const content = st.size > 0 && st.size < MAX_FILE_BYTES ? safeRead(abs) : \"\"\n identity.push({ path: name, title: extractTitle(content, name), size: st.size, exists: true })\n } catch { /* skip unreadable */ }\n }\n\n // Skills live under .claude/skills/<slug>/SKILL.md\n const skills: FileOverview[\"skills\"] = []\n const skillsRoot = join(wsAbs, \".claude\", \"skills\")\n if (existsSync(skillsRoot)) {\n try {\n for (const slug of readdirSync(skillsRoot)) {\n const dir = join(skillsRoot, slug)\n let st\n try { st = statSync(dir) } catch { continue }\n if (!st.isDirectory()) continue\n const file = join(dir, \"SKILL.md\")\n if (!existsSync(file)) continue\n const stf = statSync(file)\n const content = stf.size < MAX_FILE_BYTES ? safeRead(file) : \"\"\n skills.push({\n slug,\n title: extractTitle(content, slug),\n size: stf.size,\n path: `.claude/skills/${slug}/SKILL.md`,\n })\n }\n } catch { /* */ }\n }\n skills.sort((a, b) => a.slug.localeCompare(b.slug))\n\n return { identity, skills, workspace: wsAbs }\n}\n\nfunction safeRead(abs: string): string {\n try { return readFileSync(abs, \"utf-8\") } catch { return \"\" }\n}\n\n/**\n * Read a single file within the workspace. Throws if the path is outside the\n * workspace, has a disallowed extension, or doesn't exist.\n */\nexport function readAgentFile(workspace: string, relPath: string): { path: string; content: string } {\n const abs = resolveInWorkspace(workspace, relPath)\n assertEditable(abs)\n if (!existsSync(abs)) return { path: relPath, content: \"\" }\n if (statSync(abs).size > MAX_FILE_BYTES) {\n throw new Error(`File is too large to edit in the admin panel (> ${MAX_FILE_BYTES / 1024} KB).`)\n }\n return { path: relPath, content: readFileSync(abs, \"utf-8\") }\n}\n\n/**\n * Write a file. Creates parent dirs as needed. Rejects extensions / size\n * outside the allow-list the same way readAgentFile does.\n */\nexport function writeAgentFile(workspace: string, relPath: string, content: string): { path: string; bytes: number } {\n const abs = resolveInWorkspace(workspace, relPath)\n assertEditable(abs)\n const buf = Buffer.from(content, \"utf-8\")\n if (buf.length > MAX_FILE_BYTES) {\n throw new Error(`File too large (${buf.length} > ${MAX_FILE_BYTES} bytes).`)\n }\n mkdirSync(dirname(abs), { recursive: true })\n writeFileSync(abs, buf)\n return { path: relPath, bytes: buf.length }\n}\n\n/**\n * Create a new skill scaffold at .claude/skills/<slug>/SKILL.md.\n */\nexport function createAgentSkill(\n workspace: string,\n slug: string,\n opts: { title?: string; content?: string } = {},\n): { slug: string; path: string } {\n if (!/^[a-z0-9][a-z0-9_-]*$/.test(slug)) {\n throw new Error(\"Skill slug must be lowercase (letters, digits, -, _) and start with a letter or digit.\")\n }\n const relPath = `.claude/skills/${slug}/SKILL.md`\n const abs = resolveInWorkspace(workspace, relPath)\n if (existsSync(abs)) throw new Error(`Skill \"${slug}\" already exists.`)\n mkdirSync(dirname(abs), { recursive: true })\n const body = opts.content?.trim()\n ? opts.content\n : defaultSkillTemplate(slug, opts.title)\n writeFileSync(abs, body)\n return { slug, path: relPath }\n}\n\n/**\n * Remove a skill directory (.claude/skills/<slug>). Scoped to the workspace\n * so we can never touch anything else.\n */\nexport function deleteAgentSkill(workspace: string, slug: string): { slug: string } {\n if (!/^[a-z0-9][a-z0-9_-]*$/.test(slug)) throw new Error(`Invalid skill slug: ${slug}`)\n const abs = resolveInWorkspace(workspace, `.claude/skills/${slug}`)\n if (!existsSync(abs)) throw new Error(`Skill \"${slug}\" not found.`)\n const st = statSync(abs)\n if (!st.isDirectory()) throw new Error(`Skill path is not a directory: ${slug}`)\n rmSync(abs, { recursive: true, force: true })\n return { slug }\n}\n\n// --- Skill directory tree + per-file I/O ---\n//\n// A skill is `.claude/skills/<slug>/` — usually SKILL.md plus scripts,\n// lockfiles, configs. The admin UI lets the operator browse the directory\n// tree, edit any text file, and dispatch \"install deps\" through the agent.\n\nexport interface SkillTreeEntry {\n /** Path relative to the skill dir root. Directories end with \"/\". */\n path: string\n kind: \"file\" | \"dir\"\n size: number\n editable: boolean // false for binaries or disallowed extensions\n}\n\nexport interface SkillDepsHint {\n /** Which install command the operator should run (or have an agent run). */\n manager: \"npm\" | \"pnpm\" | \"yarn\" | \"pip\" | \"bundle\" | null\n /** Filename that triggered the detection, relative to skill dir. */\n file: string | null\n /** The exact command we'd recommend. */\n command: string | null\n}\n\n/** List every file in `.claude/skills/<slug>/`, recursively. Scoped so the\n * caller can't escape the skill dir via symlinks or \"..\" segments. */\nexport function listSkillTree(workspace: string, slug: string): SkillTreeEntry[] {\n if (!/^[a-z0-9][a-z0-9_-]*$/.test(slug)) throw new Error(`Invalid skill slug: ${slug}`)\n const skillRoot = resolveInWorkspace(workspace, `.claude/skills/${slug}`)\n if (!existsSync(skillRoot)) throw new Error(`Skill \"${slug}\" not found.`)\n const out: SkillTreeEntry[] = []\n walk(skillRoot, \"\", 0, out)\n out.sort((a, b) => {\n // dirs first, then alphabetical\n if (a.kind !== b.kind) return a.kind === \"dir\" ? -1 : 1\n return a.path.localeCompare(b.path)\n })\n return out\n\n function walk(absDir: string, relDir: string, depth: number, acc: SkillTreeEntry[]): void {\n if (depth > SKILL_TREE_MAX_DEPTH) return\n let entries: string[] = []\n try { entries = readdirSync(absDir) } catch { return }\n for (const name of entries) {\n if (name === \"node_modules\" || name === \".git\") continue\n const abs = join(absDir, name)\n const rel = relDir ? `${relDir}/${name}` : name\n let st\n try { st = statSync(abs) } catch { continue }\n if (st.isDirectory()) {\n acc.push({ path: `${rel}/`, kind: \"dir\", size: 0, editable: false })\n walk(abs, rel, depth + 1, acc)\n } else if (st.isFile()) {\n const ext = extname(name).toLowerCase()\n // name-only allowlist for dotfiles like .env / .gitignore (no extension)\n const allowedByName = SKILL_EDITABLE_EXTENSIONS.has(ext) ||\n SKILL_EDITABLE_EXTENSIONS.has(name.toLowerCase())\n acc.push({\n path: rel,\n kind: \"file\",\n size: st.size,\n editable: allowedByName && st.size < MAX_FILE_BYTES,\n })\n }\n }\n }\n}\n\n/** Read any file in a skill dir. Respects the skill-editable allowlist. */\nexport function readSkillFile(workspace: string, slug: string, relPath: string): { path: string; content: string } {\n if (!/^[a-z0-9][a-z0-9_-]*$/.test(slug)) throw new Error(`Invalid skill slug: ${slug}`)\n if (!relPath || relPath.includes(\"..\")) throw new Error(`Invalid file path: ${relPath}`)\n const abs = resolveInWorkspace(workspace, `.claude/skills/${slug}/${relPath}`)\n if (!existsSync(abs) || !statSync(abs).isFile()) {\n throw new Error(`File not found: ${relPath}`)\n }\n const ext = extname(abs).toLowerCase()\n const name = basename(abs).toLowerCase()\n if (!SKILL_EDITABLE_EXTENSIONS.has(ext) && !SKILL_EDITABLE_EXTENSIONS.has(name)) {\n throw new Error(`File type not editable: ${relPath}`)\n }\n if (statSync(abs).size > MAX_FILE_BYTES) {\n throw new Error(`File too large to edit (> ${MAX_FILE_BYTES / 1024} KB): ${relPath}`)\n }\n return { path: relPath, content: readFileSync(abs, \"utf-8\") }\n}\n\n/** Write any allowed file inside a skill dir. Creates parents. */\nexport function writeSkillFile(\n workspace: string,\n slug: string,\n relPath: string,\n content: string,\n): { path: string; bytes: number } {\n if (!/^[a-z0-9][a-z0-9_-]*$/.test(slug)) throw new Error(`Invalid skill slug: ${slug}`)\n if (!relPath || relPath.includes(\"..\")) throw new Error(`Invalid file path: ${relPath}`)\n const abs = resolveInWorkspace(workspace, `.claude/skills/${slug}/${relPath}`)\n const ext = extname(abs).toLowerCase()\n const name = basename(abs).toLowerCase()\n if (!SKILL_EDITABLE_EXTENSIONS.has(ext) && !SKILL_EDITABLE_EXTENSIONS.has(name)) {\n throw new Error(`File type not editable: ${relPath}`)\n }\n const buf = Buffer.from(content, \"utf-8\")\n if (buf.length > MAX_FILE_BYTES) {\n throw new Error(`File too large (${buf.length} > ${MAX_FILE_BYTES} bytes).`)\n }\n mkdirSync(dirname(abs), { recursive: true })\n writeFileSync(abs, buf)\n return { path: relPath, bytes: buf.length }\n}\n\n/** Detect which dependency manager a skill needs based on the files present. */\nexport function detectSkillDeps(workspace: string, slug: string): SkillDepsHint {\n if (!/^[a-z0-9][a-z0-9_-]*$/.test(slug)) throw new Error(`Invalid skill slug: ${slug}`)\n const skillRoot = resolveInWorkspace(workspace, `.claude/skills/${slug}`)\n if (!existsSync(skillRoot)) throw new Error(`Skill \"${slug}\" not found.`)\n const check = (name: string): boolean => existsSync(join(skillRoot, name))\n if (check(\"pnpm-lock.yaml\")) return { manager: \"pnpm\", file: \"pnpm-lock.yaml\", command: \"pnpm install\" }\n if (check(\"yarn.lock\")) return { manager: \"yarn\", file: \"yarn.lock\", command: \"yarn install\" }\n if (check(\"package.json\")) return { manager: \"npm\", file: \"package.json\", command: \"npm install\" }\n if (check(\"requirements.txt\")) return { manager: \"pip\", file: \"requirements.txt\", command: \"pip install -r requirements.txt\" }\n if (check(\"Gemfile\")) return { manager: \"bundle\", file: \"Gemfile\", command: \"bundle install\" }\n return { manager: null, file: null, command: null }\n}\n\nfunction defaultSkillTemplate(slug: string, title?: string): string {\n const displayTitle = title?.trim() || slug\n return `# ${displayTitle}\n\nDescribe what this skill does and when the agent should use it. Keep it under ~5 lines — the agent reads this to decide whether to invoke the skill.\n\n## When to use\n- Trigger condition 1\n- Trigger condition 2\n\n## How to use\nDocument the arguments, commands, or files the skill expects. Example:\n\n\\`\\`\\`bash\n# Example invocation\nclaude-code ${basename(slug)} --arg value\n\\`\\`\\`\n`\n}\n"],"mappings":"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","names":["existsSync","readdirSync","readFileSync","statSync","resolve","workflowHealth","workflows","runs","now","windowDays","span","byId","r","list","w","mine","recent","prior","failed","paused","lastRunAt","state","b","rank","cache","scanRuns","runsDir","limit","files","f","p","a","live","key","out","path","mtime","hit","summary","readSummary","lines","head","workflowId","status","i","tail","s","DEFAULT_ROUTINE_THRESHOLDS","ROUTINE_LIMITS","defaultNextFire","expression","after","timezone","getNextCronDate","DAY","buildRoutines","input","now","nextFire","out","job","runs","outcomes","r","latest","routine","cap","isoOrNull","capOpt","failStreak","scheduleFlags","finish","health","workflowHealth","w","h","runsByWorkflow","list","wf","trigger","n","cfg","state","enabled","a","b","workflowOutcome","isCron","spec","describeFilters","workflowAgent","s","o","changedAt","eventFlags","compareRoutines","t","flags","lastAt","disabled","disabledFlag","cursor","deadline","i","dormant","since","reasons","f","ATTENTION_RANK","ra","rb","status","node","filter","project","key","value","values","v","d","limit","readFileSync","writeFileSync","existsSync","readdirSync","statSync","resolve","join","existsSync","mkdirSync","readFileSync","writeFileSync","dirname","resolve","z","sourceSchema","itemSchema","fileSchema","BacklogStore","path","resolve","existsSync","p","raw","readFileSync","e","file","jsonP","mkdirSync","dirname","writeFileSync","id","i","input","now","item","inputs","existing","added","patch","idx","updated","before","lines","buckets","sections","key","label","it","check","mention","time","src","sourceBacklogId","source","resolveAssigneesForSource","agentIds","source","daemon","out","mappings","agentId","m","x","syncBacklogItemToSource","item","patch","log","syncToGitLab","syncToGitHub","gl","token","host","attempts","api","path","init","projectEnc","assigneeIds","u","res","arr","milestoneId","e","body","text","a","repo","milestoneNumber","found","nextLabels","issue","current","l","adds","r","CHECK_RE","MENTION_RE","TIME_RE","parseDuration","s","hMatch","mMatch","BacklogWorkSource","path","daemon","log","BacklogStore","full","resolve","existsSync","readFileSync","lines","writeFileSync","agentId","it","items","i","m","body","mention","timeMatch","title","_opts","itemId","item","updated","usernames","resolveAssigneesForSource","syncBacklogItemToSource","e","update","patch","sourcePatch","lineNo","line","note","stage","WikiWorkSource","root","glob","dir","out","entry","readdirSync","join","statSync","files","file","parts","GitLabWorkSource","host","token","projects","agentUsernames","username","names","issue","project","ns","labels","labelDetails","l","assignees","a","firstUsername","init","res","projectList","q","issues","iid","type","seg","mins","dur","opts","sinceDays","updatedAfter","params","state","result","deny","prefix","to","assigneeIds","users","x","milestoneId","ms","byUsername","members","b","byTitle","all","input","assignee_ids","u","milestone_id","toLabel","fromLabel","add","remove","createWorkSource","business","ws","gl","mapping","r","UNMAPPED","clientFromProject","project","projects","hit","p","slash","agentClients","business","out","orgChart","climb","agentId","seen","up","c","matchContact","channel","who","contactMap","pool","m","resolveClient","ref","contact","byAgent","parseWorkRef","sessionId","parts","agent","rest","target","KIND_DEFAULT","id","clientPolicy","parseDuration","listClients","ids","v","n","existsSync","join","DESKTOP_LABEL","DESKTOP_APP","HELPER_APP","desktopPlatformError","platform","arch","release","selectDesktopAgent","agents","requested","id","shellQuote","value","desktopPlist","opts","xml","env","k","v","helperBuildArgs","icon","installedHelper","home","resolveHelper","cwd","override","installed","execFile","existsSync","homedir","isAbsolute","DEFAULT_LOCAL","STEP_TIMEOUT_MS","run","file","args","done","execFile","err","localSettings","cfg","soundPath","name","path","existsSync","findHelper","resolveHelper","homedir","localAlert","settings","deps","exec","platform","banner","title","message","helper","steps","volume","patchLocal","current","patch","next","icon","isAbsolute","v","existsSync","readFileSync","writeFileSync","mkdirSync","readdirSync","rmSync","statSync","resolve","relative","dirname","join","extname","basename","MAX_FILE_BYTES","ALLOWED_EXTENSIONS","SKILL_EDITABLE_EXTENSIONS","SKILL_TREE_MAX_DEPTH","IDENTITY_FILENAMES","resolveInWorkspace","workspace","relPath","wsAbs","target","assertEditable","absPath","ext","extractTitle","markdown","fallback","line","l","listAgentFiles","identity","seen","rootEntries","name","abs","size","content","safeRead","st","skills","skillsRoot","slug","dir","file","stf","a","b","readAgentFile","writeAgentFile","buf","createAgentSkill","opts","body","defaultSkillTemplate","deleteAgentSkill","listSkillTree","skillRoot","out","walk","absDir","relDir","depth","acc","entries","rel","allowedByName","readSkillFile","writeSkillFile","detectSkillDeps","check","title"]}