@anyforge/control 0.10.0 → 0.12.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 (2) hide show
  1. package/dist/cli.js +182 -150
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1,76 +1,76 @@
1
1
  #!/usr/bin/env node
2
- var da=Object.defineProperty;var ua=(t,e,n)=>()=>{if(n)throw n[0];try{return t&&(e=t(t=0)),e}catch(r){throw n=[r],r}};var pa=(t,e)=>{for(var n in e)da(t,n,{get:e[n],enumerable:!0})};var Wn={};pa(Wn,{default:()=>qd});var qd,Gn=ua(()=>{qd={name:"@anyforge/control",version:"0.10.0",description:"AnyForge CLI \u2014 hosted Oracle conversations, a governed LLM proxy, and a scriptable client for approvals, crews, work items and workflows.",license:"Apache-2.0",homepage:"https://anyforge.ai",repository:{type:"git",url:"git+https://github.com/pootonline/sovereign-engineering.git",directory:"packages/control/cli"},publishConfig:{access:"public"},type:"module",main:"dist/cli.js",bin:{anyforge:"dist/cli.js",control:"dist/cli.js"},files:["dist","README.md"],scripts:{build:"esbuild src/cli.ts --bundle --minify --platform=node --format=esm --outfile=dist/cli.js --packages=external && chmod +x dist/cli.js","type-check":"tsc --noEmit",test:"npm run build && node scripts/test.mjs",dev:"tsx src/cli.ts",prepublishOnly:"npm run build","publish:beta":"npm publish --access public --tag beta","publish:stable":"npm publish --access public"},engines:{node:">=22.19.0"},dependencies:{"@agentclientprotocol/sdk":"^1.9.0","@earendil-works/pi-tui":"^1.0.2",kleur:"^4.1.5",open:"^11.0.4",prompts:"^2.4.2","proper-lockfile":"^4.1.2"},devDependencies:{"@types/node":"^22.20.0","@types/prompts":"^2.4.9","@types/proper-lockfile":"^4.1.4",esbuild:"^0.28.2","node-pty":"^1.1.0",tsx:"^4.7.0",typescript:"^7.0.2"}}});import{red as Hs,yellow as Ou,dim as ot,bold as D}from"kleur/colors";import{execFileSync as tr}from"child_process";import Oa from"open";import nr from"prompts";import{green as Nt,dim as M,bold as te,yellow as Ne,cyan as mn}from"kleur/colors";import{promises as un}from"fs";import{homedir as fa}from"os";import{join as co}from"path";var lo=co(fa(),".anyforge"),Xn=co(lo,"credentials");async function uo(t){await un.mkdir(lo,{recursive:!0,mode:448}),await un.writeFile(Xn,JSON.stringify(t,null,2),{mode:384})}async function ut(){try{let t=await un.readFile(Xn,"utf8");return JSON.parse(t)}catch(t){if(t.code==="ENOENT")return null;throw t}}async function po(){try{return await un.unlink(Xn),!0}catch(t){if(t.code==="ENOENT")return!1;throw t}}import{promises as U}from"fs";import{execFileSync as ma}from"child_process";import{homedir as Oe}from"os";import{join as Y}from"path";import pn from"prompts";import{dim as pe,green as er,yellow as fn}from"kleur/colors";var Le="# === AnyForge Control \u2014 added by `anyforge init` ===",Lt="# === end AnyForge Control ===",Ge=".anyforge-bak",ha=/claude_(max|pro|team|enterprise)/i,ga=["CLAUDE_CODE_ENABLE_TELEMETRY","OTEL_METRICS_EXPORTER","OTEL_EXPORTER_OTLP_PROTOCOL","OTEL_EXPORTER_OTLP_ENDPOINT","OTEL_EXPORTER_OTLP_HEADERS","OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE"];async function wo(t){let e=[],n=await wa(t);if(n&&e.push(n),t.scope==="repo"){let r=await ya(t);return r&&e.push(r),e}for(let r of ba){let o=await r.detect();if(!(!o||!(await pn({type:"confirm",name:"go",message:`Patch ${r.label} via shell rc (${pe(o.path)}) ?`,initial:!0})).go))try{await r.patch(o,t),e.push({label:r.label,path:o.path}),console.log(er(" \u2714")+" "+r.label)}catch(s){console.warn(" \u26A0 Skipped "+r.label+": "+s.message)}}return e}async function wa(t){let e=t.scope==="repo"&&t.repoRoot?{dir:Y(t.repoRoot,".claude"),file:"settings.local.json"}:{dir:Y(Oe(),".claude"),file:"settings.json"},n=Y(e.dir,e.file);if(!(await pn({type:"confirm",name:"go",message:`Patch Claude Code config at ${pe(n)} ?`,initial:!0})).go)return null;await U.mkdir(e.dir,{recursive:!0});let o=await U.readFile(n,"utf8").catch(()=>"{}"),i;try{i=JSON.parse(o)}catch{i={}}let s=i.env??{},a=s.ANTHROPIC_BASE_URL;return a&&!a.includes(t.endpoint)&&(a.includes("localhost")||a.includes("127.0.0.1")||a.includes("0.0.0.0"))&&(console.log(),console.log(fn(" \u26A0 Local LLM proxy detected: ")+pe(a)),console.log(pe(" AnyForge only routes Anthropic Claude models. Local models (Ollama,")),console.log(pe(" LM Studio, etc.) will not work in Claude Code after this patch.")),console.log(pe(" To restore: remove ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN")),console.log(pe(` from ${n}`)),console.log(),!(await pn({type:"confirm",name:"go",message:"Overwrite existing local LLM proxy config?",initial:!1})).go)?null:(await ge(n)&&!await ge(n+Ge)&&await U.copyFile(n,n+Ge),s.ANTHROPIC_BASE_URL=`${t.endpoint}/v1/anthropic`,s.ANTHROPIC_AUTH_TOKEN=t.apiKey,delete s.ANTHROPIC_API_KEY,i.env=s,await U.writeFile(n,JSON.stringify(i,null,2)+`
3
- `),console.log(er(" \u2714")+" Claude Code"),{label:t.scope==="repo"?"Claude Code (repo)":"Claude Code (global)",path:n})}async function yo(t){let e=Y(Oe(),".claude"),n=Y(e,"settings.json");await U.mkdir(e,{recursive:!0});let r=await U.readFile(n,"utf8").catch(()=>"{}"),o;try{o=JSON.parse(r)}catch{o={}}let i=o.env??{},s=i.OTEL_EXPORTER_OTLP_ENDPOINT;return s&&!Zn(s,ko(t.endpoint))?(console.log(fn(" \u26A0 An OpenTelemetry endpoint is already configured: ")+pe(s)),console.log(pe(" Leaving it untouched. To send Claude Code usage to AnyForge, point")),console.log(pe(" OTEL_EXPORTER_OTLP_ENDPOINT at "+t.endpoint+"/api/otel")),null):(await ge(n)&&!await ge(n+Ge)&&await U.copyFile(n,n+Ge),i.CLAUDE_CODE_ENABLE_TELEMETRY="1",i.OTEL_METRICS_EXPORTER="otlp",i.OTEL_EXPORTER_OTLP_PROTOCOL="http/json",i.OTEL_EXPORTER_OTLP_ENDPOINT=`${t.endpoint}/api/otel`,i.OTEL_EXPORTER_OTLP_HEADERS=`Authorization=Bearer ${t.apiKey}`,i.OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE="delta",o.env=i,await U.writeFile(n,JSON.stringify(o,null,2)+`
4
- `),{label:"Claude Code usage telemetry (global)",path:n})}async function ya(t){if(!t.repoRoot)return null;let e=Y(t.repoRoot,".env");if(!(await pn({type:"confirm",name:"go",message:`Write AnyForge env block to ${pe(e)} ?`,initial:!0})).go)return null;let r=[Le,`ANYFORGE_API_KEY=${t.apiKey}`,`ANTHROPIC_BASE_URL=${t.endpoint}/v1/anthropic`,`ANTHROPIC_AUTH_TOKEN=${t.apiKey}`,`OPENAI_BASE_URL=${t.endpoint}/v1/openai`,`OPENAI_API_KEY=${t.apiKey}`,Lt,""].join(`
5
- `),o="";try{o=await U.readFile(e,"utf8")}catch{}let i;return o.includes(Le)?i=o.replace(new RegExp(`${Le}[\\s\\S]*?${Lt}\\n?`),r):o.length===0?i=r:i=o.endsWith(`
2
+ var lc=Object.defineProperty;var dc=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(r){throw n=[r],r}};var uc=(e,t)=>{for(var n in t)lc(e,n,{get:t[n],enumerable:!0})};var ir={};uc(ir,{default:()=>Qu});var Qu,sr=dc(()=>{Qu={name:"@anyforge/control",version:"0.12.0",description:"AnyForge CLI \u2014 hosted Oracle conversations, a governed LLM proxy, and a scriptable client for approvals, crews, work items and workflows.",license:"Apache-2.0",homepage:"https://anyforge.ai",repository:{type:"git",url:"git+https://github.com/pootonline/sovereign-engineering.git",directory:"packages/control/cli"},publishConfig:{access:"public"},type:"module",main:"dist/cli.js",bin:{anyforge:"dist/cli.js",control:"dist/cli.js"},files:["dist","README.md"],scripts:{build:"esbuild src/cli.ts --bundle --minify --platform=node --format=esm --outfile=dist/cli.js --packages=external && chmod +x dist/cli.js","type-check":"tsc --noEmit",test:"npm run build && node scripts/test.mjs",dev:"tsx src/cli.ts",prepublishOnly:"npm run build","publish:beta":"npm publish --access public --tag beta","publish:stable":"npm publish --access public"},engines:{node:">=22.19.0"},dependencies:{"@agentclientprotocol/sdk":"^1.9.0","@earendil-works/pi-tui":"^1.0.2",kleur:"^4.1.5",open:"^11.0.4",prompts:"^2.4.2","proper-lockfile":"^4.1.2"},devDependencies:{"@types/node":"^22.20.0","@types/prompts":"^2.4.9","@types/proper-lockfile":"^4.1.4",esbuild:"^0.28.2","node-pty":"^1.1.0",tsx:"^4.7.0",typescript:"^7.0.2"}}});import{red as Ga,yellow as Mp,dim as Je,bold as W}from"kleur/colors";import{execFileSync as wr}from"child_process";import kc from"open";import xn from"prompts";import{green as Ht,dim as D,bold as oe,yellow as De,cyan as Sn}from"kleur/colors";import{promises as kn}from"fs";import{homedir as pc}from"os";import{join as Mo}from"path";var Uo=Mo(pc(),".anyforge"),mr=Mo(Uo,"credentials");async function jo(e){await kn.mkdir(Uo,{recursive:!0,mode:448}),await kn.writeFile(mr,JSON.stringify(e,null,2),{mode:384})}async function bt(){try{let e=await kn.readFile(mr,"utf8");return JSON.parse(e)}catch(e){if(e.code==="ENOENT")return null;throw e}}async function Do(){try{return await kn.unlink(mr),!0}catch(e){if(e.code==="ENOENT")return!1;throw e}}import{promises as j}from"fs";import{execFileSync as fc}from"child_process";import{homedir as Se}from"os";import{join as J}from"path";import On from"prompts";import{dim as me,green as gr,yellow as An}from"kleur/colors";var je="# === AnyForge Control \u2014 added by `anyforge init` ===",Gt="# === end AnyForge Control ===",Qe=".anyforge-bak",mc=/claude_(max|pro|team|enterprise)/i,hc=["CLAUDE_CODE_ENABLE_TELEMETRY","OTEL_METRICS_EXPORTER","OTEL_EXPORTER_OTLP_PROTOCOL","OTEL_EXPORTER_OTLP_ENDPOINT","OTEL_EXPORTER_OTLP_HEADERS","OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE"];async function zo(e){let t=[],n=await gc(e);if(n&&t.push(n),e.scope==="repo"){let r=await wc(e);return r&&t.push(r),t}for(let r of vc){let o=await r.detect();if(!(!o||!(await On({type:"confirm",name:"go",message:`Patch ${r.label} via shell rc (${me(o.path)}) ?`,initial:!0})).go))try{await r.patch(o,e),t.push({label:r.label,path:o.path}),console.log(gr(" \u2714")+" "+r.label)}catch(s){console.warn(" \u26A0 Skipped "+r.label+": "+s.message)}}return t}async function gc(e){let t=e.scope==="repo"&&e.repoRoot?{dir:J(e.repoRoot,".claude"),file:"settings.local.json"}:{dir:J(Se(),".claude"),file:"settings.json"},n=J(t.dir,t.file);if(!(await On({type:"confirm",name:"go",message:`Patch Claude Code config at ${me(n)} ?`,initial:!0})).go)return null;await j.mkdir(t.dir,{recursive:!0});let o=await j.readFile(n,"utf8").catch(()=>"{}"),i;try{i=JSON.parse(o)}catch{i={}}let s=i.env??{},a=s.ANTHROPIC_BASE_URL;return a&&!a.includes(e.endpoint)&&(a.includes("localhost")||a.includes("127.0.0.1")||a.includes("0.0.0.0"))&&(console.log(),console.log(An(" \u26A0 Local LLM proxy detected: ")+me(a)),console.log(me(" AnyForge only routes Anthropic Claude models. Local models (Ollama,")),console.log(me(" LM Studio, etc.) will not work in Claude Code after this patch.")),console.log(me(" To restore: remove ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN")),console.log(me(` from ${n}`)),console.log(),!(await On({type:"confirm",name:"go",message:"Overwrite existing local LLM proxy config?",initial:!1})).go)?null:(await ve(n)&&!await ve(n+Qe)&&await j.copyFile(n,n+Qe),s.ANTHROPIC_BASE_URL=`${e.endpoint}/v1/anthropic`,s.ANTHROPIC_AUTH_TOKEN=e.apiKey,delete s.ANTHROPIC_API_KEY,i.env=s,await j.writeFile(n,JSON.stringify(i,null,2)+`
3
+ `),console.log(gr(" \u2714")+" Claude Code"),{label:e.scope==="repo"?"Claude Code (repo)":"Claude Code (global)",path:n})}async function Ko(e){let t=J(Se(),".claude"),n=J(t,"settings.json");await j.mkdir(t,{recursive:!0});let r=await j.readFile(n,"utf8").catch(()=>"{}"),o;try{o=JSON.parse(r)}catch{o={}}let i=o.env??{},s=i.OTEL_EXPORTER_OTLP_ENDPOINT;return s&&!hr(s,Vo(e.endpoint))?(console.log(An(" \u26A0 An OpenTelemetry endpoint is already configured: ")+me(s)),console.log(me(" Leaving it untouched. To send Claude Code usage to AnyForge, point")),console.log(me(" OTEL_EXPORTER_OTLP_ENDPOINT at "+e.endpoint+"/api/otel")),null):(await ve(n)&&!await ve(n+Qe)&&await j.copyFile(n,n+Qe),i.CLAUDE_CODE_ENABLE_TELEMETRY="1",i.OTEL_METRICS_EXPORTER="otlp",i.OTEL_LOGS_EXPORTER===void 0&&i.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT===void 0&&(i.OTEL_LOGS_EXPORTER="otlp",e.toolDetails&&i.OTEL_LOG_TOOL_DETAILS===void 0&&(i.OTEL_LOG_TOOL_DETAILS="1")),i.OTEL_EXPORTER_OTLP_PROTOCOL="http/json",i.OTEL_EXPORTER_OTLP_ENDPOINT=`${e.endpoint}/api/otel`,i.OTEL_EXPORTER_OTLP_HEADERS=`Authorization=Bearer ${e.apiKey}`,i.OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE="delta",o.env=i,await j.writeFile(n,JSON.stringify(o,null,2)+`
4
+ `),{label:"Claude Code usage telemetry (global)",path:n})}async function wc(e){if(!e.repoRoot)return null;let t=J(e.repoRoot,".env");if(!(await On({type:"confirm",name:"go",message:`Write AnyForge env block to ${me(t)} ?`,initial:!0})).go)return null;let r=[je,`ANYFORGE_API_KEY=${e.apiKey}`,`ANTHROPIC_BASE_URL=${e.endpoint}/v1/anthropic`,`ANTHROPIC_AUTH_TOKEN=${e.apiKey}`,`OPENAI_BASE_URL=${e.endpoint}/v1/openai`,`OPENAI_API_KEY=${e.apiKey}`,Gt,""].join(`
5
+ `),o="";try{o=await j.readFile(t,"utf8")}catch{}let i;return o.includes(je)?i=o.replace(new RegExp(`${je}[\\s\\S]*?${Gt}\\n?`),r):o.length===0?i=r:i=o.endsWith(`
6
6
  `)?`${o}${r}`:`${o}
7
- ${r}`,i!==o&&await U.writeFile(e,i),await va(t.repoRoot),console.log(er(" \u2714")+" .env"),{label:".env (repo)",path:e}}async function va(t){let e=Y(t,".gitignore");try{(await U.readFile(e,"utf8")).split(`
8
- `).map(i=>i.trim()).some(i=>i===".env"||i==="/.env"||i==="*.env")||console.log(fn(" \u26A0 .env is not in .gitignore \u2014 add it before committing."))}catch{console.log(fn(" \u26A0 No .gitignore found \u2014 make sure .env is excluded before committing."))}}var ba=[{label:"Cursor (shell hint)",async detect(){let t=Y(Oe(),".cursor");return await ge(t)?{path:".cursor (shell hint)"}:null},async patch(t,e){await fo(e)}},{label:"Cline / Continue (shell hint)",async detect(){let t=await ge(Y(Oe(),".cline")),e=await ge(Y(Oe(),".continue"));return t||e?{path:"~/.cline or ~/.continue (shell hint)"}:null},async patch(t,e){await fo(e)}}];async function fo(t){let e=[Le,`export ANYFORGE_API_KEY="${t.apiKey}"`,`export ANTHROPIC_BASE_URL="${t.endpoint}/v1/anthropic"`,`export ANTHROPIC_AUTH_TOKEN="${t.apiKey}"`,`export OPENAI_BASE_URL="${t.endpoint}/v1/openai"`,`export OPENAI_API_KEY="${t.apiKey}"`,"unset ANTHROPIC_API_KEY 2>/dev/null || true",Lt,""].join(`
9
- `);for(let n of[".zshrc",".bashrc"]){let r=Y(Oe(),n);if(!await ge(r))continue;let o=await U.readFile(r,"utf8"),i;o.includes(Le)?i=o.replace(new RegExp(`${Le}[\\s\\S]*?${Lt}\\n?`),e):i=o.endsWith(`
7
+ ${r}`,i!==o&&await j.writeFile(t,i),await yc(e.repoRoot),console.log(gr(" \u2714")+" .env"),{label:".env (repo)",path:t}}async function yc(e){let t=J(e,".gitignore");try{(await j.readFile(t,"utf8")).split(`
8
+ `).map(i=>i.trim()).some(i=>i===".env"||i==="/.env"||i==="*.env")||console.log(An(" \u26A0 .env is not in .gitignore \u2014 add it before committing."))}catch{console.log(An(" \u26A0 No .gitignore found \u2014 make sure .env is excluded before committing."))}}var vc=[{label:"Cursor (shell hint)",async detect(){let e=J(Se(),".cursor");return await ve(e)?{path:".cursor (shell hint)"}:null},async patch(e,t){await Bo(t)}},{label:"Cline / Continue (shell hint)",async detect(){let e=await ve(J(Se(),".cline")),t=await ve(J(Se(),".continue"));return e||t?{path:"~/.cline or ~/.continue (shell hint)"}:null},async patch(e,t){await Bo(t)}}];async function Bo(e){let t=[je,`export ANYFORGE_API_KEY="${e.apiKey}"`,`export ANTHROPIC_BASE_URL="${e.endpoint}/v1/anthropic"`,`export ANTHROPIC_AUTH_TOKEN="${e.apiKey}"`,`export OPENAI_BASE_URL="${e.endpoint}/v1/openai"`,`export OPENAI_API_KEY="${e.apiKey}"`,"unset ANTHROPIC_API_KEY 2>/dev/null || true",Gt,""].join(`
9
+ `);for(let n of[".zshrc",".bashrc"]){let r=J(Se(),n);if(!await ve(r))continue;let o=await j.readFile(r,"utf8"),i;o.includes(je)?i=o.replace(new RegExp(`${je}[\\s\\S]*?${Gt}\\n?`),t):i=o.endsWith(`
10
10
  `)?`${o}
11
- ${e}`:`${o}
11
+ ${t}`:`${o}
12
12
 
13
- ${e}`,i!==o&&await U.writeFile(r,i)}}async function ge(t){try{return await U.access(t),!0}catch{return!1}}async function vo(){try{let t=await U.readFile(Y(Oe(),".claude.json"),"utf8"),n=JSON.parse(t).oauthAccount,r=n?.organizationType??"";if(n&&ha.test(r))return{isSubscription:!0,plan:/max/i.test(r)?"Max":/pro/i.test(r)?"Pro":/enterprise/i.test(r)?"Enterprise":/team/i.test(r)?"Team":r,email:n.emailAddress??null}}catch{}return{isSubscription:!1,plan:null,email:null}}async function bo(t={}){let e=t.endpoint?ko(t.endpoint):null,n=[],r=Y(Oe(),".claude","settings.json");await mo(r,e)&&n.push(r);for(let i of[".zshrc",".bashrc"]){let s=Y(Oe(),i);await ho(s)&&n.push(s)}let o=t.repoRoot??ka(process.cwd());if(o){let i=Y(o,".claude","settings.local.json");await mo(i,e)&&n.push(i);let s=Y(o,".env");await ho(s)&&n.push(s)}return{reverted:n}}function Zn(t,e){return t?t.includes("anyforge")?!0:e?t.includes(e):!1:!1}async function mo(t,e){if(!await ge(t))return!1;if(await ge(t+Ge))return await U.copyFile(t+Ge,t),await U.unlink(t+Ge).catch(()=>{}),!0;let n;try{n=JSON.parse(await U.readFile(t,"utf8"))}catch{return!1}let r=n.env;if(!r)return!1;let o=!1,i=[["ANTHROPIC_BASE_URL","ANTHROPIC_AUTH_TOKEN"],["OPENAI_BASE_URL","OPENAI_API_KEY"]];for(let[s,a]of i)Zn(r[s],e)&&(delete r[s],delete r[a],o=!0);if(r.ANYFORGE_API_KEY&&(delete r.ANYFORGE_API_KEY,o=!0),Zn(r.OTEL_EXPORTER_OTLP_ENDPOINT,e))for(let s of ga)s in r&&(delete r[s],o=!0);return o?(Object.keys(r).length===0&&delete n.env,await U.writeFile(t,JSON.stringify(n,null,2)+`
14
- `),!0):!1}async function ho(t){let e;try{e=await U.readFile(t,"utf8")}catch{return!1}if(!e.includes(Le))return!1;let n=e.replace(new RegExp(`\\n?${go(Le)}[\\s\\S]*?${go(Lt)}\\n?`),`
13
+ ${t}`,i!==o&&await j.writeFile(r,i)}}async function ve(e){try{return await j.access(e),!0}catch{return!1}}async function Yo(){try{let e=await j.readFile(J(Se(),".claude.json"),"utf8"),n=JSON.parse(e).oauthAccount,r=n?.organizationType??"";if(n&&mc.test(r))return{isSubscription:!0,plan:/max/i.test(r)?"Max":/pro/i.test(r)?"Pro":/enterprise/i.test(r)?"Enterprise":/team/i.test(r)?"Team":r,email:n.emailAddress??null}}catch{}return{isSubscription:!1,plan:null,email:null}}async function Jo(e={}){let t=e.endpoint?Vo(e.endpoint):null,n=[],r=J(Se(),".claude","settings.json");await Wo(r,t)&&n.push(r);for(let i of[".zshrc",".bashrc"]){let s=J(Se(),i);await Go(s)&&n.push(s)}let o=e.repoRoot??bc(process.cwd());if(o){let i=J(o,".claude","settings.local.json");await Wo(i,t)&&n.push(i);let s=J(o,".env");await Go(s)&&n.push(s)}return{reverted:n}}function hr(e,t){return e?e.includes("anyforge")?!0:t?e.includes(t):!1:!1}async function Wo(e,t){if(!await ve(e))return!1;if(await ve(e+Qe))return await j.copyFile(e+Qe,e),await j.unlink(e+Qe).catch(()=>{}),!0;let n;try{n=JSON.parse(await j.readFile(e,"utf8"))}catch{return!1}let r=n.env;if(!r)return!1;let o=!1,i=[["ANTHROPIC_BASE_URL","ANTHROPIC_AUTH_TOKEN"],["OPENAI_BASE_URL","OPENAI_API_KEY"]];for(let[s,a]of i)hr(r[s],t)&&(delete r[s],delete r[a],o=!0);if(r.ANYFORGE_API_KEY&&(delete r.ANYFORGE_API_KEY,o=!0),hr(r.OTEL_EXPORTER_OTLP_ENDPOINT,t)){for(let s of hc)s in r&&(delete r[s],o=!0);r.OTEL_LOGS_EXPORTER==="otlp"&&(delete r.OTEL_LOGS_EXPORTER,r.OTEL_LOG_TOOL_DETAILS==="1"&&delete r.OTEL_LOG_TOOL_DETAILS,o=!0)}return o?(Object.keys(r).length===0&&delete n.env,await j.writeFile(e,JSON.stringify(n,null,2)+`
14
+ `),!0):!1}async function Go(e){let t;try{t=await j.readFile(e,"utf8")}catch{return!1}if(!t.includes(je))return!1;let n=t.replace(new RegExp(`\\n?${Ho(je)}[\\s\\S]*?${Ho(Gt)}\\n?`),`
15
15
  `).replace(/\n{3,}/g,`
16
16
 
17
- `);return await U.writeFile(t,n),!0}function go(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function ko(t){try{return new URL(t).host}catch{return null}}function ka(t){try{return ma("git",["rev-parse","--show-toplevel"],{cwd:t,encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim()}catch{return null}}var Aa=2e3,_a=900*1e3;async function Ao(t){let{endpoint:e}=t;console.log(te("AnyForge Control \u2014 device authorisation")),console.log(M(` endpoint: ${e}`));let n=process.cwd(),r=Oo(n),o=r?Ta(r):null,s=await(await fetch(`${e}/api/cli/init`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({repo:o})})).json();if(!s.ok||!s.data)throw new Error(`Could not start the authorisation flow: ${s.error??"unknown error"}`);let{code:a,authUrl:c,pollUrl:d}=s.data;console.log(),console.log("Open this URL in your browser to authorise:"),console.log(` ${mn(c)}`),console.log(M(` Code: ${a}`)),console.log();try{await Oa(c)}catch{}let{credentials:u,modelRouting:l}=await xa({pollUrl:d,endpoint:e});if(await uo(u),console.log(Nt("\u2714")+" API key saved to "+M("~/.anyforge/credentials")),!l){await Sa(e,u.apiKey);return}let k=await vo();if(k.isSubscription&&!t.force){await Ia(k,e,u.apiKey);return}let O=process.cwd(),x=Oo(O),b=await Ea(x),m=await wo({endpoint:e,apiKey:u.apiKey,scope:b,repoRoot:x});m.length===0?(console.log(Ne("No agent configs were patched.")),console.log(M(" Set ANTHROPIC_BASE_URL="+e+"/v1/anthropic and")),console.log(M(" ANTHROPIC_AUTH_TOKEN=<your AnyForge key> manually if needed."))):Ca(m),console.log(),k.isSubscription&&t.force&&(console.log(Ne(` \u26A0 --force: your Claude ${k.plan??""} subscription login is now shadowed`)),console.log(Ne(" by the AnyForge token \u2014 Claude Code will use the BYOK proxy, which")),console.log(Ne(" needs an Anthropic API key on your company. Run ")+te("anyforge logout")),console.log(Ne(" to revert and restore your subscription."))),b==="repo"&&(console.log(M(" Re-run this command in another repo to patch it too,")),console.log(M(' or run from any directory and choose "global" to install system-wide.'))),console.log(te("Done.")+" Run `anyforge status` to check identity, local setup and recorded activity."),console.log(" MCP access, BYOK inference and optional subscription telemetry are separate. Usage: "+mn(`${e}/usage`))}async function xa(t){let e=Date.now();process.stdout.write("Waiting for browser authorisation\u2026 ");let n=0;for(;Date.now()-e<_a;){await new Promise(i=>setTimeout(i,Aa)),process.stdout.write("."),n+=1,n>40&&(process.stdout.write(`
18
- `),n=0);let o=await(await fetch(t.pollUrl)).json();if(o.status==="authorized"&&o.data)return process.stdout.write(" "+Nt("done")+`
19
- `),{credentials:{apiKey:o.data.apiKey,companyId:o.data.companyId,endpoint:t.endpoint,savedAt:new Date().toISOString()},modelRouting:o.data.modelRouting!==!1};if(o.status==="expired"||o.status==="not_found")throw process.stdout.write(`
20
- `),new Error(`Authorisation ${o.status}. Re-run \`anyforge init\`.`)}throw new Error("Authorisation timed out (15 min). Re-run `anyforge init`.")}function Ra(t,e){return["mcp","add","anyforge","--env",`ANYFORGE_API_TOKEN=${e}`,"--env",`ANYFORGE_API_URL=${t}`,"--","npx","-y","@anyforge/mcp-server"]}async function Ia(t,e,n){let r=t.email?` (${t.email})`:"";if(console.log(),console.log(Ne(` \u26A0 Claude ${t.plan??""} subscription login detected${r}.`)),console.log(),console.log(" AnyForge Control\u2019s inference proxy is "+te("BYOK-only")+" \u2014 it routes"),console.log(" Claude Code through your own Anthropic API key for metering and"),console.log(" governance. Pointing Claude Code at the proxy would "+te("override and")),console.log(" "+te("disable your subscription login")+", so we did NOT patch your config."),console.log(),console.log(" Instead, use your subscription for inference and add the "+te("AnyForge")),console.log(" "+te("MCP server")+" for control (HIL approvals, work items, crews):"),console.log(),await _o(e,n),console.log(),(await nr({type:"confirm",name:"go",message:"Send Claude Code usage to your AnyForge dashboard? (token counts only \u2014 no prompt content)",initial:!0})).go){let i=await yo({endpoint:e,apiKey:n});i&&(console.log(Nt(" \u2714")+" Usage telemetry enabled \u2014 view it at "+mn(`${e}/usage`)),console.log(M(" (written to "+i.path+"; `anyforge logout` removes it)")))}else console.log(M(" Skipped \u2014 your subscription usage stays local to Claude Code."));console.log(),console.log(M(" Want the metered BYOK proxy instead? Re-run with ")+te("--force")+M(" using an")),console.log(M(" Anthropic API key on your company (this disables your subscription in")),console.log(M(" Claude Code; ")+te("anyforge logout")+M(" reverts it).")),console.log(),console.log(te("Done.")+" Run `anyforge status` to check identity, local setup and recorded activity."),console.log(" MCP access, BYOK inference and optional subscription telemetry are separate. Usage: "+mn(`${e}/usage`))}async function Sa(t,e){console.log(),console.log(Ne(" This key is read-only")+M(" (Viewer role).")),console.log(" It reads programs, initiatives, work items and the strategy through the"),console.log(" "+te("AnyForge MCP server")+". Routing model calls through the company's keys"),console.log(" needs the Editor role, so your Claude Code config was "+te("not")+" patched."),console.log(),await _o(t,e),console.log(),console.log(te("Done.")+" Run `anyforge status` to check identity and local setup.")}async function _o(t,e){let n=Ra(t,e),r="claude mcp add anyforge -- npx -y @anyforge/mcp-server";if((await nr({type:"confirm",name:"go",message:"Add the AnyForge MCP server to Claude Code now?",initial:!0})).go)try{tr("claude",n,{stdio:"inherit"}),console.log(Nt(" \u2714")+" AnyForge MCP server added to Claude Code.")}catch{console.log(Ne(" Could not run `claude mcp add` automatically (is the `claude` CLI on PATH?).")),console.log(M(" Add it manually:")),console.log(" "+r)}else console.log(M(" Add it later with:")),console.log(" "+r)}function Oo(t){try{return tr("git",["rev-parse","--show-toplevel"],{cwd:t,encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim()}catch{return null}}async function Ea(t){return t?(await nr({type:"select",name:"scope",message:"Where should AnyForge patch Claude Code?",choices:[{title:`This repo only (${t}/.claude/settings.local.json + .env)`,value:"repo"},{title:"Globally (~/.claude/settings.json)",value:"global"}],initial:0})).scope??"repo":(console.log(M(" Not a git repo root \u2014 installing globally.")),console.log(M(" Run this from a repo root if you want a per-repo install instead.")),"global")}function Ca(t){console.log(Nt("\u2714")+" Patched "+t.length+" agent config"+(t.length===1?"":"s")+":");for(let e of t)console.log(` ${M("\u2022")} ${e.label} ${M("("+e.path+")")}`)}function Ta(t){try{let e=tr("git",["remote","get-url","origin"],{cwd:t,encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim(),n=e.match(/^git@[\w.-]+:([\w.-]+\/[\w.-]+?)(?:\.git)?$/);if(n)return n[1];let r=e.match(/^https?:\/\/[\w.-]+\/([\w.-]+\/[\w.-]+?)(?:\.git)?$/);return r?r[1]:null}catch{return null}}import{dim as ir,yellow as Ua,bold as Ma}from"kleur/colors";var g={OK:0,FAILURE:1,USAGE:2,NO_CREDENTIALS:3,API_ERROR:4,VERIFICATION_FAILED:5,INTERRUPTED:130},f=class extends Error{exitCode;hint;constructor(e,n=g.FAILURE,r){super(e),this.name="CliError",this.exitCode=n,this.hint=r}},h=class extends f{constructor(e,n){super(e,g.USAGE,n),this.name="UsageError"}},we=class extends f{constructor(){super("Not authenticated \u2014 no ANYFORGE_API_TOKEN and no saved credential.",g.NO_CREDENTIALS,"Run `npx @anyforge/control init`, or set ANYFORGE_API_TOKEN for unattended use."),this.name="NotAuthenticatedError"}},xo=4e3,oe=class extends f{code;status;details;constructor(e,n,r,o,i){super($a(e,r,o),g.API_ERROR,Pa(e,n,i)),this.name="ControlApiError",this.code=e,this.status=n,o&&(this.details=o)}};function $a(t,e,n){let r=e?`${t}: ${e}`:t;if(!n)return r;let o=JSON.stringify(n),i=o.length>xo?`${o.slice(0,xo)}\u2026(truncated)`:o;return`${r}
21
- ${i}`}function Pa(t,e,n){if(t==="api_key_expired")return`This API key has expired. Mint a new key in Settings \u2192 API keys, then re-run \`npx @anyforge/control init\` or update ANYFORGE_API_TOKEN. See ${n?`${n.replace(/\/$/,"")}/docs?page=service-accounts-and-key-expiry`:'the "Service accounts and API key expiry" guide'}.`;if(t==="missing_api_key"||t==="invalid_api_key"||t==="invalid_token"||e===401)return"The key was rejected. Re-run `npx @anyforge/control init`.";if(t==="insufficient_scope"||e===403)return"This key lacks the scope for that command. Run `anyforge whoami` to see its scopes.";if(e===404)return"No such id in this company. Check it with the matching list command.";if(e===429)return"Rate limited. Retry after a short pause."}var Ro="https://crew.anyforge.ai";async function qe(t){let e=process.env.ANYFORGE_API_TOKEN,n=t||process.env.ANYFORGE_API_URL;if(e)return{apiKey:e,endpoint:n||Ro};let r=await ut();if(r?.apiKey)return{apiKey:r.apiKey,endpoint:n||r.endpoint||Ro};throw new we}var rr=null;function Io(t){rr=t&&/^[a-z][a-z0-9-]*$/.test(t)?`anyforge ${t}`:null}async function I(t,e,n,r){let o=`${t.endpoint.replace(/\/$/,"")}/api/v1/control${n}`,i;try{i=await fetch(o,{method:e,headers:{"Content-Type":"application/json",Authorization:`Bearer ${t.apiKey}`,"X-AnyForge-Client":"anyforge-cli",...rr?{"X-AnyForge-Tool":rr}:{}},...r!==void 0?{body:JSON.stringify(r)}:{}})}catch(a){throw new f(`Cannot reach ${t.endpoint}: ${a instanceof Error?a.message:String(a)}`,g.FAILURE,"Check your network, or pass --endpoint if this workspace is self-hosted.")}let s;try{s=await i.json()}catch{throw new f(`Non-JSON response from ${o} (HTTP ${i.status}).`,g.FAILURE)}if(!i.ok||s.ok===!1){let a=typeof s.error=="string"?s.error:`http_${i.status}`,c=typeof s.message=="string"?s.message:typeof s.detail=="string"?s.detail:void 0;throw new oe(a,i.status,c,Na(s),t.endpoint)}return s.data}var La=new Set(["ok","error","message","detail"]);function Na(t){if(!t||typeof t!="object"||Array.isArray(t))return;let e=Object.entries(t).filter(([n,r])=>!La.has(n)&&r!==void 0);return e.length?Object.fromEntries(e):void 0}function H(t){let e=Object.entries(t).filter(([,n])=>n!==void 0&&n!=="").map(([n,r])=>`${encodeURIComponent(n)}=${encodeURIComponent(String(r))}`);return e.length?`?${e.join("&")}`:""}async function qt(t){return I(t,"GET","/me")}function or(t,e,n,r){let o=t?.entityLabels?.[e]?.[n];return typeof o=="string"&&o.trim()?o:r}async function So(t){try{return await qt(t)}catch{return null}}function Eo(t){return[...t].length}function pt(t,e){if(e<=0)return"";let n=[...t];return n.length<=e?t:e===1?"\u2026":`${n.slice(0,e-1).join("")}\u2026`}function B(t,e){if(t.length===0)return"";let n=t.map(i=>e.map(s=>{let a=s.value(i)??"";return s.max?pt(a,s.max):a})),r=e.map((i,s)=>Math.max(Eo(i.header),...n.map(a=>Eo(a[s])))),o=i=>i.map((s,a)=>a===i.length-1?s:s.padEnd(r[a]+2)).join("").replace(/\s+$/,"");return[o(e.map(i=>i.header)),...n.map(o)].join(`
22
- `)}function S(t){return JSON.stringify(t,null,2)}function Co(t,e=Date.now()){let n=qa(t);if(n===null)return"\u2014";let r=Math.max(0,Math.floor((e-n)/1e3));if(r<60)return`${r}s`;let o=Math.floor(r/60);if(o<60)return`${o}m`;let i=Math.floor(o/60);return i<48?`${i}h`:`${Math.floor(i/24)}d`}function qa(t){if(typeof t=="number")return Number.isFinite(t)?t:null;if(typeof t=="string"){let e=Date.parse(t);return Number.isNaN(e)?null:e}return null}function E(t,e,n="\u2014"){let r=t[e];return typeof r=="string"&&r.trim()?r:typeof r=="number"&&Number.isFinite(r)?String(r):n}function Z(t,e,n="\u2014"){for(let r of e){let o=t[r];if(typeof o=="string"&&o.trim())return o}return n}function le(t){let e=Array.isArray(t)?t:t&&typeof t=="object"?Fa(t):null;return e?e.filter(n=>!!n&&typeof n=="object"&&!Array.isArray(n)):[]}function Fa(t){for(let e of["items","results","rows","data"])if(Array.isArray(t[e]))return t[e];return null}function hn(t,e,n){let r=t?`last ${t}d`:"all time";return!e||!n?r:`last ${Math.max(1,Math.round((Date.now()-n)/864e5))}d of ${t?`${t}d`:"all time"} \u2014 read cap`}async function To(t){let e=await qe(t.endpoint),n=await qt(e);if(t.json){console.log(S(n));return}console.log(`Company: ${Ma(n.companyName??n.companyId)}${n.companyName?ir(` (${n.companyId})`):""}`),console.log(`Endpoint: ${e.endpoint}`),console.log(`Scopes: ${n.scopes.length?n.scopes.join(", "):Ua("none")}`),n.userId&&console.log(`User: ${n.userId}`);let r=await ut();console.log(r?ir(`Key tail: \u2026${r.apiKey.slice(-4)} \xB7 saved ${new Date(r.savedAt).toLocaleString()}`):ir("Key from: ANYFORGE_API_TOKEN (no saved credential file)"))}import{green as $o,dim as gn,yellow as ja}from"kleur/colors";async function Po(){let t=await ut(),e=await po(),{reverted:n}=await bo({endpoint:t?.endpoint??null});if(console.log(e?$o("\u2714")+" Local credentials cleared.":ja("No credentials were saved on this machine.")),n.length){console.log($o("\u2714")+" Reverted patched configs:");for(let r of n)console.log(` ${gn("\u2022")} ${r}`);console.log(gn(" Your previous Claude Code login (if any) is restored."))}else console.log(gn(" No AnyForge config patches found to revert."));console.log(gn(" Revoke the key server-side at /settings \u2192 API keys."))}import{readFile as wn}from"node:fs/promises";import{homedir as Da}from"node:os";import{dirname as sr,join as Ae}from"node:path";function Fe(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)?t:{}}async function Lo(t,e={home:Da(),cwd:process.cwd(),env:process.env}){let n=t.endpoint.replace(/\/$/,""),r=[],o={};try{o=Fe(JSON.parse(await wn(Ae(e.home,".anyforge","credentials"),"utf8")))}catch{}function i(a,c){let d=Fe(c),u=Fe(d.env),l=Object.values(Fe(d.mcpServers));r.push({source:a,readable:!0,mcp:l.some(k=>{let O=Fe(k),x=Fe(O.env),b=Array.isArray(O.args)?O.args:[],m=x.ANYFORGE_API_TOKEN??e.env.ANYFORGE_API_TOKEN,_=x.ANYFORGE_API_URL??e.env.ANYFORGE_API_URL,$=m??o.apiKey,j=_??(m?"https://crew.anyforge.ai":o.endpoint)??"https://crew.anyforge.ai";return b.includes("@anyforge/mcp-server")&&$===t.apiKey&&j===n}),proxy:u.ANTHROPIC_BASE_URL===`${n}/v1/anthropic`&&u.ANTHROPIC_AUTH_TOKEN===t.apiKey,subscriptionTelemetry:String(u.CLAUDE_CODE_ENABLE_TELEMETRY)==="1"&&(u.OTEL_EXPORTER_OTLP_METRICS_PROTOCOL??u.OTEL_EXPORTER_OTLP_PROTOCOL)==="http/json"&&(u.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT===void 0||u.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT===`${n}/api/otel/v1/metrics`)&&u.OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE==="delta"&&u.OTEL_METRICS_EXPORTER==="otlp"&&u.OTEL_EXPORTER_OTLP_ENDPOINT===`${n}/api/otel`&&(u.OTEL_EXPORTER_OTLP_METRICS_HEADERS??u.OTEL_EXPORTER_OTLP_HEADERS)===`Authorization=Bearer ${t.apiKey}`})}i("Current shell",{env:e.env});let s=e.cwd;for(;sr(s)!==s;){try{await wn(Ae(s,".git","HEAD"));break}catch{}try{await wn(Ae(s,".git"));break}catch{}s=sr(s)}sr(s)===s&&(s=e.cwd);for(let a of new Set([Ae(e.home,".claude","settings.json"),Ae(s,".claude","settings.json"),Ae(s,".claude","settings.local.json"),Ae(s,".mcp.json"),Ae(e.home,".claude.json")]))try{let c=JSON.parse(await wn(a,"utf8"));if(i(a,c),a===Ae(e.home,".claude.json")){let d=Fe(Fe(c).projects)[s];d&&i(`${a} (project)`,d)}}catch(c){c.code!=="ENOENT"&&r.push({source:a,mcp:!1,proxy:!1,subscriptionTelemetry:!1,readable:!1})}return r}var Ba=/\x1B(?:\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\)|[PX^_][\s\S]*?\x1B\\|[@-_])/g,Wa=/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F]/g;function p(t){return t.replace(Ba,"").replace(Wa,"")}function ar(t){try{let e=new URL(t);return e.protocol!=="https:"&&e.protocol!=="http:"||!e.hostname||e.username||e.password?null:e.toString()}catch{return null}}function No(t){return t.replace(/\x1B\]8;;([^\x1B\x07]*)(?:\x07|\x1B\\)/g,(e,n)=>ar(n)?e:"\x1B]8;;\x07")}function qo(t,e){if(!e||!t.startsWith("/")||t.startsWith("//"))return t;try{return new URL(t,e).toString()}catch{return t}}function Fo(t,e){return e?t.replace(/(\]\()(<?)(\/(?!\/)[^\s)>]*)/g,(n,r,o,i)=>`${r}${o}${qo(i,e)}`):t}function cr(t,e){let r=p(t).replace(/(!?)\[([^\]\n]+)\]\((<[^>\n]*>|[^\s)]+)(?:\s+[^)]*)?\)/g,(s,a,c,d)=>{let u=qo(d.startsWith("<")&&d.endsWith(">")?d.slice(1,-1):d,e),l=ar(u);return l?`${a}[${c}](${l})`:c}),o=new Map,i=r.replace(/^(\s{0,3}\[([^\]]+)\]:\s*)(\S+)(.*)$/gm,(s,a,c,d,u)=>{let l=ar(d);return o.set(c.trim().toLowerCase(),l),l?`${a}${l}${u}`:""});return o.size===0?i:i.replace(/(!?)\[([^\]\n]+)\](?:\[([^\]\n]*)\])?/g,(s,a,c,d)=>{let u=(d||c).trim().toLowerCase();return!o.has(u)||o.get(u)?s:c})}function _e(t){return typeof t=="string"?p(t):Array.isArray(t)?t.map(e=>_e(e)):t&&typeof t=="object"?Object.fromEntries(Object.entries(t).map(([e,n])=>[p(e),_e(n)])):t}function Uo(t){return t.state==="received"?t.lastRecordedAt??"Timestamp unavailable":t.state==="unattributed"?"Unknown: this credential has no user attribution":t.state==="unavailable"?"Unavailable: retry the check":"No recorded activity for this user in this company"}async function Mo(t){let e=await qe(t.endpoint),n=await I(e,"GET","/connection"),r=await Lo(e),o={...n,endpoint:e.endpoint,localConfiguration:r};if(t.json)console.log(S(o));else{let i=[`Endpoint: ${e.endpoint}`,`Company: ${n.identity.companyName??n.identity.companyId} (${n.identity.companyId})`,`User: ${n.identity.userId??"Unattributed credential"}`,`Granted scopes: ${n.identity.scopes.join(", ")||"none"} (individual actions also enforce role permissions)`,`Control API: authenticated at ${n.checkedAt}`,"MCP: shared Control API verified; confirm the MCP connection in your agent client."];for(let[s,a]of[["mcp","MCP"],["proxy","Inference proxy"],["subscriptionTelemetry","Subscription telemetry"]]){let c=r.filter(d=>d[s]).map(d=>d.source);i.push(`${a} matching local settings: ${c.join(", ")||"not detected"}`)}for(let s of r.filter(a=>!a.readable))i.push(`Could not inspect: ${s.source}`);i.push("Settings are observations, not proof of effective client configuration; managed settings and other clients are not inspected.",`Latest recorded proxy usage: ${Uo(n.proxy)}`,`Latest recorded subscription telemetry: ${Uo(n.subscriptionTelemetry)}`,"Activity is for this user across devices, not proof that this installation is working now.","Inference forwarding was not tested. The proxy needs a company BYOK provider key.","Subscription inference stays with your provider. Optional telemetry records token counts and estimated cost, not prompts or full traces.",`Usage: ${e.endpoint.replace(/\/$/,"")}/usage`,`Traces: ${e.endpoint.replace(/\/$/,"")}/traces (MCP access alone does not create inference traces)`),console.log(i.map(p).join(`
23
- `))}[n.proxy,n.subscriptionTelemetry].some(i=>i.state==="unavailable")&&(process.exitCode=1)}import{spawn as Ga}from"node:child_process";import{readFile as Ha}from"node:fs/promises";import{red as Ft,green as lr,yellow as za,dim as Ut,bold as jo}from"kleur/colors";async function Do(t){let e=await Ka(t.patchFile);e.trim()||(process.stderr.write(Ft(`Empty patch \u2014 nothing to apply.
24
- `)),process.exit(1));let n=Va(e);process.stderr.write(Ut(`Patch: ${n.files} ${n.files===1?"file":"files"}, `)+lr(`+${n.added}`)+" "+Ft(`-${n.removed}`)+`
25
- `);let r=await dr(["apply","--check"],e);if(r.code!==0){if(t.threeWay){let i=await dr(["apply","--3way"],e);i.code!==0&&(process.stderr.write(Ft(`3-way apply failed:
17
+ `);return await j.writeFile(e,n),!0}function Ho(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function Vo(e){try{return new URL(e).host}catch{return null}}function bc(e){try{return fc("git",["rev-parse","--show-toplevel"],{cwd:e,encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim()}catch{return null}}var Oc=2e3,Ac=900*1e3;async function Qo(e){let{endpoint:t}=e;console.log(oe("AnyForge Control \u2014 device authorisation")),console.log(D(` endpoint: ${t}`));let n=process.cwd(),r=Xo(n),o=r?yr(r):null,s=await(await fetch(`${t}/api/cli/init`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({repo:o})})).json();if(!s.ok||!s.data)throw new Error(`Could not start the authorisation flow: ${s.error??"unknown error"}`);let{code:a,authUrl:c,pollUrl:l}=s.data;console.log(),console.log("Open this URL in your browser to authorise:"),console.log(` ${Sn(c)}`),console.log(D(` Code: ${a}`)),console.log();try{await kc(c)}catch{}let{credentials:d,modelRouting:u}=await xc({pollUrl:l,endpoint:t});if(await jo(d),console.log(Ht("\u2714")+" API key saved to "+D("~/.anyforge/credentials")),!u){await _c(t,d.apiKey);return}let y=await Yo();if(y.isSubscription&&!e.force){await Rc(y,t,d.apiKey);return}let v=process.cwd(),x=Xo(v),b=await Ic(x),m=await zo({endpoint:t,apiKey:d.apiKey,scope:b,repoRoot:x});m.length===0?(console.log(De("No agent configs were patched.")),console.log(D(" Set ANTHROPIC_BASE_URL="+t+"/v1/anthropic and")),console.log(D(" ANTHROPIC_AUTH_TOKEN=<your AnyForge key> manually if needed."))):Ec(m),console.log(),y.isSubscription&&e.force&&(console.log(De(` \u26A0 --force: your Claude ${y.plan??""} subscription login is now shadowed`)),console.log(De(" by the AnyForge token \u2014 Claude Code will use the BYOK proxy, which")),console.log(De(" needs an Anthropic API key on your company. Run ")+oe("anyforge logout")),console.log(De(" to revert and restore your subscription."))),b==="repo"&&(console.log(D(" Re-run this command in another repo to patch it too,")),console.log(D(' or run from any directory and choose "global" to install system-wide.'))),console.log(oe("Done.")+" Run `anyforge status` to check identity, local setup and recorded activity."),console.log(" MCP access, BYOK inference and optional subscription telemetry are separate. Usage: "+Sn(`${t}/usage`))}async function xc(e){let t=Date.now();process.stdout.write("Waiting for browser authorisation\u2026 ");let n=0;for(;Date.now()-t<Ac;){await new Promise(i=>setTimeout(i,Oc)),process.stdout.write("."),n+=1,n>40&&(process.stdout.write(`
18
+ `),n=0);let o=await(await fetch(e.pollUrl)).json();if(o.status==="authorized"&&o.data)return process.stdout.write(" "+Ht("done")+`
19
+ `),{credentials:{apiKey:o.data.apiKey,companyId:o.data.companyId,endpoint:e.endpoint,savedAt:new Date().toISOString()},modelRouting:o.data.modelRouting!==!1};if(o.status==="expired"||o.status==="not_found")throw process.stdout.write(`
20
+ `),new Error(`Authorisation ${o.status}. Re-run \`anyforge init\`.`)}throw new Error("Authorisation timed out (15 min). Re-run `anyforge init`.")}function Sc(e,t){return["mcp","add","anyforge","--env",`ANYFORGE_API_TOKEN=${t}`,"--env",`ANYFORGE_API_URL=${e}`,"--","npx","-y","@anyforge/mcp-server"]}async function Rc(e,t,n){let r=e.email?` (${e.email})`:"";if(console.log(),console.log(De(` \u26A0 Claude ${e.plan??""} subscription login detected${r}.`)),console.log(),console.log(" AnyForge Control\u2019s inference proxy is "+oe("BYOK-only")+" \u2014 it routes"),console.log(" Claude Code through your own Anthropic API key for metering and"),console.log(" governance. Pointing Claude Code at the proxy would "+oe("override and")),console.log(" "+oe("disable your subscription login")+", so we did NOT patch your config."),console.log(),console.log(" Instead, use your subscription for inference and add the "+oe("AnyForge")),console.log(" "+oe("MCP server")+" for control (HIL approvals, work items, crews):"),console.log(),await Zo(t,n),console.log(),(await xn({type:"confirm",name:"go",message:"Send Claude Code usage to your AnyForge dashboard? (token counts and tool, MCP and skill activity \u2014 never prompt content)",initial:!0})).go){let i=await xn({type:"confirm",name:"go",message:"Also send the names of the MCP servers and skills you use? This turns on Claude Code's tool details, which also sends Bash commands, file paths and tool input to AnyForge. AnyForge drops those on arrival and never stores them.",initial:!1}),s=await Ko({endpoint:t,apiKey:n,toolDetails:i.go===!0});s&&(console.log(Ht(" \u2714")+" Usage telemetry enabled \u2014 view it at "+Sn(`${t}/usage`)),console.log(D(" (written to "+s.path+"; `anyforge logout` removes it)")))}else console.log(D(" Skipped \u2014 your subscription usage stays local to Claude Code."));console.log(),console.log(D(" Want the metered BYOK proxy instead? Re-run with ")+oe("--force")+D(" using an")),console.log(D(" Anthropic API key on your company (this disables your subscription in")),console.log(D(" Claude Code; ")+oe("anyforge logout")+D(" reverts it).")),console.log(),console.log(oe("Done.")+" Run `anyforge status` to check identity, local setup and recorded activity."),console.log(" MCP access, BYOK inference and optional subscription telemetry are separate. Usage: "+Sn(`${t}/usage`))}async function _c(e,t){console.log(),console.log(De(" This key is read-only")+D(" (Viewer role).")),console.log(" It reads programs, initiatives, work items and the strategy through the"),console.log(" "+oe("AnyForge MCP server")+". Routing model calls through the company's keys"),console.log(" needs the Editor role, so your Claude Code config was "+oe("not")+" patched."),console.log(),await Zo(e,t),console.log(),console.log(oe("Done.")+" Run `anyforge status` to check identity and local setup.")}async function Zo(e,t){let n=Sc(e,t),r="claude mcp add anyforge -- npx -y @anyforge/mcp-server";if((await xn({type:"confirm",name:"go",message:"Add the AnyForge MCP server to Claude Code now?",initial:!0})).go)try{wr("claude",n,{stdio:"inherit"}),console.log(Ht(" \u2714")+" AnyForge MCP server added to Claude Code.")}catch{console.log(De(" Could not run `claude mcp add` automatically (is the `claude` CLI on PATH?).")),console.log(D(" Add it manually:")),console.log(" "+r)}else console.log(D(" Add it later with:")),console.log(" "+r)}function Xo(e){try{return wr("git",["rev-parse","--show-toplevel"],{cwd:e,encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim()}catch{return null}}async function Ic(e){return e?(await xn({type:"select",name:"scope",message:"Where should AnyForge patch Claude Code?",choices:[{title:`This repo only (${e}/.claude/settings.local.json + .env)`,value:"repo"},{title:"Globally (~/.claude/settings.json)",value:"global"}],initial:0})).scope??"repo":(console.log(D(" Not a git repo root \u2014 installing globally.")),console.log(D(" Run this from a repo root if you want a per-repo install instead.")),"global")}function Ec(e){console.log(Ht("\u2714")+" Patched "+e.length+" agent config"+(e.length===1?"":"s")+":");for(let t of e)console.log(` ${D("\u2022")} ${t.label} ${D("("+t.path+")")}`)}function yr(e){try{let t=wr("git",["remote","get-url","origin"],{cwd:e,encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim(),n=t.match(/^git@[\w.-]+:([\w.-]+\/[\w.-]+?)(?:\.git)?$/);if(n)return n[1];let r=t.match(/^https?:\/\/[\w.-]+\/([\w.-]+\/[\w.-]+?)(?:\.git)?$/);return r?r[1]:null}catch{return null}}import{dim as kr,yellow as Fc,bold as qc}from"kleur/colors";var g={OK:0,FAILURE:1,USAGE:2,NO_CREDENTIALS:3,API_ERROR:4,VERIFICATION_FAILED:5,INTERRUPTED:130},h=class extends Error{exitCode;hint;constructor(t,n=g.FAILURE,r){super(t),this.name="CliError",this.exitCode=n,this.hint=r}},f=class extends h{constructor(t,n){super(t,g.USAGE,n),this.name="UsageError"}},be=class extends h{constructor(){super("Not authenticated \u2014 no ANYFORGE_API_TOKEN and no saved credential.",g.NO_CREDENTIALS,"Run `npx @anyforge/control init`, or set ANYFORGE_API_TOKEN for unattended use."),this.name="NotAuthenticatedError"}},ei=4e3,V=class extends h{code;status;details;constructor(t,n,r,o,i){super(Tc(t,r,o),g.API_ERROR,Cc(t,n,i)),this.name="ControlApiError",this.code=t,this.status=n,o&&(this.details=o)}};function Tc(e,t,n){let r=t?`${e}: ${t}`:e;if(!n)return r;let o=JSON.stringify(n),i=o.length>ei?`${o.slice(0,ei)}\u2026(truncated)`:o;return`${r}
21
+ ${i}`}function Cc(e,t,n){if(e==="api_key_expired")return`This API key has expired. Mint a new key in Settings \u2192 API keys, then re-run \`npx @anyforge/control init\` or update ANYFORGE_API_TOKEN. See ${n?`${n.replace(/\/$/,"")}/docs?page=service-accounts-and-key-expiry`:'the "Service accounts and API key expiry" guide'}.`;if(e==="missing_api_key"||e==="invalid_api_key"||e==="invalid_token"||t===401)return"The key was rejected. Re-run `npx @anyforge/control init`.";if(e==="insufficient_scope"||t===403)return"This key lacks the scope for that command. Run `anyforge whoami` to see its scopes.";if(t===404)return"No such id in this company. Check it with the matching list command.";if(t===429)return"Rate limited. Retry after a short pause."}var ti="https://crew.anyforge.ai";async function Re(e){let t=process.env.ANYFORGE_API_TOKEN,n=e||process.env.ANYFORGE_API_URL;if(t)return{apiKey:t,endpoint:n||ti};let r=await bt();if(r?.apiKey)return{apiKey:r.apiKey,endpoint:n||r.endpoint||ti};throw new be}var vr=null;function ni(e){vr=e&&/^[a-z][a-z0-9-]*$/.test(e)?`anyforge ${e}`:null}async function R(e,t,n,r){let o=`${e.endpoint.replace(/\/$/,"")}/api/v1/control${n}`,i;try{i=await fetch(o,{method:t,headers:{"Content-Type":"application/json",Authorization:`Bearer ${e.apiKey}`,"X-AnyForge-Client":"anyforge-cli",...vr?{"X-AnyForge-Tool":vr}:{}},...r!==void 0?{body:JSON.stringify(r)}:{}})}catch(a){throw new h(`Cannot reach ${e.endpoint}: ${a instanceof Error?a.message:String(a)}`,g.FAILURE,"Check your network, or pass --endpoint if this workspace is self-hosted.")}let s;try{s=await i.json()}catch{throw new h(`Non-JSON response from ${o} (HTTP ${i.status}).`,g.FAILURE)}if(!i.ok||s.ok===!1){let a=typeof s.error=="string"?s.error:`http_${i.status}`,c=typeof s.message=="string"?s.message:typeof s.detail=="string"?s.detail:void 0;throw new V(a,i.status,c,Pc(s),e.endpoint)}return s.data}var $c=new Set(["ok","error","message","detail"]);function Pc(e){if(!e||typeof e!="object"||Array.isArray(e))return;let t=Object.entries(e).filter(([n,r])=>!$c.has(n)&&r!==void 0);return t.length?Object.fromEntries(t):void 0}function F(e){let t=Object.entries(e).filter(([,n])=>n!==void 0&&n!=="").map(([n,r])=>`${encodeURIComponent(n)}=${encodeURIComponent(String(r))}`);return t.length?`?${t.join("&")}`:""}async function zt(e){return R(e,"GET","/me")}function br(e,t,n,r){let o=e?.entityLabels?.[t]?.[n];return typeof o=="string"&&o.trim()?o:r}async function ri(e){try{return await zt(e)}catch{return null}}function oi(e){return[...e].length}function kt(e,t){if(t<=0)return"";let n=[...e];return n.length<=t?e:t===1?"\u2026":`${n.slice(0,t-1).join("")}\u2026`}function B(e,t){if(e.length===0)return"";let n=e.map(i=>t.map(s=>{let a=s.value(i)??"";return s.max?kt(a,s.max):a})),r=t.map((i,s)=>Math.max(oi(i.header),...n.map(a=>oi(a[s])))),o=i=>i.map((s,a)=>a===i.length-1?s:s.padEnd(r[a]+2)).join("").replace(/\s+$/,"");return[o(t.map(i=>i.header)),...n.map(o)].join(`
22
+ `)}function I(e){return JSON.stringify(e,null,2)}function ii(e,t=Date.now()){let n=Lc(e);if(n===null)return"\u2014";let r=Math.max(0,Math.floor((t-n)/1e3));if(r<60)return`${r}s`;let o=Math.floor(r/60);if(o<60)return`${o}m`;let i=Math.floor(o/60);return i<48?`${i}h`:`${Math.floor(i/24)}d`}function Lc(e){if(typeof e=="number")return Number.isFinite(e)?e:null;if(typeof e=="string"){let t=Date.parse(e);return Number.isNaN(t)?null:t}return null}function T(e,t,n="\u2014"){let r=e[t];return typeof r=="string"&&r.trim()?r:typeof r=="number"&&Number.isFinite(r)?String(r):n}function te(e,t,n="\u2014"){for(let r of t){let o=e[r];if(typeof o=="string"&&o.trim())return o}return n}function ue(e){let t=Array.isArray(e)?e:e&&typeof e=="object"?Nc(e):null;return t?t.filter(n=>!!n&&typeof n=="object"&&!Array.isArray(n)):[]}function Nc(e){for(let t of["items","results","rows","data"])if(Array.isArray(e[t]))return e[t];return null}function Rn(e,t,n){let r=e?`last ${e}d`:"all time";return!t||!n?r:`last ${Math.max(1,Math.round((Date.now()-n)/864e5))}d of ${e?`${e}d`:"all time"} \u2014 read cap`}async function si(e){let t=await Re(e.endpoint),n=await zt(t);if(e.json){console.log(I(n));return}console.log(`Company: ${qc(n.companyName??n.companyId)}${n.companyName?kr(` (${n.companyId})`):""}`),console.log(`Endpoint: ${t.endpoint}`),console.log(`Scopes: ${n.scopes.length?n.scopes.join(", "):Fc("none")}`),n.userId&&console.log(`User: ${n.userId}`);let r=await bt();console.log(r?kr(`Key tail: \u2026${r.apiKey.slice(-4)} \xB7 saved ${new Date(r.savedAt).toLocaleString()}`):kr("Key from: ANYFORGE_API_TOKEN (no saved credential file)"))}import{green as ai,dim as _n,yellow as Mc}from"kleur/colors";async function ci(){let e=await bt(),t=await Do(),{reverted:n}=await Jo({endpoint:e?.endpoint??null});if(console.log(t?ai("\u2714")+" Local credentials cleared.":Mc("No credentials were saved on this machine.")),n.length){console.log(ai("\u2714")+" Reverted patched configs:");for(let r of n)console.log(` ${_n("\u2022")} ${r}`);console.log(_n(" Your previous Claude Code login (if any) is restored."))}else console.log(_n(" No AnyForge config patches found to revert."));console.log(_n(" Revoke the key server-side at /settings \u2192 API keys."))}import{readFile as In}from"node:fs/promises";import{homedir as Uc}from"node:os";import{dirname as Or,join as _e}from"node:path";function Be(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)?e:{}}async function li(e,t={home:Uc(),cwd:process.cwd(),env:process.env}){let n=e.endpoint.replace(/\/$/,""),r=[],o={};try{o=Be(JSON.parse(await In(_e(t.home,".anyforge","credentials"),"utf8")))}catch{}function i(a,c){let l=Be(c),d=Be(l.env),u=Object.values(Be(l.mcpServers));r.push({source:a,readable:!0,mcp:u.some(y=>{let v=Be(y),x=Be(v.env),b=Array.isArray(v.args)?v.args:[],m=x.ANYFORGE_API_TOKEN??t.env.ANYFORGE_API_TOKEN,A=x.ANYFORGE_API_URL??t.env.ANYFORGE_API_URL,$=m??o.apiKey,M=A??(m?"https://crew.anyforge.ai":o.endpoint)??"https://crew.anyforge.ai";return b.includes("@anyforge/mcp-server")&&$===e.apiKey&&M===n}),proxy:d.ANTHROPIC_BASE_URL===`${n}/v1/anthropic`&&d.ANTHROPIC_AUTH_TOKEN===e.apiKey,subscriptionTelemetry:String(d.CLAUDE_CODE_ENABLE_TELEMETRY)==="1"&&(d.OTEL_EXPORTER_OTLP_METRICS_PROTOCOL??d.OTEL_EXPORTER_OTLP_PROTOCOL)==="http/json"&&(d.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT===void 0||d.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT===`${n}/api/otel/v1/metrics`)&&d.OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE==="delta"&&d.OTEL_METRICS_EXPORTER==="otlp"&&d.OTEL_EXPORTER_OTLP_ENDPOINT===`${n}/api/otel`&&(d.OTEL_EXPORTER_OTLP_METRICS_HEADERS??d.OTEL_EXPORTER_OTLP_HEADERS)===`Authorization=Bearer ${e.apiKey}`})}i("Current shell",{env:t.env});let s=t.cwd;for(;Or(s)!==s;){try{await In(_e(s,".git","HEAD"));break}catch{}try{await In(_e(s,".git"));break}catch{}s=Or(s)}Or(s)===s&&(s=t.cwd);for(let a of new Set([_e(t.home,".claude","settings.json"),_e(s,".claude","settings.json"),_e(s,".claude","settings.local.json"),_e(s,".mcp.json"),_e(t.home,".claude.json")]))try{let c=JSON.parse(await In(a,"utf8"));if(i(a,c),a===_e(t.home,".claude.json")){let l=Be(Be(c).projects)[s];l&&i(`${a} (project)`,l)}}catch(c){c.code!=="ENOENT"&&r.push({source:a,mcp:!1,proxy:!1,subscriptionTelemetry:!1,readable:!1})}return r}var jc=/\x1B(?:\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\)|[PX^_][\s\S]*?\x1B\\|[@-_])/g,Dc=/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F]/g;function p(e){return e.replace(jc,"").replace(Dc,"")}function Ar(e){try{let t=new URL(e);return t.protocol!=="https:"&&t.protocol!=="http:"||!t.hostname||t.username||t.password?null:t.toString()}catch{return null}}function di(e){return e.replace(/\x1B\]8;;([^\x1B\x07]*)(?:\x07|\x1B\\)/g,(t,n)=>Ar(n)?t:"\x1B]8;;\x07")}function ui(e,t){if(!t||!e.startsWith("/")||e.startsWith("//"))return e;try{return new URL(e,t).toString()}catch{return e}}function pi(e,t){return t?e.replace(/(\]\()(<?)(\/(?!\/)[^\s)>]*)/g,(n,r,o,i)=>`${r}${o}${ui(i,t)}`):e}function xr(e,t){let r=p(e).replace(/(!?)\[([^\]\n]+)\]\((<[^>\n]*>|[^\s)]+)(?:\s+[^)]*)?\)/g,(s,a,c,l)=>{let d=ui(l.startsWith("<")&&l.endsWith(">")?l.slice(1,-1):l,t),u=Ar(d);return u?`${a}[${c}](${u})`:c}),o=new Map,i=r.replace(/^(\s{0,3}\[([^\]]+)\]:\s*)(\S+)(.*)$/gm,(s,a,c,l,d)=>{let u=Ar(l);return o.set(c.trim().toLowerCase(),u),u?`${a}${u}${d}`:""});return o.size===0?i:i.replace(/(!?)\[([^\]\n]+)\](?:\[([^\]\n]*)\])?/g,(s,a,c,l)=>{let d=(l||c).trim().toLowerCase();return!o.has(d)||o.get(d)?s:c})}function Ie(e){return typeof e=="string"?p(e):Array.isArray(e)?e.map(t=>Ie(t)):e&&typeof e=="object"?Object.fromEntries(Object.entries(e).map(([t,n])=>[p(t),Ie(n)])):e}function fi(e){return e.state==="received"?e.lastRecordedAt??"Timestamp unavailable":e.state==="unattributed"?"Unknown: this credential has no user attribution":e.state==="unavailable"?"Unavailable: retry the check":"No recorded activity for this user in this company"}async function mi(e){let t=await Re(e.endpoint),n=await R(t,"GET","/connection"),r=await li(t),o={...n,endpoint:t.endpoint,localConfiguration:r};if(e.json)console.log(I(o));else{let i=[`Endpoint: ${t.endpoint}`,`Company: ${n.identity.companyName??n.identity.companyId} (${n.identity.companyId})`,`User: ${n.identity.userId??"Unattributed credential"}`,`Granted scopes: ${n.identity.scopes.join(", ")||"none"} (individual actions also enforce role permissions)`,`Control API: authenticated at ${n.checkedAt}`,"MCP: shared Control API verified; confirm the MCP connection in your agent client."];for(let[s,a]of[["mcp","MCP"],["proxy","Inference proxy"],["subscriptionTelemetry","Subscription telemetry"]]){let c=r.filter(l=>l[s]).map(l=>l.source);i.push(`${a} matching local settings: ${c.join(", ")||"not detected"}`)}for(let s of r.filter(a=>!a.readable))i.push(`Could not inspect: ${s.source}`);i.push("Settings are observations, not proof of effective client configuration; managed settings and other clients are not inspected.",`Latest recorded proxy usage: ${fi(n.proxy)}`,`Latest recorded subscription telemetry: ${fi(n.subscriptionTelemetry)}`,"Activity is for this user across devices, not proof that this installation is working now.","Inference forwarding was not tested. The proxy needs a company BYOK provider key.","Subscription inference stays with your provider. Optional telemetry records token counts, estimated cost and, when your company turns it on, counts of the tools, MCP servers and skills used. Never prompts, commands, file paths or full traces.",`Usage: ${t.endpoint.replace(/\/$/,"")}/usage`,`Traces: ${t.endpoint.replace(/\/$/,"")}/traces (MCP access alone does not create inference traces)`),console.log(i.map(p).join(`
23
+ `))}[n.proxy,n.subscriptionTelemetry].some(i=>i.state==="unavailable")&&(process.exitCode=1)}import{spawn as Bc}from"node:child_process";import{readFile as Wc}from"node:fs/promises";import{red as Kt,green as Sr,yellow as Gc,dim as Yt,bold as hi}from"kleur/colors";async function gi(e){let t=await Hc(e.patchFile);t.trim()||(process.stderr.write(Kt(`Empty patch \u2014 nothing to apply.
24
+ `)),process.exit(1));let n=zc(t);process.stderr.write(Yt(`Patch: ${n.files} ${n.files===1?"file":"files"}, `)+Sr(`+${n.added}`)+" "+Kt(`-${n.removed}`)+`
25
+ `);let r=await Rr(["apply","--check"],t);if(r.code!==0){if(e.threeWay){let i=await Rr(["apply","--3way"],t);i.code!==0&&(process.stderr.write(Kt(`3-way apply failed:
26
26
  `)+i.stderr+`
27
- `),process.exit(1)),process.stderr.write(za(`\u2713 Applied via 3-way merge \u2014 review conflicts before committing.
28
- `));return}process.stderr.write(Ft(`Patch does not apply cleanly:
27
+ `),process.exit(1)),process.stderr.write(Gc(`\u2713 Applied via 3-way merge \u2014 review conflicts before committing.
28
+ `));return}process.stderr.write(Kt(`Patch does not apply cleanly:
29
29
  `)+r.stderr+`
30
- `),process.stderr.write(Ut("Try ")+jo("--3way")+Ut(` to attempt a 3-way merge against the index.
31
- `)),process.exit(1)}if(t.dryRun){process.stderr.write(lr(`\u2713 Patch would apply cleanly.
32
- `));return}let o=await dr(["apply"],e);o.code!==0&&(process.stderr.write(Ft(`git apply failed:
30
+ `),process.stderr.write(Yt("Try ")+hi("--3way")+Yt(` to attempt a 3-way merge against the index.
31
+ `)),process.exit(1)}if(e.dryRun){process.stderr.write(Sr(`\u2713 Patch would apply cleanly.
32
+ `));return}let o=await Rr(["apply"],t);o.code!==0&&(process.stderr.write(Kt(`git apply failed:
33
33
  `)+o.stderr+`
34
- `),process.exit(1)),process.stderr.write(lr(`\u2713 Applied.
35
- `)),n.files>0&&process.stderr.write(Ut(" Run ")+jo("git diff --stat")+Ut(` to review.
36
- `))}async function Ka(t){if(t)return await Ha(t,"utf8");let e=[];for await(let n of process.stdin)e.push(n);return Buffer.concat(e).toString("utf8")}function dr(t,e){return new Promise((n,r)=>{let o=Ga("git",t,{stdio:["pipe","pipe","pipe"]}),i="",s="";o.stdout.on("data",a=>{i+=a.toString()}),o.stderr.on("data",a=>{s+=a.toString()}),o.on("error",r),o.on("close",a=>n({code:a??0,stdout:i,stderr:s})),o.stdin.write(e),o.stdin.end()})}function Va(t){let e=0,n=0,r=0;for(let o of t.split(`
37
- `))if(o.startsWith("diff --git"))e+=1;else{if(o.startsWith("+++")||o.startsWith("---"))continue;o.startsWith("+")?n+=1:o.startsWith("-")&&(r+=1)}return e===0&&(n>0||r>0)&&(e=(t.match(/^\+\+\+ /gm)??[]).length),{files:e,added:n,removed:r}}import{bold as Ya,dim as ur,green as Ja,red as Qa}from"kleur/colors";async function Bo(t){let e=await I(t.creds,"GET",`/hil${H({limit:t.limit})}`);if(t.json){console.log(S(e));return}let n=le(e);if(n.length===0){console.log(ur("No approvals pending."));return}console.log(B(n,[{header:"ID",value:r=>E(r,"id")},{header:"CREW",value:r=>E(r,"crewId"),max:24},{header:"AGE",value:r=>Co(r.requestedAt)},{header:"SUMMARY",value:r=>Z(r,["planSummary","summary","title","description"]),max:60}])),console.log(ur(`
38
- ${n.length} pending \xB7 approve with `)+Ya("anyforge approve <id>"))}async function pr(t){if(!t.approvalId)throw new h(`${t.decision==="APPROVED"?"approve":"reject"} requires an approval id.`,"List them with `anyforge approvals`.");let e=await I(t.creds,"POST",`/hil/${encodeURIComponent(t.approvalId)}`,{decision:t.decision,...t.notes?{notes:t.notes}:{}});if(t.json){console.log(S(e));return}let n=t.decision==="APPROVED"?Ja("Approved"):Qa("Rejected");console.log(`${n} ${t.approvalId}`);let r=e&&typeof e=="object"?e.signature:void 0;typeof r=="string"&&r&&console.log(ur(`Audit signature: ${r}`))}import{bold as Xa,dim as fr}from"kleur/colors";async function Wo(t){let e=await I(t.creds,"GET",`/crews${H({status:t.status,activeOnly:t.activeOnly,limit:t.limit})}`);if(t.json){console.log(S(e));return}let n=le(e);if(n.length===0){console.log(fr(t.activeOnly?"No active crews.":"No crews."));return}console.log(B(n,[{header:"ID",value:r=>E(r,"id")},{header:"STATUS",value:r=>E(r,"status")},{header:"TASK",value:r=>Z(r,["task"],"\u2014"),max:52},{header:"NOTE",value:r=>Z(r,["errorMessage"],""),max:40}]))}async function Go(t){if(!t.crewId)throw new h("crew requires a crew id.","List them with `anyforge crews`.");let e=await I(t.creds,"GET",`/crews/${encodeURIComponent(t.crewId)}${H({messages:t.messages})}`);if(t.json){console.log(S(e));return}let n=e?.crew&&typeof e.crew=="object"?e.crew:e;console.log(`${Xa(Za(Z(n,["taskDescription"],E(n,"id"))))}`),console.log(`Status: ${E(n,"status")}`),console.log(`Repo: ${Z(n,["githubRepo"])}`);let r=E(n,"prUrl","");r&&console.log(`PR: ${r}`);let o=E(n,"url","");o&&console.log(`Console: ${o}`);let i=le(e?.chat);if(i.length>0){console.log(fr(`
39
- Last ${i.length} messages:`));for(let s of i){let a=Z(s,["agentRole","role","author","agent","type"],"?"),c=Z(s,["content","body","text","summary"],"");c&&console.log(`${fr(`[${a}]`)} ${pt(c.replace(/\s+/g," "),160)}`)}}}async function mr(t){if(!t.crewId)throw new h(`${t.action} requires a crew id.`,"List them with `anyforge crews`.");let e=await I(t.creds,"POST",`/crews/${encodeURIComponent(t.crewId)}/${t.action}`);if(t.json){console.log(S(e));return}console.log(`${t.action==="halt"?"Halted":"Resumed"} ${t.crewId}`)}function Za(t){return pt(t.split(/\r?\n/).find(e=>e.trim())?.trim()??t,80)}import{bold as ec,dim as ft,red as Ho,yellow as Ko}from"kleur/colors";var tc=new Set(["COMPLETE","HALTED","REJECTED","CANCELLED"]),nc=new Set(["HIL_PENDING","HIL_SHIP","PERMISSION_PENDING","PAUSED"]),rc={HIL_PENDING:"Decide it with `anyforge approvals`, or from the Slack alert.",HIL_SHIP:"Decide it with `anyforge approvals`, or from the Slack alert.",PERMISSION_PENDING:"The agent asked for a tool outside its profile. Allow or deny it in the Console.",PAUSED:"Restart it with `anyforge resume <id>`."};async function Vo(t){if(!t.workItemId)throw new h("crew start requires a work item id.","Find one with `anyforge work --status READY`.");let e=await I(t.creds,"PATCH",`/work/${encodeURIComponent(t.workItemId)}`,{action:"dispatch",...t.profileId?{runProfileId:t.profileId}:{}});if(t.json&&!t.follow){console.log(S(e));return}let n=typeof e?.crewId=="string"?e.crewId:"";if(!n)throw new f("Dispatch was accepted but returned no crew id.",g.FAILURE,"Check `anyforge crews --active` to see whether one started.");for(let r of e?.warnings??[])console.error(Ko(`warning: ${r}`));if(!t.follow){console.log(`Started ${ec(n)} on ${t.workItemId}`),console.log(ft(`Follow it with \`anyforge crew follow ${n}\``));return}await hr({creds:t.creds,json:t.json,crewId:n,intervalSeconds:t.intervalSeconds})}var oc=5,ic=10,sc=120,zo=30;async function hr(t){if(!t.crewId)throw new h("crew follow requires a crew id.","List them with `anyforge crews --active`.");let e=Math.min(Math.max(t.intervalSeconds??ic,oc),sc)*1e3,n=Date.now()+zo*6e4,r=new Set,o="",i=!1;for(;;){let s=await I(t.creds,"GET",`/crews/${encodeURIComponent(t.crewId)}?messages=50`),a=s?.crew??{},c=E(a,"status","");for(let d of s?.chat??[]){let u=E(d,"id","");!u||r.has(u)||(r.add(u),i&&!t.json&&ac(d))}if(i=!0,c&&c!==o&&(t.json||console.log(ft(`\u2500\u2500 ${c}`)),o=c),nc.has(c)){if(t.json)console.log(S({crewId:t.crewId,status:c,waiting:!0,url:E(a,"url","")}));else{console.log(Ko(`
40
- ${c} \u2014 this crew is waiting on a person.`));let d=rc[c];d&&console.log(ft(d));let u=E(a,"url","");u&&console.log(ft(u))}return}if(tc.has(c)){if(t.json)console.log(S({crewId:t.crewId,status:c,waiting:!1,url:E(a,"url","")}));else{let d=`
41
- ${c}`;console.log(c==="COMPLETE"?d:Ho(d));let u=E(a,"prUrl","");u&&console.log(ft(u));let l=E(a,"errorMessage","");l&&console.log(Ho(l))}if(c!=="COMPLETE")throw new f(`Crew ${t.crewId} ended ${c}.`,g.FAILURE);return}if(Date.now()>=n)throw new f(`Still ${c||"running"} after ${zo} minutes \u2014 giving up watching.`,g.FAILURE,`The crew is unaffected. Check it with \`anyforge crew ${t.crewId}\`.`);await lc(e)}}function ac(t){let e=E(t,"agentRole","")||E(t,"type","?"),n=cc(t,["content","text","summary"]);n&&console.log(`${ft(`[${e}]`)} ${pt(n.replace(/\s+/g," "),160)}`)}function cc(t,e){for(let n of e){let r=t[n];if(typeof r=="string"&&r.trim())return r.trim()}return""}function lc(t){return new Promise(e=>{setTimeout(e,t)})}import{randomUUID as yr}from"node:crypto";import{promises as vr}from"node:fs";import{relative as vc,resolve as bc,isAbsolute as kc}from"node:path";import{execFile as Oc}from"node:child_process";import{promisify as Ac}from"node:util";import _c from"open";import{execFile as hc}from"node:child_process";import{promises as ze}from"node:fs";import{tmpdir as gc}from"node:os";import{join as wr,resolve as Qo}from"node:path";var dc=[/sk-ant-[a-zA-Z0-9_-]{20,}/,/sk-(?:proj-)?[a-zA-Z0-9_-]{20,}/,/ghp_[a-zA-Z0-9]{30,}/,/gh[sou]_[a-zA-Z0-9]{30,}/,/github_pat_[a-zA-Z0-9_]{30,}/,/AIza[0-9A-Za-z_-]{30,}/,/AKIA[0-9A-Z]{16}/,/sk_(?:live|test)_[a-zA-Z0-9]{20,}/,/xox[bpaors]-[0-9]+-[0-9]+-[a-zA-Z0-9-]{20,}/,/eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+/,/-----BEGIN [A-Z ]*PRIVATE KEY-----/,/(?:api[_-]?key|secret|password|passwd|token|bearer)[\s:=]+['"]?[a-zA-Z0-9_+/=-]{16,}/i];function uc(t,e){return e.some(n=>n.test(t))}function Yo(t){return uc(t,dc)}var He=100*1024,fe=(t,e)=>({ok:!1,code:t,message:e});function gr(t){if(!t.startsWith('"'))return t;if(t.length<2||!t.endsWith('"'))return null;let e=t.slice(1,-1),n=[],r={a:7,b:8,t:9,n:10,v:11,f:12,r:13,'"':34,"\\":92};for(let o=0;o<e.length;o++){let i=e[o];if(i!=="\\"){n.push(...new TextEncoder().encode(i));continue}let s=e[o+1];if(s===void 0)return null;if(/[0-7]/.test(s)){let a=e.slice(o+1,o+4);if(!/^[0-7]{3}$/.test(a))return null;n.push(parseInt(a,8)),o+=3}else if(s in r)n.push(r[s]),o+=1;else return null}try{return new TextDecoder("utf-8",{fatal:!0}).decode(new Uint8Array(n))}catch{return null}}function pc(t){return!t||t.startsWith("/")||t.includes("\0")||t.includes("\\")?!1:t.split("/").every(e=>e!==""&&e!=="."&&e!==".."&&e.toLowerCase()!==".git")}function fc(t){if(t.startsWith('"')){let n=t.match(/^("(?:[^"\\]|\\.)*") ("(?:[^"\\]|\\.)*")$/);if(!n)return null;let[r,o]=[gr(n[1]),gr(n[2])];return r?.startsWith("a/")&&o?.startsWith("b/")&&r.slice(2)===o.slice(2)?r.slice(2):null}if((t.length-5)%2!==0)return null;let e=t.slice(2,2+(t.length-5)/2);return t===`a/${e} b/${e}`?e:null}function mc(t,e){let n=t.slice(4).replace(/\t$/,"");if(n==="/dev/null")return n;let r=gr(n);return r?.startsWith(e)?r.slice(2):null}function Jo(t){if(typeof t!="string"||t.length===0)return fe("local_changes_empty","There are no local changes to send.");let e=new TextEncoder().encode(t).length;if(e>He)return fe("local_changes_too_large",`Your uncommitted changes are ${Math.ceil(e/1024)} KB; the limit is ${He/1024} KB. Commit and push the larger part, then send the rest.`);if(!t.startsWith("diff --git "))return fe("local_changes_invalid","The local changes must be a patch made by git diff.");let n=[],r=[],o=null,i=!1,s=0;for(let a of t.split(`
42
- `)){if(a.startsWith("diff --git ")){if(o=fc(a.slice(11)),o===null)return fe("local_changes_invalid","The local changes contain a rename, a copy or a header this check cannot read. Commit and push those, then send the rest.");if(!pc(o))return fe("local_changes_unsafe_path",`The local changes name a path outside the repository: ${o}`);n.includes(o)||n.push(o),i=!1;continue}if(o===null)return fe("local_changes_invalid","The local changes must be a patch made by git diff.");if(i&&/^[ +\-\\]/.test(a)){a.startsWith("+")?(Yo(a.slice(1))&&r.length<5&&r.push(`${o}:${s}`),s++):a.startsWith(" ")&&s++;continue}let c=a.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/);if(c){i=!0,s=Number(c[1]);continue}if(i=!1,a==="")continue;if(a.startsWith("GIT binary patch")||/^Binary files .* differ$/.test(a))return fe("local_changes_binary",`${o} is a binary file. Commit and push it, then send the rest.`);let d=a.match(/^(?:old mode|new mode|new file mode|deleted file mode) (\d+)$/)??a.match(/^index [0-9a-f]+\.\.[0-9a-f]+ (\d+)$/);if(d){if(d[1]==="120000"||d[1]==="160000")return fe("local_changes_unsupported_file",`${o} is a ${d[1]==="120000"?"symbolic link":"submodule"}. Commit and push it, then send the rest.`);continue}if(!/^(?:index [0-9a-f]+\.\.[0-9a-f]+|similarity index \d+%|dissimilarity index \d+%)$/.test(a)){if(a.startsWith("--- ")||a.startsWith("+++ ")){let u=mc(a,a.startsWith("--- ")?"a/":"b/");if(u!=="/dev/null"&&u!==o)return fe("local_changes_invalid",`The patch headers for ${o} disagree.`);continue}return fe("local_changes_invalid",`The local changes for ${o} contain a line this check cannot read. Send a patch made by git diff without renames.`)}}return r.length?fe("local_changes_secret",`Your uncommitted changes add what looks like a credential at ${r.join(", ")}. Remove it, or move it out of the repository, then try again. Nothing was sent.`):{ok:!0,files:n,bytes:e}}var yn=class extends Error{constructor(n,r,o){super(r.trim()||`git ${n[0]} failed`);this.args=n;this.stderr=r;this.code=o}args;stderr;code};function J(t,e){return new Promise((n,r)=>{let o=hc("git",t,{cwd:e.cwd,encoding:"utf8",maxBuffer:e.maxBuffer??1048576,timeout:e.timeout??3e4,env:{...process.env,GIT_TERMINAL_PROMPT:"0",...e.env}},(i,s,a)=>{i?r(new yn(t,String(a??""),i.code)):n(String(s))});e.input!==void 0&&o.stdin?.end(e.input)})}var ee=(t,e)=>J(t,e).then(n=>n.trim(),()=>null);function Xo(t){let e=t.trim().match(/^(?:https:\/\/(?:[^@/]+@)?github\.com\/|ssh:\/\/git@github\.com(?::\d+)?\/|git@github\.com:)([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+?)(?:\.git)?\/?$/);return e?`${e[1]}/${e[2]}`:null}async function Zo(t){let e=await ee(["rev-parse","--show-toplevel"],{cwd:t});if(!e)throw new h("Run this inside a Git checkout of the repository.");return e}async function ei(t){let e=await Zo(t),n=await ee(["symbolic-ref","--quiet","--short","HEAD"],{cwd:e});if(!n)throw new h("This checkout is not on a branch (detached HEAD). Check out the branch you are working on.");let r=await ee(["rev-parse","--verify","HEAD^{commit}"],{cwd:e});if(!r)throw new h(`${n} has no commits yet. Commit and push it first.`);let o=await ee(["config","--get",`branch.${n}.remote`],{cwd:e})||"origin",i=await ee(["config","--get",`remote.${o}.url`],{cwd:e}),s=i?Xo(i):null;if(!s)throw new h(`The remote "${o}" is not a GitHub repository. Code Studio works on GitHub repositories.`);let a=await ee(["config","--get",`branch.${n}.merge`],{cwd:e}),c=a?.startsWith("refs/heads/")?a.slice(11):n;return{root:e,branch:n,head:r,remote:o,remoteBranch:c,repoFullName:s}}async function ti(t){let e;try{e=await J(["ls-remote","--heads",t.remote,`refs/heads/${t.remoteBranch}`],{cwd:t.root,timeout:6e4})}catch(o){throw new f(`Could not read ${t.remoteBranch} from ${t.remote}: ${o.message}`)}let n=e.split(`
43
- `).map(o=>o.split(" ")).find(([,o])=>o===`refs/heads/${t.remoteBranch}`)?.[0];if(!n)throw new h(`${t.remoteBranch} is not on ${t.remote} yet. Push it first: git push -u ${t.remote} ${t.branch}`);if(n===t.head)return;throw await ee(["merge-base","--is-ancestor",n,t.head],{cwd:t.root})!==null?new h(`${t.branch} has commits that are not pushed. Push them first: git push ${t.remote} ${t.branch}`):new h(`${t.remote}/${t.remoteBranch} has moved on from your checkout. Pull it, then try again.`)}async function ni(t){let{root:e}=t,n=(await J(["ls-files","--others","--exclude-standard","-z"],{cwd:e,maxBuffer:16*1024*1024})).split("\0").filter(Boolean),r=0;for(let i of n){let s=await ze.lstat(wr(e,i));if(s.isSymbolicLink())throw new h(`${i} is a symbolic link. Commit and push it, then send the rest.`);if(!s.isFile())throw new h(`${i} is a nested repository. Commit and push it separately, then send the rest.`);if(r+=s.size,r>He)throw new h(`Your untracked files alone are over ${He/1024} KB. Commit and push the larger ones, or add them to .gitignore, then send the rest.`)}let o=await ze.mkdtemp(wr(gc(),"anyforge-handoff-"));try{let i=wr(o,"index"),s=Qo(e,(await J(["rev-parse","--git-path","index"],{cwd:e})).trim()),a=await ze.stat(s).catch(()=>null);a!==null&&await ze.copyFile(s,i).then(()=>!0,()=>!1)?await ze.utimes(i,a.atime,a.mtime):await J(["read-tree","HEAD"],{cwd:e,env:{GIT_INDEX_FILE:i}});let d={GIT_INDEX_FILE:i};n.length&&await J(["add","--intent-to-add","--pathspec-from-file=-","--pathspec-file-nul"],{cwd:e,env:d,input:`${n.join("\0")}\0`});let u;try{u=await J(["-c","core.quotePath=true","diff","HEAD","--no-ext-diff","--no-textconv","--no-color","--no-renames","--ignore-submodules=none","--src-prefix=a/","--dst-prefix=b/"],{cwd:e,env:d,maxBuffer:He*4})}catch(k){throw k instanceof yn&&k.code==="ERR_CHILD_PROCESS_STDIO_MAXBUFFER"?new h(`Your uncommitted changes are over the ${He/1024} KB limit. Commit and push the larger part, then send the rest.`):k}if(!u)return{patch:"",files:[]};let l=Jo(u);if(!l.ok)throw new h(l.message);return{patch:u,files:l.files}}finally{await ze.rm(o,{recursive:!0,force:!0})}}async function wc(t,e){let n=Qo(t,(await J(["rev-parse","--git-path",e],{cwd:t})).trim());return ze.access(n).then(()=>!0,()=>!1)}async function yc(t,e){let n=(await J(["remote"],{cwd:t})).split(`
44
- `).filter(Boolean),r=[...n.filter(o=>o==="origin"),...n.filter(o=>o!=="origin")];for(let o of r){let i=await ee(["config","--get",`remote.${o}.url`],{cwd:t});if(i&&Xo(i)?.toLowerCase()===e.toLowerCase())return o}return null}async function ri(t,e){let n=await Zo(t),r=e.repoFullName,o=e.branch;if(!r||!o)throw new h("This session has no repository branch to pick up.");if(o.startsWith("-")||await ee(["check-ref-format","--branch",o],{cwd:n})!==o)throw new f(`The session's branch name is not a valid Git branch: ${JSON.stringify(o)}`);let i=await yc(n,r);if(!i)throw new h(`This checkout is not ${r}. Run pickup from a clone of it.`);for(let m of["MERGE_HEAD","rebase-merge","rebase-apply","CHERRY_PICK_HEAD","REVERT_HEAD"])if(await wc(n,m))throw new h("A merge, rebase, cherry-pick or revert is in progress here. Finish or abort it first.");let s=(await J(["status","--porcelain","--untracked-files=no"],{cwd:n})).split(`
45
- `).filter(Boolean);if(s.length){let m=s.slice(0,5).map(_=>_.slice(3)).join(", ");throw new h(`You have uncommitted changes (${m}${s.length>5?", \u2026":""}). Commit or stash them first; nothing was changed.`)}let a=`refs/remotes/${i}/${o}`;try{await J(["fetch","--no-tags",i,`+refs/heads/${o}:${a}`],{cwd:n,timeout:12e4})}catch(m){let _=m.message;throw/couldn't find remote ref|could not find remote ref/i.test(_)?new h(`The session has not pushed ${o} yet. Push it from Code Studio, or run: anyforge studio push ${e.id} --input '{"expectedHeadSha":"<head>"}'`):new f(`Could not fetch ${o} from ${i}: ${_}`)}let c=(await J(["rev-parse","--verify",`${a}^{commit}`],{cwd:n})).trim(),d=await ee(["rev-parse","--verify","HEAD^{commit}"],{cwd:n}),u=await ee(["rev-parse","--verify","--quiet",`refs/heads/${o}^{commit}`],{cwd:n}),l=await ee(["symbolic-ref","--quiet","--short","HEAD"],{cwd:n});if(u&&u!==c&&await ee(["merge-base","--is-ancestor",u,c],{cwd:n})===null)throw new h(`Your local ${o} has commits the session's branch does not. Nothing was changed. Rebase or merge ${i}/${o} yourself.`);try{u?(l!==o&&await J(["checkout",o,"--"],{cwd:n}),u!==c&&await J(["merge","--ff-only","--no-edit",c],{cwd:n})):await J(["checkout","--track","-b",o,a],{cwd:n})}catch(m){throw new f(`Git refused to check out ${o}: ${m.message}`)}let k=[e.localChanges?.baseCommit,u,d?await ee(["merge-base",d,c],{cwd:n}):null],O=null;for(let m of k)if(m&&/^[0-9a-f]{40}$/.test(m)&&await ee(["merge-base","--is-ancestor",m,c],{cwd:n})!==null){O=m;break}let b=(O&&O!==c?await J(["log","--no-color","--format=%H%x09%s","-n","21",`${O}..${c}`],{cwd:n}):"").split(`
46
- `).filter(Boolean).map(m=>{let[_,...$]=m.split(" ");return{sha:_,subject:$.join(" ")}});return{repoFullName:r,branch:o,headSha:c,created:!u,commits:b.slice(0,20),moreCommits:b.length>20}}var xc=Ac(Oc);function Rc(t){if(!t)return{};try{let e=JSON.parse(t);if(!e||typeof e!="object"||Array.isArray(e))throw new Error("not an object");return e}catch{throw new h("--input must be a JSON object.")}}function de(t){if(!t||!/^[A-Za-z0-9_-]+$/.test(t))throw new h("A valid Studio session or run ID is required.");return encodeURIComponent(t)}async function Ic(t){let[e,n]=await Promise.all([vr.realpath(process.cwd()),vr.realpath(bc(t))]),r=vc(e,n).replaceAll("\\","/");if(!r||kc(r)||r.split("/").includes(".."))throw new h("--file must be inside the current directory, including symlink targets.");let o=await vr.open(n,"r"),i;try{let u=await o.stat();if(!u.isFile()||u.size>2e4)throw new h("--file must be a regular file of 20 KB or smaller; select a smaller context file.");let l=Buffer.alloc(20001),k=0;for(;k<l.length;){let{bytesRead:O}=await o.read(l,k,l.length-k,k);if(!O)break;k+=O}if(k>2e4)throw new h("--file grew beyond 20 KB; select a smaller context file.");i=l.subarray(0,k).toString("utf8")}finally{await o.close()}let s=async u=>{try{return(await xc("git",u,{cwd:process.cwd(),timeout:5e3,maxBuffer:64e3})).stdout.trim()}catch{return null}},[a,c,d]=await Promise.all([s(["branch","--show-current"]),s(["rev-parse","HEAD"]),s(["status","--porcelain","--",r])]);return{content:`Local reference file: ${r}
47
-
48
- ${i}`,localReference:{path:r,branch:a,commit:c,uncommitted:d===null||d.length>0||c===null}}}async function oi(t){let e=Rc(t.input),n=t.command??"list";if(t.uncommitted&&n!=="create")throw new h("--uncommitted is only supported for studio create.");if(n==="pickup")return Tc(t);let r="GET",o;if(n==="list")o="/studio/sessions";else if(n==="create")r="POST",o="/studio/sessions",e.idempotencyKey??=yr(),t.repo&&(e.repoFullName=t.repo),t.branch&&(e.branch=t.branch),t.initiative&&(e.initiativeId=t.initiative),e.provenance={surface:"cli",workspace:"remote"},t.context&&(e.context=t.context);else if(n==="continue")r="POST",o=`/studio/sessions/${de(t.id)}/context`,e.idempotencyKey??=yr(),e.provenance={surface:"cli",workspace:"remote"},t.context&&(e.content=t.context);else if(["get","open","context","usage","changes","artifacts","runs"].includes(n))o=`/studio/sessions/${de(t.id)}${n==="get"||n==="open"?"":`/${n}`}`;else if(n==="run")r="POST",o=`/studio/sessions/${de(t.id)}/runs`,e.idempotencyKey??=yr(),t.context&&(e.content=t.context);else if(n==="events")o=`/studio/runs/${de(t.id)}/events${t.cursor?`?after=${encodeURIComponent(t.cursor)}`:""}`;else if(n==="status")o=`/studio/runs/${de(t.id)}`;else if(["cancel","pause","resume","steer","answer"].includes(n))r="POST",o=`/studio/runs/${de(t.id)}/control`,e.action=n,t.context&&(e.content=t.context);else if(["checkpoint","commit","stage","push"].includes(n))r="POST",o=`/studio/sessions/${de(t.id)}/${n==="checkpoint"?"checkpoints":n}`;else throw new h(`Unknown Studio command: ${n}.`,"Use studio list, create, continue, pickup, open, get, context, usage, run, runs, status, events, pause, resume, cancel, steer, answer, changes, artifacts, checkpoint, stage, commit, or push.");if(t.file){if(!["create","continue"].includes(n))throw new h("--file is only supported for studio create or continue.");let a=await Ic(t.file),c=n==="create"?"context":"content";e[c]=[e[c],a.content].filter(Boolean).join(`
49
-
50
- `),e.provenance={surface:"cli",workspace:"remote",localReference:a.localReference}}let i=t.uncommitted?await Sc(t,e):null,s=await I(t.creds,r,o,r==="POST"?e:void 0);i?.patch?s.localChanges=await Cc(t,String(s.id),i,String(e.idempotencyKey)):i&&process.stderr.write(`No uncommitted changes; the session starts from the pushed branch.
51
- `),n==="open"&&await _c(`${t.creds.endpoint.replace(/\/$/,"")}/studio?session=${de(t.id)}`),process.stdout.write(`${t.json?JSON.stringify(s):p(JSON.stringify(s,null,2))}
52
- `)}async function Sc(t,e){let n=await ei(process.cwd());if(t.repo&&t.repo.toLowerCase()!==n.repoFullName.toLowerCase())throw new h(`--repo ${t.repo} does not match this checkout (${n.repoFullName}).`);if(t.branch&&t.branch!==n.remoteBranch)throw new h(`--branch ${t.branch} does not match this checkout (${n.remoteBranch}).`);await ti(n);let r=await ni(n);if(e.repoFullName=n.repoFullName,e.branch=n.remoteBranch,r.patch){let o=`Uncommitted local changes to ${r.files.length} file${r.files.length===1?"":"s"} were applied to the workspace on top of ${n.head.slice(0,12)}. They are not committed.`;e.context=[e.context,o].filter(Boolean).join(`
53
-
54
- `)}return{...r,baseCommit:n.head}}var Ec=new Set(["local_changes_empty","local_changes_too_large","local_changes_invalid","local_changes_unsafe_path","local_changes_binary","local_changes_unsupported_file","local_changes_secret","local_changes_fresh_session_required","local_changes_base_mismatch","local_changes_do_not_apply","invalid_local_changes","invalid_base_commit","invalid_patch","invalid_idempotencyKey","unknown_field","insufficient_scope","forbidden_role","turn_in_progress"]);async function Cc(t,e,n,r){try{return await I(t.creds,"POST",`/studio/sessions/${de(e)}/local-changes`,{patch:n.patch,baseCommit:n.baseCommit,idempotencyKey:r})}catch(o){let i=o instanceof Error?o.message:String(o),s=o instanceof f?o.exitCode:g.FAILURE;if(!(o instanceof oe&&Ec.has(o.code)))throw new f(`Could not confirm your local changes were applied: ${i}`,s,`Session ${e} was kept. Retry with --input '{"idempotencyKey":"${r}"}' to finish, or check its Changes in Code Studio.`);let a=await I(t.creds,"PATCH",`/studio/sessions/${de(e)}`,{archived:!0}).then(()=>!0,()=>!1);throw new f(`Your local changes were not applied: ${i}`,s,a?`Session ${e} was archived; nothing in it changed.`:`Session ${e} was created without your changes. Archive it in Code Studio.`)}}async function Tc(t){let e=de(t.id),n=await I(t.creds,"GET",`/studio/sessions/${e}`),r=await I(t.creds,"GET",`/studio/sessions/${e}/changes`).catch(()=>null),o=n.latestRunId?await I(t.creds,"GET",`/studio/runs/${de(n.latestRunId)}`).catch(()=>null):null,i=!!n.latestRunId||Number(n.messageCount??0)>0,s=r?.branch||t.branch||(i?null:n.branch);if(!s)throw new f(`Could not read which branch session ${t.id} is working on; it may have moved off ${n.branch??"its starting branch"}. Nothing was changed.`,g.FAILURE,`Retry, or name the branch: anyforge studio pickup ${t.id} --branch <name>`);let a=await ri(process.cwd(),{...n,id:t.id,branch:s}),c=[];r?.exists&&r.clean===!1&&c.push("The session workspace has uncommitted changes that are not on the branch."),(!r||!r.exists&&r.latestCheckpoint)&&c.push("Could not check the session workspace for uncommitted changes. Open its Changes in Code Studio to confirm nothing is left behind."),r?.headSha&&r.headSha!==a.headSha&&c.push(`The session workspace is at ${r.headSha.slice(0,12)}, which is not what ${a.branch} holds. Push it from Code Studio, then pick up again.`),o&&["queued","running","pausing"].includes(String(o.status))&&c.push("A run is still in progress; its later work is not included.");let d={sessionId:t.id,title:n.title??null,...a,latestRun:o?{status:o.status??null,error:o.error??null,waitingOnYou:!!o.pendingQuestion}:null,warnings:c};if(t.json){process.stdout.write(`${JSON.stringify(d)}
55
- `);return}let u=[`${a.created?"Created":"Checked out"} ${a.branch} at ${a.headSha.slice(0,12)} from ${a.repoFullName}.`,`Session: ${n.title??t.id}${o?` (latest run ${o.status}${o.pendingQuestion?", waiting on you":""}${o.error?`: ${o.error}`:""})`:""}`,a.commits.length?`Commits from the session:
56
- ${a.commits.map(l=>` ${l.sha.slice(0,9)} ${l.subject}`).join(`
34
+ `),process.exit(1)),process.stderr.write(Sr(`\u2713 Applied.
35
+ `)),n.files>0&&process.stderr.write(Yt(" Run ")+hi("git diff --stat")+Yt(` to review.
36
+ `))}async function Hc(e){if(e)return await Wc(e,"utf8");let t=[];for await(let n of process.stdin)t.push(n);return Buffer.concat(t).toString("utf8")}function Rr(e,t){return new Promise((n,r)=>{let o=Bc("git",e,{stdio:["pipe","pipe","pipe"]}),i="",s="";o.stdout.on("data",a=>{i+=a.toString()}),o.stderr.on("data",a=>{s+=a.toString()}),o.on("error",r),o.on("close",a=>n({code:a??0,stdout:i,stderr:s})),o.stdin.write(t),o.stdin.end()})}function zc(e){let t=0,n=0,r=0;for(let o of e.split(`
37
+ `))if(o.startsWith("diff --git"))t+=1;else{if(o.startsWith("+++")||o.startsWith("---"))continue;o.startsWith("+")?n+=1:o.startsWith("-")&&(r+=1)}return t===0&&(n>0||r>0)&&(t=(e.match(/^\+\+\+ /gm)??[]).length),{files:t,added:n,removed:r}}import{bold as Kc,dim as _r,green as Yc,red as Jc}from"kleur/colors";async function wi(e){let t=await R(e.creds,"GET",`/hil${F({limit:e.limit})}`);if(e.json){console.log(I(t));return}let n=ue(t);if(n.length===0){console.log(_r("No approvals pending."));return}console.log(B(n,[{header:"ID",value:r=>T(r,"id")},{header:"CREW",value:r=>T(r,"crewId"),max:24},{header:"AGE",value:r=>ii(r.requestedAt)},{header:"SUMMARY",value:r=>te(r,["planSummary","summary","title","description"]),max:60}])),console.log(_r(`
38
+ ${n.length} pending \xB7 approve with `)+Kc("anyforge approve <id>"))}async function Ir(e){if(!e.approvalId)throw new f(`${e.decision==="APPROVED"?"approve":"reject"} requires an approval id.`,"List them with `anyforge approvals`.");let t=await R(e.creds,"POST",`/hil/${encodeURIComponent(e.approvalId)}`,{decision:e.decision,...e.notes?{notes:e.notes}:{}});if(e.json){console.log(I(t));return}let n=e.decision==="APPROVED"?Yc("Approved"):Jc("Rejected");console.log(`${n} ${e.approvalId}`);let r=t&&typeof t=="object"?t.signature:void 0;typeof r=="string"&&r&&console.log(_r(`Audit signature: ${r}`))}import{bold as Vc,dim as Er}from"kleur/colors";async function yi(e){let t=await R(e.creds,"GET",`/crews${F({status:e.status,activeOnly:e.activeOnly,limit:e.limit})}`);if(e.json){console.log(I(t));return}let n=ue(t);if(n.length===0){console.log(Er(e.activeOnly?"No active crews.":"No crews."));return}console.log(B(n,[{header:"ID",value:r=>T(r,"id")},{header:"STATUS",value:r=>T(r,"status")},{header:"TASK",value:r=>te(r,["task"],"\u2014"),max:52},{header:"NOTE",value:r=>te(r,["errorMessage"],""),max:40}]))}async function vi(e){if(!e.crewId)throw new f("crew requires a crew id.","List them with `anyforge crews`.");let t=await R(e.creds,"GET",`/crews/${encodeURIComponent(e.crewId)}${F({messages:e.messages})}`);if(e.json){console.log(I(t));return}let n=t?.crew&&typeof t.crew=="object"?t.crew:t;console.log(`${Vc(Xc(te(n,["taskDescription"],T(n,"id"))))}`),console.log(`Status: ${T(n,"status")}`),console.log(`Repo: ${te(n,["githubRepo"])}`);let r=T(n,"prUrl","");r&&console.log(`PR: ${r}`);let o=T(n,"url","");o&&console.log(`Console: ${o}`);let i=ue(t?.chat);if(i.length>0){console.log(Er(`
39
+ Last ${i.length} messages:`));for(let s of i){let a=te(s,["agentRole","role","author","agent","type"],"?"),c=te(s,["content","body","text","summary"],"");c&&console.log(`${Er(`[${a}]`)} ${kt(c.replace(/\s+/g," "),160)}`)}}}async function Tr(e){if(!e.crewId)throw new f(`${e.action} requires a crew id.`,"List them with `anyforge crews`.");let t=await R(e.creds,"POST",`/crews/${encodeURIComponent(e.crewId)}/${e.action}`);if(e.json){console.log(I(t));return}console.log(`${e.action==="halt"?"Halted":"Resumed"} ${e.crewId}`)}function Xc(e){return kt(e.split(/\r?\n/).find(t=>t.trim())?.trim()??e,80)}import{bold as Qc,dim as Ot,red as bi,yellow as Oi}from"kleur/colors";var Zc=new Set(["COMPLETE","HALTED","REJECTED","CANCELLED"]),el=new Set(["HIL_PENDING","HIL_SHIP","PERMISSION_PENDING","PAUSED"]),tl={HIL_PENDING:"Decide it with `anyforge approvals`, or from the Slack alert.",HIL_SHIP:"Decide it with `anyforge approvals`, or from the Slack alert.",PERMISSION_PENDING:"The agent asked for a tool outside its profile. Allow or deny it in the Console.",PAUSED:"Restart it with `anyforge resume <id>`."};async function Ai(e){if(!e.workItemId)throw new f("crew start requires a work item id.","Find one with `anyforge work --status READY`.");let t=await R(e.creds,"PATCH",`/work/${encodeURIComponent(e.workItemId)}`,{action:"dispatch",...e.profileId?{runProfileId:e.profileId}:{}});if(e.json&&!e.follow){console.log(I(t));return}let n=typeof t?.crewId=="string"?t.crewId:"";if(!n)throw new h("Dispatch was accepted but returned no crew id.",g.FAILURE,"Check `anyforge crews --active` to see whether one started.");for(let r of t?.warnings??[])console.error(Oi(`warning: ${r}`));if(!e.follow){console.log(`Started ${Qc(n)} on ${e.workItemId}`),console.log(Ot(`Follow it with \`anyforge crew follow ${n}\``));return}await Cr({creds:e.creds,json:e.json,crewId:n,intervalSeconds:e.intervalSeconds})}var nl=5,rl=10,ol=120,ki=30;async function Cr(e){if(!e.crewId)throw new f("crew follow requires a crew id.","List them with `anyforge crews --active`.");let t=Math.min(Math.max(e.intervalSeconds??rl,nl),ol)*1e3,n=Date.now()+ki*6e4,r=new Set,o="",i=!1;for(;;){let s=await R(e.creds,"GET",`/crews/${encodeURIComponent(e.crewId)}?messages=50`),a=s?.crew??{},c=T(a,"status","");for(let l of s?.chat??[]){let d=T(l,"id","");!d||r.has(d)||(r.add(d),i&&!e.json&&il(l))}if(i=!0,c&&c!==o&&(e.json||console.log(Ot(`\u2500\u2500 ${c}`)),o=c),el.has(c)){if(e.json)console.log(I({crewId:e.crewId,status:c,waiting:!0,url:T(a,"url","")}));else{console.log(Oi(`
40
+ ${c} \u2014 this crew is waiting on a person.`));let l=tl[c];l&&console.log(Ot(l));let d=T(a,"url","");d&&console.log(Ot(d))}return}if(Zc.has(c)){if(e.json)console.log(I({crewId:e.crewId,status:c,waiting:!1,url:T(a,"url","")}));else{let l=`
41
+ ${c}`;console.log(c==="COMPLETE"?l:bi(l));let d=T(a,"prUrl","");d&&console.log(Ot(d));let u=T(a,"errorMessage","");u&&console.log(bi(u))}if(c!=="COMPLETE")throw new h(`Crew ${e.crewId} ended ${c}.`,g.FAILURE);return}if(Date.now()>=n)throw new h(`Still ${c||"running"} after ${ki} minutes \u2014 giving up watching.`,g.FAILURE,`The crew is unaffected. Check it with \`anyforge crew ${e.crewId}\`.`);await al(t)}}function il(e){let t=T(e,"agentRole","")||T(e,"type","?"),n=sl(e,["content","text","summary"]);n&&console.log(`${Ot(`[${t}]`)} ${kt(n.replace(/\s+/g," "),160)}`)}function sl(e,t){for(let n of t){let r=e[n];if(typeof r=="string"&&r.trim())return r.trim()}return""}function al(e){return new Promise(t=>{setTimeout(t,e)})}import{randomUUID as Lr}from"node:crypto";import{promises as Nr}from"node:fs";import{relative as wl,resolve as yl,isAbsolute as vl}from"node:path";import{execFile as bl}from"node:child_process";import{promisify as kl}from"node:util";import Ol from"open";import{execFile as fl}from"node:child_process";import{promises as et}from"node:fs";import{tmpdir as ml}from"node:os";import{join as Pr,resolve as Ri}from"node:path";var cl=[/sk-ant-[a-zA-Z0-9_-]{20,}/,/sk-(?:proj-)?[a-zA-Z0-9_-]{20,}/,/ghp_[a-zA-Z0-9]{30,}/,/gh[sou]_[a-zA-Z0-9]{30,}/,/github_pat_[a-zA-Z0-9_]{30,}/,/AIza[0-9A-Za-z_-]{30,}/,/AKIA[0-9A-Z]{16}/,/sk_(?:live|test)_[a-zA-Z0-9]{20,}/,/xox[bpaors]-[0-9]+-[0-9]+-[a-zA-Z0-9-]{20,}/,/eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+/,/-----BEGIN [A-Z ]*PRIVATE KEY-----/,/(?:api[_-]?key|secret|password|passwd|token|bearer)[\s:=]+['"]?[a-zA-Z0-9_+/=-]{16,}/i];function ll(e,t){return t.some(n=>n.test(e))}function xi(e){return ll(e,cl)}var Ze=100*1024,he=(e,t)=>({ok:!1,code:e,message:t});function $r(e){if(!e.startsWith('"'))return e;if(e.length<2||!e.endsWith('"'))return null;let t=e.slice(1,-1),n=[],r={a:7,b:8,t:9,n:10,v:11,f:12,r:13,'"':34,"\\":92};for(let o=0;o<t.length;o++){let i=t[o];if(i!=="\\"){n.push(...new TextEncoder().encode(i));continue}let s=t[o+1];if(s===void 0)return null;if(/[0-7]/.test(s)){let a=t.slice(o+1,o+4);if(!/^[0-7]{3}$/.test(a))return null;n.push(parseInt(a,8)),o+=3}else if(s in r)n.push(r[s]),o+=1;else return null}try{return new TextDecoder("utf-8",{fatal:!0}).decode(new Uint8Array(n))}catch{return null}}function dl(e){return!e||e.startsWith("/")||e.includes("\0")||e.includes("\\")?!1:e.split("/").every(t=>t!==""&&t!=="."&&t!==".."&&t.toLowerCase()!==".git")}function ul(e){if(e.startsWith('"')){let n=e.match(/^("(?:[^"\\]|\\.)*") ("(?:[^"\\]|\\.)*")$/);if(!n)return null;let[r,o]=[$r(n[1]),$r(n[2])];return r?.startsWith("a/")&&o?.startsWith("b/")&&r.slice(2)===o.slice(2)?r.slice(2):null}if((e.length-5)%2!==0)return null;let t=e.slice(2,2+(e.length-5)/2);return e===`a/${t} b/${t}`?t:null}function pl(e,t){let n=e.slice(4).replace(/\t$/,"");if(n==="/dev/null")return n;let r=$r(n);return r?.startsWith(t)?r.slice(2):null}function Si(e){if(typeof e!="string"||e.length===0)return he("local_changes_empty","There are no local changes to send.");let t=new TextEncoder().encode(e).length;if(t>Ze)return he("local_changes_too_large",`Your uncommitted changes are ${Math.ceil(t/1024)} KB; the limit is ${Ze/1024} KB. Commit and push the larger part, then send the rest.`);if(!e.startsWith("diff --git "))return he("local_changes_invalid","The local changes must be a patch made by git diff.");let n=[],r=[],o=null,i=!1,s=0;for(let a of e.split(`
42
+ `)){if(a.startsWith("diff --git ")){if(o=ul(a.slice(11)),o===null)return he("local_changes_invalid","The local changes contain a rename, a copy or a header this check cannot read. Commit and push those, then send the rest.");if(!dl(o))return he("local_changes_unsafe_path",`The local changes name a path outside the repository: ${o}`);n.includes(o)||n.push(o),i=!1;continue}if(o===null)return he("local_changes_invalid","The local changes must be a patch made by git diff.");if(i&&/^[ +\-\\]/.test(a)){a.startsWith("+")?(xi(a.slice(1))&&r.length<5&&r.push(`${o}:${s}`),s++):a.startsWith(" ")&&s++;continue}let c=a.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/);if(c){i=!0,s=Number(c[1]);continue}if(i=!1,a==="")continue;if(a.startsWith("GIT binary patch")||/^Binary files .* differ$/.test(a))return he("local_changes_binary",`${o} is a binary file. Commit and push it, then send the rest.`);let l=a.match(/^(?:old mode|new mode|new file mode|deleted file mode) (\d+)$/)??a.match(/^index [0-9a-f]+\.\.[0-9a-f]+ (\d+)$/);if(l){if(l[1]==="120000"||l[1]==="160000")return he("local_changes_unsupported_file",`${o} is a ${l[1]==="120000"?"symbolic link":"submodule"}. Commit and push it, then send the rest.`);continue}if(!/^(?:index [0-9a-f]+\.\.[0-9a-f]+|similarity index \d+%|dissimilarity index \d+%)$/.test(a)){if(a.startsWith("--- ")||a.startsWith("+++ ")){let d=pl(a,a.startsWith("--- ")?"a/":"b/");if(d!=="/dev/null"&&d!==o)return he("local_changes_invalid",`The patch headers for ${o} disagree.`);continue}return he("local_changes_invalid",`The local changes for ${o} contain a line this check cannot read. Send a patch made by git diff without renames.`)}}return r.length?he("local_changes_secret",`Your uncommitted changes add what looks like a credential at ${r.join(", ")}. Remove it, or move it out of the repository, then try again. Nothing was sent.`):{ok:!0,files:n,bytes:t}}var En=class extends Error{constructor(n,r,o){super(r.trim()||`git ${n[0]} failed`);this.args=n;this.stderr=r;this.code=o}args;stderr;code};function X(e,t){return new Promise((n,r)=>{let o=fl("git",e,{cwd:t.cwd,encoding:"utf8",maxBuffer:t.maxBuffer??1048576,timeout:t.timeout??3e4,env:{...process.env,GIT_TERMINAL_PROMPT:"0",...t.env}},(i,s,a)=>{i?r(new En(e,String(a??""),i.code)):n(String(s))});t.input!==void 0&&o.stdin?.end(t.input)})}var ne=(e,t)=>X(e,t).then(n=>n.trim(),()=>null);function _i(e){let t=e.trim().match(/^(?:https:\/\/(?:[^@/]+@)?github\.com\/|ssh:\/\/git@github\.com(?::\d+)?\/|git@github\.com:)([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+?)(?:\.git)?\/?$/);return t?`${t[1]}/${t[2]}`:null}async function Ii(e){let t=await ne(["rev-parse","--show-toplevel"],{cwd:e});if(!t)throw new f("Run this inside a Git checkout of the repository.");return t}async function Ei(e){let t=await Ii(e),n=await ne(["symbolic-ref","--quiet","--short","HEAD"],{cwd:t});if(!n)throw new f("This checkout is not on a branch (detached HEAD). Check out the branch you are working on.");let r=await ne(["rev-parse","--verify","HEAD^{commit}"],{cwd:t});if(!r)throw new f(`${n} has no commits yet. Commit and push it first.`);let o=await ne(["config","--get",`branch.${n}.remote`],{cwd:t})||"origin",i=await ne(["config","--get",`remote.${o}.url`],{cwd:t}),s=i?_i(i):null;if(!s)throw new f(`The remote "${o}" is not a GitHub repository. Code Studio works on GitHub repositories.`);let a=await ne(["config","--get",`branch.${n}.merge`],{cwd:t}),c=a?.startsWith("refs/heads/")?a.slice(11):n;return{root:t,branch:n,head:r,remote:o,remoteBranch:c,repoFullName:s}}async function Ti(e){let t;try{t=await X(["ls-remote","--heads",e.remote,`refs/heads/${e.remoteBranch}`],{cwd:e.root,timeout:6e4})}catch(o){throw new h(`Could not read ${e.remoteBranch} from ${e.remote}: ${o.message}`)}let n=t.split(`
43
+ `).map(o=>o.split(" ")).find(([,o])=>o===`refs/heads/${e.remoteBranch}`)?.[0];if(!n)throw new f(`${e.remoteBranch} is not on ${e.remote} yet. Push it first: git push -u ${e.remote} ${e.branch}`);if(n===e.head)return;throw await ne(["merge-base","--is-ancestor",n,e.head],{cwd:e.root})!==null?new f(`${e.branch} has commits that are not pushed. Push them first: git push ${e.remote} ${e.branch}`):new f(`${e.remote}/${e.remoteBranch} has moved on from your checkout. Pull it, then try again.`)}async function Ci(e){let{root:t}=e,n=(await X(["ls-files","--others","--exclude-standard","-z"],{cwd:t,maxBuffer:16*1024*1024})).split("\0").filter(Boolean),r=0;for(let i of n){let s=await et.lstat(Pr(t,i));if(s.isSymbolicLink())throw new f(`${i} is a symbolic link. Commit and push it, then send the rest.`);if(!s.isFile())throw new f(`${i} is a nested repository. Commit and push it separately, then send the rest.`);if(r+=s.size,r>Ze)throw new f(`Your untracked files alone are over ${Ze/1024} KB. Commit and push the larger ones, or add them to .gitignore, then send the rest.`)}let o=await et.mkdtemp(Pr(ml(),"anyforge-handoff-"));try{let i=Pr(o,"index"),s=Ri(t,(await X(["rev-parse","--git-path","index"],{cwd:t})).trim()),a=await et.stat(s).catch(()=>null);a!==null&&await et.copyFile(s,i).then(()=>!0,()=>!1)?await et.utimes(i,a.atime,a.mtime):await X(["read-tree","HEAD"],{cwd:t,env:{GIT_INDEX_FILE:i}});let l={GIT_INDEX_FILE:i};n.length&&await X(["add","--intent-to-add","--pathspec-from-file=-","--pathspec-file-nul"],{cwd:t,env:l,input:`${n.join("\0")}\0`});let d;try{d=await X(["-c","core.quotePath=true","diff","HEAD","--no-ext-diff","--no-textconv","--no-color","--no-renames","--ignore-submodules=none","--src-prefix=a/","--dst-prefix=b/"],{cwd:t,env:l,maxBuffer:Ze*4})}catch(y){throw y instanceof En&&y.code==="ERR_CHILD_PROCESS_STDIO_MAXBUFFER"?new f(`Your uncommitted changes are over the ${Ze/1024} KB limit. Commit and push the larger part, then send the rest.`):y}if(!d)return{patch:"",files:[]};let u=Si(d);if(!u.ok)throw new f(u.message);return{patch:d,files:u.files}}finally{await et.rm(o,{recursive:!0,force:!0})}}async function hl(e,t){let n=Ri(e,(await X(["rev-parse","--git-path",t],{cwd:e})).trim());return et.access(n).then(()=>!0,()=>!1)}async function gl(e,t){let n=(await X(["remote"],{cwd:e})).split(`
44
+ `).filter(Boolean),r=[...n.filter(o=>o==="origin"),...n.filter(o=>o!=="origin")];for(let o of r){let i=await ne(["config","--get",`remote.${o}.url`],{cwd:e});if(i&&_i(i)?.toLowerCase()===t.toLowerCase())return o}return null}async function $i(e,t){let n=await Ii(e),r=t.repoFullName,o=t.branch;if(!r||!o)throw new f("This session has no repository branch to pick up.");if(o.startsWith("-")||await ne(["check-ref-format","--branch",o],{cwd:n})!==o)throw new h(`The session's branch name is not a valid Git branch: ${JSON.stringify(o)}`);let i=await gl(n,r);if(!i)throw new f(`This checkout is not ${r}. Run pickup from a clone of it.`);for(let m of["MERGE_HEAD","rebase-merge","rebase-apply","CHERRY_PICK_HEAD","REVERT_HEAD"])if(await hl(n,m))throw new f("A merge, rebase, cherry-pick or revert is in progress here. Finish or abort it first.");let s=(await X(["status","--porcelain","--untracked-files=no"],{cwd:n})).split(`
45
+ `).filter(Boolean);if(s.length){let m=s.slice(0,5).map(A=>A.slice(3)).join(", ");throw new f(`You have uncommitted changes (${m}${s.length>5?", \u2026":""}). Commit or stash them first; nothing was changed.`)}let a=`refs/remotes/${i}/${o}`;try{await X(["fetch","--no-tags",i,`+refs/heads/${o}:${a}`],{cwd:n,timeout:12e4})}catch(m){let A=m.message;throw/couldn't find remote ref|could not find remote ref/i.test(A)?new f(`The session has not pushed ${o} yet. Push it from Code Studio, or run: anyforge studio push ${t.id} --input '{"expectedHeadSha":"<head>"}'`):new h(`Could not fetch ${o} from ${i}: ${A}`)}let c=(await X(["rev-parse","--verify",`${a}^{commit}`],{cwd:n})).trim(),l=await ne(["rev-parse","--verify","HEAD^{commit}"],{cwd:n}),d=await ne(["rev-parse","--verify","--quiet",`refs/heads/${o}^{commit}`],{cwd:n}),u=await ne(["symbolic-ref","--quiet","--short","HEAD"],{cwd:n});if(d&&d!==c&&await ne(["merge-base","--is-ancestor",d,c],{cwd:n})===null)throw new f(`Your local ${o} has commits the session's branch does not. Nothing was changed. Rebase or merge ${i}/${o} yourself.`);try{d?(u!==o&&await X(["checkout",o,"--"],{cwd:n}),d!==c&&await X(["merge","--ff-only","--no-edit",c],{cwd:n})):await X(["checkout","--track","-b",o,a],{cwd:n})}catch(m){throw new h(`Git refused to check out ${o}: ${m.message}`)}let y=[t.localChanges?.baseCommit,d,l?await ne(["merge-base",l,c],{cwd:n}):null],v=null;for(let m of y)if(m&&/^[0-9a-f]{40}$/.test(m)&&await ne(["merge-base","--is-ancestor",m,c],{cwd:n})!==null){v=m;break}let b=(v&&v!==c?await X(["log","--no-color","--format=%H%x09%s","-n","21",`${v}..${c}`],{cwd:n}):"").split(`
46
+ `).filter(Boolean).map(m=>{let[A,...$]=m.split(" ");return{sha:A,subject:$.join(" ")}});return{repoFullName:r,branch:o,headSha:c,created:!d,commits:b.slice(0,20),moreCommits:b.length>20}}var Al=kl(bl);function xl(e){if(!e)return{};try{let t=JSON.parse(e);if(!t||typeof t!="object"||Array.isArray(t))throw new Error("not an object");return t}catch{throw new f("--input must be a JSON object.")}}function pe(e){if(!e||!/^[A-Za-z0-9_-]+$/.test(e))throw new f("A valid Studio session or run ID is required.");return encodeURIComponent(e)}async function Sl(e){let[t,n]=await Promise.all([Nr.realpath(process.cwd()),Nr.realpath(yl(e))]),r=wl(t,n).replaceAll("\\","/");if(!r||vl(r)||r.split("/").includes(".."))throw new f("--file must be inside the current directory, including symlink targets.");let o=await Nr.open(n,"r"),i;try{let d=await o.stat();if(!d.isFile()||d.size>2e4)throw new f("--file must be a regular file of 20 KB or smaller; select a smaller context file.");let u=Buffer.alloc(20001),y=0;for(;y<u.length;){let{bytesRead:v}=await o.read(u,y,u.length-y,y);if(!v)break;y+=v}if(y>2e4)throw new f("--file grew beyond 20 KB; select a smaller context file.");i=u.subarray(0,y).toString("utf8")}finally{await o.close()}let s=async d=>{try{return(await Al("git",d,{cwd:process.cwd(),timeout:5e3,maxBuffer:64e3})).stdout.trim()}catch{return null}},[a,c,l]=await Promise.all([s(["branch","--show-current"]),s(["rev-parse","HEAD"]),s(["status","--porcelain","--",r])]);return{content:`Local reference file: ${r}
47
+
48
+ ${i}`,localReference:{path:r,branch:a,commit:c,uncommitted:l===null||l.length>0||c===null}}}async function Pi(e){let t=xl(e.input),n=e.command??"list";if(e.uncommitted&&n!=="create")throw new f("--uncommitted is only supported for studio create.");if(n==="pickup")return El(e);let r="GET",o;if(n==="list")o="/studio/sessions";else if(n==="create")r="POST",o="/studio/sessions",t.idempotencyKey??=Lr(),e.repo&&(t.repoFullName=e.repo),e.branch&&(t.branch=e.branch),e.initiative&&(t.initiativeId=e.initiative),t.provenance={surface:"cli",workspace:"remote"},e.context&&(t.context=e.context);else if(n==="continue")r="POST",o=`/studio/sessions/${pe(e.id)}/context`,t.idempotencyKey??=Lr(),t.provenance={surface:"cli",workspace:"remote"},e.context&&(t.content=e.context);else if(["get","open","context","usage","changes","artifacts","runs"].includes(n))o=`/studio/sessions/${pe(e.id)}${n==="get"||n==="open"?"":`/${n}`}`;else if(n==="run")r="POST",o=`/studio/sessions/${pe(e.id)}/runs`,t.idempotencyKey??=Lr(),e.context&&(t.content=e.context);else if(n==="events")o=`/studio/runs/${pe(e.id)}/events${e.cursor?`?after=${encodeURIComponent(e.cursor)}`:""}`;else if(n==="status")o=`/studio/runs/${pe(e.id)}`;else if(["cancel","pause","resume","steer","answer"].includes(n))r="POST",o=`/studio/runs/${pe(e.id)}/control`,t.action=n,e.context&&(t.content=e.context);else if(["checkpoint","commit","stage","push"].includes(n))r="POST",o=`/studio/sessions/${pe(e.id)}/${n==="checkpoint"?"checkpoints":n}`;else throw new f(`Unknown Studio command: ${n}.`,"Use studio list, create, continue, pickup, open, get, context, usage, run, runs, status, events, pause, resume, cancel, steer, answer, changes, artifacts, checkpoint, stage, commit, or push.");if(e.file){if(!["create","continue"].includes(n))throw new f("--file is only supported for studio create or continue.");let a=await Sl(e.file),c=n==="create"?"context":"content";t[c]=[t[c],a.content].filter(Boolean).join(`
49
+
50
+ `),t.provenance={surface:"cli",workspace:"remote",localReference:a.localReference}}let i=e.uncommitted?await Rl(e,t):null,s=await R(e.creds,r,o,r==="POST"?t:void 0);i?.patch?s.localChanges=await Il(e,String(s.id),i,String(t.idempotencyKey)):i&&process.stderr.write(`No uncommitted changes; the session starts from the pushed branch.
51
+ `),n==="open"&&await Ol(`${e.creds.endpoint.replace(/\/$/,"")}/studio?session=${pe(e.id)}`),process.stdout.write(`${e.json?JSON.stringify(s):p(JSON.stringify(s,null,2))}
52
+ `)}async function Rl(e,t){let n=await Ei(process.cwd());if(e.repo&&e.repo.toLowerCase()!==n.repoFullName.toLowerCase())throw new f(`--repo ${e.repo} does not match this checkout (${n.repoFullName}).`);if(e.branch&&e.branch!==n.remoteBranch)throw new f(`--branch ${e.branch} does not match this checkout (${n.remoteBranch}).`);await Ti(n);let r=await Ci(n);if(t.repoFullName=n.repoFullName,t.branch=n.remoteBranch,r.patch){let o=`Uncommitted local changes to ${r.files.length} file${r.files.length===1?"":"s"} were applied to the workspace on top of ${n.head.slice(0,12)}. They are not committed.`;t.context=[t.context,o].filter(Boolean).join(`
53
+
54
+ `)}return{...r,baseCommit:n.head}}var _l=new Set(["local_changes_empty","local_changes_too_large","local_changes_invalid","local_changes_unsafe_path","local_changes_binary","local_changes_unsupported_file","local_changes_secret","local_changes_fresh_session_required","local_changes_base_mismatch","local_changes_do_not_apply","invalid_local_changes","invalid_base_commit","invalid_patch","invalid_idempotencyKey","unknown_field","insufficient_scope","forbidden_role","turn_in_progress"]);async function Il(e,t,n,r){try{return await R(e.creds,"POST",`/studio/sessions/${pe(t)}/local-changes`,{patch:n.patch,baseCommit:n.baseCommit,idempotencyKey:r})}catch(o){let i=o instanceof Error?o.message:String(o),s=o instanceof h?o.exitCode:g.FAILURE;if(!(o instanceof V&&_l.has(o.code)))throw new h(`Could not confirm your local changes were applied: ${i}`,s,`Session ${t} was kept. Retry with --input '{"idempotencyKey":"${r}"}' to finish, or check its Changes in Code Studio.`);let a=await R(e.creds,"PATCH",`/studio/sessions/${pe(t)}`,{archived:!0}).then(()=>!0,()=>!1);throw new h(`Your local changes were not applied: ${i}`,s,a?`Session ${t} was archived; nothing in it changed.`:`Session ${t} was created without your changes. Archive it in Code Studio.`)}}async function El(e){let t=pe(e.id),n=await R(e.creds,"GET",`/studio/sessions/${t}`),r=await R(e.creds,"GET",`/studio/sessions/${t}/changes`).catch(()=>null),o=n.latestRunId?await R(e.creds,"GET",`/studio/runs/${pe(n.latestRunId)}`).catch(()=>null):null,i=!!n.latestRunId||Number(n.messageCount??0)>0,s=r?.branch||e.branch||(i?null:n.branch);if(!s)throw new h(`Could not read which branch session ${e.id} is working on; it may have moved off ${n.branch??"its starting branch"}. Nothing was changed.`,g.FAILURE,`Retry, or name the branch: anyforge studio pickup ${e.id} --branch <name>`);let a=await $i(process.cwd(),{...n,id:e.id,branch:s}),c=[];r?.exists&&r.clean===!1&&c.push("The session workspace has uncommitted changes that are not on the branch."),(!r||!r.exists&&r.latestCheckpoint)&&c.push("Could not check the session workspace for uncommitted changes. Open its Changes in Code Studio to confirm nothing is left behind."),r?.headSha&&r.headSha!==a.headSha&&c.push(`The session workspace is at ${r.headSha.slice(0,12)}, which is not what ${a.branch} holds. Push it from Code Studio, then pick up again.`),o&&["queued","running","pausing"].includes(String(o.status))&&c.push("A run is still in progress; its later work is not included.");let l={sessionId:e.id,title:n.title??null,...a,latestRun:o?{status:o.status??null,error:o.error??null,waitingOnYou:!!o.pendingQuestion}:null,warnings:c};if(e.json){process.stdout.write(`${JSON.stringify(l)}
55
+ `);return}let d=[`${a.created?"Created":"Checked out"} ${a.branch} at ${a.headSha.slice(0,12)} from ${a.repoFullName}.`,`Session: ${n.title??e.id}${o?` (latest run ${o.status}${o.pendingQuestion?", waiting on you":""}${o.error?`: ${o.error}`:""})`:""}`,a.commits.length?`Commits from the session:
56
+ ${a.commits.map(u=>` ${u.sha.slice(0,9)} ${u.subject}`).join(`
57
57
  `)}${a.moreCommits?`
58
- \u2026`:""}`:"No new commits from the session.",...c.map(l=>`Note: ${l}`)];process.stdout.write(`${p(u.join(`
58
+ \u2026`:""}`:"No new commits from the session.",...c.map(u=>`Note: ${u}`)];process.stdout.write(`${p(d.join(`
59
59
  `))}
60
- `)}import{dim as vn}from"kleur/colors";var si=100;function ii(t){let e=t&&typeof t=="object"?t:{};return{total:typeof e.total=="number"?e.total:null,hasMore:e.hasMore===!0,cursor:typeof e.nextCursor=="string"?e.nextCursor:null}}async function $c(t){let e=s=>`/work${H({status:t.status,type:t.type,priority:t.priority,initiative:t.initiative,program:t.program,search:t.search,assignee:t.assignee,includeArchived:t.includeArchived,limit:t.limit,cursor:s})}`,n=await I(t.creds,"GET",e()),r=le(n),o=ii(n),i=1;if(!t.all)return{rows:r,total:o.total,hasMore:o.hasMore,raw:n,pagesFetched:i};for(;o.hasMore&&o.cursor&&i<si;){let s=await I(t.creds,"GET",e(o.cursor));r.push(...le(s)),o=ii(s),i+=1}return{rows:r,total:o.total,hasMore:o.hasMore,raw:n,pagesFetched:i}}async function ai(t){let e=await $c(t);if(t.json){console.log(S(t.all?{items:e.rows,total:e.total,hasMore:e.hasMore,nextCursor:null}:e.raw));return}let n=e.rows,r=await So(t.creds),o=or(r,"work_item","plural","Work items");if(n.length===0){console.log(vn(`No ${o.toLowerCase()} match that filter.`));return}console.log(B(n,[{header:"ID",value:c=>E(c,"id")},{header:"PRI",value:c=>E(c,"priority","")},{header:"TYPE",value:c=>E(c,"type","")},{header:"STATUS",value:c=>E(c,"status")},{header:"TITLE",value:c=>Z(c,["title","name","summary"]),max:58}]));let i=(n.length===1?or(r,"work_item","singular","work item"):o).toLowerCase(),s=e.total!==null&&e.total!==n.length?`${n.length} of ${e.total} ${o.toLowerCase()}`:`${n.length} ${i}`,a=e.hasMore?t.all?vn(` \u2014 stopped after ${si} pages; narrow the filter`):vn(" \u2014 pass --all for the rest"):"";console.log(`${vn(`
61
- ${s}`)}${a}`)}import{bold as Pc,dim as bn}from"kleur/colors";async function ci(t){let e=await I(t.creds,"GET","/workflows");if(t.json){console.log(S(e));return}let n=le(e);if(n.length===0){console.log(bn("No workflows defined."));return}console.log(B(n,[{header:"ID",value:r=>E(r,"id")},{header:"STATUS",value:r=>Z(r,["status","state"],"")},{header:"NAME",value:r=>Z(r,["name","title"]),max:58}])),console.log(bn(`
62
- Dispatch with `)+Pc("anyforge run <id>"))}async function li(t){if(!t.workflowId)throw new h("run requires a workflow id.","List them with `anyforge workflows`.");let e;if(t.input!==void 0){try{e=JSON.parse(t.input)}catch(o){throw new h(`--input is not valid JSON: ${o instanceof Error?o.message:String(o)}`,`Example: --input '{"repo":"acme/web"}'`)}if(!e||typeof e!="object"||Array.isArray(e))throw new h("--input must be a JSON object.",`Example: --input '{"repo":"acme/web"}'`)}let n=await I(t.creds,"POST",`/workflows/${encodeURIComponent(t.workflowId)}/run`,e===void 0?void 0:{inputs:e});if(t.json){console.log(S(n));return}let r=Z(n??{},["id","runId"],"");console.log(`Queued run ${r||bn("(id not returned)")}`),console.log(bn("Runs pause at gate steps for an operator decision."))}import{dim as Lc}from"kleur/colors";async function di(t){let e=t.question?.trim();if(!e)throw new h("ask requires a question.",'Example: anyforge ask "which initiatives slipped this cycle?"');!t.json&&process.stderr.isTTY&&process.stderr.write(Lc(`Asking the Oracle (this can take a few minutes)\u2026
63
- `));let n=await I(t.creds,"POST","/oracle",{question:e,...t.context?{context:t.context}:{}});if(t.json){console.log(S(n));return}let r=n&&typeof n=="object"?["answer","text","content","response"].map(o=>n[o]).find(o=>typeof o=="string"&&o.trim()):void 0;console.log(typeof r=="string"?r:S(n))}import{bold as br,dim as ui,green as Nc,yellow as qc}from"kleur/colors";function Fc(t){return typeof t=="number"?`${Math.round(t*100)}%`:"\u2014"}async function pi(t){let e=await I(t.creds,"GET",`/backtest-corpus${H({days:t.days})}`);if(t.json){console.log(S(e));return}let n=e.incidentsInWindow??0,r=e.backTestable??0,o=e.windowDays?`last ${e.windowDays}d`:"all time";console.log(`${br(String(r))} of ${br(String(n))} incidents are back-testable `+ui(`(${Fc(e.coverage)} \xB7 ${o})`)),e.viable?console.log(Nc("Enough to run a back-test.")):console.log(qc(`Not yet viable \u2014 ${e.shortfall??0} more needed.`));let i=(e.excluded??[]).filter(s=>s.count>0);i.length>0&&console.log(B(i,[{header:"EXCLUDED",value:s=>String(s.count)},{header:"REASON",value:s=>s.label,max:56}])),e.biggestGap&&console.log(ui(`
64
- Biggest fixable gap: `)+br(`${e.biggestGap.label} (${e.biggestGap.count})`))}import{bold as kn,dim as Mt,green as Uc,yellow as fi}from"kleur/colors";var kr=t=>typeof t=="number"?`${Math.round(t*1e3)/10}%`:"\u2014";async function mi(t){let e=await I(t.creds,"GET",`/review-interception${H({days:t.days})}`);if(t.json){console.log(S(e));return}let n=hn(e.windowDays,e.truncated,e.oldestReviewMs),r=i=>{console.log(i)};if(r(`${kn(String(e.seriousRaised??0))} serious findings raised across ${kn(String(e.prsReviewed??0))} pull requests ${Mt(`(${n})`)}`),e.verdict==="measured")r(`${Uc(String(e.disappearedSerious??0))} no longer raised by the next round \u2014 ${kn(kr(e.interceptionFloor))} to ${kn(kr(e.interceptionCeiling))}`),r(Mt(`floor counts every unmeasured finding as unresolved \xB7 ceiling is over the ${e.measurableSerious??0} that had a later round`));else{r(fi("Not enough findings had a later round to measure a rate."));for(let i of e.inconclusiveReasons??[])r(Mt(` ${i}`))}let o=(e.byLens??[]).filter(i=>i.serious>0);if(o.length>0&&r(B(o,[{header:"LENS",value:i=>i.lens,max:16},{header:"RAISED",value:i=>String(i.raised)},{header:"SERIOUS",value:i=>String(i.serious)},{header:"CHECKED",value:i=>String(i.measurable)},{header:"GONE",value:i=>String(i.disappeared)}])),e.truncated){let i=e.oldestReviewMs?new Date(e.oldestReviewMs).toISOString().slice(0,10):null;r(fi(`
65
- Read cap reached \u2014 these are the most recent reviews${i?` (back to ${i})`:""}, not the whole window. Every count is a lower bound.`))}e.reviews&&e.skippedShare!=null&&e.skippedShare>0&&r(Mt(`
66
- ${e.reviews.skipped} of ${e.reviews.total} reviews were skipped (${kr(e.skippedShare)}) \u2014 drafts, size caps or path policy.`));for(let i of e.caveats??[])r(Mt(`
67
- ${i}`))}import{bold as jt,dim as Ke,green as Mc,red as jc,yellow as Or}from"kleur/colors";var Dc=t=>typeof t=="number"?`${Math.round(t*1e3)/10}%`:"\u2014";function Ar(t){if(typeof t!="number")return"\u2014";if(t<60)return`${t}m`;if(t<1440){let n=Math.floor(t/60);return`${n}h ${Math.round(t-n*60)}m`}let e=Math.floor(t/1440);return`${e}d ${Math.round((t-e*1440)/60)}h`}var Bc={capacity:"a limit fired",policy:"configured off",by_design:"correctly declined",unclassified:"reason not recognised"};async function hi(t){let e=await I(t.creds,"GET",`/review-coverage${H({days:t.days})}`);if(t.json){console.log(S(e));return}let n=s=>{console.log(s)},r=hn(e.windowDays,e.truncated,e.oldestRecordMs);n(`${jt(String(e.pullRequestsReviewed??0))} of ${jt(String(e.pullRequestsSeen??0))} recorded pull requests reviewed ${e.coverageRate!=null?jt(`(${Dc(e.coverageRate)})`):""} ${Ke(`(${r})`)}`),e.repositoriesReviewed&&n(`across ${jt(String(e.repositoriesReviewed))} repositories`),n(Ke("recorded = pull requests AnyForge was enabled to watch, not every pull request you opened")),e.medianLatencyMinutes!=null?(n(`
68
- Feedback reached the author in ${Mc(Ar(e.medianLatencyMinutes))} median, ${jt(Ar(e.p90LatencyMinutes))} at p90 ${Ke(`(${e.latencySamples??0} reviews)`)}`),e.medianOnOpenLatencyMinutes!=null&&n(Ke(` ${Ar(e.medianOnOpenLatencyMinutes)} median on pull-request open (${e.onOpenSamples??0} reviews, no quiet period in the way)`))):n(Or(`
69
- Not enough timed reviews to report a latency figure.`));let o=e.capacityGapPullRequests??0;o>0&&n(`
70
- ${jc(String(o))} pull requests went unreviewed because a limit fired `+Ke("\u2014 a cap or a diff over the size ceiling, not a decision anyone made"));let i=e.bySkipClass??[];i.length>0&&n(B(i,[{header:"WHY UNREVIEWED",value:s=>Bc[s.skipClass]??s.skipClass,max:24},{header:"PRS",value:s=>String(s.pullRequests)},{header:"REASONS",value:s=>s.reasons.join(", "),max:46}])),e.failedOnlyPullRequests&&n(Ke(`${e.failedOnlyPullRequests} pull requests had every review attempt error out.`));for(let s of e.inconclusiveReasons??[])n(Or(`
71
- ${s}`));if(e.truncated){let s=e.oldestRecordMs?new Date(e.oldestRecordMs).toISOString().slice(0,10):null;n(Or(`
72
- Read cap reached \u2014 these are the most recent records${s?` (back to ${s})`:""}, not the whole window. Every count is a lower bound.`))}for(let s of e.caveats??[])n(Ke(`
73
- ${s}`))}import{createHash as Yc}from"node:crypto";import{dim as ki,green as Jc,red as Qc,yellow as Xc}from"kleur/colors";function wi(t){return Wc({eventType:t.eventType,actor:t.actor,payload:t.payload,timestamp:t.timestampMs})}function Wc(t){return JSON.stringify(_r(t))}function _r(t){if(Array.isArray(t))return t.map(_r);if(t===null||typeof t!="object"||Object.getPrototypeOf(t)!==Object.prototype&&Object.getPrototypeOf(t)!==null)return t;let e=t,n={};for(let r of Object.keys(e).sort())n[r]=_r(e[r]);return n}var Gc="";function yi(t,e,n){return t(`${n??""}${Gc}${e}`)}function Hc(t){let e=t.findIndex(n=>(n.sealVersion??1)>=2);return e===-1?t.length:e}function vi(t,e){let n=Hc(t),r=n;for(let o=n;o<t.length;o++){let i=t[o];if(typeof i.hash!="string"||!i.hash)return{valid:!1,verified:o-n,unverifiable:r,brokenAt:o,brokenId:i.id,reason:"missing_hash",detail:`Row ${i.id} carries seal version ${i.sealVersion??1} but no hash.`};if(o>n){let s=t[o-1].hash??null;if((i.previousHash??null)!==s)return{valid:!1,verified:o-n,unverifiable:r,brokenAt:o,brokenId:i.id,reason:"link_mismatch",detail:`Row ${i.id} links to ${gi(i.previousHash)}, but the prior row hashes to ${gi(s)}.`}}if(i.hash!==e(i))return{valid:!1,verified:o-n,unverifiable:r,brokenAt:o,brokenId:i.id,reason:"content_mismatch",detail:`Row ${i.id} does not match its own contents.`}}return{valid:!0,verified:t.length-n,unverifiable:r}}function gi(t){return t?`${t.slice(0,12)}\u2026`:"nothing"}function Kc(t){return t?JSON.parse(JSON.stringify(t)):{}}function Vc(t){return{id:t.id,eventType:t.eventType,actor:t.actor,payload:Kc(t.payload),timestampMs:t.timestampMs,hash:t.hash,previousHash:t.prevHash,...typeof t.sealVersion=="number"?{sealVersion:t.sealVersion}:{}}}function bi(t,e,n={}){let r=t.map(Vc),o=t.length>0?t[t.length-1].hash??null:null,i=n.anchorHash;return i&&r.length>0&&(r[0].sealVersion??1)>=2&&(r[0].previousHash??null)!==i?{valid:!1,verified:0,unverifiable:0,brokenAt:0,brokenId:r[0].id,reason:"link_mismatch",detail:`Entry ${r[0].id} does not link onto the previous page's last hash.`,lastHash:o}:{...vi(r,a=>yi(e,wi({eventType:a.eventType,actor:a.actor,payload:a.payload,timestampMs:a.timestampMs}),a.previousHash??null)),lastHash:o}}var Zc=200,el=t=>Yc("sha256").update(t).digest("hex");function tl(t,e=el){let n=null,r=0,o=0;for(let i of t){if(i.length===0)continue;let s=bi(i,e,{anchorHash:n});if(r+=s.verified,o+=s.unverifiable,!s.valid)return{valid:!1,verified:r,unverifiable:o,brokenId:s.brokenId,reason:s.reason,detail:s.detail};n=s.lastHash}return{valid:!0,verified:r,unverifiable:o}}async function Oi(t){if(t.verify&&(t.eventType||t.actor))throw new h("--verify needs an unfiltered read.","Drop --event and --actor: a filtered page skips rows, so its chain links cannot meet. --from and --to are fine.");let e=c=>`/audit${H({eventType:t.eventType,actor:t.actor,from:t.from,to:t.to,limit:t.limit,cursor:c})}`,n=[],r=t.cursor;do{let c=await I(t.creds,"GET",e(r));n.push(c),r=c.hasMore&&c.nextCursor?c.nextCursor:void 0}while(t.all&&r&&n.length<Zc);let o=n.flatMap(c=>c.entries),i=n[n.length-1],s=t.verify?tl(n.map(c=>c.entries)):null,a=c=>{console.log(c)};if(t.json)a(S(n.length===1&&!s?i:{entries:o,nextCursor:i.nextCursor,hasMore:i.hasMore,chainContiguous:i.chainContiguous,pages:n.length,...s?{verification:s}:{}}));else{o.length===0?a("No audit entries match."):a(B(o,[{header:"SEQUENCE",value:d=>String(d.sequence)},{header:"TIME",value:d=>d.timestamp},{header:"EVENT",value:d=>d.eventType,max:34},{header:"ACTOR",value:d=>d.actor,max:28},{header:"HASH",value:d=>d.hash?`${d.hash.slice(0,12)}\u2026`:"\u2014"}]));let c=n.length===1?"page":"pages";a(ki(`${o.length} entries from ${n.length} ${c}.`)),i.hasMore&&i.nextCursor&&a(Xc(`More entries exist. Continue with --cursor ${i.nextCursor}${t.all?"":" or pass --all"}.`)),s&&(s.valid?a(Jc(`Chain verified: ${s.verified} sealed entries link and recompute.`)+(s.unverifiable?ki(` ${s.unverifiable} earlier entries predate sealing and cannot be verified.`):"")):a(Qc(`Chain break at ${s.brokenId}: ${s.reason}.`)+(s.detail?` ${s.detail}`:"")))}if(s&&!s.valid)throw new f(`Audit chain verification failed at ${s.brokenId??"an entry"} (${s.reason??"unknown"}).`,g.VERIFICATION_FAILED)}import{dim as Ai}from"kleur/colors";var nl=["invite","invitations","withdraw"];function _i(t,e){if(!t.email)throw new h(`people ${e} requires an email address.`,`Example: anyforge people ${e} person@example.com`);return t.email}function rl(t){if(t===void 0)return;let e=t.split(",").map(n=>n.trim()).filter(Boolean);return e.length?e:void 0}function Ve(t){console.log(t)}async function xi(t){switch(t.subcommand){case"invite":{let e=_i(t,"invite");if(!t.role)throw new h("people invite requires --role.","One of operator, editor, viewer or filer.");let n=rl(t.team),r=await I(t.creds,"POST","/people/invitations",{email:e,role:t.role,...n?{teamIds:n}:{},...t.notify?{}:{notify:!1}});if(t.json){Ve(S(r));return}let o=r.created?"Invited":"Refreshed the pending invitation for",i=r.emailSent?"Invitation email sent.":"No invitation email sent.";Ve(`${o} ${r.email} as ${r.role}. ${Ai(i)}`);return}case"invitations":{let e=await I(t.creds,"GET","/people/invitations");if(t.json){Ve(S(e));return}let n=le(e);if(n.length===0){Ve(Ai("No invitations."));return}Ve(B(n,[{header:"EMAIL",value:r=>E(r,"email"),max:48},{header:"ROLE",value:r=>E(r,"role")},{header:"STATUS",value:r=>E(r,"status")},{header:"TEAMS",value:r=>Array.isArray(r.teamIds)&&r.teamIds.length?r.teamIds.join(","):"\u2014",max:32}]));return}case"withdraw":{let e=_i(t,"withdraw"),n=await I(t.creds,"DELETE",`/people/invitations${H({email:e})}`);if(t.json){Ve(S(n));return}Ve(`Withdrew the invitation for ${n.email}.`);return}default:throw new h(t.subcommand?`Unknown people command: ${t.subcommand}`:"people needs a command.",`One of: ${nl.join(", ")}.`)}}import{promises as Dt}from"node:fs";import{homedir as al}from"node:os";import{dirname as Ii,join as Ye}from"node:path";import{dim as Si,green as cl,yellow as ll}from"kleur/colors";var ye="anyforge-platform",ol="Orient an agent to AnyForge's structure, delivery model, surfaces, live control tools, and safety boundaries.",Ri="anyforge://guide/platform",il="text/markdown",On={"SKILL.md":`---
60
+ `)}import{dim as Tn}from"kleur/colors";var Ni=100;function Li(e){let t=e&&typeof e=="object"?e:{};return{total:typeof t.total=="number"?t.total:null,hasMore:t.hasMore===!0,cursor:typeof t.nextCursor=="string"?t.nextCursor:null}}async function Tl(e){let t=s=>`/work${F({status:e.status,type:e.type,priority:e.priority,initiative:e.initiative,program:e.program,search:e.search,assignee:e.assignee,includeArchived:e.includeArchived,limit:e.limit,cursor:s})}`,n=await R(e.creds,"GET",t()),r=ue(n),o=Li(n),i=1;if(!e.all)return{rows:r,total:o.total,hasMore:o.hasMore,raw:n,pagesFetched:i};for(;o.hasMore&&o.cursor&&i<Ni;){let s=await R(e.creds,"GET",t(o.cursor));r.push(...ue(s)),o=Li(s),i+=1}return{rows:r,total:o.total,hasMore:o.hasMore,raw:n,pagesFetched:i}}async function Fi(e){let t=await Tl(e);if(e.json){console.log(I(e.all?{items:t.rows,total:t.total,hasMore:t.hasMore,nextCursor:null}:t.raw));return}let n=t.rows,r=await ri(e.creds),o=br(r,"work_item","plural","Work items");if(n.length===0){console.log(Tn(`No ${o.toLowerCase()} match that filter.`));return}console.log(B(n,[{header:"ID",value:c=>T(c,"id")},{header:"PRI",value:c=>T(c,"priority","")},{header:"TYPE",value:c=>T(c,"type","")},{header:"STATUS",value:c=>T(c,"status")},{header:"TITLE",value:c=>te(c,["title","name","summary"]),max:58}]));let i=(n.length===1?br(r,"work_item","singular","work item"):o).toLowerCase(),s=t.total!==null&&t.total!==n.length?`${n.length} of ${t.total} ${o.toLowerCase()}`:`${n.length} ${i}`,a=t.hasMore?e.all?Tn(` \u2014 stopped after ${Ni} pages; narrow the filter`):Tn(" \u2014 pass --all for the rest"):"";console.log(`${Tn(`
61
+ ${s}`)}${a}`)}import{bold as Cl,dim as Cn}from"kleur/colors";async function qi(e){let t=await R(e.creds,"GET","/workflows");if(e.json){console.log(I(t));return}let n=ue(t);if(n.length===0){console.log(Cn("No workflows defined."));return}console.log(B(n,[{header:"ID",value:r=>T(r,"id")},{header:"STATUS",value:r=>te(r,["status","state"],"")},{header:"NAME",value:r=>te(r,["name","title"]),max:58}])),console.log(Cn(`
62
+ Dispatch with `)+Cl("anyforge run <id>"))}async function Mi(e){if(!e.workflowId)throw new f("run requires a workflow id.","List them with `anyforge workflows`.");let t;if(e.input!==void 0){try{t=JSON.parse(e.input)}catch(o){throw new f(`--input is not valid JSON: ${o instanceof Error?o.message:String(o)}`,`Example: --input '{"repo":"acme/web"}'`)}if(!t||typeof t!="object"||Array.isArray(t))throw new f("--input must be a JSON object.",`Example: --input '{"repo":"acme/web"}'`)}let n=await R(e.creds,"POST",`/workflows/${encodeURIComponent(e.workflowId)}/run`,t===void 0?void 0:{inputs:t});if(e.json){console.log(I(n));return}let r=te(n??{},["id","runId"],"");console.log(`Queued run ${r||Cn("(id not returned)")}`),console.log(Cn("Runs pause at gate steps for an operator decision."))}import{dim as $l}from"kleur/colors";async function Ui(e){let t=e.question?.trim();if(!t)throw new f("ask requires a question.",'Example: anyforge ask "which initiatives slipped this cycle?"');!e.json&&process.stderr.isTTY&&process.stderr.write($l(`Asking the Oracle (this can take a few minutes)\u2026
63
+ `));let n=await R(e.creds,"POST","/oracle",{question:t,...e.context?{context:e.context}:{}});if(e.json){console.log(I(n));return}let r=n&&typeof n=="object"?["answer","text","content","response"].map(o=>n[o]).find(o=>typeof o=="string"&&o.trim()):void 0;console.log(typeof r=="string"?r:I(n))}import{bold as Fr,dim as ji,green as Pl,yellow as Ll}from"kleur/colors";function Nl(e){return typeof e=="number"?`${Math.round(e*100)}%`:"\u2014"}async function Di(e){let t=await R(e.creds,"GET",`/backtest-corpus${F({days:e.days})}`);if(e.json){console.log(I(t));return}let n=t.incidentsInWindow??0,r=t.backTestable??0,o=t.windowDays?`last ${t.windowDays}d`:"all time";console.log(`${Fr(String(r))} of ${Fr(String(n))} incidents are back-testable `+ji(`(${Nl(t.coverage)} \xB7 ${o})`)),t.viable?console.log(Pl("Enough to run a back-test.")):console.log(Ll(`Not yet viable \u2014 ${t.shortfall??0} more needed.`));let i=(t.excluded??[]).filter(s=>s.count>0);i.length>0&&console.log(B(i,[{header:"EXCLUDED",value:s=>String(s.count)},{header:"REASON",value:s=>s.label,max:56}])),t.biggestGap&&console.log(ji(`
64
+ Biggest fixable gap: `)+Fr(`${t.biggestGap.label} (${t.biggestGap.count})`))}import{bold as $n,dim as Jt,green as Fl,yellow as Bi}from"kleur/colors";var qr=e=>typeof e=="number"?`${Math.round(e*1e3)/10}%`:"\u2014";async function Wi(e){let t=await R(e.creds,"GET",`/review-interception${F({days:e.days})}`);if(e.json){console.log(I(t));return}let n=Rn(t.windowDays,t.truncated,t.oldestReviewMs),r=i=>{console.log(i)};if(r(`${$n(String(t.seriousRaised??0))} serious findings raised across ${$n(String(t.prsReviewed??0))} pull requests ${Jt(`(${n})`)}`),t.verdict==="measured")r(`${Fl(String(t.disappearedSerious??0))} no longer raised by the next round \u2014 ${$n(qr(t.interceptionFloor))} to ${$n(qr(t.interceptionCeiling))}`),r(Jt(`floor counts every unmeasured finding as unresolved \xB7 ceiling is over the ${t.measurableSerious??0} that had a later round`));else{r(Bi("Not enough findings had a later round to measure a rate."));for(let i of t.inconclusiveReasons??[])r(Jt(` ${i}`))}let o=(t.byLens??[]).filter(i=>i.serious>0);if(o.length>0&&r(B(o,[{header:"LENS",value:i=>i.lens,max:16},{header:"RAISED",value:i=>String(i.raised)},{header:"SERIOUS",value:i=>String(i.serious)},{header:"CHECKED",value:i=>String(i.measurable)},{header:"GONE",value:i=>String(i.disappeared)}])),t.truncated){let i=t.oldestReviewMs?new Date(t.oldestReviewMs).toISOString().slice(0,10):null;r(Bi(`
65
+ Read cap reached \u2014 these are the most recent reviews${i?` (back to ${i})`:""}, not the whole window. Every count is a lower bound.`))}t.reviews&&t.skippedShare!=null&&t.skippedShare>0&&r(Jt(`
66
+ ${t.reviews.skipped} of ${t.reviews.total} reviews were skipped (${qr(t.skippedShare)}) \u2014 drafts, size caps or path policy.`));for(let i of t.caveats??[])r(Jt(`
67
+ ${i}`))}import{bold as Vt,dim as tt,green as ql,red as Ml,yellow as Mr}from"kleur/colors";var Ul=e=>typeof e=="number"?`${Math.round(e*1e3)/10}%`:"\u2014";function Ur(e){if(typeof e!="number")return"\u2014";if(e<60)return`${e}m`;if(e<1440){let n=Math.floor(e/60);return`${n}h ${Math.round(e-n*60)}m`}let t=Math.floor(e/1440);return`${t}d ${Math.round((e-t*1440)/60)}h`}var jl={capacity:"a limit fired",policy:"configured off",by_design:"correctly declined",unclassified:"reason not recognised"};async function Gi(e){let t=await R(e.creds,"GET",`/review-coverage${F({days:e.days})}`);if(e.json){console.log(I(t));return}let n=s=>{console.log(s)},r=Rn(t.windowDays,t.truncated,t.oldestRecordMs);n(`${Vt(String(t.pullRequestsReviewed??0))} of ${Vt(String(t.pullRequestsSeen??0))} recorded pull requests reviewed ${t.coverageRate!=null?Vt(`(${Ul(t.coverageRate)})`):""} ${tt(`(${r})`)}`),t.repositoriesReviewed&&n(`across ${Vt(String(t.repositoriesReviewed))} repositories`),n(tt("recorded = pull requests AnyForge was enabled to watch, not every pull request you opened")),t.medianLatencyMinutes!=null?(n(`
68
+ Feedback reached the author in ${ql(Ur(t.medianLatencyMinutes))} median, ${Vt(Ur(t.p90LatencyMinutes))} at p90 ${tt(`(${t.latencySamples??0} reviews)`)}`),t.medianOnOpenLatencyMinutes!=null&&n(tt(` ${Ur(t.medianOnOpenLatencyMinutes)} median on pull-request open (${t.onOpenSamples??0} reviews, no quiet period in the way)`))):n(Mr(`
69
+ Not enough timed reviews to report a latency figure.`));let o=t.capacityGapPullRequests??0;o>0&&n(`
70
+ ${Ml(String(o))} pull requests went unreviewed because a limit fired `+tt("\u2014 a cap or a diff over the size ceiling, not a decision anyone made"));let i=t.bySkipClass??[];i.length>0&&n(B(i,[{header:"WHY UNREVIEWED",value:s=>jl[s.skipClass]??s.skipClass,max:24},{header:"PRS",value:s=>String(s.pullRequests)},{header:"REASONS",value:s=>s.reasons.join(", "),max:46}])),t.failedOnlyPullRequests&&n(tt(`${t.failedOnlyPullRequests} pull requests had every review attempt error out.`));for(let s of t.inconclusiveReasons??[])n(Mr(`
71
+ ${s}`));if(t.truncated){let s=t.oldestRecordMs?new Date(t.oldestRecordMs).toISOString().slice(0,10):null;n(Mr(`
72
+ Read cap reached \u2014 these are the most recent records${s?` (back to ${s})`:""}, not the whole window. Every count is a lower bound.`))}for(let s of t.caveats??[])n(tt(`
73
+ ${s}`))}import{createHash as Kl}from"node:crypto";import{dim as Vi,green as Yl,red as Jl,yellow as Vl}from"kleur/colors";function zi(e){return Dl({eventType:e.eventType,actor:e.actor,payload:e.payload,timestamp:e.timestampMs})}function Dl(e){return JSON.stringify(jr(e))}function jr(e){if(Array.isArray(e))return e.map(jr);if(e===null||typeof e!="object"||Object.getPrototypeOf(e)!==Object.prototype&&Object.getPrototypeOf(e)!==null)return e;let t=e,n={};for(let r of Object.keys(t).sort())n[r]=jr(t[r]);return n}var Bl="";function Ki(e,t,n){return e(`${n??""}${Bl}${t}`)}function Wl(e){let t=e.findIndex(n=>(n.sealVersion??1)>=2);return t===-1?e.length:t}function Yi(e,t){let n=Wl(e),r=n;for(let o=n;o<e.length;o++){let i=e[o];if(typeof i.hash!="string"||!i.hash)return{valid:!1,verified:o-n,unverifiable:r,brokenAt:o,brokenId:i.id,reason:"missing_hash",detail:`Row ${i.id} carries seal version ${i.sealVersion??1} but no hash.`};if(o>n){let s=e[o-1].hash??null;if((i.previousHash??null)!==s)return{valid:!1,verified:o-n,unverifiable:r,brokenAt:o,brokenId:i.id,reason:"link_mismatch",detail:`Row ${i.id} links to ${Hi(i.previousHash)}, but the prior row hashes to ${Hi(s)}.`}}if(i.hash!==t(i))return{valid:!1,verified:o-n,unverifiable:r,brokenAt:o,brokenId:i.id,reason:"content_mismatch",detail:`Row ${i.id} does not match its own contents.`}}return{valid:!0,verified:e.length-n,unverifiable:r}}function Hi(e){return e?`${e.slice(0,12)}\u2026`:"nothing"}function Hl(e){return e?JSON.parse(JSON.stringify(e)):{}}function zl(e){return{id:e.id,eventType:e.eventType,actor:e.actor,payload:Hl(e.payload),timestampMs:e.timestampMs,hash:e.hash,previousHash:e.prevHash,...typeof e.sealVersion=="number"?{sealVersion:e.sealVersion}:{}}}function Ji(e,t,n={}){let r=e.map(zl),o=e.length>0?e[e.length-1].hash??null:null,i=n.anchorHash;return i&&r.length>0&&(r[0].sealVersion??1)>=2&&(r[0].previousHash??null)!==i?{valid:!1,verified:0,unverifiable:0,brokenAt:0,brokenId:r[0].id,reason:"link_mismatch",detail:`Entry ${r[0].id} does not link onto the previous page's last hash.`,lastHash:o}:{...Yi(r,a=>Ki(t,zi({eventType:a.eventType,actor:a.actor,payload:a.payload,timestampMs:a.timestampMs}),a.previousHash??null)),lastHash:o}}var Xl=200,Ql=e=>Kl("sha256").update(e).digest("hex");function Zl(e,t=Ql){let n=null,r=0,o=0;for(let i of e){if(i.length===0)continue;let s=Ji(i,t,{anchorHash:n});if(r+=s.verified,o+=s.unverifiable,!s.valid)return{valid:!1,verified:r,unverifiable:o,brokenId:s.brokenId,reason:s.reason,detail:s.detail};n=s.lastHash}return{valid:!0,verified:r,unverifiable:o}}async function Xi(e){if(e.verify&&(e.eventType||e.actor))throw new f("--verify needs an unfiltered read.","Drop --event and --actor: a filtered page skips rows, so its chain links cannot meet. --from and --to are fine.");let t=c=>`/audit${F({eventType:e.eventType,actor:e.actor,from:e.from,to:e.to,limit:e.limit,cursor:c})}`,n=[],r=e.cursor;do{let c=await R(e.creds,"GET",t(r));n.push(c),r=c.hasMore&&c.nextCursor?c.nextCursor:void 0}while(e.all&&r&&n.length<Xl);let o=n.flatMap(c=>c.entries),i=n[n.length-1],s=e.verify?Zl(n.map(c=>c.entries)):null,a=c=>{console.log(c)};if(e.json)a(I(n.length===1&&!s?i:{entries:o,nextCursor:i.nextCursor,hasMore:i.hasMore,chainContiguous:i.chainContiguous,pages:n.length,...s?{verification:s}:{}}));else{o.length===0?a("No audit entries match."):a(B(o,[{header:"SEQUENCE",value:l=>String(l.sequence)},{header:"TIME",value:l=>l.timestamp},{header:"EVENT",value:l=>l.eventType,max:34},{header:"ACTOR",value:l=>l.actor,max:28},{header:"HASH",value:l=>l.hash?`${l.hash.slice(0,12)}\u2026`:"\u2014"}]));let c=n.length===1?"page":"pages";a(Vi(`${o.length} entries from ${n.length} ${c}.`)),i.hasMore&&i.nextCursor&&a(Vl(`More entries exist. Continue with --cursor ${i.nextCursor}${e.all?"":" or pass --all"}.`)),s&&(s.valid?a(Yl(`Chain verified: ${s.verified} sealed entries link and recompute.`)+(s.unverifiable?Vi(` ${s.unverifiable} earlier entries predate sealing and cannot be verified.`):"")):a(Jl(`Chain break at ${s.brokenId}: ${s.reason}.`)+(s.detail?` ${s.detail}`:"")))}if(s&&!s.valid)throw new h(`Audit chain verification failed at ${s.brokenId??"an entry"} (${s.reason??"unknown"}).`,g.VERIFICATION_FAILED)}import{dim as Qi}from"kleur/colors";var ed=["invite","invitations","withdraw"];function Zi(e,t){if(!e.email)throw new f(`people ${t} requires an email address.`,`Example: anyforge people ${t} person@example.com`);return e.email}function td(e){if(e===void 0)return;let t=e.split(",").map(n=>n.trim()).filter(Boolean);return t.length?t:void 0}function nt(e){console.log(e)}async function es(e){switch(e.subcommand){case"invite":{let t=Zi(e,"invite");if(!e.role)throw new f("people invite requires --role.","One of operator, editor, viewer or filer.");let n=td(e.team),r=await R(e.creds,"POST","/people/invitations",{email:t,role:e.role,...n?{teamIds:n}:{},...e.notify?{}:{notify:!1}});if(e.json){nt(I(r));return}let o=r.created?"Invited":"Refreshed the pending invitation for",i=r.emailSent?"Invitation email sent.":"No invitation email sent.";nt(`${o} ${r.email} as ${r.role}. ${Qi(i)}`);return}case"invitations":{let t=await R(e.creds,"GET","/people/invitations");if(e.json){nt(I(t));return}let n=ue(t);if(n.length===0){nt(Qi("No invitations."));return}nt(B(n,[{header:"EMAIL",value:r=>T(r,"email"),max:48},{header:"ROLE",value:r=>T(r,"role")},{header:"STATUS",value:r=>T(r,"status")},{header:"TEAMS",value:r=>Array.isArray(r.teamIds)&&r.teamIds.length?r.teamIds.join(","):"\u2014",max:32}]));return}case"withdraw":{let t=Zi(e,"withdraw"),n=await R(e.creds,"DELETE",`/people/invitations${F({email:t})}`);if(e.json){nt(I(n));return}nt(`Withdrew the invitation for ${n.email}.`);return}default:throw new f(e.subcommand?`Unknown people command: ${e.subcommand}`:"people needs a command.",`One of: ${ed.join(", ")}.`)}}import{promises as ts}from"node:fs";import{dim as Pn}from"kleur/colors";var nd=["week","book"],Ln=256e3;function rt(e){console.log(e)}function rd(e=new Date){let t=n=>String(n).padStart(2,"0");return`${e.getFullYear()}-${t(e.getMonth()+1)}-${t(e.getDate())}`}function ns(e){return`${Math.floor(e/60)}:${String(e%60).padStart(2,"0")}`}async function od(){let e=[],t=0;for await(let n of process.stdin){let r=Buffer.isBuffer(n)?n:Buffer.from(String(n));if(t+=r.length,t>Ln)throw new f(`Standard input is larger than ${Ln} bytes.`);e.push(r)}return Buffer.concat(e).toString("utf8")}function rs(e){let t;try{t=JSON.parse(e)}catch{throw new f("The entries are not valid JSON.",'Send a list: [{"initiativeId":"\u2026","date":"YYYY-MM-DD","minutes":90}]')}let n=Array.isArray(t)?t:t&&typeof t=="object"&&Array.isArray(t.entries)?t.entries:null;if(!n)throw new f('The entries must be a JSON list, or an object with an "entries" list.');return n}async function id(e){if(!e.entries)throw new f("time book requires --entries.","Example: anyforge time book --entries week.json (or --entries - to read standard input)");if(e.entries==="-")return rs(await(e.readStdin??od)());let t=await ts.stat(e.entries).catch(()=>null);if(!t||!t.isFile())throw new f(`--entries ${e.entries} is not a file.`);if(t.size>Ln)throw new f(`--entries ${e.entries} is larger than ${Ln} bytes.`);return rs(await ts.readFile(e.entries,"utf8"))}async function os(e){let t=e.today??rd();switch(e.subcommand){case"week":{let n=await R(e.creds,"GET",`/timesheets/week${F({date:e.date,today:t})}`);if(e.json){rt(I(n));return}let r=new Map([...n.rows,...n.addable,...n.categories].map(s=>[s.initiativeId,s.code?`${s.code} ${s.name}`:s.name]));if(rt(Pn(`Week of ${n.days[0]} to ${n.days[n.days.length-1]}`)),n.entries.length===0){rt(Pn("No time booked this week."));return}let o=[...n.entries].sort((s,a)=>s.date.localeCompare(a.date)||s.initiativeId.localeCompare(a.initiativeId));rt(B(o,[{header:"DATE",value:s=>s.date},{header:"INITIATIVE",value:s=>r.get(s.initiativeId)??s.initiativeId,max:48},{header:"ID",value:s=>s.initiativeId,max:32},{header:"HOURS",value:s=>ns(s.minutes)},{header:"SOURCE",value:s=>s.source},{header:"NOTE",value:s=>s.note??"\u2014",max:40}]));let i=o.reduce((s,a)=>s+a.minutes,0);rt(Pn(`Total ${ns(i)}`));return}case"book":{let n=await id(e),r=await R(e.creds,"PUT","/timesheets/entries",{entries:n,today:t});if(e.json){rt(I(r));return}rt(r.written===0?Pn("Nothing to write."):`Wrote ${r.written} ${r.written===1?"cell":"cells"}.`);return}default:throw new f(e.subcommand?`Unknown time command: ${e.subcommand}`:"time needs a command.",`One of: ${nd.join(", ")}.`)}}import{promises as it}from"node:fs";import{homedir as gd}from"node:os";import{dirname as Yr,join as Te}from"node:path";import{dim as ge,green as Jr,red as wd,yellow as vs}from"kleur/colors";var ke="anyforge-platform",sd="Orient an agent to AnyForge's structure, delivery model, surfaces, live control tools, and safety boundaries.",is="anyforge://guide/platform",ad="text/markdown",Nn={"SKILL.md":`---
74
74
  name: anyforge-platform
75
75
  description: >
76
76
  Orient an agent to AnyForge's structure, delivery model, surfaces, live control
@@ -416,65 +416,78 @@ these sources can grant network, filesystem, tool, or approval authority.
416
416
  Do not place API keys, tokens, credentials, private tenant records, or customer
417
417
  data in a reusable skill, prompt, log, or generated example. Use the client's
418
418
  credential mechanism and let the server enforce access.
419
- `,"references/tool-map.md":'# AnyForge MCP tool map\n\nTool names can grow as the platform grows. Use `tools/list` and the current\ntool description as the contract. The names below are representative entry\npoints in the public MCP server.\n\n## Discover and inspect\n\n- `anyforge_list_repo_index` and `anyforge_get_repo_analysis` discover connected\n repositories and their analysis.\n- `anyforge_list_programs` and `anyforge_get_program` inspect program records.\n- `anyforge_list_initiatives` and `anyforge_get_initiative` inspect scoped work.\n- `anyforge_list_work_items` and `anyforge_get_work_item` inspect delivery\n units.\n- `anyforge_list_crews` and `anyforge_get_crew` inspect Crew runs and output.\n For where one crew stands, call `anyforge_get_crew` with `messages: 0`: the\n crew leads with `status`, the halt reason, its work item and pull request,\n and the default summary shape leaves out the large snapshots (named in\n `omittedFields`). Pass `shape: "full"` only for a left-out field.\n- `anyforge_list_pending_approvals` and `anyforge_get_approval_detail` inspect\n human gates.\n- `anyforge_ask_oracle` asks a read-oriented cross-workspace question. A long investigation returns `running` with an `askId`; collect it with `anyforge_get_oracle_answer` rather than asking again. A turn that runs out of time is continued server-side under the same `askId` (`maxContinuations`, default 2), so a long answer arrives whole without a "continue" ask.\n\n### Find a work item by code\n\nFor a known full code, call `anyforge_list_work_items` with `itemId`:\n\n```json\n{ "itemId": "WBA-004", "limit": 5 }\n```\n\nUse the returned item\'s `id` with `anyforge_get_work_item` for its full details.\nFor a partial code or title, use `search` instead, for example\n`{ "search": "WBA-", "limit": 20 }`. Search is case-insensitive and matches\nsubstrings, so check the returned code and title before selecting a record.\nArchived items are excluded by default, including code lookups; add\n`"includeArchived": true` when looking for archived work. Check `hasMore` and\nfollow `nextCursor` with unchanged filters when there are more results.\n\n## Plan and write\n\n- `anyforge_create_program` and `anyforge_update_program` manage program\n records, including the lead and whether the program is long-lived;\n `anyforge_set_program_request_intake` switches its incoming requests, and\n `anyforge_set_program_status_line` writes its status line.\n- `anyforge_create_initiative`, `anyforge_update_initiative`, and\n `anyforge_dispatch_architect` manage initiative planning and dispatch.\n- `anyforge_create_work_item` and `anyforge_update_work_item` manage concrete\n delivery units. Labels are `tags`: set them on create, and change them with\n `action: \'update\'` and `addTags` / `removeTags` (no revision needed, at any\n status, finished items included). Replacing the whole list with `tags` needs\n `ifClassificationRevision`, the item\'s `classificationRevision`.\n `anyforge_get_work_item` returns `nextActions`, every status move the key can\n make now with the arguments to send; `status` with no action moves an item\n back to BACKLOG, REFINED or READY.\n- Program Genesis also exposes tools for the structured brief and reconciling\n program delivery. Read their schemas before use.\n\n## Approve and control\n\n- `anyforge_approve_backlog` and `anyforge_approve_task_proposal` advance\n approval-gated planning.\n- `anyforge_submit_approval` records a human decision when the caller has the\n required approval scope.\n- `anyforge_resume_crew`, `anyforge_halt_crew`,\n `anyforge_message_crew`, and `anyforge_amend_crew` control a Crew run.\n `anyforge_fix_crew_ci`, `anyforge_resume_crew_with_pr` and\n `anyforge_decide_crew_permission` are the Build view\'s recovery actions.\n Explain the effect before calling them.\n\n## Selection rules\n\n1. Prefer the narrowest read tool that answers the question.\n2. Use a detail tool after a list result when a decision depends on fields not\n present in the list.\n3. Filter and page large lists. Do not request an unbounded workspace dump.\n4. Treat names and descriptions as hints; use the tool schema for required\n fields and allowed values.\n5. Before a mutation, restate the target, change, and likely side effect.\n6. After a mutation, read the target again and report the resulting state.\n'},em=Object.entries(On).map(([t,e])=>({uri:t==="SKILL.md"?Ri:`${Ri}/${t}`,name:t==="SKILL.md"?ye:`${ye}/${t.replace(/^references\//,"")}`,title:t==="SKILL.md"?"AnyForge platform guide":t,description:t==="SKILL.md"?ol:`Reference for the ${ye} skill: ${t}`,mimeType:il,size:new TextEncoder().encode(e).byteLength}));var sl=On["SKILL.md"],tm=sl.replace(/^---[\s\S]*?\r?\n---[ \t]*(?:\r?\n|$)/,"");var Ei={agents:".agents/skills",claude:".claude/skills",codex:".codex/skills"};function dl(t){let e=t.scope==="user"?t.homeDir??al():t.cwd??process.cwd();if(t.scope==="user"&&t.client==="codex"){let n=t.codexHome??process.env.CODEX_HOME??Ye(e,".codex");return Ye(n,"skills",ye)}return Ye(e,Ei[t.client],ye)}function ul(t){if(t.scope!=="project"&&t.scope!=="user")throw new h(`--scope must be project or user, got "${t.scope}".`);if(!Object.hasOwn(Ei,t.client))throw new h(`--client must be agents, claude, or codex, got "${t.client}".`)}async function xr(t){try{return(await Dt.lstat(t)).isSymbolicLink()}catch(e){if(e.code==="ENOENT")return!1;throw e}}async function pl(t){ul(t);let e=dl(t),n=Object.entries(On),r=n.map(([s])=>Ye(e,s));if(await xr(e))throw new h(`Refusing to install through the symbolic link ${e}.`);let o=new Set(n.map(([s])=>Ii(s)).filter(s=>s!=="."));for(let s of o)if(await xr(Ye(e,s)))throw new h(`Refusing to install through the symbolic link ${Ye(e,s)}.`);if(!t.force){let s=[];for(let a of r)try{await Dt.access(a),s.push(a)}catch(c){if(c.code!=="ENOENT")throw c}if(s.length>0)throw new h(`The ${ye} skill already exists at ${e}.`,"Use --force only if you want to replace AnyForge-managed files in that directory.")}await Dt.mkdir(e,{recursive:!0});let i=[];for(let[s,a]of n){let c=Ye(e,s);if(await Dt.mkdir(Ii(c),{recursive:!0}),await xr(c))throw new h(`Refusing to replace the symbolic link ${c}.`);await Dt.writeFile(c,a,{encoding:"utf8",mode:420}),i.push(s)}return{name:ye,scope:t.scope,client:t.client,directory:e,files:i}}async function Ci(t){if(t.subcommand!=="install")throw new h(`Unknown skill command${t.subcommand?`: ${t.subcommand}`:""}.`,"Use `anyforge skill install anyforge-platform`.");if(t.name!==ye)throw new h(`Unknown skill${t.name?`: ${t.name}`:""}.`,`The only portable platform skill is ${ye}.`);let e=t.scope??"project",n=t.client??"agents";if(!["project","user"].includes(e))throw new h(`--scope must be project or user, got "${e}".`);if(!["agents","claude","codex"].includes(n))throw new h(`--client must be agents, claude, or codex, got "${n}".`);let r=await pl({scope:e,client:n,force:t.force});if(t.json){process.stdout.write(JSON.stringify(r)+`
420
- `);return}console.log(cl("Installed ")+`${r.name} ${Si(`(${r.scope}, ${r.client})`)}`),console.log(` ${r.directory}`),console.log(Si(` ${r.files.length} files installed. Re-run with --force to refresh them.`)),r.scope==="project"&&r.client==="agents"&&console.log(ll(" This portable location is detected by AnyForge repository analysis."))}import{randomUUID as Fd}from"node:crypto";import{open as Ud,realpath as Md,stat as jd}from"node:fs/promises";import{basename as Dd,join as Bd,resolve as Wd}from"node:path";import{bold as Xr,cyan as qs,dim as $e,yellow as Hn}from"kleur/colors";import{ProcessTerminal as Gd,SelectList as Hd,Text as zd,TuiAltScreen as Kd}from"@earendil-works/pi-tui";var fl=new Set(["find_callers","find_definition","find_imports","trace_path","get_neighbors","graph_stats"]);function ml(t){return fl.has(t)?"querying the code graph":t==="search_code_semantic"?"searching the code index":t==="search_codebase_docs"?"searching codebase docs":null}function mt(t){let e=ml(t);if(e)return e;let n=/^mcp__(.+?)__(.+)$/.exec(t);return n?`${n[1].replace(/[_-]+/g," ")} \xB7 ${n[2].replace(/_/g," ")}`:t.replace(/_/g," ")}var hl=new Set(["ask_user","present_catch_up_decision","present_memory_candidates","present_skill_proposal"]);function ht(t){return typeof t=="string"&&hl.has(t)}import{truncateToWidth as Qt,wrapTextWithAnsi as ns}from"@earendil-works/pi-tui";import{cyan as Mr,dim as Ze,green as rs,italic as Vl,red as jr,yellow as Dr}from"kleur/colors";import{truncateToWidth as Bt,visibleWidth as Rr,wrapTextWithAnsi as gl}from"@earendil-works/pi-tui";import{bold as wl,dim as yl}from"kleur/colors";function z(t){return t<1024?`${t} B`:t<1024*1024?`${Wt(t/1024)} KiB`:`${Wt(t/(1024*1024))} MiB`}function Gt(t){let e=Math.max(0,t);if(e<1e4)return`${Wt(e/1e3)}s`;let n=Math.round(e/1e3);if(n<60)return`${n}s`;let r=Math.floor(n/3600),o=Math.floor(n%3600/60),i=n%60;return r?o?`${r}h ${o}m`:`${r}h`:i?`${o}m ${i}s`:`${o}m`}function Ir(t){let e=Math.floor(Math.max(0,t)/1e3);return e<60?`${e}s`:`${Math.floor(e/60)}m ${String(e%60).padStart(2,"0")}s`}function me(t){return Math.abs(t)<1e3?String(Math.round(t)):Math.abs(t)<1e6?`${Wt(t/1e3,t<1e5?1:0)}k`:`${Wt(t/1e6)}M`}function Wt(t,e=1){return t.toFixed(e).replace(/\.0+$/,"")}function vl(t){return p(t).replace(/(Ms|Bytes)$/,"").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/[_-]+/g," ").toLowerCase().trim()}function bl(t,e){return typeof e=="boolean"?e?"on":"off":typeof e=="string"?p(e):/Ms$/.test(t)?Gt(e):/Bytes$/.test(t)?z(e):/tokens?/i.test(t)?me(e):String(e)}function Ti(t){return t?Object.entries(t).filter(e=>e[1]!==null).map(([e,n])=>`${vl(e)} ${bl(e,n)}`):[]}function Sr(t,e,n=2){let r=Math.min(16,Math.max(...t.map(([s])=>Rr(s)),0)+2),o=Math.max(10,e-n-r),i=[];for(let[s,a]of t)gl(a,o).forEach((d,u)=>{let l=u===0?yl(Bt(s,r-1).padEnd(r)):" ".repeat(r);i.push(Bt(`${" ".repeat(n)}${l}${d}`,e))});return i}function $i(t,e,n){return[Bt(wl(t),n),...Sr(e,n)]}function Pi(t,e,n){let r=Rr(n),o=e.filter(Boolean).join(" \xB7 ");if(r+4>t)return Bt(o||n,t);let i=t-r-2,s=Bt(o,i),a=Math.max(2,t-Rr(s)-r);return`${s}${" ".repeat(a)}${n}`}import{bold as wt,cyan as gt,dim as Ue,green as kl,italic as Li,strikethrough as Ol,underline as Ni,yellow as Al}from"kleur/colors";import{Markdown as _l}from"@earendil-works/pi-tui";var xl={heading:t=>wt(gt(t)),link:t=>Ni(gt(t)),linkUrl:t=>Ue(t),code:t=>Al(t),codeBlock:t=>t,codeBlockBorder:t=>Ue(t),quote:t=>Li(t),quoteBorder:t=>Ue(t),hr:t=>Ue(t),listBullet:t=>gt(t),bold:t=>wt(t),italic:t=>Li(t),strikethrough:t=>Ol(t),underline:t=>Ni(t)},vt={selectedPrefix:t=>gt(t),selectedText:t=>wt(t),description:t=>Ue(t),scrollInfo:t=>Ue(t),noMatch:t=>Ue(t)},qi={borderColor:t=>Ue(t),selectList:vt},yt=class{constructor(e,n){this.linkOrigin=n;this.content=p(e),this.markdown=new _l(cr(this.content,n),0,0,xl)}linkOrigin;content;markdown;cache;getText(){return this.content}setText(e){let n=p(e);n!==this.content&&(this.content=n,this.markdown.setText(cr(n,this.linkOrigin)),this.cache=void 0)}invalidate(){this.markdown.invalidate(),this.cache=void 0}render(e){let n=Math.max(12,e);if(this.cache?.width===n)return this.cache.lines;let r=this.content?this.markdown.render(n).map(No):[];return this.cache={width:n,lines:r},r}};function Ht(t,e){return t==="user"?`${wt(kl(e?p(e):"You"))}`:`${wt(gt("Oracle"))}`}function Fi(t,e,n){let r=gt("\u2502"),o=Math.max(1,e-4);return["",`${r} ${Ht("user",n)}`,...t.render(o).map(i=>`${r} ${wt(i)}`),""]}function xe(t){return t.shared||t.visibility==="company"?`Shared with the company${t.entityKey?` \xB7 ${p(t.entityKey)}`:""}`:"Private to you"}function Ui(t){return t.shared||t.visibility==="company"?"shared":"private"}import{createReadStream as Dl}from"node:fs";import{stat as Bl}from"node:fs/promises";import{basename as Wl}from"node:path";import{setTimeout as Xi}from"node:timers/promises";var Mi={txt:"text/plain",log:"text/plain",md:"text/markdown",markdown:"text/markdown",csv:"text/csv",json:"application/json",pdf:"application/pdf",ts:"text/plain",tsx:"text/plain",js:"text/plain",jsx:"text/plain",mjs:"text/plain",cjs:"text/plain",py:"text/plain",go:"text/plain",rs:"text/plain",yaml:"text/plain",yml:"text/plain",toml:"text/plain",xml:"text/plain",html:"text/plain",css:"text/plain",sh:"text/plain",sql:"text/plain",java:"text/plain",kt:"text/plain",swift:"text/plain",c:"text/plain",h:"text/plain",cpp:"text/plain",rb:"text/plain",php:"text/plain",tf:"text/plain",diff:"text/plain",patch:"text/plain",png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",webp:"image/webp",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",pptx:"application/vnd.openxmlformats-officedocument.presentationml.presentation",xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"};function ji(t){let e=t.slice(t.lastIndexOf(".")+1).toLowerCase();return Object.hasOwn(Mi,e)?Mi[e]:null}var Er="/api/v1/oracle";var Di="workspace";function Me(t){let e;try{e=new URL(t)}catch{throw new f(`Invalid Oracle endpoint: ${t}`,g.USAGE)}if(!["http:","https:"].includes(e.protocol)||e.username||e.password)throw new f("Oracle endpoint must be an http or https URL without embedded credentials.",g.USAGE);if(e.pathname!=="/"||e.search||e.hash)throw new f("Oracle endpoint must be the workspace origin, without a path, query or fragment.",g.USAGE);return e.origin}function Re(t,e){return`${Me(t)}${Er}${e}`}function Rl(t,e,n){let r;try{r=new URL(t)}catch{throw new f(`Oracle server returned an invalid ${n} endpoint.`,g.API_ERROR)}let o=new URL(Me(e));if(r.origin!==o.origin||!r.pathname.startsWith("/api/oauth/")||r.search||r.hash||r.username||r.password)throw new f(`Oracle server returned an unsafe ${n} endpoint.`,g.API_ERROR);return r.toString()}function Il(t,e){if(!t||typeof t!="object")throw new f("Oracle server omitted OAuth endpoints.",g.API_ERROR);let n=t,r=["registration","authorization","token","revocation"],o={};for(let i of r){if(typeof n[i]!="string")throw new f(`Oracle server omitted its OAuth ${i} endpoint.`,g.API_ERROR);o[i]=Rl(n[i],e,i)}return o}async function Je(t,e=fetch,n,r){let o=Me(t),i;try{i=await e(Re(o,"/config"),{headers:{Accept:"application/json",...n?{Authorization:`Bearer ${n}`}:{}},redirect:"error",signal:r?AbortSignal.any([r,AbortSignal.timeout(2e4)]):AbortSignal.timeout(2e4)})}catch(l){throw new f(`Cannot reach ${o}: ${l instanceof Error?l.message:String(l)}`,g.FAILURE,"Check your network, or pass --endpoint for a self-hosted workspace.")}let s=await Ot(i,Re(o,"/config")),a=N(kt(s,i.status)),c=`${o}${Er}`;if(a.protocolVersion!==1)throw new f(`Oracle protocol mismatch: this CLI supports ${1}, and the server reports ${String(a.protocolVersion)}.`,g.API_ERROR,"Update @anyforge/control to a version supported by this workspace.");if(a.resource!==c)throw new f("Oracle server resource does not match this workspace origin.",g.API_ERROR);let d=Array.isArray(a.scopes)?a.scopes:null;if(!d||!d.every(l=>l==="read"||l==="write"||l==="approve"))throw new f("Oracle server returned an invalid scope list.",g.API_ERROR);let u={protocolVersion:1,resource:c,scopes:d,oauth:Il(a.oauth,o)};if(a.identity!==void 0){let l=N(a.identity);if(typeof l.uid!="string"||typeof l.companyId!="string")throw new f("Oracle server returned an invalid identity.",g.API_ERROR);let k=typeof l.companyName=="string"?p(l.companyName).trim().slice(0,120):"";u.identity={uid:p(l.uid),companyId:p(l.companyId),...k?{companyName:k}:{}}}if(a.capabilities!==void 0){let l=N(a.capabilities);if(![l.read,l.write,l.approve].every(k=>typeof k=="boolean"))throw new f("Oracle server returned invalid capabilities.",g.API_ERROR);u.capabilities={read:l.read,write:l.write,approve:l.approve}}if(a.model!==void 0){let l=N(a.model);if(typeof l.id!="string"||typeof l.provider!="string")throw new f("Oracle server returned invalid model metadata.",g.API_ERROR);u.model={id:p(l.id),provider:p(l.provider)}}if(a.limits!==void 0){let l=N(a.limits);if(Object.values(l).some(k=>!["string","number","boolean"].includes(typeof k)&&k!==null))throw new f("Oracle server returned invalid limits metadata.",g.API_ERROR);u.limits=Object.fromEntries(Object.entries(l).map(([k,O])=>[p(k),typeof O=="string"?p(O):O]))}return Array.isArray(a.commands)&&(u.commands=Sl(a.commands)),u}function Sl(t){let e=[];for(let n of t.slice(0,50)){let r=N(n);if(typeof r.name!="string"||!/^[a-z][a-z0-9_-]{0,31}$/.test(r.name)||typeof r.description!="string"||e.some(i=>i.name===r.name))continue;let o=typeof r.args=="string"?p(r.args).slice(0,60).trim():"";e.push({name:r.name,description:p(r.description).slice(0,200),...o?{args:o}:{}})}return e}var ue=class{endpoint;scopes;source;#e;#n;#t;cachedConfig;constructor(e,n=fetch){this.endpoint=Me(e.endpoint),this.#e=e.getAccessToken??(async()=>e.accessToken),this.#n=e.onIdentity,this.#t=n,this.scopes=new Set(e.scopes),this.source=e.source}async config(e){if(!this.cachedConfig){let n=await this.#e();try{this.cachedConfig=await Je(this.endpoint,this.#t,n,e)}catch(o){if(this.source!=="oauth"||!(o instanceof oe)||o.status!==401)throw o;let i=await this.#e(n);if(i===n)throw o;this.cachedConfig=await Je(this.endpoint,this.#t,i,e)}let r=this.cachedConfig.identity;r&&this.#n&&await this.#n(r).catch(()=>{})}return this.cachedConfig}async listSessions(){let e=await this.json("GET","/sessions"),n=Array.isArray(e)?e:N(e).sessions;if(!Array.isArray(n))throw new f("Oracle server returned an invalid sessions list.",g.API_ERROR);return n.map(Gi)}async createSession(e={},n){let r=await this.json("POST","/sessions",e,n),o=N(r);return!Array.isArray(o.messages)&&typeof o.sessionId=="string"?this.getSession(o.sessionId,n):Bi(r)}async getSession(e,n){return Bi(await this.json("GET",`/sessions/${encodeURIComponent(e)}`,void 0,n))}async submitTurn(e,n,r,o){let i,s=Re(this.endpoint,`/sessions/${encodeURIComponent(e)}/turns`),a=async d=>this.#t(s,{method:"POST",headers:{Authorization:`Bearer ${d}`,Accept:"text/event-stream, application/json","Content-Type":"application/json"},redirect:"error",body:JSON.stringify(n),...r?{signal:r}:{}});try{let d=await this.#e();if(i=await a(d),i.status===401&&this.source==="oauth"){let u=await this.#e(d);u!==d&&(i=await a(u))}}catch(d){throw d instanceof f||r?.aborted?d:new f(`Oracle connection was interrupted: ${d instanceof Error?d.message:String(d)}`,g.FAILURE,"The server may still be working. Resume this session to replay its saved stream.")}if(o?.(i),i.ok||await bt(i),!(i.headers.get("content-type")?.toLowerCase()??"").includes("text/event-stream")){let d=await Ot(i,Re(this.endpoint,`/sessions/${e}/turns`)),u=kt(d,i.status);throw new f(`Oracle server did not start a stream (${El(u)}).`,g.API_ERROR)}return i}async replayEvents(e,n,r){let o=[];for await(let i of this.streamReplayEvents(e,n,r))o.push(i);return o}async*streamReplayEvents(e,n,r,o,i){let s=`/sessions/${encodeURIComponent(e)}/events?streamId=${encodeURIComponent(n)}&from=${Math.max(0,r)}`,a=await this.authenticatedFetch("GET",s,{headers:{Accept:"text/event-stream, application/json"},signal:i},0);if(a.ok||await bt(a),(a.headers.get("content-type")?.toLowerCase()??"").includes("text/event-stream")){yield*zt(a,o);return}let d=kt(await Ot(a,Re(this.endpoint,s)),a.status),u=Array.isArray(d)?d:N(d).events;if(!Array.isArray(u))throw new f("Oracle replay response did not contain events.",g.API_ERROR);for(let l of u)yield Cr(l)}async*streamRequestEvents(e,n,r=-1,o,i){let s=`/sessions/${encodeURIComponent(e)}/events?requestId=${encodeURIComponent(n)}&from=${r}`,a=await this.authenticatedFetch("GET",s,{headers:{Accept:"text/event-stream, application/json"},signal:i},0);if(a.ok||await bt(a),(a.headers.get("content-type")?.toLowerCase()??"").includes("text/event-stream")){yield*zt(a,o);return}let d=kt(await Ot(a,Re(this.endpoint,s)),a.status),u=Array.isArray(d)?d:N(d).events;if(!Array.isArray(u))throw new f("Oracle request replay response did not contain events.",g.API_ERROR);for(let l of u)yield Cr(l)}async cancelTurn(e,n){return this.json("POST",`/sessions/${encodeURIComponent(e)}/cancel`,{streamId:n})}async cancelRequest(e,n,r){return this.json("POST",`/sessions/${encodeURIComponent(e)}/cancel`,{requestId:n},r)}async upload(e,n){let r=new FormData;r.set("sessionId",e),r.set("file",n,n.name);let o=await this.authenticatedFetch("POST","/uploads",{method:"POST",body:r,headers:{Accept:"application/json"}});return o.ok||await bt(o),Tl(kt(await Ot(o,Re(this.endpoint,"/uploads")),o.status))}async download(e,n){let r=await this.authenticatedFetch("GET",`/sessions/${encodeURIComponent(e)}/files/${encodeURIComponent(n)}`,{headers:{Accept:"application/octet-stream, application/json"}});return r.ok||await bt(r),r}async json(e,n,r,o){let i=await this.authenticatedFetch(e,n,{headers:{Accept:"application/json",...r!==void 0?{"Content-Type":"application/json"}:{}},...r!==void 0?{body:JSON.stringify(r)}:{},...o?{signal:o}:{}});return i.ok||await bt(i),kt(await Ot(i,Re(this.endpoint,n)),i.status)}async authenticatedFetch(e,n,r={},o=2e4){try{let i=Re(this.endpoint,n),s=Object.fromEntries(new Headers(r.headers).entries()),a=o<=0?void 0:AbortSignal.timeout(o),c=r.signal&&a?AbortSignal.any([r.signal,a]):r.signal??a,d=O=>this.#t(i,{...r,method:e,headers:{...s,Authorization:`Bearer ${O}`},redirect:"error",...c?{signal:c}:{}}),u=await this.#e(),l=await d(u),k=r.body===void 0||typeof r.body=="string"||r.body===null;if(l.status===401&&this.source==="oauth"){let O=await this.#e(u);O!==u&&k&&(l=await d(O))}return l}catch(i){throw i instanceof f?i:new f(`Cannot reach ${this.endpoint}: ${i instanceof Error?i.message:String(i)}`,g.FAILURE,"Check your network, or pass --endpoint for a self-hosted workspace.")}}};async function*zt(t,e){if(!t.body)throw new f("Oracle stream has no response body.",g.FAILURE);let n=t.body.getReader(),r=new TextDecoder,o="",i=0,s=[],a=0,c=-1,d=()=>{if(s.length===0)return null;let u=s.join(`
421
- `);if(s=[],a=0,u==="[DONE]")return null;let l;try{l=JSON.parse(u)}catch{throw new f("Oracle stream returned invalid JSON.",g.API_ERROR)}let k=Cr(l);return k.type==="finalizing"?k:k.idx<=c?null:(c=k.idx,k)};try{for(;;){let{value:l,done:k}=await n.read();l?.byteLength&&e?.();let O=r.decode(l,{stream:!k});if(o+=O,i+=An(O),i>2*1024*1024)throw new f("Oracle stream event exceeded the 2 MiB protocol limit.",g.API_ERROR);let x=0,b=o.indexOf(`
422
- `,x);for(;b>=0;){let m=o.slice(x,b).replace(/\r$/,"");if(i-=An(o.slice(x,b+1)),x=b+1,m===""){let _=d();_&&(yield _)}else if(!m.startsWith(":")){let _=m.indexOf(":"),$=_<0?m:m.slice(0,_),j=_<0?"":m.slice(_+1).replace(/^ /,"");if($==="data"&&(s.push(j),a+=An(j)+1,a>2*1024*1024))throw new f("Oracle stream event exceeded the 2 MiB protocol limit.",g.API_ERROR)}b=o.indexOf(`
423
- `,x)}if(o=o.slice(x),k)break}if(o.trim()){let l=o.replace(/\r$/,"");if(l.startsWith("data:")&&(s.push(l.slice(5).replace(/^ /,"")),a+=An(s[s.length-1]??"")+1,a>2*1024*1024))throw new f("Oracle stream event exceeded the 2 MiB protocol limit.",g.API_ERROR)}let u=d();u&&(yield u)}catch(u){throw await n.cancel("Invalid or oversized Oracle event stream.").catch(()=>{}),u}finally{n.releaseLock()}}function An(t){return Buffer.byteLength(t,"utf8")}function Tr(t){let e=["program","initiative","codebase"].filter(o=>typeof t[o]=="string");if(e.length>1)throw new f("Choose only one of --program, --initiative or --codebase.",g.USAGE);let n=e[0];if(!n)return;let r=t[n];if(typeof r!="string"||!r.trim())throw new f(`--${n} needs an id.`,g.USAGE);return{kind:n,id:r.trim()}}function N(t){return!t||typeof t!="object"||Array.isArray(t)?{}:t}function El(t){if(typeof t=="string")return p(t);try{return p(JSON.stringify(t))}catch{return"unexpected response"}}function kt(t,e){let n=N(t);if(n.ok===!1){let r=Wi(n.error,e),o=typeof n.message=="string"?p(n.message).slice(0,2e3):void 0;throw new oe(r,e,o)}return n.ok===!0&&"data"in n?n.data:t}async function Ot(t,e){try{return await t.json()}catch{throw new f(`Non-JSON response from ${e} (HTTP ${t.status}).`,g.FAILURE)}}async function bt(t){let e;try{e=await t.clone().json()}catch{e=null}let n=N(e),r=Wi(n.error??n.error_description,t.status),o=typeof n.message=="string"?n.message:typeof n.error_description=="string"?n.error_description:typeof n.detail=="string"?n.detail:void 0;throw new oe(r,t.status,typeof o=="string"?p(o).slice(0,2e3):void 0,void 0,Cl(t))}function Cl(t){try{return t.url?new URL(t.url).origin:void 0}catch{return}}function Wi(t,e){if(typeof t!="string")return`http_${e}`;let n=p(t).slice(0,120);return/^[A-Za-z0-9_.-]+$/.test(n)?n:`http_${e}`}function Gi(t){let e=N(t);if(typeof e.sessionId!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(e.sessionId)||typeof e.title!="string"||e.visibility!=="private"&&e.visibility!=="company"||typeof e.shared!="boolean")throw new f("Oracle server returned an invalid session summary.",g.API_ERROR);return{sessionId:e.sessionId,title:p(e.title),visibility:e.visibility,shared:e.shared,...typeof e.entityKey=="string"?{entityKey:p(e.entityKey)}:{},...typeof e.updatedAt=="number"||e.updatedAt===null?{updatedAt:e.updatedAt}:{}}}function Bi(t){let e=N(t),n=Gi(e);if(!Array.isArray(e.messages))throw new f("Oracle server returned an invalid session transcript.",g.API_ERROR);let r=e.messages.map((o,i)=>{let s=N(o);if(s.role!=="user"&&s.role!=="assistant"||typeof s.content!="string")throw new f(`Oracle session message ${i+1} is invalid.`,g.API_ERROR);let a=Array.isArray(s.attachments)?s.attachments.map(Nl):void 0,c=Array.isArray(s.files)?s.files.map(ql):void 0;return{role:s.role,content:p(s.content),...typeof s.id=="string"?{id:s.id}:{},...typeof s.authorName=="string"?{authorName:p(s.authorName)}:{},...s.question&&typeof s.question=="object"?{question:$l(s.question)}:{},...s.questionResponse&&typeof s.questionResponse=="object"?{questionResponse:Pl(s.questionResponse)}:{},...a?{attachments:a}:{},...c?{files:c}:{}}});return{...n,messages:r,...typeof e.activeStreamId=="string"?{activeStreamId:e.activeStreamId}:{}}}function Cr(t){let e=N(t);if(e.protocolVersion!==1||typeof e.idx!="number"||!Number.isSafeInteger(e.idx)||e.idx<0||typeof e.type!="string")throw new f("Oracle stream event does not match protocol version 1.",g.API_ERROR);return _e(e)}function Tl(t){let e=N(t);if(typeof e.fileId!="string"||typeof e.name!="string"||typeof e.mediaType!="string")throw new f("Oracle upload response is missing file metadata.",g.API_ERROR);return{fileId:e.fileId,name:p(e.name),mediaType:p(e.mediaType),...typeof e.sizeBytes=="number"?{sizeBytes:e.sizeBytes}:{}}}function $l(t){let e=N(t);if(typeof e.id!="string"||!Array.isArray(e.questions))throw new f("Oracle returned an invalid question card.",g.API_ERROR);return{id:p(e.id),questions:e.questions.map((n,r)=>{let o=N(n);if(typeof o.id!="string"||typeof o.header!="string"||typeof o.question!="string"||typeof o.multiSelect!="boolean"||!Array.isArray(o.options))throw new f(`Oracle question ${r+1} is invalid.`,g.API_ERROR);return{id:p(o.id),header:p(o.header),question:p(o.question),multiSelect:o.multiSelect,options:o.options.map((i,s)=>{let a=N(i);if(typeof a.id!="string"||typeof a.label!="string"||typeof a.description!="string")throw new f(`Oracle question option ${s+1} is invalid.`,g.API_ERROR);return{id:p(a.id),label:p(a.label),description:p(a.description),...typeof a.recommended=="boolean"?{recommended:a.recommended}:{},...typeof a.confirms=="boolean"?{confirms:a.confirms}:{}}})}}),optional:e.optional===!0}}function Pl(t){let e=N(t);if(typeof e.questionId!="string"||!Array.isArray(e.answers))throw new f("Oracle returned an invalid question response.",g.API_ERROR);return{questionId:p(e.questionId),answers:e.answers.map(Ll)}}function Ll(t){let e=N(t);if(typeof e.questionId!="string"||!Array.isArray(e.optionIds)||!e.optionIds.every(n=>typeof n=="string"))throw new f("Oracle returned an invalid question answer.",g.API_ERROR);return{questionId:p(e.questionId),optionIds:e.optionIds.map(n=>p(n)),...typeof e.customText=="string"?{customText:p(e.customText).slice(0,8e3)}:{}}}function Nl(t){let e=N(t);if(typeof e.fileId!="string"||typeof e.name!="string"||typeof e.mediaType!="string")throw new f("Oracle session attachment metadata is invalid.",g.API_ERROR);return{fileId:p(e.fileId),name:p(e.name),mediaType:p(e.mediaType),...typeof e.sizeBytes=="number"&&Number.isFinite(e.sizeBytes)?{sizeBytes:e.sizeBytes}:{}}}function ql(t){let e=N(t);if(typeof e.fileId!="string"||typeof e.filename!="string"||typeof e.mediaType!="string"||typeof e.sizeBytes!="number"||!Number.isFinite(e.sizeBytes))throw new f("Oracle session file metadata is invalid.",g.API_ERROR);return{fileId:p(e.fileId),filename:p(e.filename),mediaType:p(e.mediaType),sizeBytes:e.sizeBytes,...typeof e.format=="string"?{format:p(e.format)}:{}}}import{randomUUID as Fl}from"node:crypto";import{promises as Ie}from"node:fs";import{homedir as Ul}from"node:os";import{join as Pr}from"node:path";import*as $r from"proper-lockfile";function Rn(){return Pr(Ul(),".anyforge")}function xn(){return Pr(Rn(),"oracle")}function zi(){return Pr(Rn(),"oracle.last-session")}function Ml(){return{version:2,active:{},logins:[]}}function _n(t){return t===void 0||typeof t=="string"}function Hi(t){if(!t||typeof t!="object"||Array.isArray(t))return null;let e=t;return typeof e.endpoint!="string"||typeof e.clientId!="string"||typeof e.redirectUri!="string"||typeof e.accessToken!="string"||typeof e.refreshToken!="string"||typeof e.expiresAt!="number"||!Number.isFinite(e.expiresAt)||!Array.isArray(e.scopes)||!e.scopes.every(n=>typeof n=="string")||!_n(e.companyId)||!_n(e.companyName)||!_n(e.uid)?null:{endpoint:e.endpoint,...e.companyId?{companyId:e.companyId}:{},...e.companyName?{companyName:e.companyName}:{},...e.uid?{uid:e.uid}:{},clientId:e.clientId,redirectUri:e.redirectUri,accessToken:e.accessToken,refreshToken:e.refreshToken,expiresAt:e.expiresAt,scopes:e.scopes}}function jl(t){if(!t||typeof t!="object"||Array.isArray(t))return null;let e=t;if(e.version===1){let o=Hi(e);return o?{version:2,endpoint:o.endpoint,active:{[o.endpoint]:o.clientId},logins:[o]}:null}if(e.version!==2||!Array.isArray(e.logins)||!_n(e.endpoint)||!e.active||typeof e.active!="object"||Array.isArray(e.active))return null;let n=e.logins.map(Hi);if(n.some(o=>o===null))return null;let r={};for(let[o,i]of Object.entries(e.active)){if(typeof i!="string")return null;r[o]=i}return{version:2,...e.endpoint?{endpoint:e.endpoint}:{},active:r,logins:n}}async function ie(){let t=xn(),e;try{e=await Ie.readFile(t,"utf8")}catch(o){if(o.code==="ENOENT")return Ml();throw o}let n;try{n=JSON.parse(e)}catch{throw new Error(`Oracle credentials at ${t} are invalid JSON; run \`anyforge oracle logout --all\` and sign in again.`)}let r=jl(n);if(!r)throw new Error(`Oracle credentials at ${t} have an unsupported format; run \`anyforge oracle logout --all\` and sign in again.`);return r}async function Kt(t){if(!t.logins.length){await Ie.rm(xn(),{force:!0});return}await Qi(xn(),t)}async function In(t){return Cn(async()=>{let e=await ie(),n=await t(e);return await Kt(e),n})}function Se(t,e){return t.logins.filter(n=>n.endpoint===e)}function Sn(t,e){let n=Se(t,e);return n.find(r=>r.clientId===t.active[e])??n[0]}function se(t){return t.companyName||t.companyId||"workspace not known yet"}function Ki(t,e){let n=e.companyId?t.logins.find(r=>r.endpoint===e.endpoint&&r.companyId===e.companyId&&r.clientId!==e.clientId):void 0;return t.logins=[...t.logins.filter(r=>r!==n&&r.clientId!==e.clientId),e],t.active[e.endpoint]=e.clientId,t.endpoint=e.endpoint,n}function Lr(t,e){t.logins=t.logins.filter(o=>!(o.endpoint===e.endpoint&&o.clientId===e.clientId));let n=Se(t,e.endpoint);if(!n.length){if(delete t.active[e.endpoint],t.endpoint===e.endpoint){let o=t.logins[0]?.endpoint;o?t.endpoint=o:delete t.endpoint}return}if(t.active[e.endpoint]!==e.clientId&&n.some(o=>o.clientId===t.active[e.endpoint]))return;let r=n[0];return t.active[e.endpoint]=r.clientId,r}function Vi(t,e,n,r){let o=t.logins.find(a=>a.endpoint===e&&a.clientId===n);if(!o)return{changed:!1};let i=o.companyId!==r.companyId||r.companyName!==void 0&&o.companyName!==r.companyName||r.uid!==void 0&&o.uid!==r.uid;o.companyId=r.companyId,r.companyName!==void 0&&(o.companyName=r.companyName),r.uid!==void 0&&(o.uid=r.uid);let s=t.logins.find(a=>a!==o&&a.endpoint===e&&a.companyId===r.companyId);return s&&(t.logins=t.logins.filter(a=>a!==s),t.active[e]===s.clientId&&(t.active[e]=o.clientId)),{changed:i||!!s,...s?{duplicate:s}:{}}}function Qe(t,e){let n=e.trim(),r=t.find(a=>a.companyId===n);if(r)return r;let o=t.filter(a=>a.companyName?.toLowerCase()===n.toLowerCase());if(o.length===1)return o[0];let i=t.map(a=>`${se(a)}${a.companyId&&a.companyName?` (${a.companyId})`:""}`),s=i.length?`Saved workspaces: ${i.join(", ")}.`:"No Oracle logins are saved for this endpoint.";throw o.length>1?new h(`More than one saved workspace is named "${n}". Use its id instead.`,s):new h(`No saved Oracle login for workspace "${n}".`,`${s} Add one with \`anyforge oracle login --workspace "${n}"\`.`)}async function Yi(){let t;try{t=await Ie.readFile(zi(),"utf8")}catch(r){if(r.code==="ENOENT")return[];throw r}let e;try{e=JSON.parse(t)}catch{return[]}if(!e||typeof e!="object"||Array.isArray(e))return[];let n=e;return n.version!==2||!Array.isArray(n.sessions)?[]:n.sessions.filter(r=>{if(!r||typeof r!="object")return!1;let o=r;return typeof o.endpoint=="string"&&typeof o.companyId=="string"&&typeof o.sessionId=="string"})}async function Ji(t,e,n=""){let r=(await Yi()).filter(o=>!(o.endpoint===e&&o.companyId===n));await Qi(zi(),{version:2,sessions:[...r,{endpoint:e,companyId:n,sessionId:t}]})}async function En(t,e=""){return(await Yi()).find(n=>n.endpoint===t&&n.companyId===e)?.sessionId}async function Qi(t,e){await Ie.mkdir(Rn(),{recursive:!0,mode:448});let n=`${t}.${Fl()}.tmp`;try{await Ie.writeFile(n,`${JSON.stringify(e,null,2)}
424
- `,{mode:384,flag:"wx"}),await Ie.rename(n,t),await Ie.chmod(t,384)}finally{await Ie.rm(n,{force:!0}).catch(()=>{})}}async function Cn(t){await Ie.mkdir(Rn(),{recursive:!0,mode:448});let e=xn(),n=`${e}.refresh-admission`,r;try{r=await $r.lock(n,{realpath:!1,lockfilePath:n,stale:Number.MAX_SAFE_INTEGER,update:1e4,retries:{retries:300,minTimeout:100,maxTimeout:100,randomize:!1}})}catch(o){throw o.code==="ELOCKED"?new Error(`Oracle credential access is still locked; another CLI command may be running. An abrupt exit can leave this guard behind. Stop all AnyForge CLI commands before removing the guard directory at ${n}.`,{cause:o}):o}try{let o=await $r.lock(e,{realpath:!1,lockfilePath:`${e}.refresh-lock`,stale:6e4,update:1e4,retries:{retries:300,minTimeout:100,maxTimeout:100,randomize:!1}});try{return await t()}finally{await o()}}finally{await r()}}var Nr=8192,Zi=10*1048576,Gl=20*1048576,es=10,ts=3;function qr(t,e){if(t.type!=="tool_call"&&t.type!=="tool_call_start"||typeof t.spawnId=="string")return!1;let n=typeof t.id=="string"?t.id:void 0;if(n){if(e.has(n))return!1;e.add(n)}return!ht(t.name)}var Ee=class{lastIdx=-1;answer="";question;error;done;streamId;files=[];toolIds=new Set;accept(e){return e.type==="finalizing"?!0:e.idx<=this.lastIdx?!1:(this.lastIdx=e.idx,e.type==="stream_start"&&typeof e.streamId=="string"?this.streamId=p(e.streamId):e.type==="delta"&&typeof e.content=="string"?this.answer+=e.content:qr(e,this.toolIds)?this.answer="":e.type==="question"?this.question=Yt(e):e.type==="error"?this.error=Ur(e):e.type==="done"?this.done=e:e.type==="navigator_file_created"&&Hl(this.files,e.file),!0)}get settled(){return!!(this.done||this.error)}outcome(){return{answer:this.answer,...this.question?{question:this.question}:{},...this.error?{error:this.error}:{},...this.done?{done:this.done}:{},...this.streamId?{streamId:this.streamId}:{},...this.files.length?{files:[...this.files]}:{}}}};async function _t(t,e,n,r,o,i=new Ee){let s=async l=>{let k=i.streamId;i.accept(l)&&(i.streamId&&i.streamId!==k&&r.onStream?.(i.streamId),await r.onEvent(l))},a=()=>Fr(t,e,i,s,o,r.onActivity),c=async()=>{try{for await(let l of t.streamRequestEvents(e,n.requestId,-1,r.onActivity,o))await s(l)}catch(l){throw o?.aborted?l:new f("Oracle connection failed and the submitted request could not be recovered by its request id. It was not resubmitted with a new id.",g.FAILURE,`Run anyforge oracle resume ${e} to inspect the saved session.`)}if(i.settled)return i.outcome();if(i.streamId)return a();throw new f("Oracle request lookup returned no stream events. It was not resubmitted with a new id.",g.FAILURE,`Run anyforge oracle resume ${e} to inspect the saved session.`)},d=async()=>{try{let l=await t.submitTurn(e,n,o);for await(let k of zt(l,r.onActivity))await s(k)}catch(l){if(o?.aborted||l instanceof f&&l.exitCode===g.API_ERROR)throw l;return i.streamId?a():c()}return i.settled?i.outcome():i.streamId?a():c()},u;try{u=await t.submitTurn(e,n,o)}catch(l){if(o?.aborted||l instanceof f&&l.exitCode!==g.FAILURE)throw l;return d()}if(!u)throw new f("Oracle did not return a stream response.",g.FAILURE);r.onResponse?.();try{for await(let l of zt(u,r.onActivity))await s(l)}catch(l){if(o?.aborted||l instanceof f)throw l;return i.streamId?a():d()}return i.settled?i.outcome():i.streamId?a():d()}async function Fr(t,e,n,r,o,i){let s=n.streamId;if(!s)throw new f("Oracle stream replay needs a stream id.",g.FAILURE);let a=0;for(;!n.settled&&a<ts;){if(o?.aborted)throw o.reason;try{for await(let c of t.streamReplayEvents(e,s,n.lastIdx+1,i,o))await r(c);if(!n.settled){if((await t.getSession(e,o)).activeStreamId!==s)break;await Xi(300,void 0,o?{signal:o}:void 0)}}catch(c){if(o?.aborted||c instanceof f&&c.exitCode===g.API_ERROR||(a+=1,a>=ts))throw c;await Xi(300,void 0,o?{signal:o}:void 0)}}if(!n.settled)throw new f("Oracle stream could not be fully replayed; the turn was not submitted again.",g.FAILURE,`Run anyforge oracle resume ${e} to inspect the session.`);return n.outcome()}function Vt(t,e){let n=e.done,r=n?.servedBy,o=Xe(n?.tokens),i=Q(n?.rounds),s=typeof n?.exitReason=="string"?p(n.exitReason):void 0,a=n?.exitReason==="completed"&&!e.question,c=e.files??(Array.isArray(n?.files)?n.files.map(je):void 0),d=e.error?"error":e.question?"awaiting_input":a?"complete":"interrupted";return{protocolVersion:1,sessionId:t,status:d,answer:p(e.answer),partial:!a,...typeof n?.messageId=="string"?{messageId:p(n.messageId)}:typeof n?.answerId=="string"?{messageId:p(n.answerId)}:{},...e.question?{question:e.question}:{},...e.error?{error:p(e.error)}:{},...e.streamId?{streamId:p(e.streamId)}:{},...r?{servedBy:_e(r)}:{},...o?{usage:{...o,...i!==void 0?{rounds:i}:{}}}:{},...i!==void 0?{rounds:i}:{},...s?{exitReason:s}:{},...c?.length?{files:_e(c)}:{}}}function T(t,e){return typeof t=="string"?p(t).slice(0,e):""}function Ur(t){return typeof t.message=="string"?p(t.message).slice(0,2e3):"Oracle turn failed."}function K(t){return p(t instanceof Error?t.message:String(t)).slice(0,2e3)}function Yt(t){let e=t.question;if(!e||typeof e!="object")throw new f("Oracle question event omitted its question card.",g.API_ERROR);return zl(e)}function je(t){let e=t&&typeof t=="object"&&!Array.isArray(t)?t:{};if(typeof e.fileId!="string"||!e.fileId||typeof e.filename!="string"||!e.filename||typeof e.mediaType!="string"||!e.mediaType||typeof e.sizeBytes!="number"||!Number.isSafeInteger(e.sizeBytes)||e.sizeBytes<0)throw new f("Oracle generated-file event omitted safe file metadata.",g.API_ERROR);return{fileId:p(e.fileId),filename:p(e.filename),mediaType:p(e.mediaType),sizeBytes:e.sizeBytes,...typeof e.format=="string"?{format:p(e.format)}:{}}}function Hl(t,e){let n=je(e);t.some(r=>r.fileId===n.fileId)||t.push(n)}function zl(t){let e=t;if(typeof e.id!="string"||!Array.isArray(e.questions))throw new f("Oracle returned an invalid question card.",g.API_ERROR);return{id:p(e.id),optional:e.optional===!0,questions:e.questions.map((n,r)=>{let o=n&&typeof n=="object"?n:{};if(typeof o.id!="string"||typeof o.header!="string"||typeof o.question!="string"||typeof o.multiSelect!="boolean"||!Array.isArray(o.options))throw new f(`Oracle question ${r+1} is invalid.`,g.API_ERROR);return{id:p(o.id),header:p(o.header),question:p(o.question),multiSelect:o.multiSelect,options:o.options.map(i=>{let s=i&&typeof i=="object"?i:{};if(typeof s.id!="string"||typeof s.label!="string"||typeof s.description!="string")throw new f("Oracle question option is invalid.",g.API_ERROR);return{id:p(s.id),label:p(s.label),description:p(s.description),...typeof s.recommended=="boolean"?{recommended:s.recommended}:{},...typeof s.confirms=="boolean"?{confirms:s.confirms}:{}}})}})}}function Q(t){return typeof t=="number"&&Number.isFinite(t)?t:void 0}function Xe(t){if(typeof t=="number"&&Number.isFinite(t))return{totalTokens:t};if(!t||typeof t!="object"||Array.isArray(t))return;let e=t,n=Q(e.in)??Q(e.input)??Q(e.inputTokens),r=Q(e.cacheRead),o=Q(e.cacheWrite),i=n===void 0?void 0:n+(r??0)+(o??0),s=Q(e.out)??Q(e.output)??Q(e.outputTokens),a=Q(e.total)??Q(e.totalTokens)??(i!==void 0||s!==void 0?(i??0)+(s??0):void 0);if(!(i===void 0&&s===void 0&&a===void 0))return{...i!==void 0?{inputTokens:i}:{},...r?{cachedInputTokens:r}:{},...s!==void 0?{outputTokens:s}:{},...a!==void 0?{totalTokens:a}:{}}}function xt(t){if(t&&typeof t=="object"){let e=t;if(typeof e.accepted=="boolean")return e.accepted?"Oracle server accepted the cancellation request.":"Oracle server did not accept the cancellation request.";if(typeof e.cancelled=="boolean")return e.cancelled?"Oracle server cancelled the active turn.":"Oracle server did not cancel the active turn."}return"Oracle server acknowledged the cancellation request."}function Rt(t,e){let n=e&&t&&(t.kind==="program"||t.kind==="initiative")?{kind:t.kind,id:t.id}:void 0;return{...!e&&t?{context:t}:{},...n?{sharedEntity:n}:{},visibility:e?"company":"private"}}async function Jt(t,e,n){await Ji(t,e,n).catch(()=>{})}async function Tn(t,e,n,r){if(!n.length)return[];if(n.length>es)throw new h(`Attach no more than ${es} files per turn.`);let o=0,i=[];for(let a of n){let c=await Bl(a);if(!c.isFile())throw new h(`Attachment is not a file: ${a}`);if(c.size>Zi)throw new h(`Attachment exceeds the 10 MiB per-file CLI limit: ${a}`);let d=await Kl(a,Zi);if(o+=d.byteLength,o>Gl)throw new h("Attachments exceed the 20 MiB total CLI limit.");let u=Wl(a).replace(/[\u0000-\u001f\u007f/\\]/g,"_").slice(0,200)||"attachment",l=ji(u)??"";if(!l)throw new h(`Unsupported attachment type: ${a}`);i.push({content:d,name:u,mediaType:l})}let s=[];for(let{content:a,name:c,mediaType:d}of i){await r(p(c),a.byteLength);let u=new ArrayBuffer(a.byteLength);new Uint8Array(u).set(a);let l=new File([u],c,{type:d});s.push(await t.upload(e,l))}return s}async function Kl(t,e){let n=[],r=0;for await(let o of Dl(t)){let i=Buffer.isBuffer(o)?o:Buffer.from(o);if(r+=i.byteLength,r>e)throw new h(`Attachment exceeds the ${z(e)} limit: ${t}`);n.push(i)}return Buffer.concat(n)}var et=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],Yl=2e4,Jl=6,Ql={lookup:"Looking things up",quick_edit:"Making a quick edit",final_answer:"Composing the answer",follow_up:"Suggesting follow-ups"},Xl={max_rounds:"reached the step limit",max_tokens:"reached the output limit",token_budget:"reached the token budget",deadline:"ran out of time",interrupted:"interrupted",loop_detected:"stopped a repeating loop",cancelled:"cancelled"},$n=class{constructor(e,n){this.startedAt=e;this.sessionId=n;this.lastSignalAt=e}startedAt;sessionId;phase="sending";answer="";tools=[];notices=[];files=[];lastSignalAt;endedAt;end;done;upload;round;maxRounds;serverPhase;agents=new Map;toolIds=new Set;get toolCount(){return this.tools.length}get failedCount(){return this.tools.filter(e=>e.status==="failed").length}get finished(){return this.end!==void 0}setUploading(e,n,r){this.phase="uploading",this.upload=`${e} (${z(n)})`,this.lastSignalAt=r}setPhase(e,n){this.phase=e,this.lastSignalAt=n}touch(e){this.lastSignalAt=e}apply(e,n){if(this.lastSignalAt=n,this.phase==="cancelling")return this.record(e);switch(e.type){case"stream_start":case"thinking":this.phase!=="finishing"&&(this.phase="thinking");break;case"round_start":this.round=Q(e.round),this.maxRounds=Q(e.maxRounds)??this.maxRounds;for(let r=this.tools.length-1;r>=0;r--)this.tools[r]?.started&&this.tools.splice(r,1);this.phase!=="finishing"&&(this.phase="thinking");break;case"phase":this.serverPhase=typeof e.phase=="string"?e.phase:void 0;break;case"delta":this.phase!=="finishing"&&(this.phase="writing");break;case"tool_result":this.phase!=="finishing"&&(this.phase="thinking");break;case"finalizing":this.phase="finishing";break}this.record(e)}record(e){switch(e.type){case"delta":typeof e.content=="string"&&(this.answer+=e.content);break;case"tool_call_start":case"tool_call":this.recordToolCall(e);break;case"tool_result":{let n=this.tools.find(r=>r.id!==void 0&&r.id===e.toolUseId);n&&(n.status=e.ok===!1?"failed":"ok");break}case"subagent_start":typeof e.spawnId=="string"&&this.agents.set(e.spawnId,"running");break;case"subagent_done":typeof e.spawnId=="string"&&this.agents.set(e.spawnId,"done");break;case"warning":this.notices.push({tone:"warning",text:`Warning: ${T(e.message,500)}`});break;case"navigator_file_error":this.notices.push({tone:"warning",text:`Could not generate ${T(e.filename,200)}: ${T(e.error,500)}`});break;case"attachment_notice":if(Array.isArray(e.notices))for(let n of e.notices){let r=n&&typeof n=="object"?n:{};this.notices.push({tone:"warning",text:`${T(r.name,160)}: ${T(r.message,500)}`})}break;case"provider_fallback":{let n=T(e.from,120),r=T(e.to,120),o=T(e.reason,40).replaceAll("_"," ");this.notices.push({tone:"info",text:`Switched model${n?` from ${n}`:""}${r?` to ${r}`:""}${o?` (${o})`:""}.`});break}case"navigator_file_created":{let n=je(e.file);if(this.files.some(r=>r.fileId===n.fileId))break;this.files.push(n),this.notices.push({tone:"info",text:`Generated ${n.filename} (${z(n.sizeBytes)}). Download: anyforge oracle download ${this.sessionId} ${n.fileId} --output .`});break}case"error":this.notices.push({tone:"error",text:Ur(e)});break;case"done":this.done=e;break}}recordToolCall(e){let n=typeof e.id=="string"?e.id:void 0,r=qr(e,this.toolIds),o=typeof e.name=="string"?e.name:"";if(ht(o))return;let i=T(typeof e.label=="string"?e.label:mt(o),120)||"tool",s=n?this.tools.find(c=>c.id===n):void 0;if(s){s.label=i,e.type==="tool_call"&&delete s.started;return}let a={label:i,status:"running",...n?{id:n}:{},...typeof e.spawnId=="string"?{spawnId:e.spawnId}:{},...e.type==="tool_call_start"?{started:!0}:{}};r&&this.answer.trim()&&(a.note=p(this.answer.trim()),this.answer=""),this.tools.push(a),this.phase!=="finishing"&&(this.phase="thinking")}note(e,n){this.notices.push({tone:e,text:p(n)})}finish(e,n){this.end=e,this.endedAt=n}label(){if(this.phase==="cancelling")return"Cancelling";if(this.phase==="uploading")return`Uploading ${this.upload??"attachment"}`;if(this.phase==="sending")return"Sending";if(this.phase==="preparing")return"Preparing context";if(this.phase==="finishing")return"Finishing up";let e=[...this.agents.values()].filter(r=>r==="running").length;if(e)return`Researching (${e} agent${e===1?"":"s"})`;let n=this.tools.filter(r=>r.status==="running"&&!r.spawnId);return n.length===1?`Running ${n[0]?.label}`:n.length>1?`Running ${n.length} tools`:this.phase==="writing"?"Writing":this.serverPhase&&Ql[this.serverPhase]||"Thinking"}statusLine(e,n){let r=[this.label(),Ir(e-this.startedAt)];this.round&&r.push(this.maxRounds?`step ${this.round}/${this.maxRounds}`:`step ${this.round}`);let o=e-this.lastSignalAt;return o>=Yl&&this.phase!=="cancelling"&&r.push(`no update for ${Ir(o)}`),n&&e-this.startedAt>=n*.9&&this.phase!=="finishing"&&r.push("wrapping up"),r.join(" \xB7 ")}summaryLine(){let e=[],n=Gt((this.endedAt??this.startedAt)-this.startedAt),r=typeof this.done?.exitReason=="string"?this.done.exitReason:void 0,o;this.end==="complete"?o=rs("\u2713"):this.end==="awaiting_input"?o=Mr("?")+" Waiting for your answer":this.end==="cancelled"?o=Dr("\u2717")+" Interrupted":this.end==="error"?o=jr("\u2717")+" Error":o=Dr("\u2717")+` Stopped: ${r&&Xl[r]||"no final answer"}`,e.push(n),this.tools.length&&e.push(`${this.tools.length} tool${this.tools.length===1?"":"s"}`);let i=Xe(this.done?.tokens);(i?.inputTokens!==void 0||i?.outputTokens!==void 0)&&e.push(`${me(i.inputTokens??0)} in${i.cachedInputTokens?` (${me(i.cachedInputTokens)} cached)`:""} / ${me(i.outputTokens??0)} out`);let s=Xt(this.done?.servedBy);return s&&e.push(s),`${o} ${Ze(e.join(" \xB7 "))}`}};function Xt(t){if(!t||typeof t!="object")return"";let e=t,n=T(e.model??e.id,100);if(!n)return"";let r=e.fallback&&typeof e.fallback=="object"?e.fallback:void 0;if(r&&typeof r.from=="string"){let o=typeof r.reason=="string"?`: ${T(r.reason,40).replaceAll("_"," ")}`:"";return`${n} (fallback from ${T(r.from,100)}${o})`}return n}var Pn=class{constructor(e,n){this.activity=e;this.view=n;this.answer=new yt("",n.linkOrigin)}activity;view;answer;getText(){return this.answer.getText()}invalidate(){this.answer.invalidate()}render(e){let n=this.activity;this.answer.setText(n.answer);let r=[Ht("assistant")];if(!n.finished&&!n.tools.length&&!n.answer.trim()){let o=Mr(et[this.view.frame()%et.length]??"*");r.push(Qt(` ${o} ${Ze(n.label())}`,e))}r.push(...this.renderTools(e)),r.push(...this.answer.render(e));for(let o of n.notices){let i=o.tone==="error"?jr:o.tone==="warning"?Dr:Ze;r.push(...ns(i(o.text),e))}return n.finished&&r.push(Qt(n.summaryLine(),e)),r}renderTools(e){let{tools:n}=this.activity;if(!n.length)return[];if(this.activity.finished&&!this.view.expanded()){let s=this.activity.failedCount,a=`Used ${n.length} tool${n.length===1?"":"s"}${s?` \xB7 ${s} failed`:""} (ctrl+o to expand)`;return[Qt(Ze(` \u23BF ${a}`),e)]}let r=this.activity.finished?n:n.slice(-Jl),o=n.length-r.length,i=[];return o&&i.push(Qt(Ze(` \u23BF \u2026 ${o} earlier tool${o===1?"":"s"}`),e)),r.forEach((s,a)=>{if(s.note)for(let l of ns(s.note,Math.max(10,e-4)).slice(0,3))i.push(` ${Ze(Vl(l))}`);let c=s.status==="running"?Mr(et[this.view.frame()%et.length]??"*"):s.status==="ok"?rs("\u2713"):jr("\u2717"),d=a===0&&!o?" \u23BF ":" ",u=s.spawnId?` ${Ze("agent \xB7")} `:"";i.push(Qt(`${d}${u}${c} ${s.label}`,e))}),i}};import{truncateToWidth as nd,visibleWidth as rd,wrapTextWithAnsi as is}from"@earendil-works/pi-tui";import{bold as ss,cyan as od,dim as Ce}from"kleur/colors";import{CombinedAutocompleteProvider as Zl}from"@earendil-works/pi-tui";var Zt=[{name:"help",description:"Show commands and keyboard shortcuts"},{name:"status",description:"Show session, access, model, usage and limits"},{name:"model",description:"Show the workspace model and the model that answered last"},{name:"usage",description:"Show turns and tokens used in this terminal"},{name:"new",args:"[shared]",description:"Start a new conversation",idleOnly:!0,completions:["shared"]},{name:"clear",description:"Clear the screen and start a new private conversation",idleOnly:!0},{name:"resume",args:"[session-id]",description:"Pick or open a saved conversation",idleOnly:!0},{name:"workspace",args:"[name|id]",description:"Switch to another saved workspace and start a new conversation there",idleOnly:!0},{name:"context",args:"[clear|program|initiative|codebase <id>]",description:"Show or change the context for the next message",completions:["clear","program ","initiative ","codebase "]},{name:"attach",args:"<path>",description:"Attach a file to your next message"},{name:"copy",description:"Copy the last answer as Markdown"},{name:"files",description:"List files Oracle generated in this conversation"},{name:"exit",description:"Leave the Oracle",aliases:["quit"]}],ed=[{name:"design",args:"[topic]",description:"Talk through a technical design before anything is created"},{name:"catchup",description:"Catch up on your work: what needs you, what landed, what is running"}];function Br(t){let e=new Set(Zt.flatMap(n=>[n.name,...n.aliases??[]]));return(t??ed).filter(n=>!e.has(n.name))}function os(t,e){let n=/^\/([A-Za-z][\w-]*)(?:\s+([\s\S]*))?$/.exec(t.trim());if(!n)return null;let r=(n[1]??"").toLowerCase(),o=(n[2]??"").trim(),i=Zt.find(a=>a.name===r||a.aliases?.includes(r));if(i)return{kind:"local",command:i,arg:o,args:o?o.split(/\s+/):[]};let s=e.find(a=>a.name===r);return s?{kind:"server",command:s}:{kind:"unknown",name:r}}function Wr(t,e){let n=[...Zt.flatMap(i=>[i,...(i.aliases??[]).map(s=>({...i,name:s}))]).map(i=>({name:i.name,description:i.description,...i.args?{argumentHint:i.args}:{},...i.completions?{getArgumentCompletions:s=>td(i.completions??[],s)}:{}})),...t.map(i=>({name:i.name,description:i.description,...i.args?{argumentHint:i.args}:{}}))],r=new Zl(n,e),o=/^\s*\/attach\s+/;return{async getSuggestions(i,s,a,c){let d=(i[s]??"").slice(0,a);return s!==0||!d.trimStart().startsWith("/")?null:o.test(d)?r.getSuggestions(i,s,a,{...c,force:!0}):r.getSuggestions(i,s,a,c)},applyCompletion(i,s,a,c,d){return r.applyCompletion(i,s,a,c,d)},shouldTriggerFileCompletion(i,s,a){return s===0&&o.test((i[s]??"").slice(0,a))}}}function td(t,e){let n=t.filter(r=>r.startsWith(e.toLowerCase())&&r!==e).map(r=>({value:r,label:r.trim()}));return n.length?n:null}function en(t,e){return t?e?"write + approve":"write":"read only"}function It(t){return t?`${t.kind} ${p(t.id)}`:"no context"}function De(t){return t?p(t.companyName||t.companyId):""}function as(t,e){let n=[`${ss(od("\u26A1\uFE0E AnyForge Oracle"))}${Ce(` \xB7 v${p(t.version)}`)}`,`${t.workspace?`${ss(t.workspace)} \xB7 `:""}${xe(t.session)} \xB7 ${t.access} \xB7 ${It(t.context)}`,...t.session.messages.length?[`Resumed: ${p(t.session.title)}`]:[],Ce("Only your messages and the files you attach leave this terminal."),Ce("/help commands \xB7 /status details")];if(e<24)return n.flatMap(s=>is(s,Math.max(1,e)));let r=Math.min(e,76),o=r-4,i=[Ce(`\u256D${"\u2500".repeat(r-2)}\u256E`)];for(let s of n.flatMap(a=>is(a,o))){let a=nd(s,o);i.push(`${Ce("\u2502")} ${a}${" ".repeat(Math.max(0,o-rd(a)))} ${Ce("\u2502")}`)}return i.push(Ce(`\u2570${"\u2500".repeat(r-2)}\u256F`)),i}function cs(t){let e=(r,o)=>r?`${Ce(r)} ${o}`:o,n=[{title:"Commands",rows:Zt.map(r=>[`/${r.name}`,e(r.args,r.aliases?.length?`${r.description} (also /${r.aliases.join(", /")})`:r.description)])}];if(t.server.length){let r=t.server.map(o=>[`/${o.name}`,e(o.args,p(o.description))]);t.server.some(o=>o.name==="catchup")&&!t.canApprove&&r.push(["",Ce("Acting on /catchup decisions needs a session started with `anyforge oracle --write --approve`.")]),n.push({title:"Workspace commands (answered by Oracle)",rows:r})}return n.push({title:"Keys",rows:[["Enter","Send. While Oracle works, queues one message for when it finishes"],["Shift+Enter","New line (Ctrl+J also works)"],["Esc","Interrupt the current answer"],["Ctrl+C","Interrupt the answer, or clear the input; press twice to exit"],["Ctrl+D","Exit when the input is empty"],["Ctrl+O","Show or hide tool activity for finished answers"],["Ctrl+Shift+F","Search the conversation"],["Up / Down","Recall messages sent in this terminal"],["Tab","Complete a command, or a file path after /attach"],["PgUp / PgDn","Scroll; Home and End jump to the top and bottom"]]}),n}function Gr(t){let n=[["Model",`workspace default ${t.model?`${p(t.model.provider)}/${p(t.model.id)}`:"chosen by the server"}`]],r=Xt(t.lastServedBy);if(r){let o=t.lastServedBy,i=T(o.routing,40).replaceAll("_"," ");n.push(["Last answer",`${r}${i?` \xB7 ${i}`:""}`])}return n}function Hr(t){return[["This run",t.turns?`${t.turns} turn${t.turns===1?"":"s"} \xB7 ${t.tools} tool${t.tools===1?"":"s"} \xB7 ${me(t.inputTokens)} in / ${me(t.outputTokens)} out tokens`:"no turns yet"]]}function zr(t){if(!t?.finished)return[];let e=[Gt((t.endedAt??t.startedAt)-t.startedAt)];e.push(`${t.toolCount} tool${t.toolCount===1?"":"s"}${t.failedCount?` (${t.failedCount} failed)`:""}`);let n=Q(t.done?.rounds);n!==void 0&&e.push(`${n} round${n===1?"":"s"}`);let r=Xe(t.done?.tokens);(r?.inputTokens!==void 0||r?.outputTokens!==void 0)&&e.push(`${me(r.inputTokens??0)} in${r.cachedInputTokens?` (${me(r.cachedInputTokens)} cached)`:""} / ${me(r.outputTokens??0)} out tokens`);let o=T(t.done?.exitReason,40);return e.push(o?o.replaceAll("_"," "):t.end??""),[["Last turn",e.filter(Boolean).join(" \xB7 ")]]}function ls(t){let e=t.capabilities?["read","write","approve"].filter(o=>t.capabilities?.[o]).join(", ")||"none":"not reported",n=t.contextText?` \xB7 note ${z(Buffer.byteLength(t.contextText,"utf8"))}`:"",r=Ti(t.limits);return[["Session",`${t.session.sessionId} \xB7 ${p(t.session.title)||"Untitled"}`],["Audience",xe(t.session)],["Access",`this session ${en(t.write,t.approve)} \xB7 grant ${t.grantScopes.join(", ")||"none"} \xB7 workspace role ${e}`],["Context",`${It(t.context)}${n}`],...Gr(t),...zr(t.lastTurn),...Hr(t.totals),["Limits",r.length?r.join(" \xB7 "):"not reported"],...t.identity?[["Workspace",id(t.identity,t.savedWorkspaces)]]:[],["Endpoint",t.endpoint],["Version",`CLI ${p(t.version)} \xB7 protocol ${1}`],["Attached",t.pendingFiles.length?`${t.pendingFiles.map(p).join(", ")} (uploads with your next message)`:"nothing waiting"]]}function id(t,e){let n=t.companyName?`${p(t.companyName)} (${p(t.companyId)})`:`company ${p(t.companyId)}`,r=e!==void 0&&e>1?` \xB7 ${e} saved, /workspace to switch`:"";return`${n} \xB7 user ${p(t.uid)}${r}`}function ds(t,e){return e.length?e.map((n,r)=>[`${r+1}.`,`${p(n.filename)} (${z(n.sizeBytes)}) \xB7 anyforge oracle download ${t} ${p(n.fileId)} --output .`]):[["Files","Oracle has not generated files in this conversation."]]}import{randomUUID as ld}from"node:crypto";import{stat as dd}from"node:fs/promises";import{basename as us}from"node:path";import{bold as ud,cyan as ps,dim as fs,red as Et,yellow as pd}from"kleur/colors";import{Container as fd,Editor as md,ProcessTerminal as hd,ScrollView as gd,SelectList as ms,TuiAltScreen as wd,VStack as yd,getNativeClipboard as vd,matchesKey as qn,truncateToWidth as hs,wrapTextWithAnsi as bd}from"@earendil-works/pi-tui";import{bold as sd,cyan as Kr,dim as Ln,yellow as ad}from"kleur/colors";import{Input as cd,Text as tt,matchesKey as St}from"@earendil-works/pi-tui";var Nn=class{constructor(e,n,r){this.tui=e;this.question=n;this.onComplete=r;(n.questions[0]?.options.length??0)===0&&(this.mode="custom"),this.customInput.onSubmit=o=>{let i=o.trim().slice(0,8e3);if(!i){this.errorMessage="Enter a short answer before continuing.",this.tui.requestRender();return}this.recordCurrent(i)},this.customInput.onEscape=()=>{if(!this.question.questions[this.questionIndex]?.options.length){this.question.optional?this.finish(null):this.requireAnswer();return}this.customInput.setValue(""),this.mode="select",this.tui.setFocus(this),this.tui.requestRender()}}tui;question;onComplete;focused=!1;questionIndex=0;optionIndex=0;mode="select";errorMessage="";selected=new Map;answers=[];customInput=new cd({prompt:`${Kr("Your answer: ")} `,placeholder:"Write a short answer and press Enter",placeholderStyle:Ln});invalidate(){}render(e){let n=this.question.questions[this.questionIndex];if(!n)return[];this.customInput.focused=this.focused&&this.mode==="custom";let r=Math.max(12,e-2),o=this.question.questions.length>1?` \xB7 ${this.questionIndex+1}/${this.question.questions.length}`:"",i=[...new tt(`${sd(Kr(p(n.header)))}${Ln(o)}`,0,0).render(r),...new tt(p(n.question),0,0).render(r),...this.renderOptions(n,r)];return this.mode==="custom"?(i.push(...this.customInput.render(r)),i.push(...new tt(Ln(`Enter submits your text${n.options.length?" \xB7 Esc back to the choices":this.question.optional?" \xB7 Esc skip":""}`),0,0).render(r))):i.push(...new tt(Ln(this.instructions(n)),0,0).render(r)),this.errorMessage&&i.push(...new tt(ad(this.errorMessage),0,0).render(r)),i.map(s=>`${Kr("\u2502")} ${s}`)}handleInput(e){if(this.mode==="custom"){this.customInput.handleInput(e);return}let n=this.question.questions[this.questionIndex];if(!n)return;let r=this.selectedFor(n);if(n.options.length===0){if(e.toLowerCase()==="s"&&this.question.optional){this.finish(null);return}if(St(e,"escape")){this.question.optional?this.finish(null):this.requireAnswer();return}this.customInput.handleInput(e);return}if(St(e,"up")){this.optionIndex=Math.max(0,this.optionIndex-1),this.errorMessage="",this.tui.requestRender();return}if(St(e,"down")){this.optionIndex=Math.min(n.options.length-1,this.optionIndex+1),this.errorMessage="",this.tui.requestRender();return}if(St(e,"escape")){this.question.optional?this.finish(null):this.requireAnswer();return}if(e.toLowerCase()==="s"&&this.question.optional){this.finish(null);return}if(e.toLowerCase()==="o"){this.mode="custom",this.tui.setFocus(this),this.tui.requestRender();return}let o=Number(e);if(Number.isInteger(o)&&o>=1&&o<=n.options.length){let i=n.options[o-1]?.id;i&&(this.optionIndex=o-1,n.multiSelect?r.has(i)?r.delete(i):r.add(i):(r.clear(),r.add(i)),this.tui.requestRender());return}if(St(e,"space")&&n.multiSelect){let i=n.options[this.optionIndex];i&&(r.has(i.id)?r.delete(i.id):r.add(i.id),this.tui.requestRender());return}if(St(e,"enter")){if(r.size===0){this.errorMessage="Select an option explicitly, type a custom answer, or skip if optional.",this.tui.requestRender();return}this.recordCurrent()}}renderOptions(e,n){let r=this.selectedFor(e),o=[];return e.options.forEach((i,s)=>{let a=r.has(i.id),c=e.multiSelect?a?"[x]":"[ ]":a?"(x)":"( )",d=s===this.optionIndex?"\u203A":" ",u=i.recommended?" \xB7 recommended":"",l=i.confirms?" \xB7 confirms the shown action":"",k=`${d} ${s+1}. ${c} ${p(i.label)}${u}${l}`;o.push(...new tt(k,0,0).render(n)),o.push(...new tt(` ${p(i.description)}`,0,0).render(n))}),o}instructions(e){return e.options.length===0?`Write a short answer${this.question.optional?" \xB7 s or Esc skip card":""}`:`\u2191/\u2193 move \xB7 1\u2013${e.options.length} ${e.multiSelect?"toggle":"select"}${e.multiSelect?" \xB7 Space toggle":""} \xB7 Enter continue \xB7 o write your own${this.question.optional?" \xB7 s or Esc skip card":""}`}requireAnswer(){let e=this.question.questions[this.questionIndex];this.errorMessage=e?.options.length?"Oracle needs an answer to continue: pick an option, or press o to write your own.":"Oracle needs an answer to continue: type it and press Enter.",this.tui.requestRender()}selectedFor(e){let n=this.selected.get(e.id);return n||(n=new Set,this.selected.set(e.id,n)),n}recordCurrent(e){let n=this.question.questions[this.questionIndex];if(!n)return;e&&this.selectedFor(n).clear();let r=[...this.selectedFor(n)],o={questionId:n.id,optionIds:r,...e?{customText:e}:{}},i=this.answers.findIndex(s=>s.questionId===n.id);if(i>=0?this.answers.splice(i,1,o):this.answers.push(o),this.customInput.setValue(""),this.mode="select",this.questionIndex<this.question.questions.length-1){this.questionIndex+=1,this.optionIndex=0,this.mode=this.question.questions[this.questionIndex]?.options.length===0?"custom":"select",this.errorMessage="",this.tui.setFocus(this),this.tui.requestRender();return}this.finish({questionId:this.question.id,answers:this.answers.map(s=>({...s}))})}finish(e){this.onComplete(e)}};var gs=1500,kd=100,Od="\x1B]133;A\x07";function Ct(t){return{render:t,invalidate(){}}}function ws(t,e,n,r){let o=new yt(e,r);return{getText:()=>o.getText(),invalidate:()=>o.invalidate(),render:i=>t==="user"?Fi(o,i,n).map((s,a)=>a===1?`${Od}${s}`:s):[Ht("assistant"),...o.render(i)]}}async function ys(t){let{client:e,config:n}=t,r=new hd,o=new wd(r,void 0,void 0,{scrollToEndIndicator:()=>" more below \xB7 End to jump "}),i=new fd,s=new gd(i,{follow:"end",primary:!0,overscroll:"chain"}),a=new md(o,qi),c=Br(n.commands),d=()=>typeof n.limits?.turnSoftMs=="number"?n.limits.turnSoftMs:void 0,u=t.session,l=t.initialContext,k=t.contextText,O=t.canWrite,x=t.canApprove,b,m,_,$,j=[...t.initialFiles],G=!1,be=!1,it=!1,Vn=0,st,eo=0,rn=!1,Be="",Pt,on,at=[],ct={turns:0,inputTokens:0,outputTokens:0,tools:0},sn=new Map,ke,Vs=Ct(w=>{if(b){let y=ps(et[Vn%et.length]??"*");return[hs(`${y} ${b.activity.statusLine(Date.now(),d())}`,w)]}return ke?[hs(`${ps("?")} Waiting for your answer to the question above`,w)]:[]}),Ys=Ct(w=>{let y=[De(n.identity),en(O,x),It(l),Ui(u),j.length?`${j.length} file${j.length===1?"":"s"} attached`:"",m!==void 0?"1 message queued":""];return[fs(Pi(w,y,b?"esc to interrupt":"/help \xB7 /status"))]});o.setLayoutRoot(new yd([{component:s,basis:0,grow:1,minSize:1},{component:Vs,basis:"auto",shrink:0},{component:a,basis:"auto",shrink:0,minSize:2},{component:Ys,basis:"auto",shrink:0,minSize:1}]));let ae=()=>o.requestRender(),F=w=>o.flash(p(w),3e3),Pe=w=>{i.addChild(w),ae()},V=(w,y=fs)=>{let v=p(w);Pe(Ct(A=>bd(y(v),Math.max(1,A))))},an=(w,y)=>{Pe(Ct(v=>["",...$i(w,y,v)]))},Js=()=>{st||(st=setInterval(()=>{Vn+=1,ae()},kd),st.unref())},Yn=()=>{st&&clearInterval(st),st=void 0},lt=()=>{G||o.hasOverlay()||o.setFocus(ke??a)},to=w=>{_=w;let y=sn.get(w.id);y&&i.removeChild(y);let v=new Nn(o,w,A=>{sn.delete(w.id),ke===v&&(ke=void 0),i.removeChild(v),Zs(A??{questionId:w.id,skipped:!0,answers:[]}),lt()});sn.set(w.id,v),ke=v,Pe(v)},cn=w=>{i.clear(),sn.clear(),ke=void 0,u=w,_=void 0,$=void 0,at=[],Be="";let y={version:t.version,workspace:De(n.identity),session:w,access:en(O,x),context:l};Pe(Ct(A=>as(y,A)));let v;for(let A of w.messages){Pe(ws(A.role,A.content,A.authorName,e.endpoint)),A.role==="assistant"&&(Be=A.content),A.question&&(v=A.question),A.questionResponse&&(V("A stored response was recorded for this question."),v?.id===A.questionResponse.questionId&&(v=void 0));for(let R of A.files??[])at.some(P=>P.fileId===R.fileId)||at.push(R),V(`File: ${R.filename} (${R.mediaType}, ${z(R.sizeBytes)}) \xB7 anyforge oracle download ${w.sessionId} ${R.fileId} --output .`)}v&&to(v),lt(),Jt(w.sessionId,e.endpoint,n.identity?.companyId)},no,Qs=new Promise(w=>{no=w}),ln=()=>{G||(G=!0,Yn(),b?.controller.abort(new Error("Detached from the active Oracle stream.")),o.stop(),no())},Jn=async()=>{let w=b;if(!w||w.cancelling)return;w.cancelling=!0,w.activity.setPhase("cancelling",Date.now()),ae();let{streamId:y,requestId:v}=w;w.controller.abort(new Error("Turn cancelled by user."));let A;if(y)try{A=xt(await e.cancelTurn(u.sessionId,y))}catch(R){A=`Could not confirm server cancellation: ${K(R)}`}else if(v)try{A=xt(await e.cancelRequest(u.sessionId,v))}catch(R){A=`Could not confirm server cancellation by request id: ${K(R)}`}else A="The turn had not reached the server; nothing was sent.";w.activity.note("info",A),ae()},dt=async w=>{if(b||G)return;if(w.kind==="submit"&&w.response){let L=_;if(!L||L.id!==w.response.questionId){F("That question is no longer active. Resume the session to refresh it.");return}V(Ad(L,w.response)),_=void 0}else w.kind==="submit"&&Pe(ws("user",w.text));let y=u.sessionId,v=new $n(Date.now(),y),A=new Pn(v,{expanded:()=>it,frame:()=>Vn,linkOrigin:e.endpoint}),R={activity:v,view:A,controller:new AbortController,cancelling:!1};b=R,Pe(A),Js();let P=new Ee,X=L=>{v.apply(L,Date.now()),L.type==="question"&&(to(Yt(L)),lt()),ae()},We={onEvent:X,onStream:L=>{R.streamId=L},onResponse:()=>{v.phase==="sending"&&v.setPhase("preparing",Date.now())},onActivity:()=>v.touch(Date.now())},ce="error";try{if(w.kind==="replay"){P.streamId=w.streamId,R.streamId=w.streamId,v.setPhase("thinking",Date.now());let L=await Fr(e,y,P,async he=>{P.accept(he)&&X(he)},R.controller.signal,We.onActivity);ce=Vt(y,L).status}else{let L=w.response?[]:j.splice(0),he=await Tn(e,y,L,(Qn,la)=>{v.setUploading(Qn,la,Date.now()),ae()});R.controller.signal.throwIfAborted(),v.setPhase("sending",Date.now());let dn=ld();R.requestId=dn;let aa={requestId:dn,text:w.response?"":w.text,readOnly:!O,...x?{allowApprove:!0}:{},...l?{context:l}:{},...k?{contextText:k}:{},...w.response?{questionResponse:w.response}:{},...he.length?{attachments:he.map(Qn=>({fileId:Qn.fileId}))}:{}},ca=await _t(e,y,aa,We,R.controller.signal,P);ce=Vt(y,ca).status}}catch(L){if(R.controller.signal.aborted)ce="cancelled";else{ce="error";let he=L instanceof f&&L.hint?` ${p(L.hint)}`:"";v.note("error",`${K(L)}${he}`)}}finally{v.finish(ce,Date.now()),b=void 0,Yn(),G||(be=ce==="cancelled"),Pt=v,v.answer.trim()&&(Be=v.answer),v.done?.servedBy&&(on=v.done.servedBy);let L=Xe(v.done?.tokens);ct.turns+=1,ct.tools+=v.toolCount,ct.inputTokens+=L?.inputTokens??0,ct.outputTokens+=L?.outputTokens??0;for(let he of v.files)at.some(dn=>dn.fileId===he.fileId)||at.push(he);ae()}if(!G){if(R.streamId&&ce!=="cancelled"){let L=await e.getSession(y).catch(()=>{});L&&L.sessionId===u.sessionId&&(u=L)}Xs(ce)}},Xs=w=>{if(!(G||b)){if($){let y=$;$=void 0,dt({kind:"submit",text:"",response:y});return}if(m!==void 0){let y=m;if(m=void 0,w==="complete"&&!ke){dt({kind:"submit",text:y});return}a.getText()||a.setText(y),F("Your queued message is back in the input; press Enter to send it.")}lt(),ae()}},Zs=w=>{if(b){$=w,F("Answer saved; Oracle continues once the current turn settles.");return}dt({kind:"submit",text:"",response:w})},ro=async w=>{try{let y=await e.getSession(w);cn(y),y.activeStreamId&&dt({kind:"replay",streamId:y.activeStreamId}),F(`Resumed ${y.title||y.sessionId}.`)}catch(y){V(K(y),Et)}},ea=async()=>{try{let w=await e.listSessions();if(!w.length){F("No saved conversations to resume.");return}let y=new ms(w.map(P=>({value:P.sessionId,label:P.title,description:xe(P)})),Math.min(12,w.length),vt),v=await En(e.endpoint,n.identity?.companyId),A=v?w.findIndex(P=>P.sessionId===v):-1;A>=0&&y.setSelectedIndex(A);let R=o.showOverlay(y,{width:"85%",maxHeight:"80%",anchor:"center"});y.onSelect=P=>{R.hide(),ro(P.value)},y.onCancel=()=>{R.hide(),lt()},R.focus()}catch(w){V(K(w),Et)}},oo=async w=>{if(w&&(!l||l.kind!=="program"&&l.kind!=="initiative")){F("Use /context program <id> or /context initiative <id> before /new shared.");return}try{let y=await e.createSession(Rt(l,w));cn(y),F(`Started a new conversation \xB7 ${xe(y)}.`)}catch(y){V(K(y),Et)}},io=async(w,y)=>{let v=p(se(y));if(y.companyId&&y.companyId===n.identity?.companyId){F(`Already in ${v}.`);return}rn=!0;try{F(`Connecting to ${v}...`);let A=await w.connect(y),R=await A.client.createSession(Rt(void 0,!1));e=A.client,n=A.config,c=Br(n.commands),a.setAutocompleteProvider(Wr(c,process.cwd()));let P=e.scopes.has("write")&&n.capabilities?.write===!0,X=e.scopes.has("approve")&&n.capabilities?.approve===!0,We=O&&!P||x&&!X;O&&!P&&(O=!1),(!O||!X)&&(x=!1);let ce=l!==void 0;l=void 0,on=void 0,Pt=void 0,cn(R),V(`Switched to ${De(n.identity)||v}. This is a new conversation in that workspace.${ce?" Context was cleared because its id belongs to the previous workspace.":""}`),We&&V(`Access here is ${en(O,x)}: this workspace's login or your role there does not allow more.`,pd)}catch(A){let R=A instanceof f&&A.hint?` ${p(A.hint)}`:"";V(`Could not switch to ${v}: ${K(A)}${R}`,Et)}finally{rn=!1}},ta=async(w,y)=>{let v=n.identity?.companyId,A=new ms(y.map(X=>({value:X.clientId,label:se(X),description:[X.companyId,X.scopes.join(", "),X.companyId===v?"current":""].filter(Boolean).join(" \xB7 ")})),Math.min(12,y.length),vt),R=y.findIndex(X=>X.companyId===v);R>=0&&A.setSelectedIndex(R);let P=o.showOverlay(A,{width:"85%",maxHeight:"80%",anchor:"center"});A.onSelect=X=>{P.hide();let We=y.find(ce=>ce.clientId===X.value);We&&io(w,We)},A.onCancel=()=>{P.hide(),lt()},P.focus()},na=async w=>{let y=t.workspaces;if(!y){V(`This terminal signs in with ANYFORGE_API_TOKEN, which is bound to ${De(n.identity)||"one workspace"}. To switch workspaces, unset it and sign in with \`anyforge oracle login --workspace <name>\`.`);return}let v=await y.list();if(w){let A;try{A=Qe(v,w)}catch(R){let P=R instanceof f&&R.hint?` ${p(R.hint)}`:"";V(`${K(R)}${P}`,Et);return}await io(y,A);return}if(v.length<=1){V(`${De(n.identity)||"This workspace"} is the only saved workspace. To add another, run \`anyforge oracle login --workspace <name>\` in another terminal, then use /workspace here.`);return}await ta(y,v)},ra=w=>({session:u,write:O,approve:x,grantScopes:[...e.scopes],...n.capabilities?{capabilities:n.capabilities}:{},...l?{context:l}:{},...k?{contextText:k}:{},...n.model?{model:n.model}:{},lastServedBy:on,...Pt?{lastTurn:Pt}:{},totals:{...ct},...n.limits?{limits:n.limits}:{},...n.identity?{identity:n.identity}:{},...w!==void 0?{savedWorkspaces:w}:{},endpoint:e.endpoint,version:t.version,pendingFiles:j.map(y=>us(y))}),oa=async()=>{if(!Be.trim()){F("There is no answer to copy yet.");return}try{let w=vd();w?.setText?await w.setText(Be):r.write(`\x1B]52;c;${Buffer.from(Be,"utf8").toString("base64")}\x07`),F(`Copied the last answer (${Be.length} characters).`)}catch(w){F(`Could not copy: ${K(w)}`)}},ia=async(w,y,v)=>{if(w.idleOnly&&rn){F("Wait for the workspace switch to finish.");return}if(w.idleOnly&&b){F(`Wait for Oracle to finish, or press Esc, before /${w.name}.`);return}switch(w.name){case"help":Pe(Ct(A=>["",...cs({server:c,canApprove:x}).flatMap(R=>[ud(R.title),...Sr(R.rows,A)])]));return;case"status":{let A=await t.workspaces?.list().then(R=>R.length).catch(()=>{});an("Status",ls(ra(A)));return}case"workspace":await na(v);return;case"model":an("Model",Gr({...n.model?{model:n.model}:{},lastServedBy:on}));return;case"usage":an("Usage",[...zr(Pt),...Hr(ct)]);return;case"files":an("Generated files",ds(u.sessionId,at));return;case"copy":await oa();return;case"exit":ln();return;case"new":if(v&&v!=="shared"){F("Usage: /new or /new shared");return}await oo(v==="shared");return;case"clear":await oo(!1);return;case"resume":v?await ro(v):await ea();return;case"context":{if(!v){V(`Context: ${It(l)}${k?" + free-text note":""}. Use /context <program|initiative|codebase> <id> or /context clear.`);return}if(v==="clear")l=void 0;else{let[A,R,...P]=y;if(!["program","initiative","codebase"].includes(A??"")||!R||P.length){F("Usage: /context program <id>, /context initiative <id>, /context codebase <id>, or /context clear");return}l={kind:A,id:R}}V(`Context for your next message: ${It(l)}.`);return}case"attach":{if(!v){F("Usage: /attach <file path>");return}let A=await dd(v).catch(()=>{});if(!A?.isFile()){F(`Not a readable file: ${v}`);return}j.push(v),V(`Attached ${us(v)} (${z(A.size)}). It uploads with your next message.`);return}}},so=w=>{let y=w.trim();if(!y)return;a.addToHistory(y);let v=os(y,c);if(v?.kind==="local"){ia(v.command,v.args,v.arg).catch(A=>V(K(A),Et));return}if(v?.kind==="unknown"){a.setText(y),F(`Unknown command /${v.name}. Type /help to see the commands.`);return}if(rn){a.setText(y),F("Switching workspace; send your message once it has opened.");return}if(b){let A=m!==void 0;m=y,F(A?"Replaced your queued message. It sends when Oracle finishes.":"Queued. It sends when Oracle finishes; Esc interrupts instead."),ae();return}dt({kind:"submit",text:y})},sa=o.addInputListener(w=>{if(qn(w,"ctrl+c")){if(b)return Jn(),{consume:!0};if(o.hasOverlay())return;if(o.getFocusedComponent()===a&&a.getText())return a.setText(""),ae(),{consume:!0};let y=Date.now();return y-eo<=gs?ln():(eo=y,o.flash("Press Ctrl+C again to exit",gs)),{consume:!0}}if(qn(w,"escape")){let y=ke!==void 0&&o.getFocusedComponent()===ke;return b&&!a.isShowingAutocomplete()&&!o.hasOverlay()&&!y?(Jn(),{consume:!0}):void 0}if(qn(w,"ctrl+d"))return o.getFocusedComponent()===a&&!a.getText()?(ln(),{consume:!0}):void 0;if(qn(w,"ctrl+o"))return it=!it,F(it?"Showing tool activity":"Hiding tool activity"),ae(),{consume:!0}}),ao=()=>{let w=!!b;Jn().finally(()=>{w&&(be=!0),ln()})};process.on("SIGINT",ao),a.onSubmit=so,a.onChange=()=>ae(),a.setAutocompleteProvider(Wr(c,process.cwd())),cn(t.session),o.start(),u.activeStreamId&&dt({kind:"replay",streamId:u.activeStreamId}),t.initialPrompt&&so(t.initialPrompt),await Qs,sa(),process.off("SIGINT",ao),Yn(),b?.controller.abort(),await r.drainInput().catch(()=>{}),be&&(process.exitCode=130)}function Ad(t,e){if(e.skipped)return"Skipped the optional Oracle question.";let n=[];for(let r of e.answers){let o=t.questions.find(i=>i.id===r.questionId);for(let i of r.optionIds){let s=o?.options.find(a=>a.id===i);s&&n.push(s.label)}r.customText&&n.push(r.customText)}return`You answered: ${n.map(p).join("; ")||"no options selected"}`}import{createHash as _d,randomBytes as vs,timingSafeEqual as xd}from"node:crypto";import{createServer as Rd}from"node:http";import Id from"open";var jn="https://crew.anyforge.ai",Sd=6e4,Ed=5*6e4,Fn=new Map;async function nt(t){t.signal?.throwIfAborted();let e=Me(t.endpoint),n=await Je(e,fetch,void 0,t.signal),r=["read"];t.write&&r.push("write"),t.approve&&(r.includes("write")||r.push("write"),r.push("approve"));let o=vs(32).toString("base64url"),i=await Pd(o,t.signal);try{let s={client_name:"AnyForge CLI",redirect_uris:[i.redirectUri],grant_types:["authorization_code","refresh_token"],response_types:["code"],token_endpoint_auth_method:"none"},a=await Ld(n.oauth.registration,s,t.signal);if(typeof a.client_id!="string"||!a.client_id)throw new f("OAuth registration did not return a client id.",g.API_ERROR);let c=vs(32).toString("base64url"),d=_d("sha256").update(c).digest("base64url"),u={client_id:a.client_id,redirect_uri:i.redirectUri,response_type:"code",scope:r.join(" "),state:o,code_challenge:d,code_challenge_method:"S256",resource:n.resource},l=new URL(n.oauth.authorization);for(let[G,be]of Object.entries(u))l.searchParams.set(G,String(be));let k=t.workspace?.trim();k&&l.searchParams.set(Di,k),t.onAuthorizationUrl?.(l.toString()),t.signal?.throwIfAborted();try{t.openBrowser?await t.openBrowser(l.toString()):await Id(l.toString())}catch{process.stderr.write(`Open this URL to authorize AnyForge Oracle:
425
- ${l.toString()}
426
- `)}let O=await i.wait();if(!Rs(O.state,o))throw new f("Oracle sign-in callback state did not match; authorization was rejected.",g.API_ERROR);if(O.error)throw new f(`Oracle authorization failed: ${p(O.error)}${O.errorDescription?` (${p(O.errorDescription)})`:""}`,g.API_ERROR);if(!O.code)throw new f("Oracle authorization callback did not include a code.",g.API_ERROR);let x={client_id:a.client_id,grant_type:"authorization_code",code:O.code,redirect_uri:i.redirectUri,code_verifier:c,resource:n.resource},b=await Es(n.oauth.token,x,t.signal);Ss(b);let m=Is(b.scope);if(!m.includes("read")||m.some(G=>!r.includes(G)))throw await Mn(n.oauth.revocation,a.client_id,b.access_token,b.refresh_token).catch(()=>{}),new f("The granted Oracle scopes are invalid for this request.",g.API_ERROR);let _=await Je(e,fetch,b.access_token,t.signal).then(G=>G.identity).catch(()=>{});t.signal?.throwIfAborted();let $={endpoint:e,...ks(_),clientId:a.client_id,redirectUri:i.redirectUri,accessToken:b.access_token,refreshToken:b.refresh_token,expiresAt:Date.now()+b.expires_in*1e3,scopes:m},j=await In(G=>Ki(G,$));return j&&await Mn(n.oauth.revocation,j.clientId,j.accessToken,j.refreshToken).catch(()=>{}),$}finally{await i.close()}}function ks(t){return t?{companyId:t.companyId,...t.companyName?{companyName:t.companyName}:{},uid:t.uid}:{}}async function Os(t={}){if(t.workspace&&!t.all){let n=tn(await ie(),t.endpointOverride);n&&await Vr(n)}let e=await Cn(async()=>{let n=await ie(),r=[];if(t.all)r=[...n.logins];else{let s=tn(n,t.endpointOverride);t.workspace?r=[Qe(s?Se(n,s):[],t.workspace)]:s&&(r=[Sn(n,s)].filter(a=>!!a))}let o=[],i;for(let s of r){try{await As(s)}catch(a){o.push({login:s,error:a})}i=Lr(n,s)??i}return await Kt(n),{removed:r,failures:o,...i&&!t.all?{nowActive:i}:{}}});if(e.failures.length){let n=e.failures.map(({login:r,error:o})=>{let i=o instanceof Error?p(o.message):"unknown error";return e.failures.length>1||t.all?`${p(se(r))}: ${i}`:i});throw new f(`Local Oracle credentials were cleared, but server revocation failed for ${e.failures.length===1?"one login":`${e.failures.length} logins`}: ${n.join("; ")}`,g.FAILURE,"Run `anyforge oracle login` again if you still need access. The server will expire these access tokens automatically.")}return{removed:e.removed,...e.nowActive?{nowActive:e.nowActive}:{}}}async function As(t){let e=await Je(t.endpoint);await Mn(e.oauth.revocation,t.clientId,t.accessToken,t.refreshToken)}function tn(t,e){let n=e||process.env.ANYFORGE_ENDPOINT;return n?Me(n):t.endpoint}function _s(){return new h("ANYFORGE_API_TOKEN is set, so Oracle uses that key and its one workspace; saved Oracle logins are not used.","Unset ANYFORGE_API_TOKEN to choose between your saved workspaces.")}async function Dn(t,e){let n=t||process.env.ANYFORGE_ENDPOINT;if(process.env.ANYFORGE_API_TOKEN){if(e)throw _s();return bs(n)}let r=await ie(),o=tn(r,n);if(e)return Te(await Cd(o,e));let i=o?Sn(r,o):void 0;if(i)return Te(i);try{return await bs(n)}catch(s){throw r.logins.length&&n?new we:s instanceof we?new we:s}}async function Cd(t,e){if(!t)return Qe([],e);let n=await Bn(t);return Qe(n,e)}function Te(t){return{endpoint:t.endpoint,accessToken:t.accessToken,scopes:t.scopes.filter(Yr),source:"oauth",getAccessToken:async e=>Td(t.endpoint,t.clientId,e),onIdentity:async e=>{if(t.companyId===e.companyId&&(e.companyName===void 0||t.companyName===e.companyName)&&t.uid===e.uid)return;let r=await In(i=>Vi(i,t.endpoint,t.clientId,e));Object.assign(t,ks(e));let o=r.duplicate;o&&await As(o).catch(()=>{})}}}async function Bn(t){return await Vr(t),Se(await ie(),t)}async function Vr(t){let e=Se(await ie(),t).filter(n=>!n.companyId);for(let n of e)await new ue(Te(n)).config().catch(()=>{})}async function Tt(t){let e=tn(await ie(),t);if(!e)return{logins:[]};let n=await Bn(e),r=Sn(await ie(),e);return{endpoint:e,logins:n,...r?{active:r}:{}}}async function xs(t,e){if(process.env.ANYFORGE_API_TOKEN)throw _s();let n=tn(await ie(),t);return n&&await Vr(n),In(r=>{let o=Qe(n?Se(r,n):[],e);return r.active[o.endpoint]=o.clientId,r.endpoint=o.endpoint,o})}async function bs(t){let e=await qe(t),r=(await qt({apiKey:e.apiKey,endpoint:e.endpoint})).scopes.filter(Yr);return{endpoint:e.endpoint,accessToken:e.apiKey,scopes:r,source:"control"}}async function Td(t,e,n){let r=Me(t),o=`${r}
427
- ${e}`,i=Fn.get(o);if(i)return i;let s=Cn(()=>$d(r,e,n));Fn.set(o,s);try{return await s}finally{Fn.get(o)===s&&Fn.delete(o)}}async function $d(t,e,n){let r=await ie(),o=r.logins.find(d=>d.endpoint===t&&d.clientId===e);if(!o)throw new we;if(o.expiresAt>Date.now()+Sd&&(!n||o.accessToken!==n))return o.accessToken;let i=async()=>{Lr(r,o),await Kt(r)},s=await Je(o.endpoint),a;try{let d={client_id:o.clientId,grant_type:"refresh_token",refresh_token:o.refreshToken,resource:s.resource};a=await Es(s.oauth.token,d)}catch(d){throw d instanceof Un&&["invalid_grant","invalid_client","invalid_target"].includes(d.code)?(await i(),new we):d}Ss(a);let c;try{c=Is(a.scope)}catch(d){throw await i(),d}if(c.some(d=>!o.scopes.includes(d)))throw await Mn(s.oauth.revocation,o.clientId,a.access_token,a.refresh_token),await i(),new f("Oracle refresh attempted to widen the saved grant; credentials were cleared.",g.API_ERROR);return o.accessToken=a.access_token,o.refreshToken=a.refresh_token,o.expiresAt=Date.now()+a.expires_in*1e3,o.scopes=c,await Kt(r),o.accessToken}var Un=class extends f{constructor(n,r,o){super(`OAuth ${n}: ${r}`,o===401?g.NO_CREDENTIALS:g.API_ERROR);this.code=n;this.name="OAuthResponseError"}code};async function Pd(t,e){e?.throwIfAborted();let n,r,o=!1,i=new Promise((u,l)=>{n=u,r=l});i.catch(()=>{});let s=Rd((u,l)=>{if((u.url?.length??0)>8192){l.writeHead(414,{"Content-Type":"text/plain; charset=utf-8"}).end("Authorization response is too large.");return}let k=u.url??"/";if(!k.startsWith("/callback")||k.startsWith("//")){l.writeHead(400,{"Content-Type":"text/plain; charset=utf-8"}).end("Authorization response path is invalid.");return}let O=new URL(k,"http://127.0.0.1");if(u.method!=="GET"||O.pathname!=="/callback"){l.writeHead(404,{"Content-Type":"text/plain; charset=utf-8"}).end("Not found");return}let x=O.searchParams.get("code")?.slice(0,2048)??"",b=O.searchParams.get("state")?.slice(0,256)??"",m=O.searchParams.get("error")?.slice(0,256)??void 0,_=O.searchParams.get("error_description")?.slice(0,1024)??void 0;if(!b){l.writeHead(400,{"Content-Type":"text/plain; charset=utf-8"}).end("Authorization response is missing state.");return}if(!Rs(b,t)){l.writeHead(400,{"Content-Type":"text/plain; charset=utf-8"}).end("Authorization response state did not match.");return}if(o){l.writeHead(409,{"Content-Type":"text/plain; charset=utf-8"}).end("Authorization response already received.");return}o=!0,l.writeHead(200,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store","Referrer-Policy":"no-referrer","Content-Security-Policy":"default-src 'none'; style-src 'unsafe-inline'"}).end('<!doctype html><meta charset="utf-8"><title>AnyForge Oracle</title><p>Sign-in response received. You may close this tab.</p>'),n({code:x,state:b,...m?{error:m}:{},..._?{errorDescription:_}:{}})});await new Promise((u,l)=>{s.once("error",l),s.listen(0,"127.0.0.1",()=>{s.removeListener("error",l),u()})});let a=s.address();if(!a||typeof a=="string")throw new f("Could not bind an Oracle sign-in callback port.",g.FAILURE);let c=setTimeout(()=>{o||(o=!0,r(new f("Oracle sign-in timed out before the browser returned.",g.FAILURE)))},Ed);c.unref();let d=()=>{o||(o=!0,r(e?.reason??new DOMException("Oracle sign-in cancelled.","AbortError"))),clearTimeout(c),s.closeAllConnections(),s.close()};return e?.addEventListener("abort",d,{once:!0}),e?.aborted&&d(),{redirectUri:`http://127.0.0.1:${a.port}/callback`,wait:()=>i,close:async()=>{e?.removeEventListener("abort",d),clearTimeout(c),s.listening&&(s.closeAllConnections(),await new Promise(u=>s.close(()=>u())))}}}function Rs(t,e){let n=Buffer.from(t),r=Buffer.from(e);return n.length===r.length&&xd(n,r)}function Is(t){let e=t.split(/[\s+]+/).filter(Boolean);if(!e.every(Yr))throw new f("OAuth server returned an unknown Oracle scope.",g.API_ERROR);let n=[...new Set(e)];if(!n.includes("read"))throw new f("Oracle OAuth grant is missing the required read scope.",g.API_ERROR);return n}function Yr(t){return t==="read"||t==="write"||t==="approve"}function Ss(t){if(!t||typeof t.access_token!="string"||!t.access_token||typeof t.refresh_token!="string"||!t.refresh_token||typeof t.token_type!="string"||t.token_type.toLowerCase()!=="bearer"||typeof t.expires_in!="number"||!Number.isFinite(t.expires_in)||t.expires_in<=0||typeof t.scope!="string")throw new f("OAuth token response did not match the supported format.",g.API_ERROR)}async function Ld(t,e,n){return Cs(t,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(e),signal:n})}async function Es(t,e,n){return Cs(t,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},body:new URLSearchParams(Object.entries(e).map(([r,o])=>[r,String(o)])).toString(),signal:n})}async function Cs(t,e){let n,r=AbortSignal.timeout(2e4);try{n=await fetch(t,{...e,redirect:"error",signal:e.signal?AbortSignal.any([e.signal,r]):r})}catch(i){throw e.signal?.throwIfAborted(),new f(`OAuth request failed: ${p(i instanceof Error?i.message:String(i))}`,g.FAILURE)}let o=null;try{o=await n.json()}catch{}if(e.signal?.throwIfAborted(),!n.ok){let i=o&&typeof o=="object"?o:{},s=typeof i.error=="string"?i.error:`http_${n.status}`,a=typeof i.error_description=="string"?p(i.error_description):s;throw new Un(s,a,n.status)}if(!o||typeof o!="object"||Array.isArray(o))throw new f(`OAuth endpoint returned invalid JSON (${n.status}).`,g.API_ERROR);return o}async function Mn(t,e,n,r){let o=async i=>{let s=await fetch(t,{method:"POST",redirect:"error",signal:AbortSignal.timeout(2e4),headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},body:new URLSearchParams({client_id:e,token:i}).toString()});if(!s.ok){let a=`HTTP ${s.status}`;try{let c=await s.json();typeof c.error_description=="string"&&(a=p(c.error_description))}catch{}throw new f(`OAuth token revocation failed: ${a}`,g.API_ERROR)}};await o(r),n!==r&&await o(n)}var Vd=1048576,Yd=25*1048576;async function Fs(t){let[e,...n]=t.positional,r=["exec","sessions","resume","login","logout","download","workspaces","use"].includes(e??""),o=rt(t.flags,"print");if(o!==void 0&&t.positional.length)throw new h("-p/--print takes the prompt as its value and cannot be combined with a subcommand or positional prompt.");let i=rt(t.flags,"workspace")?.trim()||void 0,s=r?void 0:t.positional.join(" ");if(W(t.flags,"read-only")&&(W(t.flags,"write")||W(t.flags,"approve")))throw new h("--read-only cannot be combined with --write or --approve.");if(!r&&o===void 0&&(!process.stdin.isTTY||!process.stdout.isTTY))throw new h("Interactive Oracle needs a terminal.",'Use `anyforge oracle exec "your question"` for scripts and pipes.');if(e==="login"){if(n.length)throw new h("oracle login does not accept positional arguments.");let b=await nt({endpoint:t.endpointOverride??t.endpoint,write:W(t.flags,"write"),approve:W(t.flags,"approve"),...i?{workspace:i}:{}}),_=[W(t.flags,"write")?"write":void 0,W(t.flags,"approve")?"approve":void 0].filter($=>!!$).filter($=>!b.scopes.includes($));process.stdout.write(`${Ts(b)}
428
- `),_.length&&process.stderr.write(`This account was granted read only; the server withheld: ${_.join(", ")}.
429
- `),await Qd(b,i);return}if(e==="logout"){if(n.length)throw new h("oracle logout does not accept positional arguments.");let b=W(t.flags,"all");if(b&&i)throw new h("Choose either --workspace or --all.");let m=await Os({...t.endpointOverride?{endpointOverride:t.endpointOverride}:{},...i?{workspace:i}:{},all:b});if(!m.removed.length){process.stdout.write(`No saved Oracle login.
430
- `);return}let _=m.removed.map($=>p(se($))).join(", ");process.stdout.write(`Logged out of ${_}; ${m.removed.length===1?"its access was":"their access was"} revoked.
431
- `),m.nowActive&&process.stdout.write(`Active workspace is now ${p(se(m.nowActive))}.
432
- `);return}if(e==="workspaces"){if(n.length)throw new h("oracle workspaces does not accept positional arguments.");await Zd(t.endpointOverride,t.flags.json===!0);return}if(e==="use"){let b=n.join(" ").trim();if(!b)throw new h("oracle use needs a workspace name or id.","Run `anyforge oracle workspaces` to list your saved workspaces.");let m=await xs(t.endpointOverride,b);process.stdout.write(`Now using ${p(se(m))} (${m.scopes.join(", ")}) at ${m.endpoint}.
433
- `);return}let a;try{a=await Ls(t.endpointOverride,i)}catch(b){if(!r&&o===void 0&&b instanceof f&&b.exitCode===g.NO_CREDENTIALS&&process.stdin.isTTY&&process.stdout.isTTY){let m=(await import("prompts")).default;if((await m({type:"confirm",name:"login",message:"Sign in to AnyForge Oracle now?",initial:!0})).login!==!0)throw b;let $=await nt({endpoint:t.endpointOverride??t.endpoint,...i?{workspace:i}:{}});process.stderr.write(`${Ts($)}
434
- `),a=await Ls(t.endpointOverride)}else throw b}if(e==="sessions"){if(n.length)throw new h("oracle sessions does not accept positional arguments.");let b=t.flags.json===!0;b||await $s(await a.config().catch(()=>{}));let m=await a.listSessions();tu(m,b);return}if(e==="download"){await iu(a,n,t.flags);return}if(e==="exec"||o!==void 0){if(W(t.flags,"write")||W(t.flags,"approve"))throw new h("oracle exec is read-only. Use interactive `anyforge oracle --write` for authorized actions.");if(t.flags.json===!0&&W(t.flags,"stream-json"))throw new h("Choose either --json or --stream-json.");let b=await eu(o??n.join(" ")),m=await a.config();Jr(a,m),t.flags.json!==!0&&!W(t.flags,"stream-json")&&await $s(m),Ns(rt(t.flags,"context"));let _=Tr(t.flags),$=await a.createSession(Rt(_,!1));await Jt($.sessionId,a.endpoint,m.identity?.companyId);let j=await Tn(a,$.sessionId,Ps(t.flags),(be,it)=>ne(process.stderr,`Uploading explicit attachment ${be} (${z(it)}).
435
- `)),G={requestId:Fd(),text:b,readOnly:!0,..._?{context:_}:{},...rt(t.flags,"context")?{contextText:rt(t.flags,"context")}:{},...j.length?{attachments:j.map(be=>({fileId:be.fileId}))}:{}};await ru(a,$.sessionId,G,{json:t.flags.json===!0,streamJson:W(t.flags,"stream-json"),config:m});return}if(e==="resume"&&n.length>1)throw new h("oracle resume accepts at most one session id.");if(e==="resume"&&(!process.stdin.isTTY||!process.stdout.isTTY))throw new h("oracle resume is interactive and needs a terminal.","Use `anyforge oracle sessions` to list sessions, or run `anyforge oracle exec` for a new scripted turn.");let c=await a.config();Jr(a,c);let d=Tr(t.flags),u=rt(t.flags,"context");Ns(u);let l=W(t.flags,"shared"),k=W(t.flags,"write")||W(t.flags,"approve");if(k&&(!a.scopes.has("write")||c.capabilities?.write!==!0))throw new f("This Oracle grant or current workspace role does not allow write actions.",g.API_ERROR);if(W(t.flags,"approve")&&(!a.scopes.has("approve")||c.capabilities?.approve!==!0))throw new f("This Oracle grant or current workspace role does not allow approval actions.",g.API_ERROR);if(l&&(!d||d.kind!=="program"&&d.kind!=="initiative"))throw new h("--shared requires --program or --initiative.");let O;if(e==="resume"){let m=(n[0]||void 0)??await nu(await a.listSessions(),await En(a.endpoint,c.identity?.companyId));O=await a.getSession(m)}else O=await a.createSession(Rt(d,l));await Jt(O.sessionId,a.endpoint,c.identity?.companyId);let x=await Promise.resolve().then(()=>(Gn(),Wn));await ys({version:x.default.version,client:a,config:c,session:O,initialPrompt:s,initialContext:d,contextText:u,shared:l,canWrite:k,canApprove:W(t.flags,"approve"),initialFiles:Ps(t.flags),...a.source==="oauth"?{workspaces:Jd(a.endpoint)}:{}})}function Jd(t){return{list:()=>Bn(t),connect:async e=>{let n=new ue(Te(e)),r=await n.config();return Jr(n,r),{client:n,config:r}}}}function Ts(t){let e=t.scopes.join(", ");return t.companyId?`Connected to ${p(se(t))} (${e}) at ${t.endpoint}.`:`Connected to ${t.endpoint} (${e}). The server did not say which workspace; \`anyforge oracle workspaces\` shows it once it does.`}async function Qd(t,e){let n=p(se(t));e&&t.companyId&&!Xd(t,e)&&process.stderr.write(`You asked for "${p(e)}", but ${n} was chosen on the consent page, so this login is for ${n}.
436
- `);let r=Se(await ie(),t.endpoint).length;r>1&&process.stderr.write(`${n} is now your active workspace (${r} saved). List them with \`anyforge oracle workspaces\`; switch with \`anyforge oracle use <name>\`.
437
- `)}function Xd(t,e){return t.companyId===e||t.companyName?.toLowerCase()===e.toLowerCase()}async function $s(t){let e=De(t?.identity);e&&await ne(process.stderr,`${$e(`Workspace: ${e}`)}
438
- `)}async function Zd(t,e){let n=await Tt(t),r=n.logins.map(o=>({name:o.companyName??null,companyId:o.companyId??null,endpoint:o.endpoint,scopes:o.scopes,active:o.clientId===n.active?.clientId}));if(e)process.stdout.write(`${JSON.stringify(r,null,2)}
439
- `);else if(!r.length)process.stdout.write("No saved Oracle logins. Run `anyforge oracle login` to add one.\n");else{process.stdout.write(`${$e(`Oracle workspaces saved for ${n.endpoint??"this endpoint"}:`)}
440
- `);for(let o of n.logins){let i=o.clientId===n.active?.clientId,s=p(se(o)),a=o.companyId&&o.companyName?p(o.companyId):"",c=[i?Xr(`* ${s}`):` ${s}`,a?$e(a):"",$e(o.scopes.join(", ")),i?qs("active"):""];process.stdout.write(`${c.filter(Boolean).join(" ")}
419
+ `,"references/tool-map.md":'# AnyForge MCP tool map\n\nTool names can grow as the platform grows. Use `tools/list` and the current\ntool description as the contract. The names below are representative entry\npoints in the public MCP server.\n\n## Discover and inspect\n\n- `anyforge_list_repo_index` and `anyforge_get_repo_analysis` discover connected\n repositories and their analysis.\n- `anyforge_list_programs` and `anyforge_get_program` inspect program records.\n- `anyforge_list_initiatives` and `anyforge_get_initiative` inspect scoped work.\n- `anyforge_list_work_items` and `anyforge_get_work_item` inspect delivery\n units.\n- `anyforge_list_crews` and `anyforge_get_crew` inspect Crew runs and output.\n For where one crew stands, call `anyforge_get_crew` with `messages: 0`: the\n crew leads with `status`, the halt reason, its work item and pull request,\n and the default summary shape leaves out the large snapshots (named in\n `omittedFields`). Pass `shape: "full"` only for a left-out field.\n- `anyforge_list_pending_approvals` and `anyforge_get_approval_detail` inspect\n human gates.\n- `anyforge_ask_oracle` asks a read-oriented cross-workspace question. A long investigation returns `running` with an `askId`; collect it with `anyforge_get_oracle_answer` rather than asking again. A turn that runs out of time is continued server-side under the same `askId` (`maxContinuations`, default 2), so a long answer arrives whole without a "continue" ask.\n\n### Find a work item by code\n\nFor a known full code, call `anyforge_list_work_items` with `itemId`:\n\n```json\n{ "itemId": "WBA-004", "limit": 5 }\n```\n\nUse the returned item\'s `id` with `anyforge_get_work_item` for its full details.\nFor a partial code or title, use `search` instead, for example\n`{ "search": "WBA-", "limit": 20 }`. Search is case-insensitive and matches\nsubstrings, so check the returned code and title before selecting a record.\nArchived items are excluded by default, including code lookups; add\n`"includeArchived": true` when looking for archived work. Check `hasMore` and\nfollow `nextCursor` with unchanged filters when there are more results.\n\n## Plan and write\n\n- `anyforge_create_program` and `anyforge_update_program` manage program\n records, including the lead and whether the program is long-lived;\n `anyforge_set_program_request_intake` switches its incoming requests, and\n `anyforge_set_program_status_line` writes its status line.\n- `anyforge_create_initiative`, `anyforge_update_initiative`, and\n `anyforge_dispatch_architect` manage initiative planning and dispatch.\n- `anyforge_create_work_item` and `anyforge_update_work_item` manage concrete\n delivery units. Labels are `tags`: set them on create, and change them with\n `action: \'update\'` and `addTags` / `removeTags` (no revision needed, at any\n status, finished items included). Replacing the whole list with `tags` needs\n `ifClassificationRevision`, the item\'s `classificationRevision`.\n `anyforge_get_work_item` returns `nextActions`, every status move the key can\n make now with the arguments to send; `status` with no action moves an item\n back to BACKLOG, REFINED or READY.\n- Program Genesis also exposes tools for the structured brief and reconciling\n program delivery. Read their schemas before use.\n\n## Approve and control\n\n- `anyforge_approve_backlog` and `anyforge_approve_task_proposal` advance\n approval-gated planning.\n- `anyforge_publish_initiative_spec` publishes a pending spec submission\n when the person has read it and asks for it (hosted MCP only).\n- `anyforge_submit_approval` records a human decision when the caller has the\n required approval scope.\n- `anyforge_resume_crew`, `anyforge_halt_crew`,\n `anyforge_message_crew`, and `anyforge_amend_crew` control a Crew run.\n `anyforge_fix_crew_ci`, `anyforge_resume_crew_with_pr` and\n `anyforge_decide_crew_permission` are the Build view\'s recovery actions.\n Explain the effect before calling them.\n\n## Selection rules\n\n1. Prefer the narrowest read tool that answers the question.\n2. Use a detail tool after a list result when a decision depends on fields not\n present in the list.\n3. Filter and page large lists. Do not request an unbounded workspace dump.\n4. Treat names and descriptions as hints; use the tool schema for required\n fields and allowed values.\n5. Before a mutation, restate the target, change, and likely side effect.\n6. After a mutation, read the target again and report the resulting state.\n'},Oh=Object.entries(Nn).map(([e,t])=>({uri:e==="SKILL.md"?is:`${is}/${e}`,name:e==="SKILL.md"?ke:`${ke}/${e.replace(/^references\//,"")}`,title:e==="SKILL.md"?"AnyForge platform guide":e,description:e==="SKILL.md"?sd:`Reference for the ${ke} skill: ${e}`,mimeType:ad,size:new TextEncoder().encode(t).byteLength}));var cd=Nn["SKILL.md"],Ah=cd.replace(/^---[\s\S]*?\r?\n---[ \t]*(?:\r?\n|$)/,"");import{createHash as ld,randomBytes as dd}from"node:crypto";import{promises as re}from"node:fs";import{dirname as ot,isAbsolute as ud,join as Ee,relative as pd,sep as ss}from"node:path";var as=100,cs=256*1024,ls=1024*1024,fd=240,md=8,At=".anyforge-skill.json",hd=/^[A-Za-z0-9._@+-][A-Za-z0-9 ._@+-]*$/;function us(e){if(typeof e!="string")return null;let t=e.startsWith("./")?e.slice(2):e;if(!t||t.length>fd||t.startsWith("/"))return null;let n=t.split("/");if(n.length>md)return null;for(let o of n)if(o===""||o==="."||o===".."||o==="__pycache__"||!hd.test(o))return null;let r=t.toLowerCase();return r==="skill.md"||r===At.toLowerCase()?null:t}function Dr(e){return e.startsWith("scripts/")&&e.length>8}function Ge(e){return ld("sha256").update(e,"utf8").digest("hex")}function Br(e,t){let n=[`SKILL.md\0${e}\0-`];for(let r of[...t].sort((o,i)=>o.path<i.path?-1:o.path>i.path?1:0))n.push(`${r.path}\0${r.sha256}\0${r.executable?"x":"-"}`);return Ge(`${n.join(`
420
+ `)}
421
+ `)}var Q=class extends Error{constructor(t){super(t),this.name="SkillBundleError"}};function Fn(e){if(e.length>as)throw new Q(`The skill has ${e.length} files; at most ${as} are installed.`);let t=new Set,n=0;for(let r of e){if(us(r.path)!==r.path)throw new Q(`Refusing the file path "${String(r.path)}": it is not inside the skill's folder.`);let o=r.path.toLowerCase();if(t.has(o))throw new Q(`Refusing two files named "${r.path}" that differ only by case.`);if(t.add(o),!Number.isSafeInteger(r.size)||r.size<0||r.size>cs)throw new Q(`Refusing "${r.path}": it is larger than ${cs} bytes.`);if(typeof r.sha256!="string"||!/^[0-9a-f]{64}$/.test(r.sha256))throw new Q(`Refusing "${r.path}": it has no valid hash.`);n+=r.size}if(n>ls)throw new Q(`The skill's files are larger than ${ls} bytes together.`)}function ps(e,t,n,r){if(Fn(n),Br(Ge(e),n)!==t)throw new Q("The skill does not match its version hash. Nothing was installed; run the command again.");let o=new Map(r.map(i=>[i.path,i]));if(o.size!==r.length||r.length!==n.length)throw new Q("The skill's files do not match its file list. Nothing was installed.");for(let i of n){let s=o.get(i.path);if(!s||typeof s.content!="string")throw new Q(`The file "${i.path}" was not sent. Nothing was installed.`);if(Ge(s.content)!==i.sha256||Buffer.byteLength(s.content,"utf8")!==i.size)throw new Q(`The file "${i.path}" does not match its hash. Nothing was installed.`)}}async function Wr(e){try{return(await re.lstat(e)).isSymbolicLink()}catch(t){let n=t.code;if(n==="ENOENT"||n==="ENOTDIR")return!1;throw t}}async function We(e,t){let n=pd(e,t);if(n==="")return;if(n===".."||n.startsWith(`..${ss}`)||ud(n))throw new Q(`Refusing to write to ${t}, outside ${e}.`);let r=e;for(let o of n.split(ss))if(r=Ee(r,o),await Wr(r))throw new Q(`Refusing to write through the symbolic link ${r}.`)}async function Gr(e){let t=Ee(e,At);if(await Wr(t))return null;let n;try{n=await re.readFile(t,"utf8")}catch(r){if(r.code==="ENOENT")return null;throw r}try{let r=JSON.parse(n);if(r.schema!==1||r.kind!=="library"&&r.kind!=="source"||typeof r.id!="string"||typeof r.version!="string"||!r.files||typeof r.files!="object")return null;let o={};for(let[i,s]of Object.entries(r.files))(i==="SKILL.md"||us(i)===i)&&typeof s=="string"&&(o[i]=s);return{...r,files:o}}catch{return null}}async function Xt(e,t){let n=[];for(let[r,o]of Object.entries(t.files)){let i=Ee(e,r);if(await Wr(i)){n.push(r);continue}try{Ge(await re.readFile(i,"utf8"))!==o&&n.push(r)}catch(s){let a=s.code;if(a==="ENOENT"||a==="ENOTDIR"||a==="EISDIR")n.push(r);else throw s}}return n}async function fs(e,t,n){let r=[];for(let o of n){if(Object.hasOwn(t.files,o.path))continue;let i=Ee(e,o.path);try{(!(await re.lstat(i)).isFile()||Ge(await re.readFile(i,"utf8"))!==o.sha256)&&r.push(o.path)}catch(s){s.code!=="ENOENT"&&r.push(o.path)}}return r}async function ds(e,t,n){let r=Ee(ot(e),`.anyforge-${dd(6).toString("hex")}.tmp`),o=await re.open(r,"wx",n?493:420);try{await o.writeFile(t,"utf8")}finally{await o.close()}try{await re.chmod(r,n?493:420),await re.rename(r,e)}catch(i){throw await re.rm(r,{force:!0}),i}}async function Hr(e){let{base:t,directory:n}=e;await We(t,n),await re.mkdir(n,{recursive:!0}),await We(t,n);let r=new Set(e.previous?await Xt(n,e.previous):[]),o=[],i={};for(let l of e.files){let d=Ee(n,l.path);await We(n,ot(d)),await re.mkdir(ot(d),{recursive:!0}),await We(n,ot(d)),await ds(d,l.content,l.executable&&Dr(l.path)),i[l.path]=Ge(l.content),o.push(l.path)}let s=[],a=[];for(let l of Object.keys(e.previous?.files??{}))if(!Object.hasOwn(i,l)){if(r.has(l)&&!e.force){a.push(l);continue}await ms(n,l),s.push(l)}let c={...e.manifest,files:i,installedAt:new Date().toISOString()};return await ds(Ee(n,At),`${JSON.stringify(c,null,2)}
422
+ `,!1),{written:o,removed:s,kept:a}}async function ms(e,t){let n=Ee(e,t);await We(e,ot(n)),await re.rm(n,{force:!0});let r=ot(n);for(;r!==e&&r.startsWith(e);){try{await re.rmdir(r)}catch{break}r=ot(r)}}async function hs(e,t,n,r){await We(e,t);let o=new Set(await Xt(t,n)),i=[];for(let s of Object.keys(n.files)){if(o.has(s)&&!r){i.push(s);continue}await ms(t,s)}if(i.length>0)return i;await re.rm(Ee(t,At),{force:!0});try{await re.rmdir(t)}catch{}return i}var bs={agents:".agents/skills",claude:".claude/skills",codex:".codex/skills"};function Xr(e,t=ke){let n=e.scope==="user"?e.homeDir??gd():e.cwd??process.cwd();if(e.scope==="user"&&e.client==="codex"){let r=e.codexHome??process.env.CODEX_HOME??Te(n,".codex");return Te(r,"skills",t)}return Te(n,bs[e.client],t)}function Qr(e){if(e.scope!=="project"&&e.scope!=="user")throw new f(`--scope must be project or user, got "${e.scope}".`);if(!Object.hasOwn(bs,e.client))throw new f(`--client must be agents, claude, or codex, got "${e.client}".`)}async function zr(e){try{return(await it.lstat(e)).isSymbolicLink()}catch(t){if(t.code==="ENOENT")return!1;throw t}}function yd(e){return vd(ke,Nn,e)}async function vd(e,t,n){Qr(n);let r=Xr(n,e),o=Object.entries(t),i=o.map(([c])=>Te(r,c));if(await zr(r))throw new f(`Refusing to install through the symbolic link ${r}.`);let s=new Set(o.map(([c])=>Yr(c)).filter(c=>c!=="."));for(let c of s)if(await zr(Te(r,c)))throw new f(`Refusing to install through the symbolic link ${Te(r,c)}.`);if(!n.force){let c=[];for(let l of i)try{await it.access(l),c.push(l)}catch(d){if(d.code!=="ENOENT")throw d}if(c.length>0)throw new f(`The ${e} skill already exists at ${r}.`,"Use --force only if you want to replace AnyForge-managed files in that directory.")}await it.mkdir(r,{recursive:!0});let a=[];for(let[c,l]of o){let d=Te(r,c);if(await it.mkdir(Yr(d),{recursive:!0}),await zr(d))throw new f(`Refusing to replace the symbolic link ${d}.`);await it.writeFile(d,l,{encoding:"utf8",mode:420}),a.push(c)}return{name:e,scope:n.scope,client:n.client,directory:r,files:a}}function Vr(e){let t=e.toLowerCase().normalize("NFKD").replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,64).replace(/-+$/,"");if(!t)throw new f(`The skill "${e}" has no name that can be used as a folder.`);return t}function Zr(e){try{return Vr(e.name)}catch{return Vr(`skill-${e.id}`)}}function bd(e){let t=/^---\r?\n([\s\S]*?)\r?\n---\r?\n/.exec(e);return!!t&&/^name:[ \t]*\S/m.test(t[1])&&/^description:[ \t]*\S/m.test(t[1])}function kd(e){let t=e.content.endsWith(`
423
+ `)?e.content:`${e.content}
424
+ `;if(bd(t))return{"SKILL.md":t};let n=(e.description??"").trim()||e.name;return{"SKILL.md":`---
425
+ name: ${Zr(e)}
426
+ description: ${JSON.stringify(n)}
427
+ ---
428
+
429
+ ${t}`}}async function ks(e){let{skills:t}=await R(e,"GET","/skills");return t}async function Od(e,t){let n=t.trim().toLowerCase(),r=await ks(e),o=s=>{try{return Vr(s)}catch{return null}},i=r.find(s=>s.name.trim().toLowerCase()===n)??r.find(s=>o(s.name)===n);if(!i)throw new f(`No skill named "${t}" in your company's library.`,"Run `anyforge skill list` to see the skills you can install.");return R(e,"GET",`/skills/${encodeURIComponent(i.id)}`)}var Ad=new Set(["skill_not_found","skill_no_longer_available"]);function xd(e){return e instanceof V&&e.status===404&&Ad.has(e.code)}async function Os(e,t){let n=await R(e,"GET",`/skills/${encodeURIComponent(t)}`),r=Array.isArray(n.files)?n.files:[];return{kind:"library",id:n.id,name:n.name,description:n.description,content:n.content,files:r,version:typeof n.version?.hash=="string"?n.version.hash:Br(Ge(n.content),r),...typeof n.version?.revision=="number"?{revision:n.version.revision}:{}}}async function eo(e,t,n){let r=await R(e,"GET",`/skill-sources/skills/${encodeURIComponent(t)}${F({repo:n??void 0})}`);return{kind:"source",id:r.id,name:r.name,description:r.description,content:r.content,files:Array.isArray(r.files)?r.files:[],version:r.version.hash,commit:r.version.commit,repo:r.repo}}function Sd(e,t,n,r){return t==="library"?Os(e,n):eo(e,n,r)}async function As(e,t,n){return R(e,"GET",`/skill-sources/skills${F({repo:t??void 0,team:n})}`)}async function xs(e,t,n){Fn(t.files);let r=[];if(t.files.length>0){let s=t.kind==="library"?`/skills/${encodeURIComponent(t.id)}/files`:`/skill-sources/skills/${encodeURIComponent(t.id)}/files${F({repo:n??void 0})}`;r=(await R(e,"GET",s)).files??[]}ps(t.content,t.version,t.files,r);let o=t.kind==="library"?kd(t)["SKILL.md"]:t.content,i=new Map(r.map(s=>[s.path,s]));return[{path:"SKILL.md",content:o,executable:!1},...t.files.map(s=>({path:s.path,content:i.get(s.path).content,executable:s.executable===!0&&Dr(s.path)}))]}function Ss(e){return{schema:1,kind:e.kind,id:e.id,name:e.name,...e.repo?{repo:e.repo}:{},...e.commit?{commit:e.commit}:{},...e.revision!==void 0?{revision:e.revision}:{},version:e.version}}function Rs(e){return Yr(Xr(e,"x"))}function Mn(e){return e.scope==="project"?e.cwd??process.cwd():Rs(e)}async function _s(e,t,n){Qr(n);let r=Zr(t),o=Xr(n,r),i=await xs(e,t,n.repo??null),s=await Gr(o);if(!n.force){if(s&&s.id===t.id)throw new f(`The ${r} skill is already installed at ${o}.`,"Run `anyforge skill sync` to bring it up to date, or --force to reinstall it.");let c=[];for(let l of[...i.map(d=>d.path),At])try{await it.lstat(Te(o,l)),c.push(l)}catch(d){if(d.code!=="ENOENT")throw d}if(c.length>0)throw new f(`The ${r} skill already exists at ${o}.`,"Use --force only if you want to replace AnyForge-managed files in that directory.")}let a=await Hr({base:Mn(n),directory:o,files:i,manifest:Ss(t),previous:s&&s.id===t.id?s:null,force:n.force===!0});return{name:r,scope:n.scope,client:n.client,directory:o,files:a.written,id:t.id,kind:t.kind,version:t.version,removed:a.removed}}async function Rd(e){let t;try{t=await it.readdir(e,{withFileTypes:!0})}catch(r){if(r.code==="ENOENT")return[];throw r}let n=[];for(let r of t.sort((o,i)=>o.name.localeCompare(i.name))){if(!r.isDirectory())continue;let o=Te(e,r.name),i=await Gr(o);i&&n.push({directory:o,manifest:i})}return n}function qn(e){return e instanceof Error?e.message:String(e)}async function gs(e,t){Qr(t);let n=Rs(t),r=t.repo??null,o=t.force===!0,i=t.dryRun===!0;await We(Mn(t),n);let s=null;if(t.team&&(s=await As(e,r,t.team),s.sources.length===0))throw new f(`No skill source is owned by the team "${t.team}".`,"Check the team's slug on its page in the Console.");let a=new Set(s?.sources.map(y=>y.id)??[]),c=y=>{let v=y.startsWith("source:")?y.slice(7):null;return v&&v.includes(":")?v.slice(0,v.indexOf(":")):null},l=(await Rd(n)).filter(({manifest:y})=>!s||y.kind==="source"&&a.has(c(y.id)??"")),d=[];for(let{directory:y,manifest:v}of l){let x={name:v.name,id:v.id,kind:v.kind},b;try{b=await Sd(e,v.kind,v.id,r)}catch(m){if(!xd(m)){d.push({...x,action:"failed",reason:qn(m)});continue}try{let A=await Xt(y,v);if(A.length>0&&!o){d.push({...x,action:"kept",files:A,reason:"removed upstream, but you edited it"});continue}i||await hs(Mn(t),y,v,o),d.push({...x,action:"removed"})}catch(A){d.push({...x,action:"failed",reason:qn(A)})}continue}try{let m=await Xt(y,v);if(b.version===v.version&&!(o&&m.length>0)){d.push({...x,action:"unchanged"});continue}if(m.length>0&&!o){d.push({...x,action:"kept",files:m,reason:"you edited it"});continue}let A=await fs(y,v,b.files);if(A.length>0&&!o){d.push({...x,action:"kept",files:A,reason:"the new version adds files you already have"});continue}if(i){Fn(b.files),d.push({...x,action:"updated"});continue}let $=await xs(e,b,r),M=await Hr({base:Mn(t),directory:y,files:$,manifest:Ss(b),previous:v,force:o});d.push({...x,action:"updated",...M.removed.length>0?{files:M.removed}:{}})}catch(m){d.push({...x,action:"failed",reason:qn(m)})}}let u=new Set(l.map(({manifest:y})=>y.id));for(let y of s?.skills??[]){if(u.has(y.id))continue;let v={name:y.name,id:y.id,kind:"source"};try{if(i){d.push({...v,action:"added"});continue}let x=await eo(e,y.id,r);await _s(e,x,{...t,repo:r,force:!1}),d.push({...v,action:"added"})}catch(x){d.push({...v,action:"failed",reason:qn(x)})}}return{dryRun:i,scope:t.scope,client:t.client,directory:n,results:d}}function Kr(e){let t=e.scope??"project",n=e.client??"agents";if(!["project","user"].includes(t))throw new f(`--scope must be project or user, got "${t}".`);if(!["agents","claude","codex"].includes(n))throw new f(`--client must be agents, claude, or codex, got "${n}".`);let r=e.repo!==void 0?e.repo:t==="project"?yr(e.cwd??process.cwd()):null;return{scope:t,client:n,force:e.force,...e.cwd?{cwd:e.cwd}:{},...e.homeDir?{homeDir:e.homeDir}:{},...e.codexHome?{codexHome:e.codexHome}:{},repo:r}}async function Is(e,t,n){try{return(await As(e,t,n)).skills}catch(r){if(!n&&r instanceof V&&(r.status===403||r.status===404))return[];throw r}}async function _d(e,t,n,r){let o=t.trim().toLowerCase();if(!r)try{let a=await Od(e,t);return Os(e,a.id)}catch(a){if(!(a instanceof f))throw a}let i=await Is(e,n,r),s=i.find(a=>a.name.toLowerCase()===o)??i.find(a=>Zr(a)===o);if(!s)throw new f(r?`The team "${r}" has no skill named "${t}" in its skill sources.`:`No skill named "${t}" in your company's library or skill sources.`,"Run `anyforge skill list` to see the skills you can install.");return eo(e,s.id,n)}var Id={added:"added",updated:"updated",unchanged:"unchanged",removed:"removed upstream",kept:"kept",failed:"failed"};function ws(e){let t=e.dryRun?"Would sync":"Synced";console.log(`${Jr(t)} ${e.results.length} ${e.results.length===1?"skill":"skills"} ${ge(`(${e.scope}, ${e.client})`)}`),console.log(` ${e.directory}`);for(let n of e.results){let r=Id[n.action].padEnd(16),o=n.action==="failed"?wd:n.action==="kept"?vs:n.action==="unchanged"?ge:Jr,i=n.action==="kept"?ge(` \u2014 ${n.reason}: ${(n.files??[]).join(", ")}. --force replaces it.`):n.action==="failed"?ge(` \u2014 ${n.reason}`):"";console.log(` ${o(r)} ${n.name}${i}`)}e.results.length===0&&console.log(ge(" No skills installed by AnyForge here. Install one with `anyforge skill install <name>`."))}async function Es(e){if(e.subcommand==="list"){let i=await e.resolveCreds(),{repo:s}=Kr(e),[a,c]=await Promise.all([e.team?Promise.resolve([]):ks(i),Is(i,s,e.team)]);if(e.json){process.stdout.write(JSON.stringify({skills:a,sourceSkills:c})+`
430
+ `);return}a.length===0&&c.length===0&&console.log(ge("Your company has no skills in its library yet."));for(let l of a)console.log(`${l.name}${l.description?ge(` \u2014 ${l.description}`):""}`);for(let l of c)console.log(`${l.name} ${ge(`(${l.repo})`)}${l.description?ge(` \u2014 ${l.description}`):""}`);return}if(e.subcommand==="sync"){let i=await gs(await e.resolveCreds(),{...Kr(e),team:e.team,dryRun:e.dryRun});e.json?process.stdout.write(JSON.stringify(i)+`
431
+ `):ws(i);let s=i.results.filter(a=>a.action==="failed").length;if(s>0)throw new h(`${s} ${s===1?"skill":"skills"} could not be synced.`,g.FAILURE);return}if(e.subcommand!=="install")throw new f(`Unknown skill command${e.subcommand?`: ${e.subcommand}`:""}.`,"Use `anyforge skill list`, `anyforge skill install <name>` or `anyforge skill sync`.");if(!e.name&&!e.team)throw new f("Name the skill to install.",`Use \`anyforge skill install ${ke}\`, or \`anyforge skill list\` for your company's skills.`);let t=Kr(e);if(e.name===ke&&!e.team){let i=await yd(t);ys(i,e.json);return}let n=await e.resolveCreds();if(!e.name&&e.team){let i=await gs(n,{...t,team:e.team});e.json?process.stdout.write(JSON.stringify(i)+`
432
+ `):ws(i);let s=i.results.filter(a=>a.action==="failed").length;if(s>0)throw new h(`${s} ${s===1?"skill":"skills"} could not be installed.`,g.FAILURE);return}let r=await _d(n,e.name,t.repo,e.team),o;try{o=await _s(n,r,t)}catch(i){throw i instanceof Q?new h(i.message,g.FAILURE):i}ys(o,e.json)}function ys(e,t){if(t){process.stdout.write(JSON.stringify(e)+`
433
+ `);return}console.log(Jr("Installed ")+`${e.name} ${ge(`(${e.scope}, ${e.client})`)}`),console.log(` ${e.directory}`);let n=e.name===ke?"Re-run with --force to refresh them.":"Run `anyforge skill sync` to update it.";console.log(ge(` ${e.files.length} ${e.files.length===1?"file":"files"} installed. ${n}`)),e.name===ke&&e.scope==="project"&&e.client==="agents"&&console.log(vs(" This portable location is detected by AnyForge repository analysis."))}import{randomUUID as Zu}from"node:crypto";import{open as ep,realpath as tp,stat as np}from"node:fs/promises";import{basename as rp,join as op,resolve as ip}from"node:path";import{bold as Io,cyan as Na,dim as Me,yellow as ar}from"kleur/colors";import{ProcessTerminal as sp,SelectList as ap,Text as cp,TuiAltScreen as lp}from"@earendil-works/pi-tui";var Ed=new Set(["find_callers","find_definition","find_imports","trace_path","get_neighbors","graph_stats"]);function Td(e){return Ed.has(e)?"querying the code graph":e==="search_code_semantic"?"searching the code index":e==="search_codebase_docs"?"searching codebase docs":null}function xt(e){let t=Td(e);if(t)return t;let n=/^mcp__(.+?)__(.+)$/.exec(e);return n?`${n[1].replace(/[_-]+/g," ")} \xB7 ${n[2].replace(/_/g," ")}`:e.replace(/_/g," ")}var Cd=new Set(["ask_user","present_catch_up_decision","present_memory_candidates","present_skill_proposal"]);function St(e){return typeof e=="string"&&Cd.has(e)}import{truncateToWidth as cn,wrapTextWithAnsi as ta}from"@earendil-works/pi-tui";import{cyan as ho,dim as lt,green as na,italic as du,red as go,yellow as wo}from"kleur/colors";import{truncateToWidth as Qt,visibleWidth as to,wrapTextWithAnsi as $d}from"@earendil-works/pi-tui";import{bold as Pd,dim as Ld}from"kleur/colors";function z(e){return e<1024?`${e} B`:e<1024*1024?`${Zt(e/1024)} KiB`:`${Zt(e/(1024*1024))} MiB`}function en(e){let t=Math.max(0,e);if(t<1e4)return`${Zt(t/1e3)}s`;let n=Math.round(t/1e3);if(n<60)return`${n}s`;let r=Math.floor(n/3600),o=Math.floor(n%3600/60),i=n%60;return r?o?`${r}h ${o}m`:`${r}h`:i?`${o}m ${i}s`:`${o}m`}function no(e){let t=Math.floor(Math.max(0,e)/1e3);return t<60?`${t}s`:`${Math.floor(t/60)}m ${String(t%60).padStart(2,"0")}s`}function we(e){return Math.abs(e)<1e3?String(Math.round(e)):Math.abs(e)<1e6?`${Zt(e/1e3,e<1e5?1:0)}k`:`${Zt(e/1e6)}M`}function Zt(e,t=1){return e.toFixed(t).replace(/\.0+$/,"")}function Nd(e){return p(e).replace(/(Ms|Bytes)$/,"").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/[_-]+/g," ").toLowerCase().trim()}function Fd(e,t){return typeof t=="boolean"?t?"on":"off":typeof t=="string"?p(t):/Ms$/.test(e)?en(t):/Bytes$/.test(e)?z(t):/tokens?/i.test(e)?we(t):String(t)}function Ts(e){return e?Object.entries(e).filter(t=>t[1]!==null).map(([t,n])=>`${Nd(t)} ${Fd(t,n)}`):[]}function ro(e,t,n=2){let r=Math.min(16,Math.max(...e.map(([s])=>to(s)),0)+2),o=Math.max(10,t-n-r),i=[];for(let[s,a]of e)$d(a,o).forEach((l,d)=>{let u=d===0?Ld(Qt(s,r-1).padEnd(r)):" ".repeat(r);i.push(Qt(`${" ".repeat(n)}${u}${l}`,t))});return i}function Cs(e,t,n){return[Qt(Pd(e),n),...ro(t,n)]}function $s(e,t,n){let r=to(n),o=t.filter(Boolean).join(" \xB7 ");if(r+4>e)return Qt(o||n,e);let i=e-r-2,s=Qt(o,i),a=Math.max(2,e-to(s)-r);return`${s}${" ".repeat(a)}${n}`}import{bold as _t,cyan as Rt,dim as He,green as qd,italic as Ps,strikethrough as Md,underline as Ls,yellow as Ud}from"kleur/colors";import{Markdown as jd}from"@earendil-works/pi-tui";var Dd={heading:e=>_t(Rt(e)),link:e=>Ls(Rt(e)),linkUrl:e=>He(e),code:e=>Ud(e),codeBlock:e=>e,codeBlockBorder:e=>He(e),quote:e=>Ps(e),quoteBorder:e=>He(e),hr:e=>He(e),listBullet:e=>Rt(e),bold:e=>_t(e),italic:e=>Ps(e),strikethrough:e=>Md(e),underline:e=>Ls(e)},Et={selectedPrefix:e=>Rt(e),selectedText:e=>_t(e),description:e=>He(e),scrollInfo:e=>He(e),noMatch:e=>He(e)},Ns={borderColor:e=>He(e),selectList:Et},It=class{constructor(t,n){this.linkOrigin=n;this.content=p(t),this.markdown=new jd(xr(this.content,n),0,0,Dd)}linkOrigin;content;markdown;cache;getText(){return this.content}setText(t){let n=p(t);n!==this.content&&(this.content=n,this.markdown.setText(xr(n,this.linkOrigin)),this.cache=void 0)}invalidate(){this.markdown.invalidate(),this.cache=void 0}render(t){let n=Math.max(12,t);if(this.cache?.width===n)return this.cache.lines;let r=this.content?this.markdown.render(n).map(di):[];return this.cache={width:n,lines:r},r}};function tn(e,t){return e==="user"?`${_t(qd(t?p(t):"You"))}`:`${_t(Rt("Oracle"))}`}function Fs(e,t,n){let r=Rt("\u2502"),o=Math.max(1,t-4);return["",`${r} ${tn("user",n)}`,...e.render(o).map(i=>`${r} ${_t(i)}`),""]}function Ce(e){return e.shared||e.visibility==="company"?`Shared with the company${e.entityKey?` \xB7 ${p(e.entityKey)}`:""}`:"Private to you"}function qs(e){return e.shared||e.visibility==="company"?"shared":"private"}import{createReadStream as ru}from"node:fs";import{stat as ou}from"node:fs/promises";import{basename as iu}from"node:path";import{setTimeout as Xs}from"node:timers/promises";var Ms={txt:"text/plain",log:"text/plain",md:"text/markdown",markdown:"text/markdown",csv:"text/csv",json:"application/json",pdf:"application/pdf",ts:"text/plain",tsx:"text/plain",js:"text/plain",jsx:"text/plain",mjs:"text/plain",cjs:"text/plain",py:"text/plain",go:"text/plain",rs:"text/plain",yaml:"text/plain",yml:"text/plain",toml:"text/plain",xml:"text/plain",html:"text/plain",css:"text/plain",sh:"text/plain",sql:"text/plain",java:"text/plain",kt:"text/plain",swift:"text/plain",c:"text/plain",h:"text/plain",cpp:"text/plain",rb:"text/plain",php:"text/plain",tf:"text/plain",diff:"text/plain",patch:"text/plain",png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",webp:"image/webp",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",pptx:"application/vnd.openxmlformats-officedocument.presentationml.presentation",xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"};function Us(e){let t=e.slice(e.lastIndexOf(".")+1).toLowerCase();return Object.hasOwn(Ms,t)?Ms[t]:null}var oo="/api/v1/oracle";var js="workspace";function ze(e){let t;try{t=new URL(e)}catch{throw new h(`Invalid Oracle endpoint: ${e}`,g.USAGE)}if(!["http:","https:"].includes(t.protocol)||t.username||t.password)throw new h("Oracle endpoint must be an http or https URL without embedded credentials.",g.USAGE);if(t.pathname!=="/"||t.search||t.hash)throw new h("Oracle endpoint must be the workspace origin, without a path, query or fragment.",g.USAGE);return t.origin}function $e(e,t){return`${ze(e)}${oo}${t}`}function Bd(e,t,n){let r;try{r=new URL(e)}catch{throw new h(`Oracle server returned an invalid ${n} endpoint.`,g.API_ERROR)}let o=new URL(ze(t));if(r.origin!==o.origin||!r.pathname.startsWith("/api/oauth/")||r.search||r.hash||r.username||r.password)throw new h(`Oracle server returned an unsafe ${n} endpoint.`,g.API_ERROR);return r.toString()}function Wd(e,t){if(!e||typeof e!="object")throw new h("Oracle server omitted OAuth endpoints.",g.API_ERROR);let n=e,r=["registration","authorization","token","revocation"],o={};for(let i of r){if(typeof n[i]!="string")throw new h(`Oracle server omitted its OAuth ${i} endpoint.`,g.API_ERROR);o[i]=Bd(n[i],t,i)}return o}async function st(e,t=fetch,n,r){let o=ze(e),i;try{i=await t($e(o,"/config"),{headers:{Accept:"application/json",...n?{Authorization:`Bearer ${n}`}:{}},redirect:"error",signal:r?AbortSignal.any([r,AbortSignal.timeout(2e4)]):AbortSignal.timeout(2e4)})}catch(u){throw new h(`Cannot reach ${o}: ${u instanceof Error?u.message:String(u)}`,g.FAILURE,"Check your network, or pass --endpoint for a self-hosted workspace.")}let s=await $t(i,$e(o,"/config")),a=N(Ct(s,i.status)),c=`${o}${oo}`;if(a.protocolVersion!==1)throw new h(`Oracle protocol mismatch: this CLI supports ${1}, and the server reports ${String(a.protocolVersion)}.`,g.API_ERROR,"Update @anyforge/control to a version supported by this workspace.");if(a.resource!==c)throw new h("Oracle server resource does not match this workspace origin.",g.API_ERROR);let l=Array.isArray(a.scopes)?a.scopes:null;if(!l||!l.every(u=>u==="read"||u==="write"||u==="approve"))throw new h("Oracle server returned an invalid scope list.",g.API_ERROR);let d={protocolVersion:1,resource:c,scopes:l,oauth:Wd(a.oauth,o)};if(a.identity!==void 0){let u=N(a.identity);if(typeof u.uid!="string"||typeof u.companyId!="string")throw new h("Oracle server returned an invalid identity.",g.API_ERROR);let y=typeof u.companyName=="string"?p(u.companyName).trim().slice(0,120):"";d.identity={uid:p(u.uid),companyId:p(u.companyId),...y?{companyName:y}:{}}}if(a.capabilities!==void 0){let u=N(a.capabilities);if(![u.read,u.write,u.approve].every(y=>typeof y=="boolean"))throw new h("Oracle server returned invalid capabilities.",g.API_ERROR);d.capabilities={read:u.read,write:u.write,approve:u.approve}}if(a.model!==void 0){let u=N(a.model);if(typeof u.id!="string"||typeof u.provider!="string")throw new h("Oracle server returned invalid model metadata.",g.API_ERROR);d.model={id:p(u.id),provider:p(u.provider)}}if(a.limits!==void 0){let u=N(a.limits);if(Object.values(u).some(y=>!["string","number","boolean"].includes(typeof y)&&y!==null))throw new h("Oracle server returned invalid limits metadata.",g.API_ERROR);d.limits=Object.fromEntries(Object.entries(u).map(([y,v])=>[p(y),typeof v=="string"?p(v):v]))}return Array.isArray(a.commands)&&(d.commands=Gd(a.commands)),d}function Gd(e){let t=[];for(let n of e.slice(0,50)){let r=N(n);if(typeof r.name!="string"||!/^[a-z][a-z0-9_-]{0,31}$/.test(r.name)||typeof r.description!="string"||t.some(i=>i.name===r.name))continue;let o=typeof r.args=="string"?p(r.args).slice(0,60).trim():"";t.push({name:r.name,description:p(r.description).slice(0,200),...o?{args:o}:{}})}return t}var fe=class{endpoint;scopes;source;#e;#n;#t;cachedConfig;constructor(t,n=fetch){this.endpoint=ze(t.endpoint),this.#e=t.getAccessToken??(async()=>t.accessToken),this.#n=t.onIdentity,this.#t=n,this.scopes=new Set(t.scopes),this.source=t.source}async config(t){if(!this.cachedConfig){let n=await this.#e();try{this.cachedConfig=await st(this.endpoint,this.#t,n,t)}catch(o){if(this.source!=="oauth"||!(o instanceof V)||o.status!==401)throw o;let i=await this.#e(n);if(i===n)throw o;this.cachedConfig=await st(this.endpoint,this.#t,i,t)}let r=this.cachedConfig.identity;r&&this.#n&&await this.#n(r).catch(()=>{})}return this.cachedConfig}async listSessions(){let t=await this.json("GET","/sessions"),n=Array.isArray(t)?t:N(t).sessions;if(!Array.isArray(n))throw new h("Oracle server returned an invalid sessions list.",g.API_ERROR);return n.map(Ws)}async createSession(t={},n){let r=await this.json("POST","/sessions",t,n),o=N(r);return!Array.isArray(o.messages)&&typeof o.sessionId=="string"?this.getSession(o.sessionId,n):Ds(r)}async getSession(t,n){return Ds(await this.json("GET",`/sessions/${encodeURIComponent(t)}`,void 0,n))}async submitTurn(t,n,r,o){let i,s=$e(this.endpoint,`/sessions/${encodeURIComponent(t)}/turns`),a=async l=>this.#t(s,{method:"POST",headers:{Authorization:`Bearer ${l}`,Accept:"text/event-stream, application/json","Content-Type":"application/json"},redirect:"error",body:JSON.stringify(n),...r?{signal:r}:{}});try{let l=await this.#e();if(i=await a(l),i.status===401&&this.source==="oauth"){let d=await this.#e(l);d!==l&&(i=await a(d))}}catch(l){throw l instanceof h||r?.aborted?l:new h(`Oracle connection was interrupted: ${l instanceof Error?l.message:String(l)}`,g.FAILURE,"The server may still be working. Resume this session to replay its saved stream.")}if(o?.(i),i.ok||await Tt(i),!(i.headers.get("content-type")?.toLowerCase()??"").includes("text/event-stream")){let l=await $t(i,$e(this.endpoint,`/sessions/${t}/turns`)),d=Ct(l,i.status);throw new h(`Oracle server did not start a stream (${Hd(d)}).`,g.API_ERROR)}return i}async replayEvents(t,n,r){let o=[];for await(let i of this.streamReplayEvents(t,n,r))o.push(i);return o}async*streamReplayEvents(t,n,r,o,i){let s=`/sessions/${encodeURIComponent(t)}/events?streamId=${encodeURIComponent(n)}&from=${Math.max(0,r)}`,a=await this.authenticatedFetch("GET",s,{headers:{Accept:"text/event-stream, application/json"},signal:i},0);if(a.ok||await Tt(a),(a.headers.get("content-type")?.toLowerCase()??"").includes("text/event-stream")){yield*nn(a,o);return}let l=Ct(await $t(a,$e(this.endpoint,s)),a.status),d=Array.isArray(l)?l:N(l).events;if(!Array.isArray(d))throw new h("Oracle replay response did not contain events.",g.API_ERROR);for(let u of d)yield io(u)}async*streamRequestEvents(t,n,r=-1,o,i){let s=`/sessions/${encodeURIComponent(t)}/events?requestId=${encodeURIComponent(n)}&from=${r}`,a=await this.authenticatedFetch("GET",s,{headers:{Accept:"text/event-stream, application/json"},signal:i},0);if(a.ok||await Tt(a),(a.headers.get("content-type")?.toLowerCase()??"").includes("text/event-stream")){yield*nn(a,o);return}let l=Ct(await $t(a,$e(this.endpoint,s)),a.status),d=Array.isArray(l)?l:N(l).events;if(!Array.isArray(d))throw new h("Oracle request replay response did not contain events.",g.API_ERROR);for(let u of d)yield io(u)}async cancelTurn(t,n){return this.json("POST",`/sessions/${encodeURIComponent(t)}/cancel`,{streamId:n})}async cancelRequest(t,n,r){return this.json("POST",`/sessions/${encodeURIComponent(t)}/cancel`,{requestId:n},r)}async upload(t,n){let r=new FormData;r.set("sessionId",t),r.set("file",n,n.name);let o=await this.authenticatedFetch("POST","/uploads",{method:"POST",body:r,headers:{Accept:"application/json"}});return o.ok||await Tt(o),Kd(Ct(await $t(o,$e(this.endpoint,"/uploads")),o.status))}async download(t,n){let r=await this.authenticatedFetch("GET",`/sessions/${encodeURIComponent(t)}/files/${encodeURIComponent(n)}`,{headers:{Accept:"application/octet-stream, application/json"}});return r.ok||await Tt(r),r}async json(t,n,r,o){let i=await this.authenticatedFetch(t,n,{headers:{Accept:"application/json",...r!==void 0?{"Content-Type":"application/json"}:{}},...r!==void 0?{body:JSON.stringify(r)}:{},...o?{signal:o}:{}});return i.ok||await Tt(i),Ct(await $t(i,$e(this.endpoint,n)),i.status)}async authenticatedFetch(t,n,r={},o=2e4){try{let i=$e(this.endpoint,n),s=Object.fromEntries(new Headers(r.headers).entries()),a=o<=0?void 0:AbortSignal.timeout(o),c=r.signal&&a?AbortSignal.any([r.signal,a]):r.signal??a,l=v=>this.#t(i,{...r,method:t,headers:{...s,Authorization:`Bearer ${v}`},redirect:"error",...c?{signal:c}:{}}),d=await this.#e(),u=await l(d),y=r.body===void 0||typeof r.body=="string"||r.body===null;if(u.status===401&&this.source==="oauth"){let v=await this.#e(d);v!==d&&y&&(u=await l(v))}return u}catch(i){throw i instanceof h?i:new h(`Cannot reach ${this.endpoint}: ${i instanceof Error?i.message:String(i)}`,g.FAILURE,"Check your network, or pass --endpoint for a self-hosted workspace.")}}};async function*nn(e,t){if(!e.body)throw new h("Oracle stream has no response body.",g.FAILURE);let n=e.body.getReader(),r=new TextDecoder,o="",i=0,s=[],a=0,c=-1,l=()=>{if(s.length===0)return null;let d=s.join(`
434
+ `);if(s=[],a=0,d==="[DONE]")return null;let u;try{u=JSON.parse(d)}catch{throw new h("Oracle stream returned invalid JSON.",g.API_ERROR)}let y=io(u);return y.type==="finalizing"?y:y.idx<=c?null:(c=y.idx,y)};try{for(;;){let{value:u,done:y}=await n.read();u?.byteLength&&t?.();let v=r.decode(u,{stream:!y});if(o+=v,i+=Un(v),i>2*1024*1024)throw new h("Oracle stream event exceeded the 2 MiB protocol limit.",g.API_ERROR);let x=0,b=o.indexOf(`
435
+ `,x);for(;b>=0;){let m=o.slice(x,b).replace(/\r$/,"");if(i-=Un(o.slice(x,b+1)),x=b+1,m===""){let A=l();A&&(yield A)}else if(!m.startsWith(":")){let A=m.indexOf(":"),$=A<0?m:m.slice(0,A),M=A<0?"":m.slice(A+1).replace(/^ /,"");if($==="data"&&(s.push(M),a+=Un(M)+1,a>2*1024*1024))throw new h("Oracle stream event exceeded the 2 MiB protocol limit.",g.API_ERROR)}b=o.indexOf(`
436
+ `,x)}if(o=o.slice(x),y)break}if(o.trim()){let u=o.replace(/\r$/,"");if(u.startsWith("data:")&&(s.push(u.slice(5).replace(/^ /,"")),a+=Un(s[s.length-1]??"")+1,a>2*1024*1024))throw new h("Oracle stream event exceeded the 2 MiB protocol limit.",g.API_ERROR)}let d=l();d&&(yield d)}catch(d){throw await n.cancel("Invalid or oversized Oracle event stream.").catch(()=>{}),d}finally{n.releaseLock()}}function Un(e){return Buffer.byteLength(e,"utf8")}function so(e){let t=["program","initiative","codebase"].filter(o=>typeof e[o]=="string");if(t.length>1)throw new h("Choose only one of --program, --initiative or --codebase.",g.USAGE);let n=t[0];if(!n)return;let r=e[n];if(typeof r!="string"||!r.trim())throw new h(`--${n} needs an id.`,g.USAGE);return{kind:n,id:r.trim()}}function N(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function Hd(e){if(typeof e=="string")return p(e);try{return p(JSON.stringify(e))}catch{return"unexpected response"}}function Ct(e,t){let n=N(e);if(n.ok===!1){let r=Bs(n.error,t),o=typeof n.message=="string"?p(n.message).slice(0,2e3):void 0;throw new V(r,t,o)}return n.ok===!0&&"data"in n?n.data:e}async function $t(e,t){try{return await e.json()}catch{throw new h(`Non-JSON response from ${t} (HTTP ${e.status}).`,g.FAILURE)}}async function Tt(e){let t;try{t=await e.clone().json()}catch{t=null}let n=N(t),r=Bs(n.error??n.error_description,e.status),o=typeof n.message=="string"?n.message:typeof n.error_description=="string"?n.error_description:typeof n.detail=="string"?n.detail:void 0;throw new V(r,e.status,typeof o=="string"?p(o).slice(0,2e3):void 0,void 0,zd(e))}function zd(e){try{return e.url?new URL(e.url).origin:void 0}catch{return}}function Bs(e,t){if(typeof e!="string")return`http_${t}`;let n=p(e).slice(0,120);return/^[A-Za-z0-9_.-]+$/.test(n)?n:`http_${t}`}function Ws(e){let t=N(e);if(typeof t.sessionId!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(t.sessionId)||typeof t.title!="string"||t.visibility!=="private"&&t.visibility!=="company"||typeof t.shared!="boolean")throw new h("Oracle server returned an invalid session summary.",g.API_ERROR);return{sessionId:t.sessionId,title:p(t.title),visibility:t.visibility,shared:t.shared,...typeof t.entityKey=="string"?{entityKey:p(t.entityKey)}:{},...typeof t.updatedAt=="number"||t.updatedAt===null?{updatedAt:t.updatedAt}:{}}}function Ds(e){let t=N(e),n=Ws(t);if(!Array.isArray(t.messages))throw new h("Oracle server returned an invalid session transcript.",g.API_ERROR);let r=t.messages.map((o,i)=>{let s=N(o);if(s.role!=="user"&&s.role!=="assistant"||typeof s.content!="string")throw new h(`Oracle session message ${i+1} is invalid.`,g.API_ERROR);let a=Array.isArray(s.attachments)?s.attachments.map(Xd):void 0,c=Array.isArray(s.files)?s.files.map(Qd):void 0;return{role:s.role,content:p(s.content),...typeof s.id=="string"?{id:s.id}:{},...typeof s.authorName=="string"?{authorName:p(s.authorName)}:{},...s.question&&typeof s.question=="object"?{question:Yd(s.question)}:{},...s.questionResponse&&typeof s.questionResponse=="object"?{questionResponse:Jd(s.questionResponse)}:{},...a?{attachments:a}:{},...c?{files:c}:{}}});return{...n,messages:r,...typeof t.activeStreamId=="string"?{activeStreamId:t.activeStreamId}:{}}}function io(e){let t=N(e);if(t.protocolVersion!==1||typeof t.idx!="number"||!Number.isSafeInteger(t.idx)||t.idx<0||typeof t.type!="string")throw new h("Oracle stream event does not match protocol version 1.",g.API_ERROR);return Ie(t)}function Kd(e){let t=N(e);if(typeof t.fileId!="string"||typeof t.name!="string"||typeof t.mediaType!="string")throw new h("Oracle upload response is missing file metadata.",g.API_ERROR);return{fileId:t.fileId,name:p(t.name),mediaType:p(t.mediaType),...typeof t.sizeBytes=="number"?{sizeBytes:t.sizeBytes}:{}}}function Yd(e){let t=N(e);if(typeof t.id!="string"||!Array.isArray(t.questions))throw new h("Oracle returned an invalid question card.",g.API_ERROR);return{id:p(t.id),questions:t.questions.map((n,r)=>{let o=N(n);if(typeof o.id!="string"||typeof o.header!="string"||typeof o.question!="string"||typeof o.multiSelect!="boolean"||!Array.isArray(o.options))throw new h(`Oracle question ${r+1} is invalid.`,g.API_ERROR);return{id:p(o.id),header:p(o.header),question:p(o.question),multiSelect:o.multiSelect,options:o.options.map((i,s)=>{let a=N(i);if(typeof a.id!="string"||typeof a.label!="string"||typeof a.description!="string")throw new h(`Oracle question option ${s+1} is invalid.`,g.API_ERROR);return{id:p(a.id),label:p(a.label),description:p(a.description),...typeof a.recommended=="boolean"?{recommended:a.recommended}:{},...typeof a.confirms=="boolean"?{confirms:a.confirms}:{}}})}}),optional:t.optional===!0}}function Jd(e){let t=N(e);if(typeof t.questionId!="string"||!Array.isArray(t.answers))throw new h("Oracle returned an invalid question response.",g.API_ERROR);return{questionId:p(t.questionId),answers:t.answers.map(Vd)}}function Vd(e){let t=N(e);if(typeof t.questionId!="string"||!Array.isArray(t.optionIds)||!t.optionIds.every(n=>typeof n=="string"))throw new h("Oracle returned an invalid question answer.",g.API_ERROR);return{questionId:p(t.questionId),optionIds:t.optionIds.map(n=>p(n)),...typeof t.customText=="string"?{customText:p(t.customText).slice(0,8e3)}:{}}}function Xd(e){let t=N(e);if(typeof t.fileId!="string"||typeof t.name!="string"||typeof t.mediaType!="string")throw new h("Oracle session attachment metadata is invalid.",g.API_ERROR);return{fileId:p(t.fileId),name:p(t.name),mediaType:p(t.mediaType),...typeof t.sizeBytes=="number"&&Number.isFinite(t.sizeBytes)?{sizeBytes:t.sizeBytes}:{}}}function Qd(e){let t=N(e);if(typeof t.fileId!="string"||typeof t.filename!="string"||typeof t.mediaType!="string"||typeof t.sizeBytes!="number"||!Number.isFinite(t.sizeBytes))throw new h("Oracle session file metadata is invalid.",g.API_ERROR);return{fileId:p(t.fileId),filename:p(t.filename),mediaType:p(t.mediaType),sizeBytes:t.sizeBytes,...typeof t.format=="string"?{format:p(t.format)}:{}}}import{randomUUID as Zd}from"node:crypto";import{promises as Pe}from"node:fs";import{homedir as eu}from"node:os";import{join as co}from"node:path";import*as ao from"proper-lockfile";function Bn(){return co(eu(),".anyforge")}function Dn(){return co(Bn(),"oracle")}function Hs(){return co(Bn(),"oracle.last-session")}function tu(){return{version:2,active:{},logins:[]}}function jn(e){return e===void 0||typeof e=="string"}function Gs(e){if(!e||typeof e!="object"||Array.isArray(e))return null;let t=e;return typeof t.endpoint!="string"||typeof t.clientId!="string"||typeof t.redirectUri!="string"||typeof t.accessToken!="string"||typeof t.refreshToken!="string"||typeof t.expiresAt!="number"||!Number.isFinite(t.expiresAt)||!Array.isArray(t.scopes)||!t.scopes.every(n=>typeof n=="string")||!jn(t.companyId)||!jn(t.companyName)||!jn(t.uid)?null:{endpoint:t.endpoint,...t.companyId?{companyId:t.companyId}:{},...t.companyName?{companyName:t.companyName}:{},...t.uid?{uid:t.uid}:{},clientId:t.clientId,redirectUri:t.redirectUri,accessToken:t.accessToken,refreshToken:t.refreshToken,expiresAt:t.expiresAt,scopes:t.scopes}}function nu(e){if(!e||typeof e!="object"||Array.isArray(e))return null;let t=e;if(t.version===1){let o=Gs(t);return o?{version:2,endpoint:o.endpoint,active:{[o.endpoint]:o.clientId},logins:[o]}:null}if(t.version!==2||!Array.isArray(t.logins)||!jn(t.endpoint)||!t.active||typeof t.active!="object"||Array.isArray(t.active))return null;let n=t.logins.map(Gs);if(n.some(o=>o===null))return null;let r={};for(let[o,i]of Object.entries(t.active)){if(typeof i!="string")return null;r[o]=i}return{version:2,...t.endpoint?{endpoint:t.endpoint}:{},active:r,logins:n}}async function ae(){let e=Dn(),t;try{t=await Pe.readFile(e,"utf8")}catch(o){if(o.code==="ENOENT")return tu();throw o}let n;try{n=JSON.parse(t)}catch{throw new Error(`Oracle credentials at ${e} are invalid JSON; run \`anyforge oracle logout --all\` and sign in again.`)}let r=nu(n);if(!r)throw new Error(`Oracle credentials at ${e} have an unsupported format; run \`anyforge oracle logout --all\` and sign in again.`);return r}async function rn(e){if(!e.logins.length){await Pe.rm(Dn(),{force:!0});return}await Vs(Dn(),e)}async function Wn(e){return zn(async()=>{let t=await ae(),n=await e(t);return await rn(t),n})}function Le(e,t){return e.logins.filter(n=>n.endpoint===t)}function Gn(e,t){let n=Le(e,t);return n.find(r=>r.clientId===e.active[t])??n[0]}function ce(e){return e.companyName||e.companyId||"workspace not known yet"}function zs(e,t){let n=t.companyId?e.logins.find(r=>r.endpoint===t.endpoint&&r.companyId===t.companyId&&r.clientId!==t.clientId):void 0;return e.logins=[...e.logins.filter(r=>r!==n&&r.clientId!==t.clientId),t],e.active[t.endpoint]=t.clientId,e.endpoint=t.endpoint,n}function lo(e,t){e.logins=e.logins.filter(o=>!(o.endpoint===t.endpoint&&o.clientId===t.clientId));let n=Le(e,t.endpoint);if(!n.length){if(delete e.active[t.endpoint],e.endpoint===t.endpoint){let o=e.logins[0]?.endpoint;o?e.endpoint=o:delete e.endpoint}return}if(e.active[t.endpoint]!==t.clientId&&n.some(o=>o.clientId===e.active[t.endpoint]))return;let r=n[0];return e.active[t.endpoint]=r.clientId,r}function Ks(e,t,n,r){let o=e.logins.find(a=>a.endpoint===t&&a.clientId===n);if(!o)return{changed:!1};let i=o.companyId!==r.companyId||r.companyName!==void 0&&o.companyName!==r.companyName||r.uid!==void 0&&o.uid!==r.uid;o.companyId=r.companyId,r.companyName!==void 0&&(o.companyName=r.companyName),r.uid!==void 0&&(o.uid=r.uid);let s=e.logins.find(a=>a!==o&&a.endpoint===t&&a.companyId===r.companyId);return s&&(e.logins=e.logins.filter(a=>a!==s),e.active[t]===s.clientId&&(e.active[t]=o.clientId)),{changed:i||!!s,...s?{duplicate:s}:{}}}function at(e,t){let n=t.trim(),r=e.find(a=>a.companyId===n);if(r)return r;let o=e.filter(a=>a.companyName?.toLowerCase()===n.toLowerCase());if(o.length===1)return o[0];let i=e.map(a=>`${ce(a)}${a.companyId&&a.companyName?` (${a.companyId})`:""}`),s=i.length?`Saved workspaces: ${i.join(", ")}.`:"No Oracle logins are saved for this endpoint.";throw o.length>1?new f(`More than one saved workspace is named "${n}". Use its id instead.`,s):new f(`No saved Oracle login for workspace "${n}".`,`${s} Add one with \`anyforge oracle login --workspace "${n}"\`.`)}async function Ys(){let e;try{e=await Pe.readFile(Hs(),"utf8")}catch(r){if(r.code==="ENOENT")return[];throw r}let t;try{t=JSON.parse(e)}catch{return[]}if(!t||typeof t!="object"||Array.isArray(t))return[];let n=t;return n.version!==2||!Array.isArray(n.sessions)?[]:n.sessions.filter(r=>{if(!r||typeof r!="object")return!1;let o=r;return typeof o.endpoint=="string"&&typeof o.companyId=="string"&&typeof o.sessionId=="string"})}async function Js(e,t,n=""){let r=(await Ys()).filter(o=>!(o.endpoint===t&&o.companyId===n));await Vs(Hs(),{version:2,sessions:[...r,{endpoint:t,companyId:n,sessionId:e}]})}async function Hn(e,t=""){return(await Ys()).find(n=>n.endpoint===e&&n.companyId===t)?.sessionId}async function Vs(e,t){await Pe.mkdir(Bn(),{recursive:!0,mode:448});let n=`${e}.${Zd()}.tmp`;try{await Pe.writeFile(n,`${JSON.stringify(t,null,2)}
437
+ `,{mode:384,flag:"wx"}),await Pe.rename(n,e),await Pe.chmod(e,384)}finally{await Pe.rm(n,{force:!0}).catch(()=>{})}}async function zn(e){await Pe.mkdir(Bn(),{recursive:!0,mode:448});let t=Dn(),n=`${t}.refresh-admission`,r;try{r=await ao.lock(n,{realpath:!1,lockfilePath:n,stale:Number.MAX_SAFE_INTEGER,update:1e4,retries:{retries:300,minTimeout:100,maxTimeout:100,randomize:!1}})}catch(o){throw o.code==="ELOCKED"?new Error(`Oracle credential access is still locked; another CLI command may be running. An abrupt exit can leave this guard behind. Stop all AnyForge CLI commands before removing the guard directory at ${n}.`,{cause:o}):o}try{let o=await ao.lock(t,{realpath:!1,lockfilePath:`${t}.refresh-lock`,stale:6e4,update:1e4,retries:{retries:300,minTimeout:100,maxTimeout:100,randomize:!1}});try{return await e()}finally{await o()}}finally{await r()}}var uo=8192,Qs=10*1048576,su=20*1048576,Zs=10,ea=3;function po(e,t){if(e.type!=="tool_call"&&e.type!=="tool_call_start"||typeof e.spawnId=="string")return!1;let n=typeof e.id=="string"?e.id:void 0;if(n){if(t.has(n))return!1;t.add(n)}return!St(e.name)}var Ne=class{lastIdx=-1;answer="";question;error;done;streamId;files=[];toolIds=new Set;accept(t){return t.type==="finalizing"?!0:t.idx<=this.lastIdx?!1:(this.lastIdx=t.idx,t.type==="stream_start"&&typeof t.streamId=="string"?this.streamId=p(t.streamId):t.type==="delta"&&typeof t.content=="string"?this.answer+=t.content:po(t,this.toolIds)?this.answer="":t.type==="question"?this.question=sn(t):t.type==="error"?this.error=mo(t):t.type==="done"?this.done=t:t.type==="navigator_file_created"&&au(this.files,t.file),!0)}get settled(){return!!(this.done||this.error)}outcome(){return{answer:this.answer,...this.question?{question:this.question}:{},...this.error?{error:this.error}:{},...this.done?{done:this.done}:{},...this.streamId?{streamId:this.streamId}:{},...this.files.length?{files:[...this.files]}:{}}}};async function Lt(e,t,n,r,o,i=new Ne){let s=async u=>{let y=i.streamId;i.accept(u)&&(i.streamId&&i.streamId!==y&&r.onStream?.(i.streamId),await r.onEvent(u))},a=()=>fo(e,t,i,s,o,r.onActivity),c=async()=>{try{for await(let u of e.streamRequestEvents(t,n.requestId,-1,r.onActivity,o))await s(u)}catch(u){throw o?.aborted?u:new h("Oracle connection failed and the submitted request could not be recovered by its request id. It was not resubmitted with a new id.",g.FAILURE,`Run anyforge oracle resume ${t} to inspect the saved session.`)}if(i.settled)return i.outcome();if(i.streamId)return a();throw new h("Oracle request lookup returned no stream events. It was not resubmitted with a new id.",g.FAILURE,`Run anyforge oracle resume ${t} to inspect the saved session.`)},l=async()=>{try{let u=await e.submitTurn(t,n,o);for await(let y of nn(u,r.onActivity))await s(y)}catch(u){if(o?.aborted||u instanceof h&&u.exitCode===g.API_ERROR)throw u;return i.streamId?a():c()}return i.settled?i.outcome():i.streamId?a():c()},d;try{d=await e.submitTurn(t,n,o)}catch(u){if(o?.aborted||u instanceof h&&u.exitCode!==g.FAILURE)throw u;return l()}if(!d)throw new h("Oracle did not return a stream response.",g.FAILURE);r.onResponse?.();try{for await(let u of nn(d,r.onActivity))await s(u)}catch(u){if(o?.aborted||u instanceof h)throw u;return i.streamId?a():l()}return i.settled?i.outcome():i.streamId?a():l()}async function fo(e,t,n,r,o,i){let s=n.streamId;if(!s)throw new h("Oracle stream replay needs a stream id.",g.FAILURE);let a=0;for(;!n.settled&&a<ea;){if(o?.aborted)throw o.reason;try{for await(let c of e.streamReplayEvents(t,s,n.lastIdx+1,i,o))await r(c);if(!n.settled){if((await e.getSession(t,o)).activeStreamId!==s)break;await Xs(300,void 0,o?{signal:o}:void 0)}}catch(c){if(o?.aborted||c instanceof h&&c.exitCode===g.API_ERROR||(a+=1,a>=ea))throw c;await Xs(300,void 0,o?{signal:o}:void 0)}}if(!n.settled)throw new h("Oracle stream could not be fully replayed; the turn was not submitted again.",g.FAILURE,`Run anyforge oracle resume ${t} to inspect the session.`);return n.outcome()}function on(e,t){let n=t.done,r=n?.servedBy,o=ct(n?.tokens),i=Z(n?.rounds),s=typeof n?.exitReason=="string"?p(n.exitReason):void 0,a=n?.exitReason==="completed"&&!t.question,c=t.files??(Array.isArray(n?.files)?n.files.map(Ke):void 0),l=t.error?"error":t.question?"awaiting_input":a?"complete":"interrupted";return{protocolVersion:1,sessionId:e,status:l,answer:p(t.answer),partial:!a,...typeof n?.messageId=="string"?{messageId:p(n.messageId)}:typeof n?.answerId=="string"?{messageId:p(n.answerId)}:{},...t.question?{question:t.question}:{},...t.error?{error:p(t.error)}:{},...t.streamId?{streamId:p(t.streamId)}:{},...r?{servedBy:Ie(r)}:{},...o?{usage:{...o,...i!==void 0?{rounds:i}:{}}}:{},...i!==void 0?{rounds:i}:{},...s?{exitReason:s}:{},...c?.length?{files:Ie(c)}:{}}}function C(e,t){return typeof e=="string"?p(e).slice(0,t):""}function mo(e){return typeof e.message=="string"?p(e.message).slice(0,2e3):"Oracle turn failed."}function K(e){return p(e instanceof Error?e.message:String(e)).slice(0,2e3)}function sn(e){let t=e.question;if(!t||typeof t!="object")throw new h("Oracle question event omitted its question card.",g.API_ERROR);return cu(t)}function Ke(e){let t=e&&typeof e=="object"&&!Array.isArray(e)?e:{};if(typeof t.fileId!="string"||!t.fileId||typeof t.filename!="string"||!t.filename||typeof t.mediaType!="string"||!t.mediaType||typeof t.sizeBytes!="number"||!Number.isSafeInteger(t.sizeBytes)||t.sizeBytes<0)throw new h("Oracle generated-file event omitted safe file metadata.",g.API_ERROR);return{fileId:p(t.fileId),filename:p(t.filename),mediaType:p(t.mediaType),sizeBytes:t.sizeBytes,...typeof t.format=="string"?{format:p(t.format)}:{}}}function au(e,t){let n=Ke(t);e.some(r=>r.fileId===n.fileId)||e.push(n)}function cu(e){let t=e;if(typeof t.id!="string"||!Array.isArray(t.questions))throw new h("Oracle returned an invalid question card.",g.API_ERROR);return{id:p(t.id),optional:t.optional===!0,questions:t.questions.map((n,r)=>{let o=n&&typeof n=="object"?n:{};if(typeof o.id!="string"||typeof o.header!="string"||typeof o.question!="string"||typeof o.multiSelect!="boolean"||!Array.isArray(o.options))throw new h(`Oracle question ${r+1} is invalid.`,g.API_ERROR);return{id:p(o.id),header:p(o.header),question:p(o.question),multiSelect:o.multiSelect,options:o.options.map(i=>{let s=i&&typeof i=="object"?i:{};if(typeof s.id!="string"||typeof s.label!="string"||typeof s.description!="string")throw new h("Oracle question option is invalid.",g.API_ERROR);return{id:p(s.id),label:p(s.label),description:p(s.description),...typeof s.recommended=="boolean"?{recommended:s.recommended}:{},...typeof s.confirms=="boolean"?{confirms:s.confirms}:{}}})}})}}function Z(e){return typeof e=="number"&&Number.isFinite(e)?e:void 0}function ct(e){if(typeof e=="number"&&Number.isFinite(e))return{totalTokens:e};if(!e||typeof e!="object"||Array.isArray(e))return;let t=e,n=Z(t.in)??Z(t.input)??Z(t.inputTokens),r=Z(t.cacheRead),o=Z(t.cacheWrite),i=n===void 0?void 0:n+(r??0)+(o??0),s=Z(t.out)??Z(t.output)??Z(t.outputTokens),a=Z(t.total)??Z(t.totalTokens)??(i!==void 0||s!==void 0?(i??0)+(s??0):void 0);if(!(i===void 0&&s===void 0&&a===void 0))return{...i!==void 0?{inputTokens:i}:{},...r?{cachedInputTokens:r}:{},...s!==void 0?{outputTokens:s}:{},...a!==void 0?{totalTokens:a}:{}}}function Nt(e){if(e&&typeof e=="object"){let t=e;if(typeof t.accepted=="boolean")return t.accepted?"Oracle server accepted the cancellation request.":"Oracle server did not accept the cancellation request.";if(typeof t.cancelled=="boolean")return t.cancelled?"Oracle server cancelled the active turn.":"Oracle server did not cancel the active turn."}return"Oracle server acknowledged the cancellation request."}function Ft(e,t){let n=t&&e&&(e.kind==="program"||e.kind==="initiative")?{kind:e.kind,id:e.id}:void 0;return{...!t&&e?{context:e}:{},...n?{sharedEntity:n}:{},visibility:t?"company":"private"}}async function an(e,t,n){await Js(e,t,n).catch(()=>{})}async function Kn(e,t,n,r){if(!n.length)return[];if(n.length>Zs)throw new f(`Attach no more than ${Zs} files per turn.`);let o=0,i=[];for(let a of n){let c=await ou(a);if(!c.isFile())throw new f(`Attachment is not a file: ${a}`);if(c.size>Qs)throw new f(`Attachment exceeds the 10 MiB per-file CLI limit: ${a}`);let l=await lu(a,Qs);if(o+=l.byteLength,o>su)throw new f("Attachments exceed the 20 MiB total CLI limit.");let d=iu(a).replace(/[\u0000-\u001f\u007f/\\]/g,"_").slice(0,200)||"attachment",u=Us(d)??"";if(!u)throw new f(`Unsupported attachment type: ${a}`);i.push({content:l,name:d,mediaType:u})}let s=[];for(let{content:a,name:c,mediaType:l}of i){await r(p(c),a.byteLength);let d=new ArrayBuffer(a.byteLength);new Uint8Array(d).set(a);let u=new File([d],c,{type:l});s.push(await e.upload(t,u))}return s}async function lu(e,t){let n=[],r=0;for await(let o of ru(e)){let i=Buffer.isBuffer(o)?o:Buffer.from(o);if(r+=i.byteLength,r>t)throw new f(`Attachment exceeds the ${z(t)} limit: ${e}`);n.push(i)}return Buffer.concat(n)}var dt=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],uu=2e4,pu=6,fu={lookup:"Looking things up",quick_edit:"Making a quick edit",final_answer:"Composing the answer",follow_up:"Suggesting follow-ups"},mu={max_rounds:"reached the step limit",max_tokens:"reached the output limit",token_budget:"reached the token budget",deadline:"ran out of time",interrupted:"interrupted",loop_detected:"stopped a repeating loop",cancelled:"cancelled"},Yn=class{constructor(t,n){this.startedAt=t;this.sessionId=n;this.lastSignalAt=t}startedAt;sessionId;phase="sending";answer="";tools=[];notices=[];files=[];lastSignalAt;endedAt;end;done;upload;round;maxRounds;serverPhase;agents=new Map;toolIds=new Set;get toolCount(){return this.tools.length}get failedCount(){return this.tools.filter(t=>t.status==="failed").length}get finished(){return this.end!==void 0}setUploading(t,n,r){this.phase="uploading",this.upload=`${t} (${z(n)})`,this.lastSignalAt=r}setPhase(t,n){this.phase=t,this.lastSignalAt=n}touch(t){this.lastSignalAt=t}apply(t,n){if(this.lastSignalAt=n,this.phase==="cancelling")return this.record(t);switch(t.type){case"stream_start":case"thinking":this.phase!=="finishing"&&(this.phase="thinking");break;case"round_start":this.round=Z(t.round),this.maxRounds=Z(t.maxRounds)??this.maxRounds;for(let r=this.tools.length-1;r>=0;r--)this.tools[r]?.started&&this.tools.splice(r,1);this.phase!=="finishing"&&(this.phase="thinking");break;case"phase":this.serverPhase=typeof t.phase=="string"?t.phase:void 0;break;case"delta":this.phase!=="finishing"&&(this.phase="writing");break;case"tool_result":this.phase!=="finishing"&&(this.phase="thinking");break;case"finalizing":this.phase="finishing";break}this.record(t)}record(t){switch(t.type){case"delta":typeof t.content=="string"&&(this.answer+=t.content);break;case"tool_call_start":case"tool_call":this.recordToolCall(t);break;case"tool_result":{let n=this.tools.find(r=>r.id!==void 0&&r.id===t.toolUseId);n&&(n.status=t.ok===!1?"failed":"ok");break}case"subagent_start":typeof t.spawnId=="string"&&this.agents.set(t.spawnId,"running");break;case"subagent_done":typeof t.spawnId=="string"&&this.agents.set(t.spawnId,"done");break;case"warning":this.notices.push({tone:"warning",text:`Warning: ${C(t.message,500)}`});break;case"navigator_file_error":this.notices.push({tone:"warning",text:`Could not generate ${C(t.filename,200)}: ${C(t.error,500)}`});break;case"attachment_notice":if(Array.isArray(t.notices))for(let n of t.notices){let r=n&&typeof n=="object"?n:{};this.notices.push({tone:"warning",text:`${C(r.name,160)}: ${C(r.message,500)}`})}break;case"provider_fallback":{let n=C(t.from,120),r=C(t.to,120),o=C(t.reason,40).replaceAll("_"," ");this.notices.push({tone:"info",text:`Switched model${n?` from ${n}`:""}${r?` to ${r}`:""}${o?` (${o})`:""}.`});break}case"navigator_file_created":{let n=Ke(t.file);if(this.files.some(r=>r.fileId===n.fileId))break;this.files.push(n),this.notices.push({tone:"info",text:`Generated ${n.filename} (${z(n.sizeBytes)}). Download: anyforge oracle download ${this.sessionId} ${n.fileId} --output .`});break}case"error":this.notices.push({tone:"error",text:mo(t)});break;case"done":this.done=t;break}}recordToolCall(t){let n=typeof t.id=="string"?t.id:void 0,r=po(t,this.toolIds),o=typeof t.name=="string"?t.name:"";if(St(o))return;let i=C(typeof t.label=="string"?t.label:xt(o),120)||"tool",s=n?this.tools.find(c=>c.id===n):void 0;if(s){s.label=i,t.type==="tool_call"&&delete s.started;return}let a={label:i,status:"running",...n?{id:n}:{},...typeof t.spawnId=="string"?{spawnId:t.spawnId}:{},...t.type==="tool_call_start"?{started:!0}:{}};r&&this.answer.trim()&&(a.note=p(this.answer.trim()),this.answer=""),this.tools.push(a),this.phase!=="finishing"&&(this.phase="thinking")}note(t,n){this.notices.push({tone:t,text:p(n)})}finish(t,n){this.end=t,this.endedAt=n}label(){if(this.phase==="cancelling")return"Cancelling";if(this.phase==="uploading")return`Uploading ${this.upload??"attachment"}`;if(this.phase==="sending")return"Sending";if(this.phase==="preparing")return"Preparing context";if(this.phase==="finishing")return"Finishing up";let t=[...this.agents.values()].filter(r=>r==="running").length;if(t)return`Researching (${t} agent${t===1?"":"s"})`;let n=this.tools.filter(r=>r.status==="running"&&!r.spawnId);return n.length===1?`Running ${n[0]?.label}`:n.length>1?`Running ${n.length} tools`:this.phase==="writing"?"Writing":this.serverPhase&&fu[this.serverPhase]||"Thinking"}statusLine(t,n){let r=[this.label(),no(t-this.startedAt)];this.round&&r.push(this.maxRounds?`step ${this.round}/${this.maxRounds}`:`step ${this.round}`);let o=t-this.lastSignalAt;return o>=uu&&this.phase!=="cancelling"&&r.push(`no update for ${no(o)}`),n&&t-this.startedAt>=n*.9&&this.phase!=="finishing"&&r.push("wrapping up"),r.join(" \xB7 ")}summaryLine(){let t=[],n=en((this.endedAt??this.startedAt)-this.startedAt),r=typeof this.done?.exitReason=="string"?this.done.exitReason:void 0,o;this.end==="complete"?o=na("\u2713"):this.end==="awaiting_input"?o=ho("?")+" Waiting for your answer":this.end==="cancelled"?o=wo("\u2717")+" Interrupted":this.end==="error"?o=go("\u2717")+" Error":o=wo("\u2717")+` Stopped: ${r&&mu[r]||"no final answer"}`,t.push(n),this.tools.length&&t.push(`${this.tools.length} tool${this.tools.length===1?"":"s"}`);let i=ct(this.done?.tokens);(i?.inputTokens!==void 0||i?.outputTokens!==void 0)&&t.push(`${we(i.inputTokens??0)} in${i.cachedInputTokens?` (${we(i.cachedInputTokens)} cached)`:""} / ${we(i.outputTokens??0)} out`);let s=ln(this.done?.servedBy);return s&&t.push(s),`${o} ${lt(t.join(" \xB7 "))}`}};function ln(e){if(!e||typeof e!="object")return"";let t=e,n=C(t.model??t.id,100);if(!n)return"";let r=t.fallback&&typeof t.fallback=="object"?t.fallback:void 0;if(r&&typeof r.from=="string"){let o=typeof r.reason=="string"?`: ${C(r.reason,40).replaceAll("_"," ")}`:"";return`${n} (fallback from ${C(r.from,100)}${o})`}return n}var Jn=class{constructor(t,n){this.activity=t;this.view=n;this.answer=new It("",n.linkOrigin)}activity;view;answer;getText(){return this.answer.getText()}invalidate(){this.answer.invalidate()}render(t){let n=this.activity;this.answer.setText(n.answer);let r=[tn("assistant")];if(!n.finished&&!n.tools.length&&!n.answer.trim()){let o=ho(dt[this.view.frame()%dt.length]??"*");r.push(cn(` ${o} ${lt(n.label())}`,t))}r.push(...this.renderTools(t)),r.push(...this.answer.render(t));for(let o of n.notices){let i=o.tone==="error"?go:o.tone==="warning"?wo:lt;r.push(...ta(i(o.text),t))}return n.finished&&r.push(cn(n.summaryLine(),t)),r}renderTools(t){let{tools:n}=this.activity;if(!n.length)return[];if(this.activity.finished&&!this.view.expanded()){let s=this.activity.failedCount,a=`Used ${n.length} tool${n.length===1?"":"s"}${s?` \xB7 ${s} failed`:""} (ctrl+o to expand)`;return[cn(lt(` \u23BF ${a}`),t)]}let r=this.activity.finished?n:n.slice(-pu),o=n.length-r.length,i=[];return o&&i.push(cn(lt(` \u23BF \u2026 ${o} earlier tool${o===1?"":"s"}`),t)),r.forEach((s,a)=>{if(s.note)for(let u of ta(s.note,Math.max(10,t-4)).slice(0,3))i.push(` ${lt(du(u))}`);let c=s.status==="running"?ho(dt[this.view.frame()%dt.length]??"*"):s.status==="ok"?na("\u2713"):go("\u2717"),l=a===0&&!o?" \u23BF ":" ",d=s.spawnId?` ${lt("agent \xB7")} `:"";i.push(cn(`${l}${d}${c} ${s.label}`,t))}),i}};import{truncateToWidth as yu,visibleWidth as vu,wrapTextWithAnsi as oa}from"@earendil-works/pi-tui";import{bold as ia,cyan as bu,dim as Fe}from"kleur/colors";import{CombinedAutocompleteProvider as hu}from"@earendil-works/pi-tui";var dn=[{name:"help",description:"Show commands and keyboard shortcuts"},{name:"status",description:"Show session, access, model, usage and limits"},{name:"model",description:"Show the workspace model and the model that answered last"},{name:"usage",description:"Show turns and tokens used in this terminal"},{name:"new",args:"[shared]",description:"Start a new conversation",idleOnly:!0,completions:["shared"]},{name:"clear",description:"Clear the screen and start a new private conversation",idleOnly:!0},{name:"resume",args:"[session-id]",description:"Pick or open a saved conversation",idleOnly:!0},{name:"workspace",args:"[name|id]",description:"Switch to another saved workspace and start a new conversation there",idleOnly:!0},{name:"context",args:"[clear|program|initiative|codebase <id>]",description:"Show or change the context for the next message",completions:["clear","program ","initiative ","codebase "]},{name:"attach",args:"<path>",description:"Attach a file to your next message"},{name:"copy",description:"Copy the last answer as Markdown"},{name:"files",description:"List files Oracle generated in this conversation"},{name:"exit",description:"Leave the Oracle",aliases:["quit"]}],gu=[{name:"design",args:"[topic]",description:"Talk through a technical design before anything is created"},{name:"catchup",description:"Catch up on your work: what needs you, what landed, what is running"}];function yo(e){let t=new Set(dn.flatMap(n=>[n.name,...n.aliases??[]]));return(e??gu).filter(n=>!t.has(n.name))}function ra(e,t){let n=/^\/([A-Za-z][\w-]*)(?:\s+([\s\S]*))?$/.exec(e.trim());if(!n)return null;let r=(n[1]??"").toLowerCase(),o=(n[2]??"").trim(),i=dn.find(a=>a.name===r||a.aliases?.includes(r));if(i)return{kind:"local",command:i,arg:o,args:o?o.split(/\s+/):[]};let s=t.find(a=>a.name===r);return s?{kind:"server",command:s}:{kind:"unknown",name:r}}function vo(e,t){let n=[...dn.flatMap(i=>[i,...(i.aliases??[]).map(s=>({...i,name:s}))]).map(i=>({name:i.name,description:i.description,...i.args?{argumentHint:i.args}:{},...i.completions?{getArgumentCompletions:s=>wu(i.completions??[],s)}:{}})),...e.map(i=>({name:i.name,description:i.description,...i.args?{argumentHint:i.args}:{}}))],r=new hu(n,t),o=/^\s*\/attach\s+/;return{async getSuggestions(i,s,a,c){let l=(i[s]??"").slice(0,a);return s!==0||!l.trimStart().startsWith("/")?null:o.test(l)?r.getSuggestions(i,s,a,{...c,force:!0}):r.getSuggestions(i,s,a,c)},applyCompletion(i,s,a,c,l){return r.applyCompletion(i,s,a,c,l)},shouldTriggerFileCompletion(i,s,a){return s===0&&o.test((i[s]??"").slice(0,a))}}}function wu(e,t){let n=e.filter(r=>r.startsWith(t.toLowerCase())&&r!==t).map(r=>({value:r,label:r.trim()}));return n.length?n:null}function un(e,t){return e?t?"write + approve":"write":"read only"}function qt(e){return e?`${e.kind} ${p(e.id)}`:"no context"}function Ye(e){return e?p(e.companyName||e.companyId):""}function sa(e,t){let n=[`${ia(bu("\u26A1\uFE0E AnyForge Oracle"))}${Fe(` \xB7 v${p(e.version)}`)}`,`${e.workspace?`${ia(e.workspace)} \xB7 `:""}${Ce(e.session)} \xB7 ${e.access} \xB7 ${qt(e.context)}`,...e.session.messages.length?[`Resumed: ${p(e.session.title)}`]:[],Fe("Only your messages and the files you attach leave this terminal."),Fe("/help commands \xB7 /status details")];if(t<24)return n.flatMap(s=>oa(s,Math.max(1,t)));let r=Math.min(t,76),o=r-4,i=[Fe(`\u256D${"\u2500".repeat(r-2)}\u256E`)];for(let s of n.flatMap(a=>oa(a,o))){let a=yu(s,o);i.push(`${Fe("\u2502")} ${a}${" ".repeat(Math.max(0,o-vu(a)))} ${Fe("\u2502")}`)}return i.push(Fe(`\u2570${"\u2500".repeat(r-2)}\u256F`)),i}function aa(e){let t=(r,o)=>r?`${Fe(r)} ${o}`:o,n=[{title:"Commands",rows:dn.map(r=>[`/${r.name}`,t(r.args,r.aliases?.length?`${r.description} (also /${r.aliases.join(", /")})`:r.description)])}];if(e.server.length){let r=e.server.map(o=>[`/${o.name}`,t(o.args,p(o.description))]);e.server.some(o=>o.name==="catchup")&&!e.canApprove&&r.push(["",Fe("Acting on /catchup decisions needs a session started with `anyforge oracle --write --approve`.")]),n.push({title:"Workspace commands (answered by Oracle)",rows:r})}return n.push({title:"Keys",rows:[["Enter","Send. While Oracle works, queues one message for when it finishes"],["Shift+Enter","New line (Ctrl+J also works)"],["Esc","Interrupt the current answer"],["Ctrl+C","Interrupt the answer, or clear the input; press twice to exit"],["Ctrl+D","Exit when the input is empty"],["Ctrl+O","Show or hide tool activity for finished answers"],["Ctrl+Shift+F","Search the conversation"],["Up / Down","Recall messages sent in this terminal"],["Tab","Complete a command, or a file path after /attach"],["PgUp / PgDn","Scroll; Home and End jump to the top and bottom"]]}),n}function bo(e){let n=[["Model",`workspace default ${e.model?`${p(e.model.provider)}/${p(e.model.id)}`:"chosen by the server"}`]],r=ln(e.lastServedBy);if(r){let o=e.lastServedBy,i=C(o.routing,40).replaceAll("_"," ");n.push(["Last answer",`${r}${i?` \xB7 ${i}`:""}`])}return n}function ko(e){return[["This run",e.turns?`${e.turns} turn${e.turns===1?"":"s"} \xB7 ${e.tools} tool${e.tools===1?"":"s"} \xB7 ${we(e.inputTokens)} in / ${we(e.outputTokens)} out tokens`:"no turns yet"]]}function Oo(e){if(!e?.finished)return[];let t=[en((e.endedAt??e.startedAt)-e.startedAt)];t.push(`${e.toolCount} tool${e.toolCount===1?"":"s"}${e.failedCount?` (${e.failedCount} failed)`:""}`);let n=Z(e.done?.rounds);n!==void 0&&t.push(`${n} round${n===1?"":"s"}`);let r=ct(e.done?.tokens);(r?.inputTokens!==void 0||r?.outputTokens!==void 0)&&t.push(`${we(r.inputTokens??0)} in${r.cachedInputTokens?` (${we(r.cachedInputTokens)} cached)`:""} / ${we(r.outputTokens??0)} out tokens`);let o=C(e.done?.exitReason,40);return t.push(o?o.replaceAll("_"," "):e.end??""),[["Last turn",t.filter(Boolean).join(" \xB7 ")]]}function ca(e){let t=e.capabilities?["read","write","approve"].filter(o=>e.capabilities?.[o]).join(", ")||"none":"not reported",n=e.contextText?` \xB7 note ${z(Buffer.byteLength(e.contextText,"utf8"))}`:"",r=Ts(e.limits);return[["Session",`${e.session.sessionId} \xB7 ${p(e.session.title)||"Untitled"}`],["Audience",Ce(e.session)],["Access",`this session ${un(e.write,e.approve)} \xB7 grant ${e.grantScopes.join(", ")||"none"} \xB7 workspace role ${t}`],["Context",`${qt(e.context)}${n}`],...bo(e),...Oo(e.lastTurn),...ko(e.totals),["Limits",r.length?r.join(" \xB7 "):"not reported"],...e.identity?[["Workspace",ku(e.identity,e.savedWorkspaces)]]:[],["Endpoint",e.endpoint],["Version",`CLI ${p(e.version)} \xB7 protocol ${1}`],["Attached",e.pendingFiles.length?`${e.pendingFiles.map(p).join(", ")} (uploads with your next message)`:"nothing waiting"]]}function ku(e,t){let n=e.companyName?`${p(e.companyName)} (${p(e.companyId)})`:`company ${p(e.companyId)}`,r=t!==void 0&&t>1?` \xB7 ${t} saved, /workspace to switch`:"";return`${n} \xB7 user ${p(e.uid)}${r}`}function la(e,t){return t.length?t.map((n,r)=>[`${r+1}.`,`${p(n.filename)} (${z(n.sizeBytes)}) \xB7 anyforge oracle download ${e} ${p(n.fileId)} --output .`]):[["Files","Oracle has not generated files in this conversation."]]}import{randomUUID as Su}from"node:crypto";import{stat as Ru}from"node:fs/promises";import{basename as da}from"node:path";import{bold as _u,cyan as ua,dim as pa,red as Ut,yellow as Iu}from"kleur/colors";import{Container as Eu,Editor as Tu,ProcessTerminal as Cu,ScrollView as $u,SelectList as fa,TuiAltScreen as Pu,VStack as Lu,getNativeClipboard as Nu,matchesKey as Qn,truncateToWidth as ma,wrapTextWithAnsi as Fu}from"@earendil-works/pi-tui";import{bold as Ou,cyan as Ao,dim as Vn,yellow as Au}from"kleur/colors";import{Input as xu,Text as ut,matchesKey as Mt}from"@earendil-works/pi-tui";var Xn=class{constructor(t,n,r){this.tui=t;this.question=n;this.onComplete=r;(n.questions[0]?.options.length??0)===0&&(this.mode="custom"),this.customInput.onSubmit=o=>{let i=o.trim().slice(0,8e3);if(!i){this.errorMessage="Enter a short answer before continuing.",this.tui.requestRender();return}this.recordCurrent(i)},this.customInput.onEscape=()=>{if(!this.question.questions[this.questionIndex]?.options.length){this.question.optional?this.finish(null):this.requireAnswer();return}this.customInput.setValue(""),this.mode="select",this.tui.setFocus(this),this.tui.requestRender()}}tui;question;onComplete;focused=!1;questionIndex=0;optionIndex=0;mode="select";errorMessage="";selected=new Map;answers=[];customInput=new xu({prompt:`${Ao("Your answer: ")} `,placeholder:"Write a short answer and press Enter",placeholderStyle:Vn});invalidate(){}render(t){let n=this.question.questions[this.questionIndex];if(!n)return[];this.customInput.focused=this.focused&&this.mode==="custom";let r=Math.max(12,t-2),o=this.question.questions.length>1?` \xB7 ${this.questionIndex+1}/${this.question.questions.length}`:"",i=[...new ut(`${Ou(Ao(p(n.header)))}${Vn(o)}`,0,0).render(r),...new ut(p(n.question),0,0).render(r),...this.renderOptions(n,r)];return this.mode==="custom"?(i.push(...this.customInput.render(r)),i.push(...new ut(Vn(`Enter submits your text${n.options.length?" \xB7 Esc back to the choices":this.question.optional?" \xB7 Esc skip":""}`),0,0).render(r))):i.push(...new ut(Vn(this.instructions(n)),0,0).render(r)),this.errorMessage&&i.push(...new ut(Au(this.errorMessage),0,0).render(r)),i.map(s=>`${Ao("\u2502")} ${s}`)}handleInput(t){if(this.mode==="custom"){this.customInput.handleInput(t);return}let n=this.question.questions[this.questionIndex];if(!n)return;let r=this.selectedFor(n);if(n.options.length===0){if(t.toLowerCase()==="s"&&this.question.optional){this.finish(null);return}if(Mt(t,"escape")){this.question.optional?this.finish(null):this.requireAnswer();return}this.customInput.handleInput(t);return}if(Mt(t,"up")){this.optionIndex=Math.max(0,this.optionIndex-1),this.errorMessage="",this.tui.requestRender();return}if(Mt(t,"down")){this.optionIndex=Math.min(n.options.length-1,this.optionIndex+1),this.errorMessage="",this.tui.requestRender();return}if(Mt(t,"escape")){this.question.optional?this.finish(null):this.requireAnswer();return}if(t.toLowerCase()==="s"&&this.question.optional){this.finish(null);return}if(t.toLowerCase()==="o"){this.mode="custom",this.tui.setFocus(this),this.tui.requestRender();return}let o=Number(t);if(Number.isInteger(o)&&o>=1&&o<=n.options.length){let i=n.options[o-1]?.id;i&&(this.optionIndex=o-1,n.multiSelect?r.has(i)?r.delete(i):r.add(i):(r.clear(),r.add(i)),this.tui.requestRender());return}if(Mt(t,"space")&&n.multiSelect){let i=n.options[this.optionIndex];i&&(r.has(i.id)?r.delete(i.id):r.add(i.id),this.tui.requestRender());return}if(Mt(t,"enter")){if(r.size===0){this.errorMessage="Select an option explicitly, type a custom answer, or skip if optional.",this.tui.requestRender();return}this.recordCurrent()}}renderOptions(t,n){let r=this.selectedFor(t),o=[];return t.options.forEach((i,s)=>{let a=r.has(i.id),c=t.multiSelect?a?"[x]":"[ ]":a?"(x)":"( )",l=s===this.optionIndex?"\u203A":" ",d=i.recommended?" \xB7 recommended":"",u=i.confirms?" \xB7 confirms the shown action":"",y=`${l} ${s+1}. ${c} ${p(i.label)}${d}${u}`;o.push(...new ut(y,0,0).render(n)),o.push(...new ut(` ${p(i.description)}`,0,0).render(n))}),o}instructions(t){return t.options.length===0?`Write a short answer${this.question.optional?" \xB7 s or Esc skip card":""}`:`\u2191/\u2193 move \xB7 1\u2013${t.options.length} ${t.multiSelect?"toggle":"select"}${t.multiSelect?" \xB7 Space toggle":""} \xB7 Enter continue \xB7 o write your own${this.question.optional?" \xB7 s or Esc skip card":""}`}requireAnswer(){let t=this.question.questions[this.questionIndex];this.errorMessage=t?.options.length?"Oracle needs an answer to continue: pick an option, or press o to write your own.":"Oracle needs an answer to continue: type it and press Enter.",this.tui.requestRender()}selectedFor(t){let n=this.selected.get(t.id);return n||(n=new Set,this.selected.set(t.id,n)),n}recordCurrent(t){let n=this.question.questions[this.questionIndex];if(!n)return;t&&this.selectedFor(n).clear();let r=[...this.selectedFor(n)],o={questionId:n.id,optionIds:r,...t?{customText:t}:{}},i=this.answers.findIndex(s=>s.questionId===n.id);if(i>=0?this.answers.splice(i,1,o):this.answers.push(o),this.customInput.setValue(""),this.mode="select",this.questionIndex<this.question.questions.length-1){this.questionIndex+=1,this.optionIndex=0,this.mode=this.question.questions[this.questionIndex]?.options.length===0?"custom":"select",this.errorMessage="",this.tui.setFocus(this),this.tui.requestRender();return}this.finish({questionId:this.question.id,answers:this.answers.map(s=>({...s}))})}finish(t){this.onComplete(t)}};var ha=1500,qu=100,Mu="\x1B]133;A\x07";function jt(e){return{render:e,invalidate(){}}}function ga(e,t,n,r){let o=new It(t,r);return{getText:()=>o.getText(),invalidate:()=>o.invalidate(),render:i=>e==="user"?Fs(o,i,n).map((s,a)=>a===1?`${Mu}${s}`:s):[tn("assistant"),...o.render(i)]}}async function wa(e){let{client:t,config:n}=e,r=new Cu,o=new Pu(r,void 0,void 0,{scrollToEndIndicator:()=>" more below \xB7 End to jump "}),i=new Eu,s=new $u(i,{follow:"end",primary:!0,overscroll:"chain"}),a=new Tu(o,Ns),c=yo(n.commands),l=()=>typeof n.limits?.turnSoftMs=="number"?n.limits.turnSoftMs:void 0,d=e.session,u=e.initialContext,y=e.contextText,v=e.canWrite,x=e.canApprove,b,m,A,$,M=[...e.initialFiles],H=!1,Ae=!1,mt=!1,dr=0,ht,To=0,mn=!1,Ve="",Wt,hn,gt=[],wt={turns:0,inputTokens:0,outputTokens:0,tools:0},gn=new Map,xe,Ka=jt(w=>{if(b){let k=ua(dt[dr%dt.length]??"*");return[ma(`${k} ${b.activity.statusLine(Date.now(),l())}`,w)]}return xe?[ma(`${ua("?")} Waiting for your answer to the question above`,w)]:[]}),Ya=jt(w=>{let k=[Ye(n.identity),un(v,x),qt(u),qs(d),M.length?`${M.length} file${M.length===1?"":"s"} attached`:"",m!==void 0?"1 message queued":""];return[pa($s(w,k,b?"esc to interrupt":"/help \xB7 /status"))]});o.setLayoutRoot(new Lu([{component:s,basis:0,grow:1,minSize:1},{component:Ka,basis:"auto",shrink:0},{component:a,basis:"auto",shrink:0,minSize:2},{component:Ya,basis:"auto",shrink:0,minSize:1}]));let le=()=>o.requestRender(),U=w=>o.flash(p(w),3e3),Ue=w=>{i.addChild(w),le()},Y=(w,k=pa)=>{let O=p(w);Ue(jt(S=>Fu(k(O),Math.max(1,S))))},wn=(w,k)=>{Ue(jt(O=>["",...Cs(w,k,O)]))},Ja=()=>{ht||(ht=setInterval(()=>{dr+=1,le()},qu),ht.unref())},ur=()=>{ht&&clearInterval(ht),ht=void 0},yt=()=>{H||o.hasOverlay()||o.setFocus(xe??a)},Co=w=>{A=w;let k=gn.get(w.id);k&&i.removeChild(k);let O=new Xn(o,w,S=>{gn.delete(w.id),xe===O&&(xe=void 0),i.removeChild(O),Qa(S??{questionId:w.id,skipped:!0,answers:[]}),yt()});gn.set(w.id,O),xe=O,Ue(O)},yn=w=>{i.clear(),gn.clear(),xe=void 0,d=w,A=void 0,$=void 0,gt=[],Ve="";let k={version:e.version,workspace:Ye(n.identity),session:w,access:un(v,x),context:u};Ue(jt(S=>sa(k,S)));let O;for(let S of w.messages){Ue(ga(S.role,S.content,S.authorName,t.endpoint)),S.role==="assistant"&&(Ve=S.content),S.question&&(O=S.question),S.questionResponse&&(Y("A stored response was recorded for this question."),O?.id===S.questionResponse.questionId&&(O=void 0));for(let _ of S.files??[])gt.some(P=>P.fileId===_.fileId)||gt.push(_),Y(`File: ${_.filename} (${_.mediaType}, ${z(_.sizeBytes)}) \xB7 anyforge oracle download ${w.sessionId} ${_.fileId} --output .`)}O&&Co(O),yt(),an(w.sessionId,t.endpoint,n.identity?.companyId)},$o,Va=new Promise(w=>{$o=w}),vn=()=>{H||(H=!0,ur(),b?.controller.abort(new Error("Detached from the active Oracle stream.")),o.stop(),$o())},pr=async()=>{let w=b;if(!w||w.cancelling)return;w.cancelling=!0,w.activity.setPhase("cancelling",Date.now()),le();let{streamId:k,requestId:O}=w;w.controller.abort(new Error("Turn cancelled by user."));let S;if(k)try{S=Nt(await t.cancelTurn(d.sessionId,k))}catch(_){S=`Could not confirm server cancellation: ${K(_)}`}else if(O)try{S=Nt(await t.cancelRequest(d.sessionId,O))}catch(_){S=`Could not confirm server cancellation by request id: ${K(_)}`}else S="The turn had not reached the server; nothing was sent.";w.activity.note("info",S),le()},vt=async w=>{if(b||H)return;if(w.kind==="submit"&&w.response){let L=A;if(!L||L.id!==w.response.questionId){U("That question is no longer active. Resume the session to refresh it.");return}Y(Uu(L,w.response)),A=void 0}else w.kind==="submit"&&Ue(ga("user",w.text));let k=d.sessionId,O=new Yn(Date.now(),k),S=new Jn(O,{expanded:()=>mt,frame:()=>dr,linkOrigin:t.endpoint}),_={activity:O,view:S,controller:new AbortController,cancelling:!1};b=_,Ue(S),Ja();let P=new Ne,ee=L=>{O.apply(L,Date.now()),L.type==="question"&&(Co(sn(L)),yt()),le()},Xe={onEvent:ee,onStream:L=>{_.streamId=L},onResponse:()=>{O.phase==="sending"&&O.setPhase("preparing",Date.now())},onActivity:()=>O.touch(Date.now())},de="error";try{if(w.kind==="replay"){P.streamId=w.streamId,_.streamId=w.streamId,O.setPhase("thinking",Date.now());let L=await fo(t,k,P,async ye=>{P.accept(ye)&&ee(ye)},_.controller.signal,Xe.onActivity);de=on(k,L).status}else{let L=w.response?[]:M.splice(0),ye=await Kn(t,k,L,(fr,cc)=>{O.setUploading(fr,cc,Date.now()),le()});_.controller.signal.throwIfAborted(),O.setPhase("sending",Date.now());let bn=Su();_.requestId=bn;let sc={requestId:bn,text:w.response?"":w.text,readOnly:!v,...x?{allowApprove:!0}:{},...u?{context:u}:{},...y?{contextText:y}:{},...w.response?{questionResponse:w.response}:{},...ye.length?{attachments:ye.map(fr=>({fileId:fr.fileId}))}:{}},ac=await Lt(t,k,sc,Xe,_.controller.signal,P);de=on(k,ac).status}}catch(L){if(_.controller.signal.aborted)de="cancelled";else{de="error";let ye=L instanceof h&&L.hint?` ${p(L.hint)}`:"";O.note("error",`${K(L)}${ye}`)}}finally{O.finish(de,Date.now()),b=void 0,ur(),H||(Ae=de==="cancelled"),Wt=O,O.answer.trim()&&(Ve=O.answer),O.done?.servedBy&&(hn=O.done.servedBy);let L=ct(O.done?.tokens);wt.turns+=1,wt.tools+=O.toolCount,wt.inputTokens+=L?.inputTokens??0,wt.outputTokens+=L?.outputTokens??0;for(let ye of O.files)gt.some(bn=>bn.fileId===ye.fileId)||gt.push(ye);le()}if(!H){if(_.streamId&&de!=="cancelled"){let L=await t.getSession(k).catch(()=>{});L&&L.sessionId===d.sessionId&&(d=L)}Xa(de)}},Xa=w=>{if(!(H||b)){if($){let k=$;$=void 0,vt({kind:"submit",text:"",response:k});return}if(m!==void 0){let k=m;if(m=void 0,w==="complete"&&!xe){vt({kind:"submit",text:k});return}a.getText()||a.setText(k),U("Your queued message is back in the input; press Enter to send it.")}yt(),le()}},Qa=w=>{if(b){$=w,U("Answer saved; Oracle continues once the current turn settles.");return}vt({kind:"submit",text:"",response:w})},Po=async w=>{try{let k=await t.getSession(w);yn(k),k.activeStreamId&&vt({kind:"replay",streamId:k.activeStreamId}),U(`Resumed ${k.title||k.sessionId}.`)}catch(k){Y(K(k),Ut)}},Za=async()=>{try{let w=await t.listSessions();if(!w.length){U("No saved conversations to resume.");return}let k=new fa(w.map(P=>({value:P.sessionId,label:P.title,description:Ce(P)})),Math.min(12,w.length),Et),O=await Hn(t.endpoint,n.identity?.companyId),S=O?w.findIndex(P=>P.sessionId===O):-1;S>=0&&k.setSelectedIndex(S);let _=o.showOverlay(k,{width:"85%",maxHeight:"80%",anchor:"center"});k.onSelect=P=>{_.hide(),Po(P.value)},k.onCancel=()=>{_.hide(),yt()},_.focus()}catch(w){Y(K(w),Ut)}},Lo=async w=>{if(w&&(!u||u.kind!=="program"&&u.kind!=="initiative")){U("Use /context program <id> or /context initiative <id> before /new shared.");return}try{let k=await t.createSession(Ft(u,w));yn(k),U(`Started a new conversation \xB7 ${Ce(k)}.`)}catch(k){Y(K(k),Ut)}},No=async(w,k)=>{let O=p(ce(k));if(k.companyId&&k.companyId===n.identity?.companyId){U(`Already in ${O}.`);return}mn=!0;try{U(`Connecting to ${O}...`);let S=await w.connect(k),_=await S.client.createSession(Ft(void 0,!1));t=S.client,n=S.config,c=yo(n.commands),a.setAutocompleteProvider(vo(c,process.cwd()));let P=t.scopes.has("write")&&n.capabilities?.write===!0,ee=t.scopes.has("approve")&&n.capabilities?.approve===!0,Xe=v&&!P||x&&!ee;v&&!P&&(v=!1),(!v||!ee)&&(x=!1);let de=u!==void 0;u=void 0,hn=void 0,Wt=void 0,yn(_),Y(`Switched to ${Ye(n.identity)||O}. This is a new conversation in that workspace.${de?" Context was cleared because its id belongs to the previous workspace.":""}`),Xe&&Y(`Access here is ${un(v,x)}: this workspace's login or your role there does not allow more.`,Iu)}catch(S){let _=S instanceof h&&S.hint?` ${p(S.hint)}`:"";Y(`Could not switch to ${O}: ${K(S)}${_}`,Ut)}finally{mn=!1}},ec=async(w,k)=>{let O=n.identity?.companyId,S=new fa(k.map(ee=>({value:ee.clientId,label:ce(ee),description:[ee.companyId,ee.scopes.join(", "),ee.companyId===O?"current":""].filter(Boolean).join(" \xB7 ")})),Math.min(12,k.length),Et),_=k.findIndex(ee=>ee.companyId===O);_>=0&&S.setSelectedIndex(_);let P=o.showOverlay(S,{width:"85%",maxHeight:"80%",anchor:"center"});S.onSelect=ee=>{P.hide();let Xe=k.find(de=>de.clientId===ee.value);Xe&&No(w,Xe)},S.onCancel=()=>{P.hide(),yt()},P.focus()},tc=async w=>{let k=e.workspaces;if(!k){Y(`This terminal signs in with ANYFORGE_API_TOKEN, which is bound to ${Ye(n.identity)||"one workspace"}. To switch workspaces, unset it and sign in with \`anyforge oracle login --workspace <name>\`.`);return}let O=await k.list();if(w){let S;try{S=at(O,w)}catch(_){let P=_ instanceof h&&_.hint?` ${p(_.hint)}`:"";Y(`${K(_)}${P}`,Ut);return}await No(k,S);return}if(O.length<=1){Y(`${Ye(n.identity)||"This workspace"} is the only saved workspace. To add another, run \`anyforge oracle login --workspace <name>\` in another terminal, then use /workspace here.`);return}await ec(k,O)},nc=w=>({session:d,write:v,approve:x,grantScopes:[...t.scopes],...n.capabilities?{capabilities:n.capabilities}:{},...u?{context:u}:{},...y?{contextText:y}:{},...n.model?{model:n.model}:{},lastServedBy:hn,...Wt?{lastTurn:Wt}:{},totals:{...wt},...n.limits?{limits:n.limits}:{},...n.identity?{identity:n.identity}:{},...w!==void 0?{savedWorkspaces:w}:{},endpoint:t.endpoint,version:e.version,pendingFiles:M.map(k=>da(k))}),rc=async()=>{if(!Ve.trim()){U("There is no answer to copy yet.");return}try{let w=Nu();w?.setText?await w.setText(Ve):r.write(`\x1B]52;c;${Buffer.from(Ve,"utf8").toString("base64")}\x07`),U(`Copied the last answer (${Ve.length} characters).`)}catch(w){U(`Could not copy: ${K(w)}`)}},oc=async(w,k,O)=>{if(w.idleOnly&&mn){U("Wait for the workspace switch to finish.");return}if(w.idleOnly&&b){U(`Wait for Oracle to finish, or press Esc, before /${w.name}.`);return}switch(w.name){case"help":Ue(jt(S=>["",...aa({server:c,canApprove:x}).flatMap(_=>[_u(_.title),...ro(_.rows,S)])]));return;case"status":{let S=await e.workspaces?.list().then(_=>_.length).catch(()=>{});wn("Status",ca(nc(S)));return}case"workspace":await tc(O);return;case"model":wn("Model",bo({...n.model?{model:n.model}:{},lastServedBy:hn}));return;case"usage":wn("Usage",[...Oo(Wt),...ko(wt)]);return;case"files":wn("Generated files",la(d.sessionId,gt));return;case"copy":await rc();return;case"exit":vn();return;case"new":if(O&&O!=="shared"){U("Usage: /new or /new shared");return}await Lo(O==="shared");return;case"clear":await Lo(!1);return;case"resume":O?await Po(O):await Za();return;case"context":{if(!O){Y(`Context: ${qt(u)}${y?" + free-text note":""}. Use /context <program|initiative|codebase> <id> or /context clear.`);return}if(O==="clear")u=void 0;else{let[S,_,...P]=k;if(!["program","initiative","codebase"].includes(S??"")||!_||P.length){U("Usage: /context program <id>, /context initiative <id>, /context codebase <id>, or /context clear");return}u={kind:S,id:_}}Y(`Context for your next message: ${qt(u)}.`);return}case"attach":{if(!O){U("Usage: /attach <file path>");return}let S=await Ru(O).catch(()=>{});if(!S?.isFile()){U(`Not a readable file: ${O}`);return}M.push(O),Y(`Attached ${da(O)} (${z(S.size)}). It uploads with your next message.`);return}}},Fo=w=>{let k=w.trim();if(!k)return;a.addToHistory(k);let O=ra(k,c);if(O?.kind==="local"){oc(O.command,O.args,O.arg).catch(S=>Y(K(S),Ut));return}if(O?.kind==="unknown"){a.setText(k),U(`Unknown command /${O.name}. Type /help to see the commands.`);return}if(mn){a.setText(k),U("Switching workspace; send your message once it has opened.");return}if(b){let S=m!==void 0;m=k,U(S?"Replaced your queued message. It sends when Oracle finishes.":"Queued. It sends when Oracle finishes; Esc interrupts instead."),le();return}vt({kind:"submit",text:k})},ic=o.addInputListener(w=>{if(Qn(w,"ctrl+c")){if(b)return pr(),{consume:!0};if(o.hasOverlay())return;if(o.getFocusedComponent()===a&&a.getText())return a.setText(""),le(),{consume:!0};let k=Date.now();return k-To<=ha?vn():(To=k,o.flash("Press Ctrl+C again to exit",ha)),{consume:!0}}if(Qn(w,"escape")){let k=xe!==void 0&&o.getFocusedComponent()===xe;return b&&!a.isShowingAutocomplete()&&!o.hasOverlay()&&!k?(pr(),{consume:!0}):void 0}if(Qn(w,"ctrl+d"))return o.getFocusedComponent()===a&&!a.getText()?(vn(),{consume:!0}):void 0;if(Qn(w,"ctrl+o"))return mt=!mt,U(mt?"Showing tool activity":"Hiding tool activity"),le(),{consume:!0}}),qo=()=>{let w=!!b;pr().finally(()=>{w&&(Ae=!0),vn()})};process.on("SIGINT",qo),a.onSubmit=Fo,a.onChange=()=>le(),a.setAutocompleteProvider(vo(c,process.cwd())),yn(e.session),o.start(),d.activeStreamId&&vt({kind:"replay",streamId:d.activeStreamId}),e.initialPrompt&&Fo(e.initialPrompt),await Va,ic(),process.off("SIGINT",qo),ur(),b?.controller.abort(),await r.drainInput().catch(()=>{}),Ae&&(process.exitCode=130)}function Uu(e,t){if(t.skipped)return"Skipped the optional Oracle question.";let n=[];for(let r of t.answers){let o=e.questions.find(i=>i.id===r.questionId);for(let i of r.optionIds){let s=o?.options.find(a=>a.id===i);s&&n.push(s.label)}r.customText&&n.push(r.customText)}return`You answered: ${n.map(p).join("; ")||"no options selected"}`}import{createHash as ju,randomBytes as ya,timingSafeEqual as Du}from"node:crypto";import{createServer as Bu}from"node:http";import Wu from"open";var nr="https://crew.anyforge.ai",Gu=6e4,Hu=5*6e4,Zn=new Map;async function pt(e){e.signal?.throwIfAborted();let t=ze(e.endpoint),n=await st(t,fetch,void 0,e.signal),r=["read"];e.write&&r.push("write"),e.approve&&(r.includes("write")||r.push("write"),r.push("approve"));let o=ya(32).toString("base64url"),i=await Ju(o,e.signal);try{let s={client_name:"AnyForge CLI",redirect_uris:[i.redirectUri],grant_types:["authorization_code","refresh_token"],response_types:["code"],token_endpoint_auth_method:"none"},a=await Vu(n.oauth.registration,s,e.signal);if(typeof a.client_id!="string"||!a.client_id)throw new h("OAuth registration did not return a client id.",g.API_ERROR);let c=ya(32).toString("base64url"),l=ju("sha256").update(c).digest("base64url"),d={client_id:a.client_id,redirect_uri:i.redirectUri,response_type:"code",scope:r.join(" "),state:o,code_challenge:l,code_challenge_method:"S256",resource:n.resource},u=new URL(n.oauth.authorization);for(let[H,Ae]of Object.entries(d))u.searchParams.set(H,String(Ae));let y=e.workspace?.trim();y&&u.searchParams.set(js,y),e.onAuthorizationUrl?.(u.toString()),e.signal?.throwIfAborted();try{e.openBrowser?await e.openBrowser(u.toString()):await Wu(u.toString())}catch{process.stderr.write(`Open this URL to authorize AnyForge Oracle:
438
+ ${u.toString()}
439
+ `)}let v=await i.wait();if(!Sa(v.state,o))throw new h("Oracle sign-in callback state did not match; authorization was rejected.",g.API_ERROR);if(v.error)throw new h(`Oracle authorization failed: ${p(v.error)}${v.errorDescription?` (${p(v.errorDescription)})`:""}`,g.API_ERROR);if(!v.code)throw new h("Oracle authorization callback did not include a code.",g.API_ERROR);let x={client_id:a.client_id,grant_type:"authorization_code",code:v.code,redirect_uri:i.redirectUri,code_verifier:c,resource:n.resource},b=await Ia(n.oauth.token,x,e.signal);_a(b);let m=Ra(b.scope);if(!m.includes("read")||m.some(H=>!r.includes(H)))throw await tr(n.oauth.revocation,a.client_id,b.access_token,b.refresh_token).catch(()=>{}),new h("The granted Oracle scopes are invalid for this request.",g.API_ERROR);let A=await st(t,fetch,b.access_token,e.signal).then(H=>H.identity).catch(()=>{});e.signal?.throwIfAborted();let $={endpoint:t,...ba(A),clientId:a.client_id,redirectUri:i.redirectUri,accessToken:b.access_token,refreshToken:b.refresh_token,expiresAt:Date.now()+b.expires_in*1e3,scopes:m},M=await Wn(H=>zs(H,$));return M&&await tr(n.oauth.revocation,M.clientId,M.accessToken,M.refreshToken).catch(()=>{}),$}finally{await i.close()}}function ba(e){return e?{companyId:e.companyId,...e.companyName?{companyName:e.companyName}:{},uid:e.uid}:{}}async function ka(e={}){if(e.workspace&&!e.all){let n=pn(await ae(),e.endpointOverride);n&&await xo(n)}let t=await zn(async()=>{let n=await ae(),r=[];if(e.all)r=[...n.logins];else{let s=pn(n,e.endpointOverride);e.workspace?r=[at(s?Le(n,s):[],e.workspace)]:s&&(r=[Gn(n,s)].filter(a=>!!a))}let o=[],i;for(let s of r){try{await Oa(s)}catch(a){o.push({login:s,error:a})}i=lo(n,s)??i}return await rn(n),{removed:r,failures:o,...i&&!e.all?{nowActive:i}:{}}});if(t.failures.length){let n=t.failures.map(({login:r,error:o})=>{let i=o instanceof Error?p(o.message):"unknown error";return t.failures.length>1||e.all?`${p(ce(r))}: ${i}`:i});throw new h(`Local Oracle credentials were cleared, but server revocation failed for ${t.failures.length===1?"one login":`${t.failures.length} logins`}: ${n.join("; ")}`,g.FAILURE,"Run `anyforge oracle login` again if you still need access. The server will expire these access tokens automatically.")}return{removed:t.removed,...t.nowActive?{nowActive:t.nowActive}:{}}}async function Oa(e){let t=await st(e.endpoint);await tr(t.oauth.revocation,e.clientId,e.accessToken,e.refreshToken)}function pn(e,t){let n=t||process.env.ANYFORGE_ENDPOINT;return n?ze(n):e.endpoint}function Aa(){return new f("ANYFORGE_API_TOKEN is set, so Oracle uses that key and its one workspace; saved Oracle logins are not used.","Unset ANYFORGE_API_TOKEN to choose between your saved workspaces.")}async function rr(e,t){let n=e||process.env.ANYFORGE_ENDPOINT;if(process.env.ANYFORGE_API_TOKEN){if(t)throw Aa();return va(n)}let r=await ae(),o=pn(r,n);if(t)return qe(await zu(o,t));let i=o?Gn(r,o):void 0;if(i)return qe(i);try{return await va(n)}catch(s){throw r.logins.length&&n?new be:s instanceof be?new be:s}}async function zu(e,t){if(!e)return at([],t);let n=await or(e);return at(n,t)}function qe(e){return{endpoint:e.endpoint,accessToken:e.accessToken,scopes:e.scopes.filter(So),source:"oauth",getAccessToken:async t=>Ku(e.endpoint,e.clientId,t),onIdentity:async t=>{if(e.companyId===t.companyId&&(t.companyName===void 0||e.companyName===t.companyName)&&e.uid===t.uid)return;let r=await Wn(i=>Ks(i,e.endpoint,e.clientId,t));Object.assign(e,ba(t));let o=r.duplicate;o&&await Oa(o).catch(()=>{})}}}async function or(e){return await xo(e),Le(await ae(),e)}async function xo(e){let t=Le(await ae(),e).filter(n=>!n.companyId);for(let n of t)await new fe(qe(n)).config().catch(()=>{})}async function Dt(e){let t=pn(await ae(),e);if(!t)return{logins:[]};let n=await or(t),r=Gn(await ae(),t);return{endpoint:t,logins:n,...r?{active:r}:{}}}async function xa(e,t){if(process.env.ANYFORGE_API_TOKEN)throw Aa();let n=pn(await ae(),e);return n&&await xo(n),Wn(r=>{let o=at(n?Le(r,n):[],t);return r.active[o.endpoint]=o.clientId,r.endpoint=o.endpoint,o})}async function va(e){let t=await Re(e),r=(await zt({apiKey:t.apiKey,endpoint:t.endpoint})).scopes.filter(So);return{endpoint:t.endpoint,accessToken:t.apiKey,scopes:r,source:"control"}}async function Ku(e,t,n){let r=ze(e),o=`${r}
440
+ ${t}`,i=Zn.get(o);if(i)return i;let s=zn(()=>Yu(r,t,n));Zn.set(o,s);try{return await s}finally{Zn.get(o)===s&&Zn.delete(o)}}async function Yu(e,t,n){let r=await ae(),o=r.logins.find(l=>l.endpoint===e&&l.clientId===t);if(!o)throw new be;if(o.expiresAt>Date.now()+Gu&&(!n||o.accessToken!==n))return o.accessToken;let i=async()=>{lo(r,o),await rn(r)},s=await st(o.endpoint),a;try{let l={client_id:o.clientId,grant_type:"refresh_token",refresh_token:o.refreshToken,resource:s.resource};a=await Ia(s.oauth.token,l)}catch(l){throw l instanceof er&&["invalid_grant","invalid_client","invalid_target"].includes(l.code)?(await i(),new be):l}_a(a);let c;try{c=Ra(a.scope)}catch(l){throw await i(),l}if(c.some(l=>!o.scopes.includes(l)))throw await tr(s.oauth.revocation,o.clientId,a.access_token,a.refresh_token),await i(),new h("Oracle refresh attempted to widen the saved grant; credentials were cleared.",g.API_ERROR);return o.accessToken=a.access_token,o.refreshToken=a.refresh_token,o.expiresAt=Date.now()+a.expires_in*1e3,o.scopes=c,await rn(r),o.accessToken}var er=class extends h{constructor(n,r,o){super(`OAuth ${n}: ${r}`,o===401?g.NO_CREDENTIALS:g.API_ERROR);this.code=n;this.name="OAuthResponseError"}code};async function Ju(e,t){t?.throwIfAborted();let n,r,o=!1,i=new Promise((d,u)=>{n=d,r=u});i.catch(()=>{});let s=Bu((d,u)=>{if((d.url?.length??0)>8192){u.writeHead(414,{"Content-Type":"text/plain; charset=utf-8"}).end("Authorization response is too large.");return}let y=d.url??"/";if(!y.startsWith("/callback")||y.startsWith("//")){u.writeHead(400,{"Content-Type":"text/plain; charset=utf-8"}).end("Authorization response path is invalid.");return}let v=new URL(y,"http://127.0.0.1");if(d.method!=="GET"||v.pathname!=="/callback"){u.writeHead(404,{"Content-Type":"text/plain; charset=utf-8"}).end("Not found");return}let x=v.searchParams.get("code")?.slice(0,2048)??"",b=v.searchParams.get("state")?.slice(0,256)??"",m=v.searchParams.get("error")?.slice(0,256)??void 0,A=v.searchParams.get("error_description")?.slice(0,1024)??void 0;if(!b){u.writeHead(400,{"Content-Type":"text/plain; charset=utf-8"}).end("Authorization response is missing state.");return}if(!Sa(b,e)){u.writeHead(400,{"Content-Type":"text/plain; charset=utf-8"}).end("Authorization response state did not match.");return}if(o){u.writeHead(409,{"Content-Type":"text/plain; charset=utf-8"}).end("Authorization response already received.");return}o=!0,u.writeHead(200,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store","Referrer-Policy":"no-referrer","Content-Security-Policy":"default-src 'none'; style-src 'unsafe-inline'"}).end('<!doctype html><meta charset="utf-8"><title>AnyForge Oracle</title><p>Sign-in response received. You may close this tab.</p>'),n({code:x,state:b,...m?{error:m}:{},...A?{errorDescription:A}:{}})});await new Promise((d,u)=>{s.once("error",u),s.listen(0,"127.0.0.1",()=>{s.removeListener("error",u),d()})});let a=s.address();if(!a||typeof a=="string")throw new h("Could not bind an Oracle sign-in callback port.",g.FAILURE);let c=setTimeout(()=>{o||(o=!0,r(new h("Oracle sign-in timed out before the browser returned.",g.FAILURE)))},Hu);c.unref();let l=()=>{o||(o=!0,r(t?.reason??new DOMException("Oracle sign-in cancelled.","AbortError"))),clearTimeout(c),s.closeAllConnections(),s.close()};return t?.addEventListener("abort",l,{once:!0}),t?.aborted&&l(),{redirectUri:`http://127.0.0.1:${a.port}/callback`,wait:()=>i,close:async()=>{t?.removeEventListener("abort",l),clearTimeout(c),s.listening&&(s.closeAllConnections(),await new Promise(d=>s.close(()=>d())))}}}function Sa(e,t){let n=Buffer.from(e),r=Buffer.from(t);return n.length===r.length&&Du(n,r)}function Ra(e){let t=e.split(/[\s+]+/).filter(Boolean);if(!t.every(So))throw new h("OAuth server returned an unknown Oracle scope.",g.API_ERROR);let n=[...new Set(t)];if(!n.includes("read"))throw new h("Oracle OAuth grant is missing the required read scope.",g.API_ERROR);return n}function So(e){return e==="read"||e==="write"||e==="approve"}function _a(e){if(!e||typeof e.access_token!="string"||!e.access_token||typeof e.refresh_token!="string"||!e.refresh_token||typeof e.token_type!="string"||e.token_type.toLowerCase()!=="bearer"||typeof e.expires_in!="number"||!Number.isFinite(e.expires_in)||e.expires_in<=0||typeof e.scope!="string")throw new h("OAuth token response did not match the supported format.",g.API_ERROR)}async function Vu(e,t,n){return Ea(e,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(t),signal:n})}async function Ia(e,t,n){return Ea(e,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},body:new URLSearchParams(Object.entries(t).map(([r,o])=>[r,String(o)])).toString(),signal:n})}async function Ea(e,t){let n,r=AbortSignal.timeout(2e4);try{n=await fetch(e,{...t,redirect:"error",signal:t.signal?AbortSignal.any([t.signal,r]):r})}catch(i){throw t.signal?.throwIfAborted(),new h(`OAuth request failed: ${p(i instanceof Error?i.message:String(i))}`,g.FAILURE)}let o=null;try{o=await n.json()}catch{}if(t.signal?.throwIfAborted(),!n.ok){let i=o&&typeof o=="object"?o:{},s=typeof i.error=="string"?i.error:`http_${n.status}`,a=typeof i.error_description=="string"?p(i.error_description):s;throw new er(s,a,n.status)}if(!o||typeof o!="object"||Array.isArray(o))throw new h(`OAuth endpoint returned invalid JSON (${n.status}).`,g.API_ERROR);return o}async function tr(e,t,n,r){let o=async i=>{let s=await fetch(e,{method:"POST",redirect:"error",signal:AbortSignal.timeout(2e4),headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},body:new URLSearchParams({client_id:t,token:i}).toString()});if(!s.ok){let a=`HTTP ${s.status}`;try{let c=await s.json();typeof c.error_description=="string"&&(a=p(c.error_description))}catch{}throw new h(`OAuth token revocation failed: ${a}`,g.API_ERROR)}};await o(r),n!==r&&await o(n)}var dp=1048576,up=25*1048576;async function Fa(e){let[t,...n]=e.positional,r=["exec","sessions","resume","login","logout","download","workspaces","use"].includes(t??""),o=ft(e.flags,"print");if(o!==void 0&&e.positional.length)throw new f("-p/--print takes the prompt as its value and cannot be combined with a subcommand or positional prompt.");let i=ft(e.flags,"workspace")?.trim()||void 0,s=r?void 0:e.positional.join(" ");if(G(e.flags,"read-only")&&(G(e.flags,"write")||G(e.flags,"approve")))throw new f("--read-only cannot be combined with --write or --approve.");if(!r&&o===void 0&&(!process.stdin.isTTY||!process.stdout.isTTY))throw new f("Interactive Oracle needs a terminal.",'Use `anyforge oracle exec "your question"` for scripts and pipes.');if(t==="login"){if(n.length)throw new f("oracle login does not accept positional arguments.");let b=await pt({endpoint:e.endpointOverride??e.endpoint,write:G(e.flags,"write"),approve:G(e.flags,"approve"),...i?{workspace:i}:{}}),A=[G(e.flags,"write")?"write":void 0,G(e.flags,"approve")?"approve":void 0].filter($=>!!$).filter($=>!b.scopes.includes($));process.stdout.write(`${Ta(b)}
441
+ `),A.length&&process.stderr.write(`This account was granted read only; the server withheld: ${A.join(", ")}.
442
+ `),await fp(b,i);return}if(t==="logout"){if(n.length)throw new f("oracle logout does not accept positional arguments.");let b=G(e.flags,"all");if(b&&i)throw new f("Choose either --workspace or --all.");let m=await ka({...e.endpointOverride?{endpointOverride:e.endpointOverride}:{},...i?{workspace:i}:{},all:b});if(!m.removed.length){process.stdout.write(`No saved Oracle login.
443
+ `);return}let A=m.removed.map($=>p(ce($))).join(", ");process.stdout.write(`Logged out of ${A}; ${m.removed.length===1?"its access was":"their access was"} revoked.
444
+ `),m.nowActive&&process.stdout.write(`Active workspace is now ${p(ce(m.nowActive))}.
445
+ `);return}if(t==="workspaces"){if(n.length)throw new f("oracle workspaces does not accept positional arguments.");await hp(e.endpointOverride,e.flags.json===!0);return}if(t==="use"){let b=n.join(" ").trim();if(!b)throw new f("oracle use needs a workspace name or id.","Run `anyforge oracle workspaces` to list your saved workspaces.");let m=await xa(e.endpointOverride,b);process.stdout.write(`Now using ${p(ce(m))} (${m.scopes.join(", ")}) at ${m.endpoint}.
446
+ `);return}let a;try{a=await Pa(e.endpointOverride,i)}catch(b){if(!r&&o===void 0&&b instanceof h&&b.exitCode===g.NO_CREDENTIALS&&process.stdin.isTTY&&process.stdout.isTTY){let m=(await import("prompts")).default;if((await m({type:"confirm",name:"login",message:"Sign in to AnyForge Oracle now?",initial:!0})).login!==!0)throw b;let $=await pt({endpoint:e.endpointOverride??e.endpoint,...i?{workspace:i}:{}});process.stderr.write(`${Ta($)}
447
+ `),a=await Pa(e.endpointOverride)}else throw b}if(t==="sessions"){if(n.length)throw new f("oracle sessions does not accept positional arguments.");let b=e.flags.json===!0;b||await Ca(await a.config().catch(()=>{}));let m=await a.listSessions();wp(m,b);return}if(t==="download"){await kp(a,n,e.flags);return}if(t==="exec"||o!==void 0){if(G(e.flags,"write")||G(e.flags,"approve"))throw new f("oracle exec is read-only. Use interactive `anyforge oracle --write` for authorized actions.");if(e.flags.json===!0&&G(e.flags,"stream-json"))throw new f("Choose either --json or --stream-json.");let b=await gp(o??n.join(" ")),m=await a.config();Ro(a,m),e.flags.json!==!0&&!G(e.flags,"stream-json")&&await Ca(m),La(ft(e.flags,"context"));let A=so(e.flags),$=await a.createSession(Ft(A,!1));await an($.sessionId,a.endpoint,m.identity?.companyId);let M=await Kn(a,$.sessionId,$a(e.flags),(Ae,mt)=>ie(process.stderr,`Uploading explicit attachment ${Ae} (${z(mt)}).
448
+ `)),H={requestId:Zu(),text:b,readOnly:!0,...A?{context:A}:{},...ft(e.flags,"context")?{contextText:ft(e.flags,"context")}:{},...M.length?{attachments:M.map(Ae=>({fileId:Ae.fileId}))}:{}};await vp(a,$.sessionId,H,{json:e.flags.json===!0,streamJson:G(e.flags,"stream-json"),config:m});return}if(t==="resume"&&n.length>1)throw new f("oracle resume accepts at most one session id.");if(t==="resume"&&(!process.stdin.isTTY||!process.stdout.isTTY))throw new f("oracle resume is interactive and needs a terminal.","Use `anyforge oracle sessions` to list sessions, or run `anyforge oracle exec` for a new scripted turn.");let c=await a.config();Ro(a,c);let l=so(e.flags),d=ft(e.flags,"context");La(d);let u=G(e.flags,"shared"),y=G(e.flags,"write")||G(e.flags,"approve");if(y&&(!a.scopes.has("write")||c.capabilities?.write!==!0))throw new h("This Oracle grant or current workspace role does not allow write actions.",g.API_ERROR);if(G(e.flags,"approve")&&(!a.scopes.has("approve")||c.capabilities?.approve!==!0))throw new h("This Oracle grant or current workspace role does not allow approval actions.",g.API_ERROR);if(u&&(!l||l.kind!=="program"&&l.kind!=="initiative"))throw new f("--shared requires --program or --initiative.");let v;if(t==="resume"){let m=(n[0]||void 0)??await yp(await a.listSessions(),await Hn(a.endpoint,c.identity?.companyId));v=await a.getSession(m)}else v=await a.createSession(Ft(l,u));await an(v.sessionId,a.endpoint,c.identity?.companyId);let x=await Promise.resolve().then(()=>(sr(),ir));await wa({version:x.default.version,client:a,config:c,session:v,initialPrompt:s,initialContext:l,contextText:d,shared:u,canWrite:y,canApprove:G(e.flags,"approve"),initialFiles:$a(e.flags),...a.source==="oauth"?{workspaces:pp(a.endpoint)}:{}})}function pp(e){return{list:()=>or(e),connect:async t=>{let n=new fe(qe(t)),r=await n.config();return Ro(n,r),{client:n,config:r}}}}function Ta(e){let t=e.scopes.join(", ");return e.companyId?`Connected to ${p(ce(e))} (${t}) at ${e.endpoint}.`:`Connected to ${e.endpoint} (${t}). The server did not say which workspace; \`anyforge oracle workspaces\` shows it once it does.`}async function fp(e,t){let n=p(ce(e));t&&e.companyId&&!mp(e,t)&&process.stderr.write(`You asked for "${p(t)}", but ${n} was chosen on the consent page, so this login is for ${n}.
449
+ `);let r=Le(await ae(),e.endpoint).length;r>1&&process.stderr.write(`${n} is now your active workspace (${r} saved). List them with \`anyforge oracle workspaces\`; switch with \`anyforge oracle use <name>\`.
450
+ `)}function mp(e,t){return e.companyId===t||e.companyName?.toLowerCase()===t.toLowerCase()}async function Ca(e){let t=Ye(e?.identity);t&&await ie(process.stderr,`${Me(`Workspace: ${t}`)}
451
+ `)}async function hp(e,t){let n=await Dt(e),r=n.logins.map(o=>({name:o.companyName??null,companyId:o.companyId??null,endpoint:o.endpoint,scopes:o.scopes,active:o.clientId===n.active?.clientId}));if(t)process.stdout.write(`${JSON.stringify(r,null,2)}
452
+ `);else if(!r.length)process.stdout.write("No saved Oracle logins. Run `anyforge oracle login` to add one.\n");else{process.stdout.write(`${Me(`Oracle workspaces saved for ${n.endpoint??"this endpoint"}:`)}
453
+ `);for(let o of n.logins){let i=o.clientId===n.active?.clientId,s=p(ce(o)),a=o.companyId&&o.companyName?p(o.companyId):"",c=[i?Io(`* ${s}`):` ${s}`,a?Me(a):"",Me(o.scopes.join(", ")),i?Na("active"):""];process.stdout.write(`${c.filter(Boolean).join(" ")}
441
454
  `)}}process.env.ANYFORGE_API_TOKEN&&process.stderr.write(`ANYFORGE_API_TOKEN is set, so Oracle commands use that key and its workspace instead of these logins.
442
- `)}function W(t,e){return t[e]===!0}function rt(t,e){let n=t[e];return typeof n=="string"?n:void 0}function Ps(t){let e=t.file;return typeof e=="string"?[e]:Array.isArray(e)?e:[]}async function Ls(t,e){try{return new ue(await Dn(t,e))}catch(n){throw n instanceof f&&n.exitCode===g.NO_CREDENTIALS?new f("Not authenticated for the hosted Oracle.",g.NO_CREDENTIALS,"Run `anyforge oracle login`, or set ANYFORGE_API_TOKEN for an existing Control grant."):n}}function Jr(t,e){if(!t.scopes.has("read")||e.capabilities?.read===!1)throw new f("This credential does not have Oracle read access.",g.API_ERROR)}function Ns(t){if(t&&Buffer.byteLength(t,"utf8")>Nr)throw new h(`--context must be ${Nr/1024} KiB or less.`)}async function eu(t){let e=t.trim();if(process.stdin.isTTY){if(e)return e;throw new h("oracle exec requires a prompt when stdin is a terminal.",'Example: anyforge oracle exec "summarize this program"')}let n=[],r=0;for await(let i of process.stdin){let s=Buffer.isBuffer(i)?i:Buffer.from(i);if(r+=s.byteLength,r>Vd)throw new h("Piped Oracle input exceeds the 1 MiB limit.");n.push(s)}let o=Buffer.concat(n).toString("utf8").trim();if(!e&&!o)throw new h("oracle exec needs a prompt in argv or piped stdin.");return[e,o].filter(Boolean).join(`
443
-
444
- `)}function tu(t,e){if(e){process.stdout.write(`${JSON.stringify(t,null,2)}
445
- `);return}if(!t.length){process.stdout.write(`No Oracle sessions are available.
446
- `);return}for(let n of t){let r=xe(n);process.stdout.write(`${Xr(n.sessionId)} ${p(n.title)} ${$e(r)}
447
- `)}}async function nu(t,e){if(!t.length)throw new f("No accessible Oracle sessions are available.",g.API_ERROR);return new Promise((n,r)=>{let o=new Gd,i=new Kd(o),s=new zd(Xr("Resume an Oracle session")),a=new Hd(t.map(l=>({value:l.sessionId,label:l.title,description:xe(l)})),12,vt),c=e?t.findIndex(l=>l.sessionId===e):-1;c>=0&&a.setSelectedIndex(c),i.addChild(s),i.addChild(a),a.onSelect=l=>{u(),n(l.value)},a.onCancel=()=>{u(),r(new f("Session selection cancelled.",g.FAILURE))};let d=()=>{u(),r(new f("Session selection interrupted.",g.INTERRUPTED))},u=()=>{process.off("SIGINT",d),i.stop()};process.once("SIGINT",d),i.start()})}async function ne(t,e){t.write(e)||await new Promise(n=>t.once("drain",n))}async function ru(t,e,n,r){let o=new Ee,i=!1,s,a=new AbortController,c=()=>s??=d(),d=async()=>{if(i=!0,process.exitCode=130,a.abort(new Error("Turn cancelled by Ctrl+C.")),o.streamId)try{let _=await t.cancelTurn(e,o.streamId);await ne(process.stderr,`${xt(_)}
448
- `)}catch(_){await ne(process.stderr,`Could not confirm server cancellation: ${K(_)}
449
- `)}else try{let _=await t.cancelRequest(e,n.requestId);await ne(process.stderr,`${xt(_)}
450
- `)}catch(_){await ne(process.stderr,`Could not confirm server cancellation by request id: ${K(_)}
451
- `)}},u=()=>{c().catch(()=>{})};process.on("SIGINT",u);let l=async _=>ne(process.stdout,`${JSON.stringify(_e(_))}
452
- `),k=new Qr(e),O=-1,x=async _=>{if(r.streamJson){if(_.idx<=O)return;_.type!=="finalizing"&&(O=_.idx),await l({..._,sessionId:e})}else await k.emit(_)},b;try{b=await _t(t,e,n,{onEvent:x},a.signal,o)}catch(_){if(!i)throw process.off("SIGINT",u),_;b=o.outcome()}finally{process.off("SIGINT",u),s&&await s}let m=Vt(e,b);return r.streamJson?await l({protocolVersion:1,type:"result",result:m}):r.json?await ne(process.stdout,`${JSON.stringify(m,null,2)}
453
- `):(m.answer&&await ne(process.stdout,`${Fo(m.answer,t.endpoint)}
454
- `),m.status==="awaiting_input"&&await ne(process.stderr,`Oracle needs a response; resume this session interactively to answer the question.
455
- `),m.status==="interrupted"&&await ne(process.stderr,`Oracle turn ended without a complete final answer.
456
- `),m.status==="error"&&m.error&&await ne(process.stderr,`Oracle error: ${p(m.error)}
457
- `),(m.servedBy||m.usage||m.rounds!==void 0)&&await ne(process.stderr,ou(m.servedBy,m.usage,m.rounds))),i?process.exitCode=130:m.status==="error"?process.exitCode=g.API_ERROR:m.status==="interrupted"&&(process.exitCode=g.FAILURE),m}var Qr=class{constructor(e){this.sessionId=e}sessionId;labels=new Map;announced=new Set;async emit(e){let n=this.describe(e);n&&await ne(process.stderr,`${n}
458
- `)}describe(e){switch(e.type){case"tool_call":{if(ht(e.name))return"";let n=T(typeof e.label=="string"?e.label:mt(T(e.name,240)),120),r=typeof e.id=="string"?e.id:void 0;if(r){if(this.labels.set(r,n),this.announced.has(r))return"";this.announced.add(r)}return`${$e(typeof e.spawnId=="string"?"Oracle \xB7 agent \xB7":"Oracle \xB7")} ${qs(n)}`}case"tool_result":{if(e.ok!==!1)return"";let n=typeof e.toolUseId=="string"?this.labels.get(e.toolUseId):void 0;return Hn(`Oracle \xB7 ${n??"a tool"} failed`)}case"warning":return Hn(`Warning: ${T(e.message,500)}`);case"navigator_file_error":return Hn(`Could not generate ${T(e.filename,200)}: ${T(e.error,500)}`);case"navigator_file_created":{let n=je(e.file);return $e(`Generated ${n.filename} (${z(n.sizeBytes)}). Download: anyforge oracle download ${this.sessionId} ${n.fileId} --output .`)}case"provider_fallback":{let n=T(e.from,120),r=T(e.to,120);return $e(`Oracle switched model${n?` from ${n}`:""}${r?` to ${r}`:""}.`)}case"attachment_notice":return Array.isArray(e.notices)?e.notices.map(n=>{let r=n&&typeof n=="object"?n:{};return`${Hn("Attachment")} ${T(r.name,160)}: ${T(r.message,500)}`}).join(`
459
- `):"";default:return""}}};function ou(t,e,n){let r=[];if(t&&typeof t=="object"){let o=t,i=Xt(o);i&&r.push(`model ${i}`),typeof o.routing=="string"&&r.push(T(o.routing.replaceAll("_"," "),40)),typeof o.configuredModel=="string"&&o.configuredModel!==o.model&&r.push(`configured ${T(o.configuredModel,100)}`),typeof o.provider=="string"&&r.push(T(o.provider,100))}return e?.inputTokens!==void 0||e?.outputTokens!==void 0?r.push(`${e.inputTokens??0} in${e.cachedInputTokens?` (${e.cachedInputTokens} cached)`:""} / ${e.outputTokens??0} out tokens`):e?.totalTokens!==void 0&&r.push(`${e.totalTokens} tokens`),n!==void 0&&r.push(`${n} rounds`),r.length?`${$e(r.join(" \xB7 "))}
460
- `:""}async function iu(t,e,n){if(e.length!==2)throw new h("oracle download needs exactly a session id and file id.");let[r,o]=e;if(!r||!o)throw new h("oracle download needs a session id and file id.");let i=rt(n,"output");if(!i)throw new h("oracle download requires --output <existing-directory>.");let s=Wd(i),a=await Md(s);if(!(await jd(a)).isDirectory())throw new h("--output must point to an existing directory.");let d=await t.download(r,o),u=au(su(d.headers.get("content-disposition"))),l=Bd(a,u),k=await cu(d,Yd),O;try{O=await Ud(l,"wx",384),await O.writeFile(k)}catch(x){throw x.code==="EEXIST"?new f(`Refusing to overwrite ${l}.`,g.FAILURE):x}finally{await O?.close()}process.stdout.write(`Downloaded ${p(u)} to ${p(l)} (${z(k.byteLength)}).
461
- `)}function su(t){if(!t)return"oracle-file.bin";let e=/filename\*=UTF-8''([^;]+)/i.exec(t)?.[1];if(e)try{return decodeURIComponent(e.replace(/^"|"$/g,""))}catch{return"oracle-file.bin"}let n=/filename="?([^";]+)"?/i.exec(t)?.[1];return n?n.trim():"oracle-file.bin"}function au(t){let e=Dd(t.replace(/\\/g,"/")).replace(/[\u0000-\u001f\u007f/:]/g,"_").replace(/^\.+/,"").trim().slice(0,180);return!e||e==="."||e===".."?"oracle-file.bin":e}async function cu(t,e){if(!t.body)return Buffer.alloc(0);let n=t.body.getReader(),r=[],o=0;try{for(;;){let{value:i,done:s}=await n.read();if(s)break;let a=Buffer.from(i);if(o+=a.byteLength,o>e)throw new f(`Oracle download exceeds the ${z(e)} limit.`,g.API_ERROR);r.push(a)}}catch(i){throw await n.cancel("Oracle download exceeded its byte limit.").catch(()=>{}),i}finally{n.releaseLock()}return Buffer.concat(r)}import{Readable as wu,Writable as yu}from"node:stream";import{ndJsonStream as vu}from"@agentclientprotocol/sdk";import{randomUUID as lu}from"node:crypto";import{isAbsolute as du}from"node:path";import{setTimeout as uu}from"node:timers/promises";import{agent as pu,RequestError as q}from"@agentclientprotocol/sdk";import{RequestError as zn}from"@agentclientprotocol/sdk";var Us=65536;function Ms(t){if(t.length>100)throw zn.invalidParams(void 0,"At most 100 prompt blocks are supported.");let n=t.map(r=>{if(r.type==="text")return r.text;if(r.type==="resource_link")return`[Untrusted resource reference; content has not been read]
462
- ${JSON.stringify({name:r.name,uri:r.uri,description:r.description})}`;throw zn.invalidParams(void 0,"Hosted Oracle accepts text and resource references only. Paste file contents as text; local files, embedded resources, images and audio are unsupported.")}).join(`
463
- `);if(!n.trim()||Buffer.byteLength(n,"utf8")>Us)throw zn.invalidParams(void 0,`Prompt must contain text and fit within ${Us} UTF-8 bytes.`);return n}function re(){throw zn.invalidParams(void 0,"Answer must match the pending question and its option ids. Use the displayed /answer JSON or /skip command; ordinary prose cannot confirm actions.")}function Zr(t,e){if(!e||typeof e!="object"||Array.isArray(e))return re();let n=e;if(n.questionId!==t.id||Object.keys(n).some(i=>!["questionId","answers","skipped"].includes(i)))return re();if(n.skipped===!0)return!t.optional||!Array.isArray(n.answers)||n.answers.length?re():{questionId:t.id,answers:[],skipped:!0};if(n.skipped!==void 0||!Array.isArray(n.answers)||n.answers.length!==t.questions.length)return re();let r=new Set,o=n.answers.map(i=>{if(!i||typeof i!="object"||Array.isArray(i))return re();let s=i;if(Object.keys(s).some(d=>!["questionId","optionIds","customText"].includes(d)))return re();let a=t.questions.find(d=>d.id===s.questionId);if(!a||r.has(a.id)||!Array.isArray(s.optionIds))return re();r.add(a.id);let c=s.optionIds;return c.some(d=>typeof d!="string"||!a.options.some(u=>u.id===d))||new Set(c).size!==c.length||!a.multiSelect&&c.length>1||s.customText!==void 0&&(typeof s.customText!="string"||!s.customText.trim()||Buffer.byteLength(s.customText)>8192)||!c.length&&s.customText===void 0?re():{questionId:a.id,optionIds:c,...typeof s.customText=="string"?{customText:s.customText}:{}}});return{questionId:t.id,answers:o}}function js(t,e){if(e===`/skip ${t.id}`)return Zr(t,{questionId:t.id,answers:[],skipped:!0});if(!e.startsWith("/answer "))return re();let n;try{n=JSON.parse(e.slice(8))}catch{return re()}return Zr(t,n)}function $t(t){let e=t.questions.map(r=>`${r.header}: ${r.question}
455
+ `)}function G(e,t){return e[t]===!0}function ft(e,t){let n=e[t];return typeof n=="string"?n:void 0}function $a(e){let t=e.file;return typeof t=="string"?[t]:Array.isArray(t)?t:[]}async function Pa(e,t){try{return new fe(await rr(e,t))}catch(n){throw n instanceof h&&n.exitCode===g.NO_CREDENTIALS?new h("Not authenticated for the hosted Oracle.",g.NO_CREDENTIALS,"Run `anyforge oracle login`, or set ANYFORGE_API_TOKEN for an existing Control grant."):n}}function Ro(e,t){if(!e.scopes.has("read")||t.capabilities?.read===!1)throw new h("This credential does not have Oracle read access.",g.API_ERROR)}function La(e){if(e&&Buffer.byteLength(e,"utf8")>uo)throw new f(`--context must be ${uo/1024} KiB or less.`)}async function gp(e){let t=e.trim();if(process.stdin.isTTY){if(t)return t;throw new f("oracle exec requires a prompt when stdin is a terminal.",'Example: anyforge oracle exec "summarize this program"')}let n=[],r=0;for await(let i of process.stdin){let s=Buffer.isBuffer(i)?i:Buffer.from(i);if(r+=s.byteLength,r>dp)throw new f("Piped Oracle input exceeds the 1 MiB limit.");n.push(s)}let o=Buffer.concat(n).toString("utf8").trim();if(!t&&!o)throw new f("oracle exec needs a prompt in argv or piped stdin.");return[t,o].filter(Boolean).join(`
456
+
457
+ `)}function wp(e,t){if(t){process.stdout.write(`${JSON.stringify(e,null,2)}
458
+ `);return}if(!e.length){process.stdout.write(`No Oracle sessions are available.
459
+ `);return}for(let n of e){let r=Ce(n);process.stdout.write(`${Io(n.sessionId)} ${p(n.title)} ${Me(r)}
460
+ `)}}async function yp(e,t){if(!e.length)throw new h("No accessible Oracle sessions are available.",g.API_ERROR);return new Promise((n,r)=>{let o=new sp,i=new lp(o),s=new cp(Io("Resume an Oracle session")),a=new ap(e.map(u=>({value:u.sessionId,label:u.title,description:Ce(u)})),12,Et),c=t?e.findIndex(u=>u.sessionId===t):-1;c>=0&&a.setSelectedIndex(c),i.addChild(s),i.addChild(a),a.onSelect=u=>{d(),n(u.value)},a.onCancel=()=>{d(),r(new h("Session selection cancelled.",g.FAILURE))};let l=()=>{d(),r(new h("Session selection interrupted.",g.INTERRUPTED))},d=()=>{process.off("SIGINT",l),i.stop()};process.once("SIGINT",l),i.start()})}async function ie(e,t){e.write(t)||await new Promise(n=>e.once("drain",n))}async function vp(e,t,n,r){let o=new Ne,i=!1,s,a=new AbortController,c=()=>s??=l(),l=async()=>{if(i=!0,process.exitCode=130,a.abort(new Error("Turn cancelled by Ctrl+C.")),o.streamId)try{let A=await e.cancelTurn(t,o.streamId);await ie(process.stderr,`${Nt(A)}
461
+ `)}catch(A){await ie(process.stderr,`Could not confirm server cancellation: ${K(A)}
462
+ `)}else try{let A=await e.cancelRequest(t,n.requestId);await ie(process.stderr,`${Nt(A)}
463
+ `)}catch(A){await ie(process.stderr,`Could not confirm server cancellation by request id: ${K(A)}
464
+ `)}},d=()=>{c().catch(()=>{})};process.on("SIGINT",d);let u=async A=>ie(process.stdout,`${JSON.stringify(Ie(A))}
465
+ `),y=new _o(t),v=-1,x=async A=>{if(r.streamJson){if(A.idx<=v)return;A.type!=="finalizing"&&(v=A.idx),await u({...A,sessionId:t})}else await y.emit(A)},b;try{b=await Lt(e,t,n,{onEvent:x},a.signal,o)}catch(A){if(!i)throw process.off("SIGINT",d),A;b=o.outcome()}finally{process.off("SIGINT",d),s&&await s}let m=on(t,b);return r.streamJson?await u({protocolVersion:1,type:"result",result:m}):r.json?await ie(process.stdout,`${JSON.stringify(m,null,2)}
466
+ `):(m.answer&&await ie(process.stdout,`${pi(m.answer,e.endpoint)}
467
+ `),m.status==="awaiting_input"&&await ie(process.stderr,`Oracle needs a response; resume this session interactively to answer the question.
468
+ `),m.status==="interrupted"&&await ie(process.stderr,`Oracle turn ended without a complete final answer.
469
+ `),m.status==="error"&&m.error&&await ie(process.stderr,`Oracle error: ${p(m.error)}
470
+ `),(m.servedBy||m.usage||m.rounds!==void 0)&&await ie(process.stderr,bp(m.servedBy,m.usage,m.rounds))),i?process.exitCode=130:m.status==="error"?process.exitCode=g.API_ERROR:m.status==="interrupted"&&(process.exitCode=g.FAILURE),m}var _o=class{constructor(t){this.sessionId=t}sessionId;labels=new Map;announced=new Set;async emit(t){let n=this.describe(t);n&&await ie(process.stderr,`${n}
471
+ `)}describe(t){switch(t.type){case"tool_call":{if(St(t.name))return"";let n=C(typeof t.label=="string"?t.label:xt(C(t.name,240)),120),r=typeof t.id=="string"?t.id:void 0;if(r){if(this.labels.set(r,n),this.announced.has(r))return"";this.announced.add(r)}return`${Me(typeof t.spawnId=="string"?"Oracle \xB7 agent \xB7":"Oracle \xB7")} ${Na(n)}`}case"tool_result":{if(t.ok!==!1)return"";let n=typeof t.toolUseId=="string"?this.labels.get(t.toolUseId):void 0;return ar(`Oracle \xB7 ${n??"a tool"} failed`)}case"warning":return ar(`Warning: ${C(t.message,500)}`);case"navigator_file_error":return ar(`Could not generate ${C(t.filename,200)}: ${C(t.error,500)}`);case"navigator_file_created":{let n=Ke(t.file);return Me(`Generated ${n.filename} (${z(n.sizeBytes)}). Download: anyforge oracle download ${this.sessionId} ${n.fileId} --output .`)}case"provider_fallback":{let n=C(t.from,120),r=C(t.to,120);return Me(`Oracle switched model${n?` from ${n}`:""}${r?` to ${r}`:""}.`)}case"attachment_notice":return Array.isArray(t.notices)?t.notices.map(n=>{let r=n&&typeof n=="object"?n:{};return`${ar("Attachment")} ${C(r.name,160)}: ${C(r.message,500)}`}).join(`
472
+ `):"";default:return""}}};function bp(e,t,n){let r=[];if(e&&typeof e=="object"){let o=e,i=ln(o);i&&r.push(`model ${i}`),typeof o.routing=="string"&&r.push(C(o.routing.replaceAll("_"," "),40)),typeof o.configuredModel=="string"&&o.configuredModel!==o.model&&r.push(`configured ${C(o.configuredModel,100)}`),typeof o.provider=="string"&&r.push(C(o.provider,100))}return t?.inputTokens!==void 0||t?.outputTokens!==void 0?r.push(`${t.inputTokens??0} in${t.cachedInputTokens?` (${t.cachedInputTokens} cached)`:""} / ${t.outputTokens??0} out tokens`):t?.totalTokens!==void 0&&r.push(`${t.totalTokens} tokens`),n!==void 0&&r.push(`${n} rounds`),r.length?`${Me(r.join(" \xB7 "))}
473
+ `:""}async function kp(e,t,n){if(t.length!==2)throw new f("oracle download needs exactly a session id and file id.");let[r,o]=t;if(!r||!o)throw new f("oracle download needs a session id and file id.");let i=ft(n,"output");if(!i)throw new f("oracle download requires --output <existing-directory>.");let s=ip(i),a=await tp(s);if(!(await np(a)).isDirectory())throw new f("--output must point to an existing directory.");let l=await e.download(r,o),d=Ap(Op(l.headers.get("content-disposition"))),u=op(a,d),y=await xp(l,up),v;try{v=await ep(u,"wx",384),await v.writeFile(y)}catch(x){throw x.code==="EEXIST"?new h(`Refusing to overwrite ${u}.`,g.FAILURE):x}finally{await v?.close()}process.stdout.write(`Downloaded ${p(d)} to ${p(u)} (${z(y.byteLength)}).
474
+ `)}function Op(e){if(!e)return"oracle-file.bin";let t=/filename\*=UTF-8''([^;]+)/i.exec(e)?.[1];if(t)try{return decodeURIComponent(t.replace(/^"|"$/g,""))}catch{return"oracle-file.bin"}let n=/filename="?([^";]+)"?/i.exec(e)?.[1];return n?n.trim():"oracle-file.bin"}function Ap(e){let t=rp(e.replace(/\\/g,"/")).replace(/[\u0000-\u001f\u007f/:]/g,"_").replace(/^\.+/,"").trim().slice(0,180);return!t||t==="."||t===".."?"oracle-file.bin":t}async function xp(e,t){if(!e.body)return Buffer.alloc(0);let n=e.body.getReader(),r=[],o=0;try{for(;;){let{value:i,done:s}=await n.read();if(s)break;let a=Buffer.from(i);if(o+=a.byteLength,o>t)throw new h(`Oracle download exceeds the ${z(t)} limit.`,g.API_ERROR);r.push(a)}}catch(i){throw await n.cancel("Oracle download exceeded its byte limit.").catch(()=>{}),i}finally{n.releaseLock()}return Buffer.concat(r)}import{Readable as Pp,Writable as Lp}from"node:stream";import{ndJsonStream as Np}from"@agentclientprotocol/sdk";import{randomUUID as Sp}from"node:crypto";import{isAbsolute as Rp}from"node:path";import{setTimeout as _p}from"node:timers/promises";import{agent as Ip,RequestError as q}from"@agentclientprotocol/sdk";import{RequestError as cr}from"@agentclientprotocol/sdk";var qa=65536;function Ma(e){if(e.length>100)throw cr.invalidParams(void 0,"At most 100 prompt blocks are supported.");let n=e.map(r=>{if(r.type==="text")return r.text;if(r.type==="resource_link")return`[Untrusted resource reference; content has not been read]
475
+ ${JSON.stringify({name:r.name,uri:r.uri,description:r.description})}`;throw cr.invalidParams(void 0,"Hosted Oracle accepts text and resource references only. Paste file contents as text; local files, embedded resources, images and audio are unsupported.")}).join(`
476
+ `);if(!n.trim()||Buffer.byteLength(n,"utf8")>qa)throw cr.invalidParams(void 0,`Prompt must contain text and fit within ${qa} UTF-8 bytes.`);return n}function se(){throw cr.invalidParams(void 0,"Answer must match the pending question and its option ids. Use the displayed /answer JSON or /skip command; ordinary prose cannot confirm actions.")}function Eo(e,t){if(!t||typeof t!="object"||Array.isArray(t))return se();let n=t;if(n.questionId!==e.id||Object.keys(n).some(i=>!["questionId","answers","skipped"].includes(i)))return se();if(n.skipped===!0)return!e.optional||!Array.isArray(n.answers)||n.answers.length?se():{questionId:e.id,answers:[],skipped:!0};if(n.skipped!==void 0||!Array.isArray(n.answers)||n.answers.length!==e.questions.length)return se();let r=new Set,o=n.answers.map(i=>{if(!i||typeof i!="object"||Array.isArray(i))return se();let s=i;if(Object.keys(s).some(l=>!["questionId","optionIds","customText"].includes(l)))return se();let a=e.questions.find(l=>l.id===s.questionId);if(!a||r.has(a.id)||!Array.isArray(s.optionIds))return se();r.add(a.id);let c=s.optionIds;return c.some(l=>typeof l!="string"||!a.options.some(d=>d.id===l))||new Set(c).size!==c.length||!a.multiSelect&&c.length>1||s.customText!==void 0&&(typeof s.customText!="string"||!s.customText.trim()||Buffer.byteLength(s.customText)>8192)||!c.length&&s.customText===void 0?se():{questionId:a.id,optionIds:c,...typeof s.customText=="string"?{customText:s.customText}:{}}});return{questionId:e.id,answers:o}}function Ua(e,t){if(t===`/skip ${e.id}`)return Eo(e,{questionId:e.id,answers:[],skipped:!0});if(!t.startsWith("/answer "))return se();let n;try{n=JSON.parse(t.slice(8))}catch{return se()}return Eo(e,n)}function Bt(e){let t=e.questions.map(r=>`${r.header}: ${r.question}
464
477
  ${r.options.map(o=>` ${JSON.stringify(o.id)}: ${o.label} \u2014 ${o.description}${o.confirms?" (confirms an action)":""}`).join(`
465
478
  `)}`).join(`
466
479
 
467
- `),n={questionId:t.id,answers:t.questions.map(r=>({questionId:r.id,optionIds:["<choose an option id>"]}))};return`${e}
480
+ `),n={questionId:e.id,answers:e.questions.map(r=>({questionId:r.id,optionIds:["<choose an option id>"]}))};return`${t}
468
481
 
469
482
  Reply with explicit option ids:
470
- /answer ${JSON.stringify(n)}${t.optional?`
471
- Or skip: /skip ${t.id}`:""}`}function Ds(t){return{type:"object",properties:Object.fromEntries(t.questions.map((e,n)=>[`answer_${n}`,e.multiSelect?{type:"array",title:e.header,description:e.question,items:{type:"string",enum:e.options.map(r=>r.id)},minItems:1}:{type:"string",title:e.header,description:`${e.question}
472
- ${e.options.map(r=>`${r.id}: ${r.label} \u2014 ${r.description}${r.confirms?" (confirms an action)":""}`).join(`
473
- `)}`,enum:e.options.map(r=>r.id),enumNames:e.options.map(r=>r.label)}])),required:t.questions.map((e,n)=>`answer_${n}`)}}function Bs(t,e){if(!e||typeof e!="object"||Array.isArray(e))return re();let n=e;return Object.keys(n).length!==t.questions.length?re():Zr(t,{questionId:t.id,answers:t.questions.map((r,o)=>{let i=n[`answer_${o}`];return typeof i=="string"&&(r.multiSelect||!r.options.some(s=>s.id===i))?{questionId:r.id,optionIds:[],customText:i}:{questionId:r.id,optionIds:r.multiSelect?i:[i]}})})}function fu(t){return{client:async()=>new ue(await Dn(t.endpointOverride,t.workspace)),login:async e=>{let n=await Tt(t.endpointOverride),r=await nt({endpoint:n.endpoint??jn,workspace:t.workspace,write:t.write,approve:t.approve,signal:e,onAuthorizationUrl:o=>process.stderr.write(`Authorize AnyForge Oracle in your browser:
483
+ /answer ${JSON.stringify(n)}${e.optional?`
484
+ Or skip: /skip ${e.id}`:""}`}function ja(e){return{type:"object",properties:Object.fromEntries(e.questions.map((t,n)=>[`answer_${n}`,t.multiSelect?{type:"array",title:t.header,description:t.question,items:{type:"string",enum:t.options.map(r=>r.id)},minItems:1}:{type:"string",title:t.header,description:`${t.question}
485
+ ${t.options.map(r=>`${r.id}: ${r.label} \u2014 ${r.description}${r.confirms?" (confirms an action)":""}`).join(`
486
+ `)}`,enum:t.options.map(r=>r.id),enumNames:t.options.map(r=>r.label)}])),required:e.questions.map((t,n)=>`answer_${n}`)}}function Da(e,t){if(!t||typeof t!="object"||Array.isArray(t))return se();let n=t;return Object.keys(n).length!==e.questions.length?se():Eo(e,{questionId:e.id,answers:e.questions.map((r,o)=>{let i=n[`answer_${o}`];return typeof i=="string"&&(r.multiSelect||!r.options.some(s=>s.id===i))?{questionId:r.id,optionIds:[],customText:i}:{questionId:r.id,optionIds:r.multiSelect?i:[i]}})})}function Ep(e){return{client:async()=>new fe(await rr(e.endpointOverride,e.workspace)),login:async t=>{let n=await Dt(e.endpointOverride),r=await pt({endpoint:n.endpoint??nr,workspace:e.workspace,write:e.write,approve:e.approve,signal:t,onAuthorizationUrl:o=>process.stderr.write(`Authorize AnyForge Oracle in your browser:
474
487
  ${o}
475
- `)});return new ue(Te(r))}}}var Kn=class{constructor(e,n=fu(e)){this.options=e;this.dependencies=n}options;dependencies;initialized=!1;formSupported=!1;sessions=new Map;loading=new Set;pendingSetups=0;clientPromise;loginPromise;stopping=!1;lifetime=new AbortController;app(){let e=async n=>{try{return await n()}catch(r){throw hu(r)}};return pu({name:"anyforge"}).onRequest("initialize",({params:n})=>this.initialize(n)).onRequest("authenticate",({params:n})=>e(async()=>{if(this.ready(),n.methodId!=="oracle-browser")throw q.invalidParams(void 0,"Unknown authentication method. Terminal authentication runs as a separate command.");return await this.authenticate(),{}})).onRequest("session/new",({params:n,client:r})=>e(()=>this.newSession(n,r))).onRequest("session/load",({params:n,client:r})=>e(()=>this.loadSession(n,r))).onRequest("session/prompt",({params:n,client:r})=>e(()=>this.prompt(n,r))).onNotification("session/cancel",({params:n})=>this.cancel(n.sessionId))}initialize(e){if(this.initialized)throw q.invalidRequest(void 0,"This connection is already initialized.");if(!Number.isSafeInteger(e.protocolVersion)||e.protocolVersion<1)throw q.unsupportedProtocolVersion(e.protocolVersion,[1]);return this.initialized=!0,this.formSupported=e.clientCapabilities?.elicitation?.form!=null,{protocolVersion:1,agentInfo:{name:"anyforge",title:"AnyForge Hosted Oracle",version:this.options.version},agentCapabilities:{loadSession:!0,promptCapabilities:{image:!1,audio:!1,embeddedContext:!1},mcpCapabilities:{http:!1,sse:!1}},authMethods:[{id:"oracle-browser",name:"Sign in to AnyForge",description:"Authorize a workspace-scoped Oracle grant in your browser."},...e.clientCapabilities?.auth?.terminal===!0?[{id:"oracle-terminal",name:"Sign in from a terminal",type:"terminal",args:["--login"]}]:[]]}}ready(){if(!this.initialized)throw q.invalidRequest(void 0,"Initialize the ACP connection first.");if(this.stopping)throw q.requestCancelled()}client(){return this.clientPromise??=this.dependencies.client().catch(e=>{throw this.clientPromise=void 0,e}),this.clientPromise}async authenticate(){if(this.sessions.size||this.pendingSetups)throw q.invalidRequest(void 0,"Restart this ACP process to change authentication after opening a session.");return this.loginPromise||(this.loginPromise=this.dependencies.login(this.lifetime.signal).then(async e=>{await e.config(this.lifetime.signal),this.clientPromise=Promise.resolve(e)}).finally(()=>{this.loginPromise=void 0})),this.loginPromise}validateSetup(e){if(this.ready(),!du(e.cwd))throw q.invalidParams(void 0,"ACP cwd must be an absolute path. Hosted mode does not read this directory.");if(this.loginPromise)throw q.invalidRequest(void 0,"Wait for authentication to finish.");if(this.sessions.size+this.pendingSetups>=128&&(!("sessionId"in e)||!this.sessions.has(e.sessionId)))throw q.invalidRequest(void 0,"This ACP process has reached its 128-session limit. Restart it to open more sessions.")}async newSession(e,n){this.validateSetup(e),this.pendingSetups+=1;try{let r=await this.client();await r.config(this.lifetime.signal);let o=await r.createSession({visibility:"private",...this.options.context?{context:this.options.context}:{}},this.lifetime.signal);if(this.stopping)throw q.requestCancelled();return this.sessions.set(o.sessionId,{client:r,mcpNoticePending:e.mcpServers.length>0}),e.mcpServers.length&&await this.mcpNotice(n,o.sessionId),{sessionId:o.sessionId}}finally{this.pendingSetups-=1}}async loadSession(e,n){if(this.validateSetup(e),this.sessions.get(e.sessionId)?.active||this.loading.has(e.sessionId))throw q.invalidRequest(void 0,"Cannot load a session during an active prompt or load.");this.loading.add(e.sessionId),this.pendingSetups+=1;try{let r=await this.client(),o=await r.getSession(e.sessionId,this.lifetime.signal);if(o.activeStreamId)throw q.invalidRequest(void 0,"The hosted session is still running. Wait for it to settle before loading it.");if(this.stopping)throw q.requestCancelled();this.sessions.set(e.sessionId,{client:r,question:mu(o),mcpNoticePending:e.mcpServers.length>0}),e.mcpServers.length&&await this.mcpNotice(n,e.sessionId);for(let i of o.messages)i.content&&await this.update(n,e.sessionId,{sessionUpdate:i.role==="user"?"user_message_chunk":"agent_message_chunk",content:{type:"text",text:p(i.content)}}),i.question&&await this.text(n,e.sessionId,$t(i.question));return{}}finally{this.loading.delete(e.sessionId),this.pendingSetups-=1}}prompt(e,n){this.ready();let r=this.sessions.get(e.sessionId);if(!r)throw q.invalidParams(void 0,"Load or create this session before prompting it.");if(r.active||this.loading.has(e.sessionId))throw q.invalidRequest(void 0,"A prompt or load is already running in this session. Wait for it to settle.");let o=Ms(e.prompt),i=r.question?js(r.question,o):void 0;if(!r.question&&(o.startsWith("/answer ")||o.startsWith("/skip ")))throw q.invalidParams(void 0,"There is no pending question in this session.");let s={cancelled:!1,abort:new AbortController,interaction:new AbortController,done:Promise.resolve({stopReason:"end_turn"})};return r.active=s,s.done=this.runPrompt(r,s,e.sessionId,i?"":o,i,n).finally(()=>{s.requestId=void 0,s.interaction.abort(),s.abort.abort(),s.deadline&&clearTimeout(s.deadline),r.active=void 0}),s.done}async runPrompt(e,n,r,o,i,s){e.mcpNoticePending&&(await this.mcpNotice(s,r),e.mcpNoticePending=!1);let a=await e.client.config(this.lifetime.signal);if(n.cancelled)return{stopReason:"cancelled"};let c=this.options.write&&e.client.scopes.has("write")&&a.capabilities?.write===!0,d=c&&this.options.approve&&e.client.scopes.has("approve")&&a.capabilities?.approve===!0;for(let u=0;u<20;u+=1){if(n.cancelled)return{stopReason:"cancelled"};n.requestId=lu();let l={requestId:n.requestId,text:o,readOnly:!c,...d?{allowApprove:!0}:{},...this.options.context?{context:this.options.context}:{},...this.options.contextText?{contextText:this.options.contextText}:{},...i?{questionResponse:i}:{}},k=new Ee,O=new Set,x=await _t(e.client,r,l,{onEvent:async m=>{if(m.type==="delta"&&typeof m.content=="string")await this.text(s,r,p(m.content));else if((m.type==="tool_call"||m.type==="tool_call_start")&&typeof m.id=="string"&&!O.has(m.id))O.add(m.id),await this.update(s,r,{sessionUpdate:"tool_call",toolCallId:m.id,title:typeof m.label=="string"?p(m.label).slice(0,200):mt(typeof m.name=="string"?m.name:""),status:"in_progress"});else if(m.type==="tool_result"&&typeof m.toolUseId=="string"&&O.has(m.toolUseId))await this.update(s,r,{sessionUpdate:"tool_call_update",toolCallId:m.toolUseId,status:m.ok===!1?"failed":"completed"});else if(m.type==="question")e.question=Yt(m);else if(m.type==="navigator_file_created"){let _=je(m.file);await this.text(s,r,`Generated file: ${_.filename} (${_.mediaType}, ${_.sizeBytes} bytes). Session id: ${r}; file id: ${_.fileId}. Download using anyforge oracle download with an explicit output directory.`)}}},n.abort.signal,k);if(n.requestId=void 0,x.error)throw q.internalError(void 0,x.error);if(i&&(e.question=x.question),n.cancelled||x.done?.exitReason==="cancelled")return{stopReason:"cancelled"};if(!x.question){let m=x.done?.exitReason;if(m==="completed")return{stopReason:"end_turn"};if(m==="max_tokens"||m==="token_budget")return{stopReason:"max_tokens"};if(m==="max_rounds")return{stopReason:"max_turn_requests"};throw q.internalError(void 0,`Oracle stopped before completion (${typeof m=="string"?p(m):"unknown reason"}). Resume the hosted session to inspect it.`)}if(e.question=x.question,!this.formSupported)return await this.text(s,r,$t(x.question)),{stopReason:"end_turn"};let b;try{b=await gu(s.request("elicitation/create",{mode:"form",sessionId:r,message:$t(x.question),requestedSchema:Ds(x.question)},{cancellationSignal:n.interaction.signal}),n.interaction.signal)}catch(m){if(n.cancelled)return{stopReason:"cancelled"};if(!(m instanceof q)||m.code!==-32601)throw m;return await this.text(s,r,$t(x.question)),{stopReason:"end_turn"}}if(n.cancelled)return{stopReason:"cancelled"};if(b.action!=="accept")return await this.text(s,r,`No answer was submitted. The question remains pending.
476
- `+$t(x.question)),{stopReason:"end_turn"};i=Bs(x.question,b.content),o=""}return{stopReason:"max_turn_requests"}}async cancel(e){let n=this.sessions.get(e),r=n?.active;if(!n||!r||(r.cancelled=!0,r.interaction.abort(),!r.requestId))return;r.deadline??=setTimeout(()=>r.abort.abort(new Error("Oracle cancellation did not settle within 15 seconds. Inspect the hosted session before retrying.")),15e3);let o=r.requestId;r.cancel??=this.cancelUntilClaimed(n.client,e,o,r).catch(i=>{r.requestId&&(process.stderr.write(`Oracle cancellation request failed: ${K(i)}
477
- `),r.abort.abort(new Error("Oracle cancellation could not be confirmed. Inspect the hosted session before retrying.")))}),await r.cancel}async cancelUntilClaimed(e,n,r,o){for(;o.requestId===r&&!o.abort.signal.aborted;)try{await e.cancelRequest(n,r,o.abort.signal);return}catch(i){if(!(i instanceof oe)||i.status!==404)throw i;await uu(100,void 0,{signal:o.abort.signal})}}async shutdown(){if(this.stopping)return;this.stopping=!0,this.lifetime.abort();let e=[...this.sessions.entries()].filter(([,o])=>o.active),n=Promise.allSettled(e.map(async([o,i])=>{await this.cancel(o),await i.active?.done})),r;await Promise.race([n,new Promise(o=>{r=setTimeout(o,3e3)})]),r&&clearTimeout(r);for(let[,o]of e)o.active?.abort.abort();this.sessions.clear()}update(e,n,r){return e.notify("session/update",{sessionId:n,update:r})}text(e,n,r){return this.update(e,n,{sessionUpdate:"agent_message_chunk",content:{type:"text",text:r}})}mcpNotice(e,n){return this.text(e,n,"AnyForge is running in hosted mode. Client MCP servers and local tools are omitted: their commands, environment and connections are never used or forwarded. Your authorized AnyForge server tools remain available. Local files and terminals are unavailable.")}};function mu(t){let e=t.messages.at(-1);return e?.role==="assistant"?e.question:void 0}function hu(t){return t instanceof q?t:t instanceof f&&t.exitCode===g.NO_CREDENTIALS||t instanceof oe&&t.status===401?q.authRequired(void 0,"Sign in with authenticate or run anyforge oracle login, then retry."):q.internalError(void 0,K(t))}function gu(t,e){return new Promise((n,r)=>{let o=()=>r(q.requestCancelled());e.addEventListener("abort",o,{once:!0}),t.then(n,r).finally(()=>e.removeEventListener("abort",o)).catch(()=>{}),e.aborted&&o()})}var bu=new Set(["endpoint","workspace","write","approve","read-only","program","initiative","codebase","context","login","help"]);function Ws(){return`AnyForge ACP \u2014 hosted Oracle over ACP v1 JSON-RPC stdio.
488
+ `)});return new fe(qe(r))}}}var lr=class{constructor(t,n=Ep(t)){this.options=t;this.dependencies=n}options;dependencies;initialized=!1;formSupported=!1;sessions=new Map;loading=new Set;pendingSetups=0;clientPromise;loginPromise;stopping=!1;lifetime=new AbortController;app(){let t=async n=>{try{return await n()}catch(r){throw Cp(r)}};return Ip({name:"anyforge"}).onRequest("initialize",({params:n})=>this.initialize(n)).onRequest("authenticate",({params:n})=>t(async()=>{if(this.ready(),n.methodId!=="oracle-browser")throw q.invalidParams(void 0,"Unknown authentication method. Terminal authentication runs as a separate command.");return await this.authenticate(),{}})).onRequest("session/new",({params:n,client:r})=>t(()=>this.newSession(n,r))).onRequest("session/load",({params:n,client:r})=>t(()=>this.loadSession(n,r))).onRequest("session/prompt",({params:n,client:r})=>t(()=>this.prompt(n,r))).onNotification("session/cancel",({params:n})=>this.cancel(n.sessionId))}initialize(t){if(this.initialized)throw q.invalidRequest(void 0,"This connection is already initialized.");if(!Number.isSafeInteger(t.protocolVersion)||t.protocolVersion<1)throw q.unsupportedProtocolVersion(t.protocolVersion,[1]);return this.initialized=!0,this.formSupported=t.clientCapabilities?.elicitation?.form!=null,{protocolVersion:1,agentInfo:{name:"anyforge",title:"AnyForge Hosted Oracle",version:this.options.version},agentCapabilities:{loadSession:!0,promptCapabilities:{image:!1,audio:!1,embeddedContext:!1},mcpCapabilities:{http:!1,sse:!1}},authMethods:[{id:"oracle-browser",name:"Sign in to AnyForge",description:"Authorize a workspace-scoped Oracle grant in your browser."},...t.clientCapabilities?.auth?.terminal===!0?[{id:"oracle-terminal",name:"Sign in from a terminal",type:"terminal",args:["--login"]}]:[]]}}ready(){if(!this.initialized)throw q.invalidRequest(void 0,"Initialize the ACP connection first.");if(this.stopping)throw q.requestCancelled()}client(){return this.clientPromise??=this.dependencies.client().catch(t=>{throw this.clientPromise=void 0,t}),this.clientPromise}async authenticate(){if(this.sessions.size||this.pendingSetups)throw q.invalidRequest(void 0,"Restart this ACP process to change authentication after opening a session.");return this.loginPromise||(this.loginPromise=this.dependencies.login(this.lifetime.signal).then(async t=>{await t.config(this.lifetime.signal),this.clientPromise=Promise.resolve(t)}).finally(()=>{this.loginPromise=void 0})),this.loginPromise}validateSetup(t){if(this.ready(),!Rp(t.cwd))throw q.invalidParams(void 0,"ACP cwd must be an absolute path. Hosted mode does not read this directory.");if(this.loginPromise)throw q.invalidRequest(void 0,"Wait for authentication to finish.");if(this.sessions.size+this.pendingSetups>=128&&(!("sessionId"in t)||!this.sessions.has(t.sessionId)))throw q.invalidRequest(void 0,"This ACP process has reached its 128-session limit. Restart it to open more sessions.")}async newSession(t,n){this.validateSetup(t),this.pendingSetups+=1;try{let r=await this.client();await r.config(this.lifetime.signal);let o=await r.createSession({visibility:"private",...this.options.context?{context:this.options.context}:{}},this.lifetime.signal);if(this.stopping)throw q.requestCancelled();return this.sessions.set(o.sessionId,{client:r,mcpNoticePending:t.mcpServers.length>0}),t.mcpServers.length&&await this.mcpNotice(n,o.sessionId),{sessionId:o.sessionId}}finally{this.pendingSetups-=1}}async loadSession(t,n){if(this.validateSetup(t),this.sessions.get(t.sessionId)?.active||this.loading.has(t.sessionId))throw q.invalidRequest(void 0,"Cannot load a session during an active prompt or load.");this.loading.add(t.sessionId),this.pendingSetups+=1;try{let r=await this.client(),o=await r.getSession(t.sessionId,this.lifetime.signal);if(o.activeStreamId)throw q.invalidRequest(void 0,"The hosted session is still running. Wait for it to settle before loading it.");if(this.stopping)throw q.requestCancelled();this.sessions.set(t.sessionId,{client:r,question:Tp(o),mcpNoticePending:t.mcpServers.length>0}),t.mcpServers.length&&await this.mcpNotice(n,t.sessionId);for(let i of o.messages)i.content&&await this.update(n,t.sessionId,{sessionUpdate:i.role==="user"?"user_message_chunk":"agent_message_chunk",content:{type:"text",text:p(i.content)}}),i.question&&await this.text(n,t.sessionId,Bt(i.question));return{}}finally{this.loading.delete(t.sessionId),this.pendingSetups-=1}}prompt(t,n){this.ready();let r=this.sessions.get(t.sessionId);if(!r)throw q.invalidParams(void 0,"Load or create this session before prompting it.");if(r.active||this.loading.has(t.sessionId))throw q.invalidRequest(void 0,"A prompt or load is already running in this session. Wait for it to settle.");let o=Ma(t.prompt),i=r.question?Ua(r.question,o):void 0;if(!r.question&&(o.startsWith("/answer ")||o.startsWith("/skip ")))throw q.invalidParams(void 0,"There is no pending question in this session.");let s={cancelled:!1,abort:new AbortController,interaction:new AbortController,done:Promise.resolve({stopReason:"end_turn"})};return r.active=s,s.done=this.runPrompt(r,s,t.sessionId,i?"":o,i,n).finally(()=>{s.requestId=void 0,s.interaction.abort(),s.abort.abort(),s.deadline&&clearTimeout(s.deadline),r.active=void 0}),s.done}async runPrompt(t,n,r,o,i,s){t.mcpNoticePending&&(await this.mcpNotice(s,r),t.mcpNoticePending=!1);let a=await t.client.config(this.lifetime.signal);if(n.cancelled)return{stopReason:"cancelled"};let c=this.options.write&&t.client.scopes.has("write")&&a.capabilities?.write===!0,l=c&&this.options.approve&&t.client.scopes.has("approve")&&a.capabilities?.approve===!0;for(let d=0;d<20;d+=1){if(n.cancelled)return{stopReason:"cancelled"};n.requestId=Sp();let u={requestId:n.requestId,text:o,readOnly:!c,...l?{allowApprove:!0}:{},...this.options.context?{context:this.options.context}:{},...this.options.contextText?{contextText:this.options.contextText}:{},...i?{questionResponse:i}:{}},y=new Ne,v=new Set,x=await Lt(t.client,r,u,{onEvent:async m=>{if(m.type==="delta"&&typeof m.content=="string")await this.text(s,r,p(m.content));else if((m.type==="tool_call"||m.type==="tool_call_start")&&typeof m.id=="string"&&!v.has(m.id))v.add(m.id),await this.update(s,r,{sessionUpdate:"tool_call",toolCallId:m.id,title:typeof m.label=="string"?p(m.label).slice(0,200):xt(typeof m.name=="string"?m.name:""),status:"in_progress"});else if(m.type==="tool_result"&&typeof m.toolUseId=="string"&&v.has(m.toolUseId))await this.update(s,r,{sessionUpdate:"tool_call_update",toolCallId:m.toolUseId,status:m.ok===!1?"failed":"completed"});else if(m.type==="question")t.question=sn(m);else if(m.type==="navigator_file_created"){let A=Ke(m.file);await this.text(s,r,`Generated file: ${A.filename} (${A.mediaType}, ${A.sizeBytes} bytes). Session id: ${r}; file id: ${A.fileId}. Download using anyforge oracle download with an explicit output directory.`)}}},n.abort.signal,y);if(n.requestId=void 0,x.error)throw q.internalError(void 0,x.error);if(i&&(t.question=x.question),n.cancelled||x.done?.exitReason==="cancelled")return{stopReason:"cancelled"};if(!x.question){let m=x.done?.exitReason;if(m==="completed")return{stopReason:"end_turn"};if(m==="max_tokens"||m==="token_budget")return{stopReason:"max_tokens"};if(m==="max_rounds")return{stopReason:"max_turn_requests"};throw q.internalError(void 0,`Oracle stopped before completion (${typeof m=="string"?p(m):"unknown reason"}). Resume the hosted session to inspect it.`)}if(t.question=x.question,!this.formSupported)return await this.text(s,r,Bt(x.question)),{stopReason:"end_turn"};let b;try{b=await $p(s.request("elicitation/create",{mode:"form",sessionId:r,message:Bt(x.question),requestedSchema:ja(x.question)},{cancellationSignal:n.interaction.signal}),n.interaction.signal)}catch(m){if(n.cancelled)return{stopReason:"cancelled"};if(!(m instanceof q)||m.code!==-32601)throw m;return await this.text(s,r,Bt(x.question)),{stopReason:"end_turn"}}if(n.cancelled)return{stopReason:"cancelled"};if(b.action!=="accept")return await this.text(s,r,`No answer was submitted. The question remains pending.
489
+ `+Bt(x.question)),{stopReason:"end_turn"};i=Da(x.question,b.content),o=""}return{stopReason:"max_turn_requests"}}async cancel(t){let n=this.sessions.get(t),r=n?.active;if(!n||!r||(r.cancelled=!0,r.interaction.abort(),!r.requestId))return;r.deadline??=setTimeout(()=>r.abort.abort(new Error("Oracle cancellation did not settle within 15 seconds. Inspect the hosted session before retrying.")),15e3);let o=r.requestId;r.cancel??=this.cancelUntilClaimed(n.client,t,o,r).catch(i=>{r.requestId&&(process.stderr.write(`Oracle cancellation request failed: ${K(i)}
490
+ `),r.abort.abort(new Error("Oracle cancellation could not be confirmed. Inspect the hosted session before retrying.")))}),await r.cancel}async cancelUntilClaimed(t,n,r,o){for(;o.requestId===r&&!o.abort.signal.aborted;)try{await t.cancelRequest(n,r,o.abort.signal);return}catch(i){if(!(i instanceof V)||i.status!==404)throw i;await _p(100,void 0,{signal:o.abort.signal})}}async shutdown(){if(this.stopping)return;this.stopping=!0,this.lifetime.abort();let t=[...this.sessions.entries()].filter(([,o])=>o.active),n=Promise.allSettled(t.map(async([o,i])=>{await this.cancel(o),await i.active?.done})),r;await Promise.race([n,new Promise(o=>{r=setTimeout(o,3e3)})]),r&&clearTimeout(r);for(let[,o]of t)o.active?.abort.abort();this.sessions.clear()}update(t,n,r){return t.notify("session/update",{sessionId:n,update:r})}text(t,n,r){return this.update(t,n,{sessionUpdate:"agent_message_chunk",content:{type:"text",text:r}})}mcpNotice(t,n){return this.text(t,n,"AnyForge is running in hosted mode. Client MCP servers and local tools are omitted: their commands, environment and connections are never used or forwarded. Your authorized AnyForge server tools remain available. Local files and terminals are unavailable.")}};function Tp(e){let t=e.messages.at(-1);return t?.role==="assistant"?t.question:void 0}function Cp(e){return e instanceof q?e:e instanceof h&&e.exitCode===g.NO_CREDENTIALS||e instanceof V&&e.status===401?q.authRequired(void 0,"Sign in with authenticate or run anyforge oracle login, then retry."):q.internalError(void 0,K(e))}function $p(e,t){return new Promise((n,r)=>{let o=()=>r(q.requestCancelled());t.addEventListener("abort",o,{once:!0}),e.then(n,r).finally(()=>t.removeEventListener("abort",o)).catch(()=>{}),t.aborted&&o()})}var Fp=new Set(["endpoint","workspace","write","approve","read-only","program","initiative","codebase","context","login","help"]);function Ba(){return`AnyForge ACP \u2014 hosted Oracle over ACP v1 JSON-RPC stdio.
478
491
 
479
492
  Usage: anyforge acp [options]
480
493
 
@@ -511,16 +524,16 @@ with the displayed /answer JSON command. Ordinary prose never confirms actions.
511
524
  Sessions support new, load, text/tool progress and cancellation. Local modes,
512
525
  model selection, session list/fork/delete and local tool execution are not offered.
513
526
  Protocol traffic uses stdout; diagnostics use stderr.
514
- `}function ku(t,e){if(t.positional.length)throw new h("acp does not accept positional arguments. Prompts arrive over ACP stdio.");let n=Object.keys(t.flags).find(d=>!bu.has(d));if(n)throw new h(`--${n} is not supported by acp. Run anyforge acp --help.`);let r=t.flags.write===!0||t.flags.approve===!0;if(t.flags["read-only"]===!0&&r)throw new h("--read-only cannot be combined with --write or --approve.");let o=["program","initiative","codebase"].filter(d=>t.flags[d]!==void 0);if(o.length>1)throw new h("Choose only one of --program, --initiative or --codebase.");let i=o[0],s=i?t.flags[i]:void 0;if(i&&(typeof s!="string"||!s.trim()||Buffer.byteLength(s)>256))throw new h("Context id must contain 1\u2013256 UTF-8 bytes.");let a=t.flags.context;if(a!==void 0&&(typeof a!="string"||Buffer.byteLength(a)>8192))throw new h("--context must fit within 8 KiB.");let c=t.flags.workspace;if(c!==void 0&&(typeof c!="string"||!c.trim()||Buffer.byteLength(c)>256))throw new h("--workspace must contain 1\u2013256 UTF-8 bytes.");return{version:e,endpointOverride:t.endpointOverride,...typeof c=="string"?{workspace:c}:{},write:r,approve:t.flags.approve===!0,...i&&typeof s=="string"?{context:{kind:i,id:s}}:{},...typeof a=="string"?{contextText:a}:{}}}async function Gs(t,e){let n=ku(t,e);if(t.flags.login===!0){let a=new AbortController,c=()=>a.abort();process.once("SIGTERM",c),process.once("SIGINT",c);try{let d=await Tt(n.endpointOverride),u=await nt({endpoint:d.endpoint??jn,workspace:n.workspace,write:n.write,approve:n.approve,signal:a.signal,onAuthorizationUrl:l=>process.stderr.write(`Authorize AnyForge Oracle in your browser:
515
- ${l}
516
- `)});await new ue(Te(u)).config(),process.stderr.write(`AnyForge Oracle login saved. Restart or retry your ACP session.
517
- `)}finally{process.removeListener("SIGTERM",c),process.removeListener("SIGINT",c)}return}let r=new Kn(n),o=vu(yu.toWeb(process.stdout),wu.toWeb(process.stdin),{maxMessageBytes:1048576}),i=r.app().connect(o),s=()=>i.close();process.once("SIGTERM",s),process.once("SIGINT",s);try{await i.closed}finally{process.removeListener("SIGTERM",s),process.removeListener("SIGINT",s),await r.shutdown(),process.stdin.destroy()}}var Ks="https://crew.anyforge.ai",Au=new Set(["endpoint","limit","status","type","priority","initiative","program","search","assignee","notes","input","context","messages","scope","client","days","profile","interval","codebase","file","output","workspace","repo","branch","print","event","actor","from","to","cursor","role","team"]),_u=new Set(["help","version","json","active","archived","all","follow","3way","dry-run","force","shared","write","approve","read-only","stream-json","verify","no-email","login","uncommitted"]);function xu(t){let e={positional:[],flags:{}};for(let n=0;n<t.length;n+=1){let r=t[n];if(!r.startsWith("-")){e.command?e.positional.push(r):e.command=r;continue}let o=r==="-h"?"help":r==="-V"?"version":r==="-f"?"force":r==="-p"||r.startsWith("-p=")?r.replace(/^-p/,"print"):r.replace(/^--?/,""),[i,s]=o.includes("=")?[o.slice(0,o.indexOf("=")),o.slice(o.indexOf("=")+1)]:[o,void 0];if(Au.has(i)){let a=s??t[++n];if(a===void 0)throw new h(`--${i} needs a value.`);if(i==="file"){let c=e.flags.file;e.flags.file=[...typeof c=="string"?[c]:Array.isArray(c)?c:[],a]}else e.flags[i]=a;continue}if(_u.has(i)){e.flags[i]=s===void 0?!0:s!=="false";continue}throw new h(`Unknown option: ${r}`,"Run `anyforge --help` for the command list.")}return e}function ve(t,e){if(t===void 0||typeof t=="boolean")return;let n=Number(t);if(!Number.isFinite(n)||n<=0)throw new h(`--${e} must be a positive number, got "${t}".`);return n}function C(t){return typeof t=="string"?t:void 0}function nn(t){return t===!0?!0:void 0}function Ru(){console.log(`
518
- ${D("AnyForge Control")} \u2014 governed LLM proxy, and a scriptable AnyForge client.
527
+ `}function qp(e,t){if(e.positional.length)throw new f("acp does not accept positional arguments. Prompts arrive over ACP stdio.");let n=Object.keys(e.flags).find(l=>!Fp.has(l));if(n)throw new f(`--${n} is not supported by acp. Run anyforge acp --help.`);let r=e.flags.write===!0||e.flags.approve===!0;if(e.flags["read-only"]===!0&&r)throw new f("--read-only cannot be combined with --write or --approve.");let o=["program","initiative","codebase"].filter(l=>e.flags[l]!==void 0);if(o.length>1)throw new f("Choose only one of --program, --initiative or --codebase.");let i=o[0],s=i?e.flags[i]:void 0;if(i&&(typeof s!="string"||!s.trim()||Buffer.byteLength(s)>256))throw new f("Context id must contain 1\u2013256 UTF-8 bytes.");let a=e.flags.context;if(a!==void 0&&(typeof a!="string"||Buffer.byteLength(a)>8192))throw new f("--context must fit within 8 KiB.");let c=e.flags.workspace;if(c!==void 0&&(typeof c!="string"||!c.trim()||Buffer.byteLength(c)>256))throw new f("--workspace must contain 1\u2013256 UTF-8 bytes.");return{version:t,endpointOverride:e.endpointOverride,...typeof c=="string"?{workspace:c}:{},write:r,approve:e.flags.approve===!0,...i&&typeof s=="string"?{context:{kind:i,id:s}}:{},...typeof a=="string"?{contextText:a}:{}}}async function Wa(e,t){let n=qp(e,t);if(e.flags.login===!0){let a=new AbortController,c=()=>a.abort();process.once("SIGTERM",c),process.once("SIGINT",c);try{let l=await Dt(n.endpointOverride),d=await pt({endpoint:l.endpoint??nr,workspace:n.workspace,write:n.write,approve:n.approve,signal:a.signal,onAuthorizationUrl:u=>process.stderr.write(`Authorize AnyForge Oracle in your browser:
528
+ ${u}
529
+ `)});await new fe(qe(d)).config(),process.stderr.write(`AnyForge Oracle login saved. Restart or retry your ACP session.
530
+ `)}finally{process.removeListener("SIGTERM",c),process.removeListener("SIGINT",c)}return}let r=new lr(n),o=Np(Lp.toWeb(process.stdout),Pp.toWeb(process.stdin),{maxMessageBytes:1048576}),i=r.app().connect(o),s=()=>i.close();process.once("SIGTERM",s),process.once("SIGINT",s);try{await i.closed}finally{process.removeListener("SIGTERM",s),process.removeListener("SIGINT",s),await r.shutdown(),process.stdin.destroy()}}var za="https://crew.anyforge.ai",Up=new Set(["endpoint","limit","status","type","priority","initiative","program","search","assignee","notes","input","context","messages","scope","client","days","profile","interval","codebase","file","output","workspace","repo","branch","print","event","actor","from","to","cursor","role","team","date","entries"]),jp=new Set(["help","version","json","active","archived","all","follow","3way","dry-run","force","shared","write","approve","read-only","stream-json","verify","no-email","login","uncommitted"]);function Dp(e){let t={positional:[],flags:{}};for(let n=0;n<e.length;n+=1){let r=e[n];if(!r.startsWith("-")){t.command?t.positional.push(r):t.command=r;continue}let o=r==="-h"?"help":r==="-V"?"version":r==="-f"?"force":r==="-p"||r.startsWith("-p=")?r.replace(/^-p/,"print"):r.replace(/^--?/,""),[i,s]=o.includes("=")?[o.slice(0,o.indexOf("=")),o.slice(o.indexOf("=")+1)]:[o,void 0];if(Up.has(i)){let a=s??e[++n];if(a===void 0)throw new f(`--${i} needs a value.`);if(i==="file"){let c=t.flags.file;t.flags.file=[...typeof c=="string"?[c]:Array.isArray(c)?c:[],a]}else t.flags[i]=a;continue}if(jp.has(i)){t.flags[i]=s===void 0?!0:s!=="false";continue}throw new f(`Unknown option: ${r}`,"Run `anyforge --help` for the command list.")}return t}function Oe(e,t){if(e===void 0||typeof e=="boolean")return;let n=Number(e);if(!Number.isFinite(n)||n<=0)throw new f(`--${t} must be a positive number, got "${e}".`);return n}function E(e){return typeof e=="string"?e:void 0}function fn(e){return e===!0?!0:void 0}function Bp(){console.log(`
531
+ ${W("AnyForge Control")} \u2014 governed LLM proxy, and a scriptable AnyForge client.
519
532
 
520
- ${D("USAGE")}
533
+ ${W("USAGE")}
521
534
  npx @anyforge/control <command> [options]
522
535
 
523
- ${D("CODE STUDIO")}
536
+ ${W("CODE STUDIO")}
524
537
  studio list List your owned coding sessions
525
538
  studio create --repo owner/repo Prepare a direct remote session
526
539
  studio create --uncommitted Hand this checkout's branch and uncommitted
@@ -535,24 +548,28 @@ ${D("CODE STUDIO")}
535
548
  studio changes|artifacts <id> Inspect remote results
536
549
  studio stage|commit|push <id> Explicit Git action with --input JSON
537
550
 
538
- ${D("SETUP")}
551
+ ${W("SETUP")}
539
552
  init Authorise this device and patch local agent configs
540
553
  whoami Show the company, scopes and endpoint this key acts as
541
554
  status Check identity, local setup and recorded activity
542
555
  logout Clear the saved API key and revert patched configs
543
556
  apply-patch [file] Apply a unified diff (file or stdin) via git apply
544
557
 
545
- ${D("SKILLS")}
546
- skill install anyforge-platform
547
- Install the portable AnyForge platform guide
548
- (--scope project|user, --client agents|claude|codex)
558
+ ${W("SKILLS")}
559
+ skill list List your company's skills and skill-source skills
560
+ skill install <name> Install a skill's whole folder (scripts and references
561
+ included) into a local agent, or anyforge-platform for
562
+ the platform guide (--scope project|user,
563
+ --client agents|claude|codex, --team <slug>)
564
+ skill sync Update installed skills to their current version; keeps
565
+ local edits unless --force (--dry-run, --team <slug>)
549
566
 
550
- ${D("APPROVALS")} ${ot("(the gate that blocks a human)")}
567
+ ${W("APPROVALS")} ${Je("(the gate that blocks a human)")}
551
568
  approvals List HIL approvals awaiting a decision
552
569
  approve <id> Approve one, optionally with --notes
553
570
  reject <id> Reject one, optionally with --notes
554
571
 
555
- ${D("CREWS")}
572
+ ${W("CREWS")}
556
573
  crews List crews (--active for running/paused only)
557
574
  crew <id> Show one crew, its status and recent output
558
575
  crew start <item> Dispatch a crew on a work item (--profile, --follow)
@@ -560,14 +577,14 @@ ${D("CREWS")}
560
577
  halt <id> Stop a crew
561
578
  resume <id> Resume a paused or halted crew
562
579
 
563
- ${D("WORK & WORKFLOWS")}
580
+ ${W("WORK & WORKFLOWS")}
564
581
  work List work items (--status, --type, --priority, --search).
565
582
  Paged: one page by default, --all walks every page
566
583
  workflows List operator workflows
567
584
  run <id> Dispatch a workflow run, with optional --input JSON
568
585
  ask <question> Ask the Oracle one question
569
586
 
570
- ${D("HOSTED ORACLE")}
587
+ ${W("HOSTED ORACLE")}
571
588
  acp Serve the hosted Oracle over ACP v1 stdio (T3 Code and editors)
572
589
  acp --help Show hosted ACP capabilities, limits and options
573
590
  oracle [prompt] Start an interactive hosted Oracle session
@@ -580,7 +597,7 @@ ${D("HOSTED ORACLE")}
580
597
  oracle logout Revoke and clear the active workspace's Oracle grant (--all for every one)
581
598
  oracle --help Show Oracle options and slash commands
582
599
 
583
- ${D("MEASUREMENT")}
600
+ ${W("MEASUREMENT")}
584
601
  backtest-corpus Can this workspace be back-tested? Counts the incidents
585
602
  that could become graded replay cases (--days 30|90|180|365)
586
603
  review-interception Serious findings the reviewer raised on your pull requests,
@@ -588,12 +605,18 @@ ${D("MEASUREMENT")}
588
605
  review-coverage How many recorded pull requests got reviewed, how fast the
589
606
  author heard back, and what a limit stopped (--days 7|30|90|180)
590
607
 
591
- ${D("AUDIT LOG")} ${ot("(needs a key with the audit:read scope)")}
608
+ ${W("AUDIT LOG")} ${Je("(needs a key with the audit:read scope)")}
592
609
  audit Page through the hash-chained audit trail, oldest first
593
610
  (--event, --actor, --from, --to, --limit, --cursor, --all).
594
611
  --verify recomputes every hash and link across the pages read
595
612
 
596
- ${D("PEOPLE")} ${ot("(needs a key with the people:invite scope, minted by an owner)")}
613
+ ${W("TIMESHEET")} ${Je("(your own time; book needs the write scope)")}
614
+ time week Your timesheet for this week (--date <day> for another)
615
+ time book --entries <f>
616
+ Book cells from a JSON list, all or nothing (--entries - reads stdin):
617
+ [{"initiativeId":"\u2026","date":"YYYY-MM-DD","minutes":90,"note":"\u2026"}]
618
+
619
+ ${W("PEOPLE")} ${Je("(needs a key with the people:invite scope, minted by an owner)")}
597
620
  people invite <email> --role <role>
598
621
  Invite one person: operator, editor, viewer or filer, on a
599
622
  verified company domain (--team <ids>, --no-email)
@@ -601,9 +624,9 @@ ${D("PEOPLE")} ${ot("(needs a key with the people:invite scope, minted by an ow
601
624
  people withdraw <email>
602
625
  Withdraw a pending invitation
603
626
 
604
- ${D("OPTIONS")}
627
+ ${W("OPTIONS")}
605
628
  --json Print the raw API payload instead of a table
606
- --endpoint <url> Override the base URL (default: ${ot(Ks)})
629
+ --endpoint <url> Override the base URL (default: ${Je(za)})
607
630
  --limit <n> Page size on list commands (work: 1-200, default 50)
608
631
  --all work: follow the cursor until the backlog is exhausted
609
632
  --notes <text> approve/reject: reviewer notes, recorded in the audit trail
@@ -620,19 +643,24 @@ ${D("OPTIONS")}
620
643
  --from / --to <time> audit: window, ISO 8601 with a time zone or epoch ms
621
644
  --cursor <c> audit: resume after a previous page's nextCursor
622
645
  --verify audit: verify the chain; exits 5 on a break (unfiltered reads only)
646
+ --date <day> time week: any day (YYYY-MM-DD) in the week to show
647
+ --entries <path> time book: a JSON list of cells, or - for standard input
623
648
  --role <role> people invite: operator, editor, viewer or filer (never owner)
624
- --team <ids> people invite: existing team ids, comma-separated
625
649
  --no-email people invite: record the invitation without sending the email
626
- --scope <scope> skill install: project (default) or user
627
- --client <client> skill install: agents (default), claude, or codex
650
+ --scope <scope> skill install/sync: project (default) or user
651
+ --client <client> skill install/sync: agents (default), claude, or codex
652
+ --team <slug> people invite: existing team ids, comma-separated
653
+ skill install/sync: that team's skill-source skills
654
+ --dry-run skill sync: report what would change, write nothing
628
655
  --force skill install: replace AnyForge-managed files
656
+ skill sync: replace local edits
629
657
  --help, -h Show this message
630
658
  --version, -V Print CLI version
631
659
 
632
- ${D("EXIT CODES")}
660
+ ${W("EXIT CODES")}
633
661
  0 ok 1 failure 2 usage 3 not authenticated 4 API error 5 audit verification failed
634
662
 
635
- ${D("EXAMPLES")}
663
+ ${W("EXAMPLES")}
636
664
  anyforge approvals
637
665
  anyforge approve hil_123 --notes "ship it"
638
666
  anyforge crews --active --json | jq -r '.[].id'
@@ -647,13 +675,17 @@ ${D("EXAMPLES")}
647
675
  anyforge review-coverage --days 30
648
676
  anyforge ask "which initiatives slipped this cycle?"
649
677
  anyforge audit --from 2026-10-01T00:00:00Z --all --verify
678
+ anyforge time week --json | jq '.entries'
679
+ anyforge time book --entries week.json
650
680
  anyforge people invite person@example.com --role editor --team platform --no-email
651
681
  anyforge people invitations --json
652
682
  anyforge people withdraw person@example.com
653
- `)}function Iu(){console.log(`
654
- ${D("AnyForge Oracle")} \u2014 chat with your hosted, workspace-governed Oracle.
683
+ anyforge skill install secret-guard --client claude
684
+ anyforge skill sync --client claude --dry-run
685
+ `)}function Wp(){console.log(`
686
+ ${W("AnyForge Oracle")} \u2014 chat with your hosted, workspace-governed Oracle.
655
687
 
656
- ${D("USAGE")}
688
+ ${W("USAGE")}
657
689
  anyforge oracle [initial prompt] [options] Start an interactive session
658
690
  anyforge oracle -p <prompt> [options] Print one read-only answer and exit
659
691
  anyforge oracle exec <prompt|stdin> [options] Submit one read-only turn
@@ -667,7 +699,7 @@ ${D("USAGE")}
667
699
  Revoke and remove the active (or named, or every) login
668
700
  anyforge oracle download <session> <file> --output <directory>
669
701
 
670
- ${D("OPTIONS")}
702
+ ${W("OPTIONS")}
671
703
  -p, --print <prompt> Print one answer to stdout and exit (like oracle exec)
672
704
  --json Print one final JSON object (exec)
673
705
  --stream-json Print event JSONL followed by a final result (exec)
@@ -690,7 +722,7 @@ CLI attachment limits are 10 MiB per file and 20 MiB total per turn. Files are
690
722
  uploaded only when named with --file or /attach. Generated files require an
691
723
  explicit \`oracle download\` destination; existing files are never overwritten.
692
724
 
693
- ${D("IN THE CONVERSATION")}
725
+ ${W("IN THE CONVERSATION")}
694
726
  /help Commands and keys /status Session, model, limits
695
727
  /new [shared] Start a conversation /clear Clear and start afresh
696
728
  /resume [id] Open a saved conversation /usage Turns and tokens so far
@@ -716,8 +748,8 @@ Examples:
716
748
  anyforge oracle login --workspace "Acme Engineering"
717
749
  anyforge oracle exec "What is blocked?" --workspace "Beta Labs"
718
750
  cat incident.log | anyforge oracle exec "Explain this log" --stream-json
719
- `)}async function Su(t,e,n,r){let o=e.flags.json===!0,[i]=e.positional;if(t==="oracle")return Fs({positional:e.positional,flags:e.flags,endpointOverride:r,endpoint:n});if(t==="acp"){let a=await Promise.resolve().then(()=>(Gn(),Wn));return Gs({positional:e.positional,flags:e.flags,endpointOverride:r},a.default.version)}switch(t){case"init":return Ao({endpoint:n,force:e.flags.force===!0});case"logout":return Po();case"status":return Mo({endpoint:r,json:o});case"whoami":return To({endpoint:r,json:o});case"apply-patch":return Do({patchFile:i,threeWay:e.flags["3way"]===!0,dryRun:e.flags["dry-run"]===!0});case"proxy":console.log(Ou("Local proxy mode is coming in a future release.")),console.log(ot(" Until then, your agents talk directly to the AnyForge proxy."));return;case"skill":return Ci({subcommand:i,name:e.positional[1],scope:C(e.flags.scope),client:C(e.flags.client),force:e.flags.force===!0,json:o})}let s=await qe(r);switch(t){case"approvals":return Bo({creds:s,json:o,limit:ve(e.flags.limit,"limit")});case"approve":return pr({creds:s,json:o,approvalId:i,decision:"APPROVED",notes:C(e.flags.notes)});case"reject":return pr({creds:s,json:o,approvalId:i,decision:"REJECTED",notes:C(e.flags.notes)});case"crews":return Wo({creds:s,json:o,status:C(e.flags.status),activeOnly:nn(e.flags.active),limit:ve(e.flags.limit,"limit")});case"crew":{let[a,c]=e.positional;return a==="start"?Vo({creds:s,json:o,workItemId:c,profileId:C(e.flags.profile),follow:e.flags.follow===!0,intervalSeconds:ve(e.flags.interval,"interval")}):a==="follow"?hr({creds:s,json:o,crewId:c,intervalSeconds:ve(e.flags.interval,"interval")}):Go({creds:s,json:o,crewId:i,messages:ve(e.flags.messages,"messages")})}case"halt":return mr({creds:s,json:o,crewId:i,action:"halt"});case"resume":return mr({creds:s,json:o,crewId:i,action:"resume"});case"studio":return oi({creds:s,json:o,command:i,id:e.positional[1],input:C(e.flags.input),context:C(e.flags.context),file:C(e.flags.file),repo:C(e.flags.repo),branch:C(e.flags.branch),initiative:C(e.flags.initiative),cursor:C(e.flags.cursor),uncommitted:nn(e.flags.uncommitted)});case"work":return ai({creds:s,json:o,status:C(e.flags.status),type:C(e.flags.type),priority:C(e.flags.priority),initiative:C(e.flags.initiative),program:C(e.flags.program),search:C(e.flags.search),assignee:C(e.flags.assignee),includeArchived:nn(e.flags.archived),limit:ve(e.flags.limit,"limit"),all:nn(e.flags.all)});case"workflows":return ci({creds:s,json:o});case"run":return li({creds:s,json:o,workflowId:i,input:C(e.flags.input)});case"ask":return di({creds:s,json:o,question:e.positional.join(" "),context:C(e.flags.context)});case"backtest-corpus":return pi({creds:s,json:o,days:ve(e.flags.days,"days")});case"review-interception":return mi({creds:s,json:o,days:ve(e.flags.days,"days")});case"review-coverage":return hi({creds:s,json:o,days:ve(e.flags.days,"days")});case"audit":return Oi({creds:s,json:o,eventType:C(e.flags.event),actor:C(e.flags.actor),from:C(e.flags.from),to:C(e.flags.to),limit:ve(e.flags.limit,"limit"),cursor:C(e.flags.cursor),all:nn(e.flags.all),verify:e.flags.verify===!0});case"people":return xi({creds:s,json:o,subcommand:i,email:e.positional[1],role:C(e.flags.role),team:C(e.flags.team),notify:e.flags["no-email"]!==!0});default:throw new h(`Unknown command: ${t}`,"Run `anyforge --help` for the command list.")}}async function Eu(){let t;try{t=xu(process.argv.slice(2))}catch(r){zs(r);return}if(!t.command&&t.flags.login===!0&&(t.command="acp"),t.flags.version===!0){let r=await Promise.resolve().then(()=>(Gn(),Wn));console.log(r.default.version);return}if(t.command==="oracle"&&t.flags.help===!0){Iu();return}if(t.command==="acp"&&t.flags.help===!0){console.log(Ws());return}if(t.flags.help===!0||!t.command){Ru();return}let e=C(t.flags.endpoint)??process.env.ANYFORGE_ENDPOINT,n=e??Ks;try{if(t.command!=="oracle"&&t.command!=="acp"){let o=["approve","codebase","file","output","print","read-only","shared","stream-json","workspace","write"].find(i=>t.flags[i]!==void 0);if(o)throw new h(`--${o} is only supported by the oracle command.`)}if(t.command!=="acp"&&t.flags.login!==void 0)throw new h("--login is only supported by the acp command.");Io(t.command),await Su(t.command,t,n,e)}catch(r){zs(r)}}function zs(t){t instanceof f&&(process.stderr.write(Hs("Error: ")+p(t.message)+`
720
- `),t.hint?process.stderr.write(ot(p(t.hint))+`
721
- `):t instanceof h&&process.stderr.write(ot("Run `anyforge --help`.")+`
722
- `),process.exit(t.exitCode)),process.stderr.write(Hs("Error: ")+p(t instanceof Error?t.message:String(t))+`
723
- `),process.exit(g.FAILURE)}Eu();
751
+ `)}async function Gp(e,t,n,r){let o=t.flags.json===!0,[i]=t.positional;if(e==="oracle")return Fa({positional:t.positional,flags:t.flags,endpointOverride:r,endpoint:n});if(e==="acp"){let a=await Promise.resolve().then(()=>(sr(),ir));return Wa({positional:t.positional,flags:t.flags,endpointOverride:r},a.default.version)}switch(e){case"init":return Qo({endpoint:n,force:t.flags.force===!0});case"logout":return ci();case"status":return mi({endpoint:r,json:o});case"whoami":return si({endpoint:r,json:o});case"apply-patch":return gi({patchFile:i,threeWay:t.flags["3way"]===!0,dryRun:t.flags["dry-run"]===!0});case"proxy":console.log(Mp("Local proxy mode is coming in a future release.")),console.log(Je(" Until then, your agents talk directly to the AnyForge proxy."));return;case"skill":return Es({subcommand:i,name:t.positional[1],scope:E(t.flags.scope),client:E(t.flags.client),team:E(t.flags.team),force:t.flags.force===!0,dryRun:t.flags["dry-run"]===!0,json:o,resolveCreds:()=>Re(r)})}let s=await Re(r);switch(e){case"approvals":return wi({creds:s,json:o,limit:Oe(t.flags.limit,"limit")});case"approve":return Ir({creds:s,json:o,approvalId:i,decision:"APPROVED",notes:E(t.flags.notes)});case"reject":return Ir({creds:s,json:o,approvalId:i,decision:"REJECTED",notes:E(t.flags.notes)});case"crews":return yi({creds:s,json:o,status:E(t.flags.status),activeOnly:fn(t.flags.active),limit:Oe(t.flags.limit,"limit")});case"crew":{let[a,c]=t.positional;return a==="start"?Ai({creds:s,json:o,workItemId:c,profileId:E(t.flags.profile),follow:t.flags.follow===!0,intervalSeconds:Oe(t.flags.interval,"interval")}):a==="follow"?Cr({creds:s,json:o,crewId:c,intervalSeconds:Oe(t.flags.interval,"interval")}):vi({creds:s,json:o,crewId:i,messages:Oe(t.flags.messages,"messages")})}case"halt":return Tr({creds:s,json:o,crewId:i,action:"halt"});case"resume":return Tr({creds:s,json:o,crewId:i,action:"resume"});case"studio":return Pi({creds:s,json:o,command:i,id:t.positional[1],input:E(t.flags.input),context:E(t.flags.context),file:E(t.flags.file),repo:E(t.flags.repo),branch:E(t.flags.branch),initiative:E(t.flags.initiative),cursor:E(t.flags.cursor),uncommitted:fn(t.flags.uncommitted)});case"work":return Fi({creds:s,json:o,status:E(t.flags.status),type:E(t.flags.type),priority:E(t.flags.priority),initiative:E(t.flags.initiative),program:E(t.flags.program),search:E(t.flags.search),assignee:E(t.flags.assignee),includeArchived:fn(t.flags.archived),limit:Oe(t.flags.limit,"limit"),all:fn(t.flags.all)});case"workflows":return qi({creds:s,json:o});case"run":return Mi({creds:s,json:o,workflowId:i,input:E(t.flags.input)});case"ask":return Ui({creds:s,json:o,question:t.positional.join(" "),context:E(t.flags.context)});case"backtest-corpus":return Di({creds:s,json:o,days:Oe(t.flags.days,"days")});case"review-interception":return Wi({creds:s,json:o,days:Oe(t.flags.days,"days")});case"review-coverage":return Gi({creds:s,json:o,days:Oe(t.flags.days,"days")});case"audit":return Xi({creds:s,json:o,eventType:E(t.flags.event),actor:E(t.flags.actor),from:E(t.flags.from),to:E(t.flags.to),limit:Oe(t.flags.limit,"limit"),cursor:E(t.flags.cursor),all:fn(t.flags.all),verify:t.flags.verify===!0});case"time":return os({creds:s,json:o,subcommand:i,date:E(t.flags.date),entries:E(t.flags.entries)});case"people":return es({creds:s,json:o,subcommand:i,email:t.positional[1],role:E(t.flags.role),team:E(t.flags.team),notify:t.flags["no-email"]!==!0});default:throw new f(`Unknown command: ${e}`,"Run `anyforge --help` for the command list.")}}async function Hp(){let e;try{e=Dp(process.argv.slice(2))}catch(r){Ha(r);return}if(!e.command&&e.flags.login===!0&&(e.command="acp"),e.flags.version===!0){let r=await Promise.resolve().then(()=>(sr(),ir));console.log(r.default.version);return}if(e.command==="oracle"&&e.flags.help===!0){Wp();return}if(e.command==="acp"&&e.flags.help===!0){console.log(Ba());return}if(e.flags.help===!0||!e.command){Bp();return}let t=E(e.flags.endpoint)??process.env.ANYFORGE_ENDPOINT,n=t??za;try{if(e.command!=="oracle"&&e.command!=="acp"){let o=["approve","codebase","file","output","print","read-only","shared","stream-json","workspace","write"].find(i=>e.flags[i]!==void 0);if(o)throw new f(`--${o} is only supported by the oracle command.`)}if(e.command!=="acp"&&e.flags.login!==void 0)throw new f("--login is only supported by the acp command.");ni(e.command),await Gp(e.command,e,n,t)}catch(r){Ha(r)}}function Ha(e){e instanceof h&&(process.stderr.write(Ga("Error: ")+p(e.message)+`
752
+ `),e.hint?process.stderr.write(Je(p(e.hint))+`
753
+ `):e instanceof f&&process.stderr.write(Je("Run `anyforge --help`.")+`
754
+ `),process.exit(e.exitCode)),process.stderr.write(Ga("Error: ")+p(e instanceof Error?e.message:String(e))+`
755
+ `),process.exit(g.FAILURE)}Hp();