@dezycro-ai/agent-plugin 2.5.2 → 2.6.1

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.
@@ -1,47 +1,56 @@
1
1
  #!/usr/bin/env node
2
- "use strict";var Pe=Object.create;var A=Object.defineProperty;var Oe=Object.getOwnPropertyDescriptor;var Me=Object.getOwnPropertyNames;var ze=Object.getPrototypeOf,Ue=Object.prototype.hasOwnProperty;var Be=(r,e)=>{for(var t in e)A(r,t,{get:e[t],enumerable:!0})},ae=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of Me(e))!Ue.call(r,o)&&o!==t&&A(r,o,{get:()=>e[o],enumerable:!(n=Oe(e,o))||n.enumerable});return r};var l=(r,e,t)=>(t=r!=null?Pe(ze(r)):{},ae(e||!r||!r.__esModule?A(t,"default",{value:r,enumerable:!0}):t,r)),_e=r=>ae(A({},"__esModule",{value:!0}),r);var We={};Be(We,{Command:()=>$e});module.exports=_e(We);var fe=l(require("node:http")),x=l(require("node:path")),P=l(require("node:fs")),ge=l(require("node:os")),V=require("node:child_process");var g={MessageStart:"message_start",ContentBlockStart:"content_block_start",ContentBlockDelta:"content_block_delta",ContentBlockStop:"content_block_stop",MessageDelta:"message_delta",MessageStop:"message_stop"};var ce={UpdateTask:"update_task"},le={Status:"status"},H={Done:"DONE",Pushed:"PUSHED"};var y={SpecPublished:"specPublished",VerifiedRecently:"verifiedRecently"};var w={Claude:"claude",Codex:"codex"},Z={Health:"/__dz/health",Stop:"/__dz/stop"},ue="last-opus-model";var I=l(require("fs")),pe=l(require("os")),p=l(require("path")),d=class r{constructor(e){this.repoRoot=e;this.config=r.readJson(p.join(e,".dezycro","config.json"))}repoRoot;config;static at(e=process.cwd()){return new r(r.findRepoRoot(e)??p.resolve(e))}get workspaceId(){return r.env("DZ_WORKSPACE_ID")??r.str(this.config?.workspaceId)}get tenantId(){return r.env("DZ_TENANT_ID")??r.str(this.config?.tenantId)}get projectId(){return r.str(this.config?.projectId)}agent(e){let t=this.agentNode(e);return{context1m:r.boolFlag("DZ_1M",t?.context1m),toolSearch:r.env("DZ_TOOL_SEARCH")??r.str(t?.toolSearch)}}agentNode(e){let t=this.config?.agents;if(!r.isObj(t))return null;let n=t[e];return r.isObj(n)?n:null}static boolFlag(e,t){let n=r.env(e);return n!==null?n==="1"||n.toLowerCase()==="true":t===!0}get apiUrl(){let e=r.env("DZ_API_URL");if(e)return r.trimSlash(e);let t=Array.isArray(this.config?.environments)?this.config?.environments:[],n=t.find(i=>r.isObj(i)&&i.default===!0)??t[0],o=r.isObj(n)?r.str(n.apiUrl):null;return o?r.trimSlash(o):null}get token(){let e=r.env("DZ_TOKEN");if(e)return e;let t=[p.join(this.repoRoot,".dezycro","companion-token.local.json"),p.join(pe.homedir(),".dezycro","token.json")];for(let n of t){let o=r.str(r.readJson(n)?.token);if(o)return o}return null}activeFeature(){let e=r.readJson(p.join(this.repoRoot,".dezycro","active-feature.json")),t=r.str(e?.featureId);if(!t)return null;let n=r.str(e?.workspaceId)??this.workspaceId;return n?{workspaceId:n,featureId:t,active:e?.active===!0}:null}connection(){let e=this.apiUrl;if(!e)return{error:"no apiUrl (set DZ_API_URL or .dezycro/config.json environments[].apiUrl)"};let t=this.workspaceId;if(!t)return{error:"no workspaceId (set DZ_WORKSPACE_ID or .dezycro/config.json workspaceId)"};let n=this.token;return n?{apiUrl:e,workspaceId:t,token:n}:{error:"no token (run `dezycro setup` or set DZ_TOKEN)"}}static findRepoRoot(e){let t=p.resolve(e||process.cwd());for(let n=0;n<64;n+=1){if(I.existsSync(p.join(t,".dezycro")))return t;let o=p.dirname(t);if(o===t)return null;t=o}return null}static readJson(e){try{return JSON.parse(I.readFileSync(e,"utf8"))}catch{return null}}static isObj(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}static str(e){return typeof e=="string"&&e.length>0?e:null}static env(e){let t=process.env[e];return t&&t.length>0?t:null}static trimSlash(e){return e.replace(/\/$/,"")}};var de=l(require("node:fs")),$=l(require("node:path"));var D=class{constructor(e){this.ctx=e}ctx;launchEnv(e){return{}}adjustArgs(e,t){return{rest:e}}get base(){return`http://127.0.0.1:${this.ctx.port}`}},W=class r extends D{gatewayEnv(){return{ANTHROPIC_BASE_URL:this.base}}launchEnv(e){let t={};return e.context1m&&!process.env.ANTHROPIC_DEFAULT_OPUS_MODEL&&(t.ANTHROPIC_DEFAULT_OPUS_MODEL=this.opusModelId()),e.toolSearch&&!process.env.ENABLE_TOOL_SEARCH&&(t.ENABLE_TOOL_SEARCH=e.toolSearch),t}adjustArgs(e,t){return r.isResume(e)?t.context1m?e.some(o=>o==="--model"||o==="-m"||o.startsWith("--model="))?{rest:e}:{rest:[...e,"--model",this.opusModelId()]}:{rest:e,notice:"dezycro: 1M context off \u2014 window caps at 200k on resume. Enable with DZ_1M=1 or agents.claude.context1m:true in .dezycro/config.json"}:{rest:e}}opusModelId(){return r.with1m(process.env.DZ_OPUS_MODEL||this.lastSeenOpusModel()||"claude-opus-4-8")}lastSeenOpusModel(){try{let e=$.join($.dirname(this.ctx.stateFile),ue);return de.readFileSync(e,"utf8").trim()||null}catch{return null}}static isResume(e){return e.some(t=>["--resume","-r","--continue","-c"].includes(t))}static with1m(e){return e.includes("[")?e:`${e}[1m]`}},G=class extends D{gatewayEnv(){return{OPENAI_BASE_URL:`${this.base}/v1`}}};function me(r,e){switch(r){case w.Codex:return new G(e);case w.Claude:default:return new W(e)}}var he=l(require("node:fs")),k=l(require("node:path")),S=class r{static MAX_DEPTH=6;static dir(){let e=__dirname;for(let t=0;t<r.MAX_DEPTH;t+=1){if(he.existsSync(k.join(e,"package.json")))return e;e=k.dirname(e)}return k.resolve(__dirname,"..","..")}static file(...e){return k.join(r.dir(),...e)}};var O=class r{static DEFAULT_SHIM_CMDS=["npm","pnpm","yarn","pip","pip3","poetry","apt-get","apt","brew","gem","bundle","mvn","gradle","make"];port=Number(process.env.DZ_PORT||13579);proxyPath=S.file("dist","gateway","proxy.js");compactPath=S.file("dist","gateway","compact-cli.js");async run(e){let t=this.applyPortFlag(e),n=t[0];if(n==="--stop"){console.error(await this.control(Z.Stop)?"dezycro: stopped":"dezycro: not running");return}if(n==="--status"){console.error(await this.health()?"dezycro: up":"dezycro: down");return}let o=n||w.Claude,i=t.slice(1),s=await this.ensureDaemon();console.error(s?`dezycro: gateway on :${this.port} \u2014 launching ${o}`:`dezycro: gateway unavailable \u2014 launching ${o} DIRECTLY (fail-open)`);let a=me(o,{port:this.port,stateFile:this.stateFile()}),c=d.at(process.cwd()).agent(o);if(s){let m=a.adjustArgs(i,c);i=m.rest,m.notice&&console.error(m.notice)}let f=this.setupShims(),se={...process.env,...s?a.gatewayEnv():{},...s?a.launchEnv(c):{},...f?.env??{}},R=()=>{if(f)try{P.default.rmSync(f.dir,{recursive:!0,force:!0})}catch{}},ie=(0,V.spawn)(o,i,{env:se,stdio:"inherit"});ie.on("exit",m=>{R(),process.exit(m??0)}),ie.on("error",m=>{R(),console.error(`dezycro: failed to launch ${o}: ${m.message}`),process.exit(127)})}applyPortFlag(e){let t=[];for(let n=0;n<e.length;n+=1){let o=e[n];if(o==="--port"){let i=Number(e[n+1]);e[n+1]!==void 0&&Number.isInteger(i)&&i>0&&(this.port=i,n+=1);continue}if(o.startsWith("--port=")){let i=Number(o.slice(7));Number.isInteger(i)&&i>0&&(this.port=i);continue}t.push(o)}return t}stateFile(){return process.env.DZ_STATE_FILE||x.default.join(process.cwd(),".dezycro","companion-state.json")}static with1m(e){return e.includes("[")?e:`${e}[1m]`}async ensureDaemon(){if(await this.health())return!0;let e={...process.env,PORT:String(this.port),INJECT:process.env.INJECT||"1",GATE:process.env.GATE||"0",CAPTURE:process.env.CAPTURE||"0",DZ_STATE_FILE:this.stateFile()};(0,V.spawn)(process.execPath,[this.proxyPath],{env:e,detached:!0,stdio:"ignore"}).unref();for(let n=0;n<30;n+=1){if(await this.health())return!0;await r.sleep(100)}return!1}setupShims(){if(process.env.DZ_NO_SHIM==="1"||process.env.DZ_SHIMS==="0")return null;let e=(process.env.DZ_SHIM_CMDS||r.DEFAULT_SHIM_CMDS.join(",")).split(",").map(t=>t.trim()).filter(Boolean);if(!e.length)return null;try{let t=P.default.mkdtempSync(x.default.join(ge.default.tmpdir(),"dz-shims-")),n=process.env.PATH||"";for(let o of e){let i=`#!/usr/bin/env bash
3
- real="$(PATH="$DZ_REAL_PATH" command -v ${o} 2>/dev/null)"
4
- if [ -z "$real" ] || [ "\${DZ_NO_SHIM:-0}" = "1" ]; then exec "${o}" "$@"; fi
2
+ "use strict";var qe=Object.create;var M=Object.defineProperty;var Ke=Object.getOwnPropertyDescriptor;var Ye=Object.getOwnPropertyNames;var Xe=Object.getPrototypeOf,Qe=Object.prototype.hasOwnProperty;var et=(o,e)=>{for(var t in e)M(o,t,{get:e[t],enumerable:!0})},we=(o,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Ye(e))!Qe.call(o,r)&&r!==t&&M(o,r,{get:()=>e[r],enumerable:!(n=Ke(e,r))||n.enumerable});return o};var p=(o,e,t)=>(t=o!=null?qe(Xe(o)):{},we(e||!o||!o.__esModule?M(t,"default",{value:o,enumerable:!0}):t,o)),tt=o=>we(M({},"__esModule",{value:!0}),o);var mt={};et(mt,{Command:()=>Ve});module.exports=tt(mt);var Pe=p(require("node:http")),$=p(require("node:path")),N=p(require("node:fs")),Ae=p(require("node:os")),re=require("node:child_process");var S={MessageStart:"message_start",ContentBlockStart:"content_block_start",ContentBlockDelta:"content_block_delta",ContentBlockStop:"content_block_stop",MessageDelta:"message_delta",MessageStop:"message_stop"};var be={UpdateTask:"update_task"},Se={Status:"status"},Q={Done:"DONE",Pushed:"PUSHED"};var T={SpecPublished:"specPublished",VerifiedRecently:"verifiedRecently"};var P={Claude:"claude",Codex:"codex"},ee={Health:"/__dz/health",Stop:"/__dz/stop"},Te="last-opus-model";var z=p(require("fs")),xe=p(require("os")),v=p(require("path")),k=class o{constructor(e){this.repoRoot=e;this.config=o.readJson(v.join(e,".dezycro","config.json"))}repoRoot;config;static at(e=process.cwd()){return new o(o.findRepoRoot(e)??v.resolve(e))}get workspaceId(){return o.env("DZ_WORKSPACE_ID")??o.str(this.config?.workspaceId)}get tenantId(){return o.env("DZ_TENANT_ID")??o.str(this.config?.tenantId)}get projectId(){return o.str(this.config?.projectId)}agent(e){let t=this.agentNode(e);return{context1m:o.boolFlag("DZ_1M",t?.context1m),toolSearch:o.env("DZ_TOOL_SEARCH")??o.str(t?.toolSearch)}}agentNode(e){let t=this.config?.agents;if(!o.isObj(t))return null;let n=t[e];return o.isObj(n)?n:null}static boolFlag(e,t){let n=o.env(e);return n!==null?n==="1"||n.toLowerCase()==="true":t===!0}get apiUrl(){let e=o.env("DZ_API_URL");if(e)return o.trimSlash(e);let t=Array.isArray(this.config?.environments)?this.config?.environments:[],n=t.find(i=>o.isObj(i)&&i.default===!0)??t[0],r=o.isObj(n)?o.str(n.apiUrl):null;return r?o.trimSlash(r):null}get token(){let e=o.env("DZ_TOKEN");if(e)return e;let t=[v.join(this.repoRoot,".dezycro","companion-token.local.json"),v.join(xe.homedir(),".dezycro","token.json")];for(let n of t){let r=o.str(o.readJson(n)?.token);if(r)return r}return null}activeFeature(){let e=o.readJson(v.join(this.repoRoot,".dezycro","active-feature.json")),t=o.str(e?.featureId);if(!t)return null;let n=o.str(e?.workspaceId)??this.workspaceId;return n?{workspaceId:n,featureId:t,active:e?.active===!0}:null}connection(){let e=this.apiUrl;if(!e)return{error:"no apiUrl (set DZ_API_URL or .dezycro/config.json environments[].apiUrl)"};let t=this.workspaceId;if(!t)return{error:"no workspaceId (set DZ_WORKSPACE_ID or .dezycro/config.json workspaceId)"};let n=this.token;return n?{apiUrl:e,workspaceId:t,token:n}:{error:"no token (run `dezycro setup` or set DZ_TOKEN)"}}static findRepoRoot(e){let t=v.resolve(e||process.cwd());for(let n=0;n<64;n+=1){if(z.existsSync(v.join(t,".dezycro")))return t;let r=v.dirname(t);if(r===t)return null;t=r}return null}static readJson(e){try{return JSON.parse(z.readFileSync(e,"utf8"))}catch{return null}}static isObj(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}static str(e){return typeof e=="string"&&e.length>0?e:null}static env(e){let t=process.env[e];return t&&t.length>0?t:null}static trimSlash(e){return e.replace(/\/$/,"")}};var Ce=p(require("node:fs")),j=p(require("node:path"));var U=class{constructor(e){this.ctx=e}ctx;launchEnv(e){return{}}adjustArgs(e,t){return{rest:e}}get base(){return`http://127.0.0.1:${this.ctx.port}`}},te=class o extends U{gatewayEnv(){return{ANTHROPIC_BASE_URL:this.base}}launchEnv(e){let t={};return e.context1m&&!process.env.ANTHROPIC_DEFAULT_OPUS_MODEL&&(t.ANTHROPIC_DEFAULT_OPUS_MODEL=this.opusModelId()),e.toolSearch&&!process.env.ENABLE_TOOL_SEARCH&&(t.ENABLE_TOOL_SEARCH=e.toolSearch),t}adjustArgs(e,t){return o.isResume(e)?t.context1m?e.some(r=>r==="--model"||r==="-m"||r.startsWith("--model="))?{rest:e}:{rest:[...e,"--model",this.opusModelId()]}:{rest:e,notice:"dezycro: 1M context off \u2014 window caps at 200k on resume. Enable with DZ_1M=1 or agents.claude.context1m:true in .dezycro/config.json"}:{rest:e}}opusModelId(){return o.with1m(process.env.DZ_OPUS_MODEL||this.lastSeenOpusModel()||"claude-opus-4-8")}lastSeenOpusModel(){try{let e=j.join(j.dirname(this.ctx.stateFile),Te);return Ce.readFileSync(e,"utf8").trim()||null}catch{return null}}static isResume(e){return e.some(t=>["--resume","-r","--continue","-c"].includes(t))}static with1m(e){return e.includes("[")?e:`${e}[1m]`}},ne=class extends U{gatewayEnv(){return{OPENAI_BASE_URL:`${this.base}/v1`}}};function Re(o,e){switch(o){case P.Codex:return new ne(e);case P.Claude:default:return new te(e)}}var Ee=p(require("node:fs")),x=p(require("node:path")),A=class o{static MAX_DEPTH=6;static dir(){let e=__dirname;for(let t=0;t<o.MAX_DEPTH;t+=1){if(Ee.existsSync(x.join(e,"package.json")))return e;e=x.dirname(e)}return x.resolve(__dirname,"..","..")}static file(...e){return x.join(o.dir(),...e)}};var _=class o{static DEFAULT_SHIM_CMDS=["npm","pnpm","yarn","pip","pip3","poetry","apt-get","apt","brew","gem","bundle","mvn","gradle","make"];port=Number(process.env.DZ_PORT||13579);proxyPath=A.file("dist","gateway","proxy.js");compactPath=A.file("dist","gateway","compact-cli.js");async run(e){let t=this.applyPortFlag(e),n=t[0];if(n==="--stop"){console.error(await this.control(ee.Stop)?"dezycro: stopped":"dezycro: not running");return}if(n==="--status"){console.error(await this.health()?"dezycro: up":"dezycro: down");return}let r=n||P.Claude,i=t.slice(1),s=await this.ensureDaemon();console.error(s?`dezycro: gateway on :${this.port} \u2014 launching ${r}`:`dezycro: gateway unavailable \u2014 launching ${r} DIRECTLY (fail-open)`);let l=Re(r,{port:this.port,stateFile:this.stateFile()}),c=k.at(process.cwd()).agent(r);if(s){let d=l.adjustArgs(i,c);i=d.rest,d.notice&&console.error(d.notice)}let m=this.setupShims(),a={...process.env,...s?l.gatewayEnv():{},...s?l.launchEnv(c):{},...m?.env??{}},u=()=>{if(m)try{N.default.rmSync(m.dir,{recursive:!0,force:!0})}catch{}},f=(0,re.spawn)(r,i,{env:a,stdio:"inherit"});f.on("exit",d=>{u(),process.exit(d??0)}),f.on("error",d=>{u(),console.error(`dezycro: failed to launch ${r}: ${d.message}`),process.exit(127)})}applyPortFlag(e){let t=[];for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="--port"){let i=Number(e[n+1]);e[n+1]!==void 0&&Number.isInteger(i)&&i>0&&(this.port=i,n+=1);continue}if(r.startsWith("--port=")){let i=Number(r.slice(7));Number.isInteger(i)&&i>0&&(this.port=i);continue}t.push(r)}return t}stateFile(){return process.env.DZ_STATE_FILE||$.default.join(process.cwd(),".dezycro","companion-state.json")}static with1m(e){return e.includes("[")?e:`${e}[1m]`}async ensureDaemon(){if(await this.health())return!0;let e={...process.env,PORT:String(this.port),INJECT:process.env.INJECT||"1",GATE:process.env.GATE||"0",CAPTURE:process.env.CAPTURE||"0",DZ_STATE_FILE:this.stateFile()};(0,re.spawn)(process.execPath,[this.proxyPath],{env:e,detached:!0,stdio:"ignore"}).unref();for(let n=0;n<30;n+=1){if(await this.health())return!0;await o.sleep(100)}return!1}setupShims(){if(process.env.DZ_NO_SHIM==="1"||process.env.DZ_SHIMS==="0")return null;let e=(process.env.DZ_SHIM_CMDS||o.DEFAULT_SHIM_CMDS.join(",")).split(",").map(t=>t.trim()).filter(Boolean);if(!e.length)return null;try{let t=N.default.mkdtempSync($.default.join(Ae.default.tmpdir(),"dz-shims-")),n=process.env.PATH||"";for(let r of e){let i=`#!/usr/bin/env bash
3
+ real="$(PATH="$DZ_REAL_PATH" command -v ${r} 2>/dev/null)"
4
+ if [ -z "$real" ] || [ "\${DZ_NO_SHIM:-0}" = "1" ]; then exec "${r}" "$@"; fi
5
5
  out="$("$real" "$@" 2>&1)"; rc=$?
6
- printf '%s\\n' "$out" | DZ_CMD=${o} node "$DZ_COMPACT_PATH"
6
+ printf '%s\\n' "$out" | DZ_CMD=${r} node "$DZ_COMPACT_PATH"
7
7
  exit $rc
8
- `;P.default.writeFileSync(x.default.join(t,o),i,{mode:493})}return{dir:t,env:{PATH:`${t}${x.default.delimiter}${n}`,DZ_REAL_PATH:n,DZ_COMPACT_PATH:this.compactPath}}}catch{return null}}health(){return this.get(Z.Health)}control(e){return this.get(e,()=>!0)}get(e,t=n=>n.statusCode===200){return new Promise(n=>{let o=fe.default.get({host:"127.0.0.1",port:this.port,path:e,timeout:800},i=>{i.resume(),n(t(i))});o.on("error",()=>n(!1)),o.on("timeout",()=>{o.destroy(),n(!1)})})}static sleep(e){return new Promise(t=>setTimeout(t,e))}};var ye=require("child_process"),q=l(require("fs")),ke=l(require("os")),Y=l(require("path")),ve=l(require("readline")),M=class r{static MCP_URL="https://mcp.dezycro.ai";run(){console.log(`
8
+ `;N.default.writeFileSync($.default.join(t,r),i,{mode:493})}return{dir:t,env:{PATH:`${t}${$.default.delimiter}${n}`,DZ_REAL_PATH:n,DZ_COMPACT_PATH:this.compactPath}}}catch{return null}}health(){return this.get(ee.Health)}control(e){return this.get(e,()=>!0)}get(e,t=n=>n.statusCode===200){return new Promise(n=>{let r=Pe.default.get({host:"127.0.0.1",port:this.port,path:e,timeout:800},i=>{i.resume(),n(t(i))});r.on("error",()=>n(!1)),r.on("timeout",()=>{r.destroy(),n(!1)})})}static sleep(e){return new Promise(t=>setTimeout(t,e))}};var oe=require("child_process"),se=p(require("fs")),$e=p(require("os")),ie=p(require("path")),De=p(require("readline")),B=class o{static MCP_URL="https://mcp.dezycro.ai";run(){console.log(`
9
9
  Dezycro Setup`),console.log(` Get your API key from Dezycro \u2192 Settings \u2192 API Keys
10
- `);let e=ve.default.createInterface({input:process.stdin,output:process.stdout});e.question(" API key (dzy_...): ",t=>{let n=(t||"").trim();(!n||!n.startsWith("dzy_"))&&(console.log(`
10
+ `);let e=De.default.createInterface({input:process.stdin,output:process.stdout});e.question(" API key (dzy_...): ",t=>{let n=(t||"").trim();(!n||!n.startsWith("dzy_"))&&(console.log(`
11
11
  Invalid key. Must start with dzy_
12
12
  `),e.close(),process.exit(1)),this.configureMcp(n),this.saveToken(n),console.log(`
13
13
  You're all set! In Claude Code run: /dezycro:import-features
14
- `),e.close()})}configureMcp(e){let t=`claude mcp add --transport http dezycro "${r.MCP_URL}/mcp" --header "Authorization: Bearer ${e}"`;try{(0,ye.execSync)(t,{stdio:"inherit"}),console.log(`
14
+ `),e.close()})}configureMcp(e){let t=`claude mcp add --transport http dezycro "${o.MCP_URL}/mcp" --header "Authorization: Bearer ${e}"`;try{try{(0,oe.execSync)("claude mcp remove dezycro",{stdio:"ignore"})}catch{}(0,oe.execSync)(t,{stdio:"inherit"}),console.log(`
15
15
  \u2713 Dezycro MCP configured`)}catch{console.error(`
16
16
  Failed to configure MCP. Run manually:`),console.error(` ${t}
17
- `)}}saveToken(e){try{let t=Y.default.join(ke.default.homedir(),".dezycro"),n=Y.default.join(t,"token.json");q.default.mkdirSync(t,{recursive:!0,mode:448}),q.default.writeFileSync(n,JSON.stringify({token:e},null,2),{mode:384}),console.log(` \u2713 Companion token saved to ${n}`)}catch(t){console.error(` Failed to save companion token: ${t.message}`),console.error(` Drop it manually at ~/.dezycro/token.json: {"token": "${e}"}
18
- `)}}};var z=l(require("fs")),be=l(require("os")),Te=require("child_process"),U=class r{static DEFAULT_PATH=".dezycro/auth.local.json";static PLACEHOLDER=/\$\{([A-Z0-9_]+)\}/g;run(e=r.DEFAULT_PATH){if(!z.default.existsSync(e)){this.warn(`no auth file at ${e}; emitting nothing`);return}let t;try{t=JSON.parse(z.default.readFileSync(e,"utf8"))}catch(i){this.warn(`parse error in ${e}: ${i.message}`),process.exit(1)}let n=this.resolveSecrets(t.secrets||{}),o=this.personaExports(t.personas||{},n);o.length&&process.stdout.write(o.join(`
17
+ `)}}saveToken(e){try{let t=ie.default.join($e.default.homedir(),".dezycro"),n=ie.default.join(t,"token.json");se.default.mkdirSync(t,{recursive:!0,mode:448}),se.default.writeFileSync(n,JSON.stringify({token:e},null,2),{mode:384}),console.log(` \u2713 Companion token saved to ${n}`)}catch(t){console.error(` Failed to save companion token: ${t.message}`),console.error(` Drop it manually at ~/.dezycro/token.json: {"token": "${e}"}
18
+ `)}}};var F=p(require("fs")),Ie=p(require("os")),Oe=require("child_process"),L=class o{static DEFAULT_PATH=".dezycro/auth.local.json";static PLACEHOLDER=/\$\{([A-Z0-9_]+)\}/g;run(e=o.DEFAULT_PATH){if(!F.default.existsSync(e)){this.warn(`no auth file at ${e}; emitting nothing`);return}let t;try{t=JSON.parse(F.default.readFileSync(e,"utf8"))}catch(i){this.warn(`parse error in ${e}: ${i.message}`),process.exit(1)}let n=this.resolveSecrets(t.secrets||{}),r=this.personaExports(t.personas||{},n);r.length&&process.stdout.write(r.join(`
19
19
  `)+`
20
- `)}resolveSecrets(e){let t={};for(let[n,o]of Object.entries(e))try{t[n]=this.resolveSecret(o)}catch(i){this.warn(`secret ${n}: ${i.message}`)}return t}personaExports(e,t){let n=[];for(let[o,i]of Object.entries(e))if(!(!i||!i.envVar))try{let s=this.substitute(i.config||{},t);n.push(`export ${i.envVar}=${r.shQuote(JSON.stringify(s))}`)}catch(s){this.warn(`persona '${o}' skipped: ${s.message}`)}return n}resolveSecret(e){if(typeof e!="string")return e;if(e.startsWith("!file:")){let t=e.slice(6);return(t==="~"||t.startsWith("~/"))&&(t=be.default.homedir()+t.slice(1)),z.default.readFileSync(t,"utf8").trimEnd()}if(e.startsWith("!env:")){let t=e.slice(5);if(!(t in process.env))throw new Error(`env var not set: ${t}`);return process.env[t]}if(e.startsWith("!cmd:")){let t=e.slice(5),o=(0,Te.execSync)(t,{encoding:"utf8",stdio:["ignore","pipe","pipe"]}).trimEnd();if(!o)throw new Error(`empty stdout from: ${t}`);return o}return e}substitute(e,t){if(typeof e=="string")return e.replace(r.PLACEHOLDER,(n,o)=>{if(!(o in t))throw new Error(`\${${o}} not in secrets`);return String(t[o])});if(Array.isArray(e))return e.map(n=>this.substitute(n,t));if(e&&typeof e=="object"){let n={};for(let[o,i]of Object.entries(e))n[o]=this.substitute(i,t);return n}return e}warn(e){process.stderr.write(`[dezycro resolve-auth] ${e}
21
- `)}static shQuote(e){return"'"+String(e).replaceAll("'","'\\''")+"'"}};var B=class r{run(e){let t=e[0],n=r.scriptFor(t);n===null&&(process.stderr.write(`Usage: dezycro completion <bash|zsh|fish>
20
+ `)}resolveSecrets(e){let t={};for(let[n,r]of Object.entries(e))try{t[n]=this.resolveSecret(r)}catch(i){this.warn(`secret ${n}: ${i.message}`)}return t}personaExports(e,t){let n=[];for(let[r,i]of Object.entries(e))if(!(!i||!i.envVar))try{let s=this.substitute(i.config||{},t);n.push(`export ${i.envVar}=${o.shQuote(JSON.stringify(s))}`)}catch(s){this.warn(`persona '${r}' skipped: ${s.message}`)}return n}resolveSecret(e){if(typeof e!="string")return e;if(e.startsWith("!file:")){let t=e.slice(6);return(t==="~"||t.startsWith("~/"))&&(t=Ie.default.homedir()+t.slice(1)),F.default.readFileSync(t,"utf8").trimEnd()}if(e.startsWith("!env:")){let t=e.slice(5);if(!(t in process.env))throw new Error(`env var not set: ${t}`);return process.env[t]}if(e.startsWith("!cmd:")){let t=e.slice(5),r=(0,Oe.execSync)(t,{encoding:"utf8",stdio:["ignore","pipe","pipe"]}).trimEnd();if(!r)throw new Error(`empty stdout from: ${t}`);return r}return e}substitute(e,t){if(typeof e=="string")return e.replace(o.PLACEHOLDER,(n,r)=>{if(!(r in t))throw new Error(`\${${r}} not in secrets`);return String(t[r])});if(Array.isArray(e))return e.map(n=>this.substitute(n,t));if(e&&typeof e=="object"){let n={};for(let[r,i]of Object.entries(e))n[r]=this.substitute(i,t);return n}return e}warn(e){process.stderr.write(`[dezycro resolve-auth] ${e}
21
+ `)}static shQuote(e){return"'"+String(e).replaceAll("'","'\\''")+"'"}};var J=class o{run(e){let t=e[0],n=o.scriptFor(t);n===null&&(process.stderr.write(`Usage: dezycro completion <bash|zsh|fish>
22
22
  `),process.stderr.write(`Then load it in your shell, e.g.: eval "$(dezycro completion zsh)"
23
- `),process.exit(2)),process.stdout.write(n)}static scriptFor(e){switch(e){case"bash":return r.bash();case"zsh":return r.zsh();case"fish":return r.fish();default:return null}}static bash(){return["_dezycro() {"," local cur prev cmd",' cur="${COMP_WORDS[COMP_CWORD]}"',' prev="${COMP_WORDS[COMP_CWORD-1]}"',' if [ "$COMP_CWORD" -eq 1 ]; then',' COMPREPLY=( $(compgen -W "run setup resolve-auth completion help --help" -- "$cur") )'," return 0"," fi",' cmd="${COMP_WORDS[1]}"',' case "$cmd" in'," run)",' if [ "$prev" = "--port" ]; then return 0; fi',' COMPREPLY=( $(compgen -W "claude --status --stop --port" -- "$cur") ) ;;'," completion)",' COMPREPLY=( $(compgen -W "bash zsh fish" -- "$cur") ) ;;'," resolve-auth)",' COMPREPLY=( $(compgen -f -- "$cur") ) ;;'," esac"," return 0","}","complete -F _dezycro dezycro",""].join(`
23
+ `),process.exit(2)),process.stdout.write(n)}static scriptFor(e){switch(e){case"bash":return o.bash();case"zsh":return o.zsh();case"fish":return o.fish();default:return null}}static bash(){return["_dezycro() {"," local cur prev cmd",' cur="${COMP_WORDS[COMP_CWORD]}"',' prev="${COMP_WORDS[COMP_CWORD-1]}"',' if [ "$COMP_CWORD" -eq 1 ]; then',' COMPREPLY=( $(compgen -W "run setup resolve-auth completion help --help" -- "$cur") )'," return 0"," fi",' cmd="${COMP_WORDS[1]}"',' case "$cmd" in'," run)",' if [ "$prev" = "--port" ]; then return 0; fi',' COMPREPLY=( $(compgen -W "claude --status --stop --port" -- "$cur") ) ;;'," completion)",' COMPREPLY=( $(compgen -W "bash zsh fish" -- "$cur") ) ;;'," resolve-auth)",' COMPREPLY=( $(compgen -f -- "$cur") ) ;;'," esac"," return 0","}","complete -F _dezycro dezycro",""].join(`
24
24
  `)}static zsh(){return["_dezycro() {"," local -a commands"," commands=(run setup resolve-auth completion help)"," if (( CURRENT == 2 )); then"," compadd -- $commands"," return"," fi"," case ${words[2]} in"," run)"," if [[ ${words[CURRENT-1]} == --port ]]; then return; fi"," compadd -- claude --status --stop --port ;;"," completion) compadd -- bash zsh fish ;;"," resolve-auth) _files ;;"," esac","}","compdef _dezycro dezycro",""].join(`
25
25
  `)}static fish(){return["complete -c dezycro -f","complete -c dezycro -n __fish_use_subcommand -a run -d 'Run an agent through the gateway'","complete -c dezycro -n __fish_use_subcommand -a setup -d 'Configure the Dezycro MCP connection'","complete -c dezycro -n __fish_use_subcommand -a resolve-auth -d 'Print verifier credential exports'","complete -c dezycro -n __fish_use_subcommand -a completion -d 'Print a shell completion script'","complete -c dezycro -n '__fish_seen_subcommand_from run' -a 'claude --status --stop --port'","complete -c dezycro -n '__fish_seen_subcommand_from completion' -a 'bash zsh fish'",""].join(`
26
- `)}};var Ae=l(require("node:path"));var _=l(require("node:fs")),we=l(require("node:os")),v=l(require("node:path")),Ne="DZ_METRICS_DIR",Fe="DZ_METRICS",b=class r{file;constructor(e="gateway"){this.file=r.prepare(e)}static enabled(){return process.env[Fe]!=="0"}static dir(){let e=process.env[Ne];if(e)return e;let t=we.homedir();switch(process.platform){case"win32":return v.join(process.env.LOCALAPPDATA||v.join(t,"AppData","Local"),"Dezycro","metrics");case"darwin":return v.join(t,"Library","Application Support","Dezycro","metrics");default:return v.join(process.env.XDG_STATE_HOME||v.join(t,".local","state"),"dezycro","metrics")}}record(e){if(this.file)try{_.appendFileSync(this.file,`${JSON.stringify({ts:new Date().toISOString(),...e})}
27
- `)}catch{}}static prepare(e){if(!r.enabled())return null;try{let t=r.dir();return _.mkdirSync(t,{recursive:!0}),v.join(t,`${e}.jsonl`)}catch{return null}}};var je={rules:[{nameIncludes:ce.UpdateTask,inputField:le.Status,inputValues:[H.Done,H.Pushed],requireAll:[y.SpecPublished,y.VerifiedRecently],reason:"Cannot mark a task DONE/PUSHED yet."}]},K=class r{constructor(e,t){this.policy=e;this.state=t}policy;state;static STATE_PREDICATES={[y.SpecPublished]:e=>!!e&&e.needsSpecPublish!==!0,[y.VerifiedRecently]:e=>!e||!e.lastVerifyCompletionTs?!1:e.lastEditTs?Date.parse(e.lastVerifyCompletionTs)>=Date.parse(e.lastEditTs):!0};static FAILED_HINT={[y.SpecPublished]:"spec not published (controller/API changed) \u2014 run /dezycro:publish-spec",[y.VerifiedRecently]:"no passing verify since last edit \u2014 run /dezycro:verify"};decide(e,t){for(let n of this.policy?.rules??[]){if(!e||!e.includes(n.nameIncludes))continue;if(n.inputField){let a=t?.[n.inputField],c=(n.inputValues??[]).map(f=>String(f).toUpperCase());if(a==null||!c.includes(String(a).toUpperCase()))continue}let o=n.requireAll??[];if(o.length===0)return this.blocked(e,n.reason);let i=o.filter(a=>!r.STATE_PREDICATES[a]?.(this.state));if(i.length===0)continue;let s=i.map(a=>r.FAILED_HINT[a]||a).join("; ");return this.blocked(e,`${n.reason||"Blocked by Dezycro policy."} ${s}.`)}return null}blocked(e,t){return{toolName:e||"?",detail:t||"Blocked by Dezycro policy."}}};var vt=new Set([g.ContentBlockStart,g.ContentBlockDelta,g.ContentBlockStop]);var Ce=l(require("node:fs")),Ee=l(require("node:path"));var T=class r{static BIG_TOOL_CHARS=4e3;static defaultFile(){return Ee.join(b.dir(),"gateway.jsonl")}static fromFile(e=r.defaultFile()){let t;try{t=Ce.readFileSync(e,"utf8")}catch{return r.empty()}return r.aggregate(t.split(`
28
- `))}static aggregate(e){let t=r.empty();for(let n of e){let o=r.parse(n);o&&(o.type==="turn"?r.addTurn(t,o):o.type==="compaction"&&r.addCompaction(t,o),r.trackTime(t,o.ts))}return t}static addTurn(e,t){e.turns+=1,e.inputTokens+=r.num(t.inputTokens),e.outputTokens+=r.num(t.outputTokens),e.cacheReadTokens+=r.num(t.cacheReadTokens),e.cacheCreationTokens+=r.num(t.cacheCreationTokens),e.totalDurationMs+=r.num(t.durationMs),e.gateBlocks+=r.num(t.blockedToolCalls);let n=r.num(t.freshToolChars);if(e.maxFreshToolChars=Math.max(e.maxFreshToolChars,n),n>r.BIG_TOOL_CHARS&&(e.bigToolTurns+=1),e.maxToolDefChars=Math.max(e.maxToolDefChars,r.num(t.toolDefChars)),e.maxSystemChars=Math.max(e.maxSystemChars,r.num(t.systemChars)),e.tailResultChars+=r.num(t.tailResultChars),e.tailTextChars+=r.num(t.tailTextChars),e.maxCacheCreationTokens=Math.max(e.maxCacheCreationTokens,r.num(t.cacheCreationTokens)),typeof t.status=="number"&&t.status!==200&&(e.errorTurns+=1),r.bump(e.models,typeof t.model=="string"?t.model:"(unknown)"),Array.isArray(t.skillsTriggered))for(let o of t.skillsTriggered)r.bump(e.skillsTriggered,String(o))}static addCompaction(e,t){e.compactions+=1,e.charsSaved+=r.num(t.charsSaved),e.estTokensSaved+=r.num(t.estTokensSaved),e.linesElided+=r.num(t.elidedLines),r.bump(e.commands,typeof t.command=="string"?t.command:"(unknown)")}static trackTime(e,t){typeof t=="string"&&((!e.firstTs||t<e.firstTs)&&(e.firstTs=t),(!e.lastTs||t>e.lastTs)&&(e.lastTs=t))}static parse(e){if(!e.trim())return null;try{return JSON.parse(e)}catch{return null}}static bump(e,t){e[t]=(e[t]??0)+1}static num(e){return typeof e=="number"&&Number.isFinite(e)?e:0}static empty(){return{turns:0,inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreationTokens:0,totalDurationMs:0,errorTurns:0,gateBlocks:0,maxFreshToolChars:0,bigToolTurns:0,maxToolDefChars:0,maxSystemChars:0,tailResultChars:0,tailTextChars:0,maxCacheCreationTokens:0,models:{},skillsTriggered:{},compactions:0,charsSaved:0,estTokensSaved:0,linesElided:0,commands:{},firstTs:null,lastTs:null}}};var u=l(require("node:fs")),Re=l(require("node:path")),E=class r{apiUrl;workspaceId;token;metricsFile;cursorFile;batchSize;timeoutMs;fetchImpl;constructor(e){this.apiUrl=e.apiUrl,this.workspaceId=e.workspaceId,this.token=e.token,this.metricsFile=e.metricsFile,this.cursorFile=e.cursorFile,this.batchSize=e.batchSize??1e3,this.timeoutMs=e.timeoutMs??15e3,this.fetchImpl=e.fetchImpl??fetch}async upload({dryRun:e=!1}={}){let{lines:t,newOffset:n}=this.readNewLines(),o=r.toBatch(t),i=o.turns.length+o.compactions.length,s={turns:o.turns.length,compactions:o.compactions.length,newOffset:n,sent:!1,dryRun:e};return e?s:i===0?(n!==this.readCursor()&&this.writeCursor(n),s):(await this.postAll(o),this.writeCursor(n),{...s,sent:!0})}static toBatch(e){let t=[],n=[];for(let o of e){let i;try{i=JSON.parse(o)}catch{continue}typeof i.ts=="string"&&(i.type==="turn"?t.push(r.mapTurn(i)):i.type==="compaction"&&n.push(r.mapCompaction(i)))}return{turns:t,compactions:n}}static mapTurn(e){return{timestamp:e.ts,provider:e.provider??null,model:e.model??null,status:e.status??null,durationMs:e.durationMs??null,inputTokens:e.inputTokens??null,outputTokens:e.outputTokens??null,cacheReadTokens:e.cacheReadTokens??null,cacheCreationTokens:e.cacheCreationTokens??null,toolDefChars:e.toolDefChars??null,systemChars:e.systemChars??null,freshToolChars:e.freshToolChars??null,tailResultChars:e.tailResultChars??null,tailTextChars:e.tailTextChars??null,blockedToolCalls:e.blockedToolCalls??null,skillsFired:Array.isArray(e.skillsTriggered)?e.skillsTriggered:[]}}static mapCompaction(e){return{timestamp:e.ts,command:e.command??null,originalChars:e.originalChars??null,outputChars:e.outputChars??null,charsSaved:e.charsSaved??null,estTokensSaved:e.estTokensSaved??null,originalLines:e.originalLines??null,outputLines:e.outputLines??null,elidedLines:e.elidedLines??null}}readNewLines(){let e=this.readCursor(),t;try{t=u.statSync(this.metricsFile).size}catch{return{lines:[],newOffset:e}}if(e>t&&(e=0),e===t)return{lines:[],newOffset:e};let n=u.openSync(this.metricsFile,"r");try{let o=t-e,i=Buffer.alloc(o);u.readSync(n,i,0,o,e);let s=i.toString("utf8"),a=s.lastIndexOf(`
29
- `);if(a<0)return{lines:[],newOffset:e};let c=s.slice(0,a+1),f=e+Buffer.byteLength(c,"utf8");return{lines:c.split(`
30
- `).filter(R=>R.trim().length>0),newOffset:f}}finally{u.closeSync(n)}}readCursor(){try{let e=JSON.parse(u.readFileSync(this.cursorFile,"utf8"));return typeof e.offset=="number"&&e.offset>=0?e.offset:0}catch{return 0}}writeCursor(e){try{u.mkdirSync(Re.dirname(this.cursorFile),{recursive:!0}),u.writeFileSync(this.cursorFile,JSON.stringify({offset:e,uploadedAt:new Date().toISOString()}))}catch{}}async postAll(e){for(let t=0;t<e.turns.length;t+=this.batchSize)await this.postBatch({turns:e.turns.slice(t,t+this.batchSize),compactions:[]});for(let t=0;t<e.compactions.length;t+=this.batchSize)await this.postBatch({turns:[],compactions:e.compactions.slice(t,t+this.batchSize)})}async postBatch(e){let t=`${this.apiUrl}/api/v1/workspaces/${this.workspaceId}/usage-telemetry`,n=new AbortController,o=setTimeout(()=>n.abort(),this.timeoutMs);try{let i=await this.fetchImpl(t,{method:"POST",headers:{Authorization:`Bearer ${this.token}`,"content-type":"application/json",accept:"application/json"},body:JSON.stringify(e),signal:n.signal});if(!i.ok)throw new Error(`usage-telemetry upload returned ${i.status}`)}finally{clearTimeout(o)}}};var N=class r{run(e){if(e.includes("--upload-dry-run")){this.uploadDryRun();return}let t=T.defaultFile(),n=T.fromFile(t);if(e.includes("--json")){process.stdout.write(`${JSON.stringify(n,null,2)}
31
- `);return}process.stdout.write(r.format(n,t))}async uploadDryRun(){let e=c=>{process.stdout.write(`${c}
32
- `)},t=d.at(process.cwd()),n=t.connection(),o=T.defaultFile(),i=Ae.join(b.dir(),"gateway.upload-cursor.json"),s="error"in n?{apiUrl:"(unset)",workspaceId:t.workspaceId??"(unset)",token:"(unset)"}:n,a=await new E({...s,metricsFile:o,cursorFile:i}).upload({dryRun:!0});e("Dezycro telemetry \u2014 dry run (nothing sent)"),e(` metrics file ${o}`),e(` target ${s.apiUrl}/api/v1/workspaces/${s.workspaceId}/usage-telemetry`),"error"in n&&e(` connection NOT configured \u2014 ${n.error}`),e(` pending upload ${a.turns} turn + ${a.compactions} compaction event(s) since the cursor`)}static format(e,t){if(e.turns===0&&e.compactions===0)return`No metrics yet at ${t}
26
+ `)}};var Be=p(require("node:path"));var H=p(require("node:fs")),Me=p(require("node:os")),C=p(require("node:path")),nt="DZ_METRICS_DIR",rt="DZ_METRICS",R=class o{file;constructor(e="gateway"){this.file=o.prepare(e)}static enabled(){return process.env[rt]!=="0"}static dir(){let e=process.env[nt];if(e)return e;let t=Me.homedir();switch(process.platform){case"win32":return C.join(process.env.LOCALAPPDATA||C.join(t,"AppData","Local"),"Dezycro","metrics");case"darwin":return C.join(t,"Library","Application Support","Dezycro","metrics");default:return C.join(process.env.XDG_STATE_HOME||C.join(t,".local","state"),"dezycro","metrics")}}record(e){if(this.file)try{H.appendFileSync(this.file,`${JSON.stringify({ts:new Date().toISOString(),...e})}
27
+ `)}catch{}}static prepare(e){if(!o.enabled())return null;try{let t=o.dir();return H.mkdirSync(t,{recursive:!0}),C.join(t,`${e}.jsonl`)}catch{return null}}};var ot={rules:[{nameIncludes:be.UpdateTask,inputField:Se.Status,inputValues:[Q.Done,Q.Pushed],requireAll:[T.SpecPublished,T.VerifiedRecently],reason:"Cannot mark a task DONE/PUSHED yet."}]},ae=class o{constructor(e,t){this.policy=e;this.state=t}policy;state;static STATE_PREDICATES={[T.SpecPublished]:e=>!!e&&e.needsSpecPublish!==!0,[T.VerifiedRecently]:e=>!e||!e.lastVerifyCompletionTs?!1:e.lastEditTs?Date.parse(e.lastVerifyCompletionTs)>=Date.parse(e.lastEditTs):!0};static FAILED_HINT={[T.SpecPublished]:"spec not published (controller/API changed) \u2014 run /dezycro:publish-spec",[T.VerifiedRecently]:"no passing verify since last edit \u2014 run /dezycro:verify"};decide(e,t){for(let n of this.policy?.rules??[]){if(!e||!e.includes(n.nameIncludes))continue;if(n.inputField){let l=t?.[n.inputField],c=(n.inputValues??[]).map(m=>String(m).toUpperCase());if(l==null||!c.includes(String(l).toUpperCase()))continue}let r=n.requireAll??[];if(r.length===0)return this.blocked(e,n.reason);let i=r.filter(l=>!o.STATE_PREDICATES[l]?.(this.state));if(i.length===0)continue;let s=i.map(l=>o.FAILED_HINT[l]||l).join("; ");return this.blocked(e,`${n.reason||"Blocked by Dezycro policy."} ${s}.`)}return null}blocked(e,t){return{toolName:e||"?",detail:t||"Blocked by Dezycro policy."}}};var Lt=new Set([S.ContentBlockStart,S.ContentBlockDelta,S.ContentBlockStop]);var je=p(require("node:fs")),Ne=p(require("node:path"));var E=class o{static BIG_TOOL_CHARS=4e3;static defaultFile(){return Ne.join(R.dir(),"gateway.jsonl")}static fromFile(e=o.defaultFile()){let t;try{t=je.readFileSync(e,"utf8")}catch{return o.empty()}return o.aggregate(t.split(`
28
+ `))}static aggregate(e){let t=o.empty();for(let n of e){let r=o.parse(n);r&&(r.type==="turn"?o.addTurn(t,r):r.type==="compaction"&&o.addCompaction(t,r),o.trackTime(t,r.ts))}return t}static addTurn(e,t){e.turns+=1,e.inputTokens+=o.num(t.inputTokens),e.outputTokens+=o.num(t.outputTokens),e.cacheReadTokens+=o.num(t.cacheReadTokens),e.cacheCreationTokens+=o.num(t.cacheCreationTokens),e.totalDurationMs+=o.num(t.durationMs),e.gateBlocks+=o.num(t.blockedToolCalls);let n=o.num(t.freshToolChars);if(e.maxFreshToolChars=Math.max(e.maxFreshToolChars,n),n>o.BIG_TOOL_CHARS&&(e.bigToolTurns+=1),e.maxToolDefChars=Math.max(e.maxToolDefChars,o.num(t.toolDefChars)),e.maxSystemChars=Math.max(e.maxSystemChars,o.num(t.systemChars)),e.tailResultChars+=o.num(t.tailResultChars),e.tailTextChars+=o.num(t.tailTextChars),e.maxCacheCreationTokens=Math.max(e.maxCacheCreationTokens,o.num(t.cacheCreationTokens)),typeof t.status=="number"&&t.status!==200&&(e.errorTurns+=1),o.bump(e.models,typeof t.model=="string"?t.model:"(unknown)"),Array.isArray(t.skillsTriggered))for(let r of t.skillsTriggered)o.bump(e.skillsTriggered,String(r))}static addCompaction(e,t){e.compactions+=1,e.charsSaved+=o.num(t.charsSaved),e.estTokensSaved+=o.num(t.estTokensSaved),e.linesElided+=o.num(t.elidedLines),o.bump(e.commands,typeof t.command=="string"?t.command:"(unknown)")}static trackTime(e,t){typeof t=="string"&&((!e.firstTs||t<e.firstTs)&&(e.firstTs=t),(!e.lastTs||t>e.lastTs)&&(e.lastTs=t))}static parse(e){if(!e.trim())return null;try{return JSON.parse(e)}catch{return null}}static bump(e,t){e[t]=(e[t]??0)+1}static num(e){return typeof e=="number"&&Number.isFinite(e)?e:0}static empty(){return{turns:0,inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreationTokens:0,totalDurationMs:0,errorTurns:0,gateBlocks:0,maxFreshToolChars:0,bigToolTurns:0,maxToolDefChars:0,maxSystemChars:0,tailResultChars:0,tailTextChars:0,maxCacheCreationTokens:0,models:{},skillsTriggered:{},compactions:0,charsSaved:0,estTokensSaved:0,linesElided:0,commands:{},firstTs:null,lastTs:null}}};var y=p(require("node:fs")),_e=p(require("node:path")),I=class o{apiUrl;workspaceId;token;metricsFile;cursorFile;batchSize;timeoutMs;fetchImpl;constructor(e){this.apiUrl=e.apiUrl,this.workspaceId=e.workspaceId,this.token=e.token,this.metricsFile=e.metricsFile,this.cursorFile=e.cursorFile,this.batchSize=e.batchSize??1e3,this.timeoutMs=e.timeoutMs??15e3,this.fetchImpl=e.fetchImpl??fetch}async upload({dryRun:e=!1}={}){let{lines:t,newOffset:n}=this.readNewLines(),r=o.toBatch(t),i=r.turns.length+r.compactions.length,s={turns:r.turns.length,compactions:r.compactions.length,newOffset:n,sent:!1,dryRun:e};return e?s:i===0?(n!==this.readCursor()&&this.writeCursor(n),s):(await this.postAll(r),this.writeCursor(n),{...s,sent:!0})}static toBatch(e){let t=[],n=[];for(let r of e){let i;try{i=JSON.parse(r)}catch{continue}typeof i.ts=="string"&&(i.type==="turn"?t.push(o.mapTurn(i)):i.type==="compaction"&&n.push(o.mapCompaction(i)))}return{turns:t,compactions:n}}static mapTurn(e){return{timestamp:e.ts,provider:e.provider??null,model:e.model??null,status:e.status??null,durationMs:e.durationMs??null,inputTokens:e.inputTokens??null,outputTokens:e.outputTokens??null,cacheReadTokens:e.cacheReadTokens??null,cacheCreationTokens:e.cacheCreationTokens??null,toolDefChars:e.toolDefChars??null,systemChars:e.systemChars??null,freshToolChars:e.freshToolChars??null,tailResultChars:e.tailResultChars??null,tailTextChars:e.tailTextChars??null,blockedToolCalls:e.blockedToolCalls??null,skillsFired:Array.isArray(e.skillsTriggered)?e.skillsTriggered:[]}}static mapCompaction(e){return{timestamp:e.ts,command:e.command??null,originalChars:e.originalChars??null,outputChars:e.outputChars??null,charsSaved:e.charsSaved??null,estTokensSaved:e.estTokensSaved??null,originalLines:e.originalLines??null,outputLines:e.outputLines??null,elidedLines:e.elidedLines??null}}readNewLines(){let e=this.readCursor(),t;try{t=y.statSync(this.metricsFile).size}catch{return{lines:[],newOffset:e}}if(e>t&&(e=0),e===t)return{lines:[],newOffset:e};let n=y.openSync(this.metricsFile,"r");try{let r=t-e,i=Buffer.alloc(r);y.readSync(n,i,0,r,e);let s=i.toString("utf8"),l=s.lastIndexOf(`
29
+ `);if(l<0)return{lines:[],newOffset:e};let c=s.slice(0,l+1),m=e+Buffer.byteLength(c,"utf8");return{lines:c.split(`
30
+ `).filter(u=>u.trim().length>0),newOffset:m}}finally{y.closeSync(n)}}readCursor(){try{let e=JSON.parse(y.readFileSync(this.cursorFile,"utf8"));return typeof e.offset=="number"&&e.offset>=0?e.offset:0}catch{return 0}}writeCursor(e){try{y.mkdirSync(_e.dirname(this.cursorFile),{recursive:!0}),y.writeFileSync(this.cursorFile,JSON.stringify({offset:e,uploadedAt:new Date().toISOString()}))}catch{}}async postAll(e){for(let t=0;t<e.turns.length;t+=this.batchSize)await this.postBatch({turns:e.turns.slice(t,t+this.batchSize),compactions:[]});for(let t=0;t<e.compactions.length;t+=this.batchSize)await this.postBatch({turns:[],compactions:e.compactions.slice(t,t+this.batchSize)})}async postBatch(e){let t=`${this.apiUrl}/api/v1/workspaces/${this.workspaceId}/usage-telemetry`,n=new AbortController,r=setTimeout(()=>n.abort(),this.timeoutMs);try{let i=await this.fetchImpl(t,{method:"POST",headers:{Authorization:`Bearer ${this.token}`,"content-type":"application/json",accept:"application/json"},body:JSON.stringify(e),signal:n.signal});if(!i.ok)throw new Error(`usage-telemetry upload returned ${i.status}`)}finally{clearTimeout(r)}}};var W=class o{run(e){if(e.includes("--upload-dry-run")){this.uploadDryRun();return}let t=E.defaultFile(),n=E.fromFile(t);if(e.includes("--json")){process.stdout.write(`${JSON.stringify(n,null,2)}
31
+ `);return}process.stdout.write(o.format(n,t))}async uploadDryRun(){let e=c=>{process.stdout.write(`${c}
32
+ `)},t=k.at(process.cwd()),n=t.connection(),r=E.defaultFile(),i=Be.join(R.dir(),"gateway.upload-cursor.json"),s="error"in n?{apiUrl:"(unset)",workspaceId:t.workspaceId??"(unset)",token:"(unset)"}:n,l=await new I({...s,metricsFile:r,cursorFile:i}).upload({dryRun:!0});e("Dezycro telemetry \u2014 dry run (nothing sent)"),e(` metrics file ${r}`),e(` target ${s.apiUrl}/api/v1/workspaces/${s.workspaceId}/usage-telemetry`),"error"in n&&e(` connection NOT configured \u2014 ${n.error}`),e(` pending upload ${l.turns} turn + ${l.compactions} compaction event(s) since the cursor`)}static format(e,t){if(e.turns===0&&e.compactions===0)return`No metrics yet at ${t}
33
33
  (They're written as you run agents through \`dezycro run\`. DZ_METRICS=0 disables.)
34
- `;let n=c=>c.toLocaleString("en-US"),o=c=>n(Math.round(c/4)),i=c=>e.turns>0?n(Math.round(c/e.turns)):"0",s=[];s.push("Dezycro gateway metrics"),s.push(` ${t}`),e.firstTs&&e.lastTs&&s.push(` ${e.firstTs} \u2192 ${e.lastTs}`),s.push(""),s.push(`Turns: ${n(e.turns)} errors: ${n(e.errorTurns)} model time: ${(e.totalDurationMs/1e3).toFixed(0)}s`);let a=[`fresh input ~${i(e.inputTokens)} tok/turn`,`output ~${i(e.outputTokens)} tok/turn`];if(e.cacheReadTokens+e.inputTokens>0){let c=100*e.cacheReadTokens/(e.cacheReadTokens+e.inputTokens);a.push(`cache hit ${c.toFixed(1)}%`)}return s.push(a.join(" \xB7 ")),s.push(""),s.push("Where the tokens are (per request \u2014 content-free)"),s.push(` tool definitions ${n(e.maxToolDefChars)} chars (~${o(e.maxToolDefChars)} tok) \u2190 fixed per-request cost; schema-bloat lever`),s.push(` system prompt ${n(e.maxSystemChars)} chars (~${o(e.maxSystemChars)} tok)`),s.push(` fresh tool results ${n(e.tailResultChars)} chars (~${o(e.tailResultChars)} tok) total \xB7 biggest single ${n(e.maxFreshToolChars)} chars (~${o(e.maxFreshToolChars)} tok)`),s.push(` fresh user text ${n(e.tailTextChars)} chars (~${o(e.tailTextChars)} tok) total`),s.push(""),s.push("Cache"),s.push(` read ${n(e.cacheReadTokens)} tok \xB7 create ${n(e.cacheCreationTokens)} tok`),e.maxCacheCreationTokens>0&&s.push(` largest prefix write ${n(e.maxCacheCreationTokens)} tok (a spike here = cache-bust \u2014 the costly case)`),s.push(""),s.push("Request-side compaction opportunity (not yet compacted)"),s.push(` turns with >1k-tok fresh tool output ${n(e.bigToolTurns)}`),s.push(" (fresh tool results are the safe headroom; reversible elision is the lever \u2014 needs sizing first)"),s.push(""),s.push("Source-side compaction (shell output \u2014 already applied)"),s.push(` est. tokens saved ${n(e.estTokensSaved)} over ${n(e.compactions)} compaction(s)${r.breakdown(e.commands)}`),s.push(` lines elided ${n(e.linesElided)}`),s.push(""),s.push(`Totals: input ${n(e.inputTokens)} \xB7 output ${n(e.outputTokens)} \xB7 cache read ${n(e.cacheReadTokens)} \xB7 cache create ${n(e.cacheCreationTokens)}`),s.push(`Gate blocks: ${n(e.gateBlocks)}`),s.push(`Skills fired:${r.breakdown(e.skillsTriggered)||" none"}`),s.push(`Models:${r.breakdown(e.models)||" (none)"}`),s.push(""),`${s.join(`
34
+ `;let n=c=>c.toLocaleString("en-US"),r=c=>n(Math.round(c/4)),i=c=>e.turns>0?n(Math.round(c/e.turns)):"0",s=[];s.push("Dezycro gateway metrics"),s.push(` ${t}`),e.firstTs&&e.lastTs&&s.push(` ${e.firstTs} \u2192 ${e.lastTs}`),s.push(""),s.push(`Turns: ${n(e.turns)} errors: ${n(e.errorTurns)} model time: ${(e.totalDurationMs/1e3).toFixed(0)}s`);let l=[`fresh input ~${i(e.inputTokens)} tok/turn`,`output ~${i(e.outputTokens)} tok/turn`];if(e.cacheReadTokens+e.inputTokens>0){let c=100*e.cacheReadTokens/(e.cacheReadTokens+e.inputTokens);l.push(`cache hit ${c.toFixed(1)}%`)}return s.push(l.join(" \xB7 ")),s.push(""),s.push("Where the tokens are (per request \u2014 content-free)"),s.push(` tool definitions ${n(e.maxToolDefChars)} chars (~${r(e.maxToolDefChars)} tok) \u2190 fixed per-request cost; schema-bloat lever`),s.push(` system prompt ${n(e.maxSystemChars)} chars (~${r(e.maxSystemChars)} tok)`),s.push(` fresh tool results ${n(e.tailResultChars)} chars (~${r(e.tailResultChars)} tok) total \xB7 biggest single ${n(e.maxFreshToolChars)} chars (~${r(e.maxFreshToolChars)} tok)`),s.push(` fresh user text ${n(e.tailTextChars)} chars (~${r(e.tailTextChars)} tok) total`),s.push(""),s.push("Cache"),s.push(` read ${n(e.cacheReadTokens)} tok \xB7 create ${n(e.cacheCreationTokens)} tok`),e.maxCacheCreationTokens>0&&s.push(` largest prefix write ${n(e.maxCacheCreationTokens)} tok (a spike here = cache-bust \u2014 the costly case)`),s.push(""),s.push("Request-side compaction opportunity (not yet compacted)"),s.push(` turns with >1k-tok fresh tool output ${n(e.bigToolTurns)}`),s.push(" (fresh tool results are the safe headroom; reversible elision is the lever \u2014 needs sizing first)"),s.push(""),s.push("Source-side compaction (shell output \u2014 already applied)"),s.push(` est. tokens saved ${n(e.estTokensSaved)} over ${n(e.compactions)} compaction(s)${o.breakdown(e.commands)}`),s.push(` lines elided ${n(e.linesElided)}`),s.push(""),s.push(`Totals: input ${n(e.inputTokens)} \xB7 output ${n(e.outputTokens)} \xB7 cache read ${n(e.cacheReadTokens)} \xB7 cache create ${n(e.cacheCreationTokens)}`),s.push(`Gate blocks: ${n(e.gateBlocks)}`),s.push(`Skills fired:${o.breakdown(e.skillsTriggered)||" none"}`),s.push(`Models:${o.breakdown(e.models)||" (none)"}`),s.push(""),`${s.join(`
35
35
  `)}
36
- `}static breakdown(e){let t=Object.entries(e).sort((n,o)=>o[1]-n[1]);return t.length===0?"":` ${t.map(([n,o])=>`${n}=${o}`).join(", ")}`}};var Q=class{now(){return Date.now()}nowIso(){return new Date().toISOString()}sleep(e){return new Promise(t=>setTimeout(t,e))}},ee=class{constructor(e,t,n,o,i){this.apiUrl=e;this.workspaceId=t;this.featureId=n;this.token=o;this.eventTypes=i}apiUrl;workspaceId;featureId;token;eventTypes;async wait(e,t){let n=new URL(`${this.apiUrl}/api/v1/workspaces/${this.workspaceId}/features/${this.featureId}/inbox/wait`);this.eventTypes&&n.searchParams.set("eventTypes",this.eventTypes),n.searchParams.set("since",e),n.searchParams.set("timeoutSec",String(t));let o=new AbortController,i=setTimeout(()=>o.abort(),(t+10)*1e3);try{let s=await fetch(n.toString(),{method:"GET",headers:{Authorization:`Bearer ${this.token}`,Accept:"application/json"},signal:o.signal});if(!s.ok)throw new Error(`inbox/wait returned ${s.status}`);let a=await s.json();return{nextCursor:typeof a.nextCursor=="string"?a.nextCursor:e,events:Array.isArray(a.events)?a.events:[]}}finally{clearTimeout(i)}}},F=class{constructor(e,t=30,n=1e3){this.totalTimeoutSec=e;this.perPollSec=t;this.errorBackoffMs=n}totalTimeoutSec;perPollSec;errorBackoffMs;static DEFAULT_TOTAL_TIMEOUT_SEC=600},te=class{constructor(e,t,n){this.gateway=e;this.clock=t;this.policy=n}gateway;clock;policy;async awaitEvent(){let e=this.clock.now()+this.policy.totalTimeoutSec*1e3,t=this.clock.nowIso(),n=!1,o="";for(;this.clock.now()<e;){let i=Math.ceil((e-this.clock.now())/1e3);if(i<=0)break;let s=Math.min(this.policy.perPollSec,i);try{let a=await this.gateway.wait(t,s);n=!0;let[c]=a.events;if(c)return{outcome:"EVENT",event:c};t=a.nextCursor||t}catch(a){o=a instanceof Error?a.message:String(a),this.clock.now()<e&&await this.clock.sleep(this.policy.errorBackoffMs)}}return n?{outcome:"TIMEOUT"}:{outcome:"ERROR",error:o||"event wait failed"}}},ne=class{constructor(e,t,n){this.result=e;this.eventTypes=t;this.timeoutSec=n}result;eventTypes;timeoutSec;render(){switch(this.result.outcome){case"EVENT":return JSON.stringify(this.result.event);case"TIMEOUT":return`TIMEOUT: no ${this.eventTypes??"any"} event within ${this.timeoutSec}s.`;case"ERROR":return`ERROR: ${this.result.error??"unknown"}`}}exitCode(){switch(this.result.outcome){case"EVENT":return 0;case"TIMEOUT":return 5;case"ERROR":return 2}}},re=class r{constructor(e,t,n,o){this.eventTypes=e;this.feature=t;this.timeoutSec=n;this.json=o}eventTypes;feature;timeoutSec;json;static fromArgv(e){let t=null,n=null,o=F.DEFAULT_TOTAL_TIMEOUT_SEC,i=!1;for(let s=0;s<e.length;s+=1){let a=e[s];if(a==="--event-types")t=e[s+1]??null,s+=1;else if(a==="--feature")n=e[s+1]??null,s+=1;else if(a==="--timeout"){let c=Number(e[s+1]);Number.isFinite(c)&&c>0&&(o=c),s+=1}else a==="--json"&&(i=!0)}return new r(t,n,o,i)}},j=class{constructor(e=new Q){this.clock=e}clock;async run(e){let t=re.fromArgv(e),n=d.at(process.cwd()),o=n.connection();if("error"in o){this.fail(`not configured \u2014 ${o.error}`);return}let i=t.feature??n.activeFeature()?.featureId??null;if(!i){this.fail("no feature (pass --feature <id>, or run /dezycro:work to bind one)");return}let s=new ee(o.apiUrl,o.workspaceId,i,o.token,t.eventTypes),a=await new te(s,this.clock,new F(t.timeoutSec)).awaitEvent(),c=new ne(a,t.eventTypes,t.timeoutSec);process.stdout.write(`${t.json?JSON.stringify(a):c.render()}
36
+ `}static breakdown(e){let t=Object.entries(e).sort((n,r)=>r[1]-n[1]);return t.length===0?"":` ${t.map(([n,r])=>`${n}=${r}`).join(", ")}`}};var le=class{now(){return Date.now()}nowIso(){return new Date().toISOString()}sleep(e){return new Promise(t=>setTimeout(t,e))}},ue=class{constructor(e,t,n,r,i){this.apiUrl=e;this.workspaceId=t;this.featureId=n;this.token=r;this.eventTypes=i}apiUrl;workspaceId;featureId;token;eventTypes;async wait(e,t){let n=new URL(`${this.apiUrl}/api/v1/workspaces/${this.workspaceId}/features/${this.featureId}/inbox/wait`);this.eventTypes&&n.searchParams.set("eventTypes",this.eventTypes),n.searchParams.set("since",e),n.searchParams.set("timeoutSec",String(t));let r=new AbortController,i=setTimeout(()=>r.abort(),(t+10)*1e3);try{let s=await fetch(n.toString(),{method:"GET",headers:{Authorization:`Bearer ${this.token}`,Accept:"application/json"},signal:r.signal});if(!s.ok)throw new Error(`inbox/wait returned ${s.status}`);let l=await s.json();return{nextCursor:typeof l.nextCursor=="string"?l.nextCursor:e,events:Array.isArray(l.events)?l.events:[]}}finally{clearTimeout(i)}}},Z=class{constructor(e,t=30,n=1e3){this.totalTimeoutSec=e;this.perPollSec=t;this.errorBackoffMs=n}totalTimeoutSec;perPollSec;errorBackoffMs;static DEFAULT_TOTAL_TIMEOUT_SEC=600},pe=class{constructor(e,t,n){this.gateway=e;this.clock=t;this.policy=n}gateway;clock;policy;async awaitEvent(){let e=this.clock.now()+this.policy.totalTimeoutSec*1e3,t=this.clock.nowIso(),n=!1,r="";for(;this.clock.now()<e;){let i=Math.ceil((e-this.clock.now())/1e3);if(i<=0)break;let s=Math.min(this.policy.perPollSec,i);try{let l=await this.gateway.wait(t,s);n=!0;let[c]=l.events;if(c)return{outcome:"EVENT",event:c};t=l.nextCursor||t}catch(l){r=l instanceof Error?l.message:String(l),this.clock.now()<e&&await this.clock.sleep(this.policy.errorBackoffMs)}}return n?{outcome:"TIMEOUT"}:{outcome:"ERROR",error:r||"event wait failed"}}},de=class{constructor(e,t,n){this.result=e;this.eventTypes=t;this.timeoutSec=n}result;eventTypes;timeoutSec;render(){switch(this.result.outcome){case"EVENT":return JSON.stringify(this.result.event);case"TIMEOUT":return`TIMEOUT: no ${this.eventTypes??"any"} event within ${this.timeoutSec}s.`;case"ERROR":return`ERROR: ${this.result.error??"unknown"}`}}exitCode(){switch(this.result.outcome){case"EVENT":return 0;case"TIMEOUT":return 5;case"ERROR":return 2}}},fe=class o{constructor(e,t,n,r){this.eventTypes=e;this.feature=t;this.timeoutSec=n;this.json=r}eventTypes;feature;timeoutSec;json;static fromArgv(e){let t=null,n=null,r=Z.DEFAULT_TOTAL_TIMEOUT_SEC,i=!1;for(let s=0;s<e.length;s+=1){let l=e[s];if(l==="--event-types")t=e[s+1]??null,s+=1;else if(l==="--feature")n=e[s+1]??null,s+=1;else if(l==="--timeout"){let c=Number(e[s+1]);Number.isFinite(c)&&c>0&&(r=c),s+=1}else l==="--json"&&(i=!0)}return new o(t,n,r,i)}},G=class{constructor(e=new le){this.clock=e}clock;async run(e){let t=fe.fromArgv(e),n=k.at(process.cwd()),r=n.connection();if("error"in r){this.fail(`not configured \u2014 ${r.error}`);return}let i=t.feature??n.activeFeature()?.featureId??null;if(!i){this.fail("no feature (pass --feature <id>, or run /dezycro:work to bind one)");return}let s=new ue(r.apiUrl,r.workspaceId,i,r.token,t.eventTypes),l=await new pe(s,this.clock,new Z(t.timeoutSec)).awaitEvent(),c=new de(l,t.eventTypes,t.timeoutSec);process.stdout.write(`${t.json?JSON.stringify(l):c.render()}
37
37
  `),process.exit(c.exitCode())}fail(e){process.stderr.write(`dezycro await-event: ${e}
38
- `),process.exit(2)}};var h=l(require("fs")),Ie=l(require("path"));var L=class{file;constructor(e){this.file=Ie.join(e,".dezycro","active-feature.json")}read(){try{let e=JSON.parse(h.readFileSync(this.file,"utf8")),t=typeof e.featureId=="string"?e.featureId:null;return t?{featureId:t,featureName:typeof e.featureName=="string"?e.featureName:void 0,workspaceId:typeof e.workspaceId=="string"?e.workspaceId:void 0,selectedAt:typeof e.selectedAt=="string"?e.selectedAt:void 0,active:e.active===!0}:null}catch{return null}}setActive(e){let t=this.read();if(!t||(t.active??!1)===e)return!1;try{return h.writeFileSync(this.file,`${JSON.stringify({...t,active:e},null,2)}
39
- `),!0}catch{return!1}}clear(){try{if(h.existsSync(this.file))return h.unlinkSync(this.file),!0}catch{}return!1}};var J=class{run(e){let t=d.at(process.cwd()).repoRoot,n=new L(t);if(e.includes("--clear")){let s=n.read()!==null;n.clear(),process.stdout.write(s?`dezycro: active feature cleared.
38
+ `),process.exit(2)}};var b=p(require("fs")),Fe=p(require("path"));var V=class{file;constructor(e){this.file=Fe.join(e,".dezycro","active-feature.json")}read(){try{let e=JSON.parse(b.readFileSync(this.file,"utf8")),t=typeof e.featureId=="string"?e.featureId:null;return t?{featureId:t,featureName:typeof e.featureName=="string"?e.featureName:void 0,workspaceId:typeof e.workspaceId=="string"?e.workspaceId:void 0,selectedAt:typeof e.selectedAt=="string"?e.selectedAt:void 0,active:e.active===!0}:null}catch{return null}}setActive(e){let t=this.read();if(!t||(t.active??!1)===e)return!1;try{return b.writeFileSync(this.file,`${JSON.stringify({...t,active:e},null,2)}
39
+ `),!0}catch{return!1}}clear(){try{if(b.existsSync(this.file))return b.unlinkSync(this.file),!0}catch{}return!1}};var q=class{run(e){let t=k.at(process.cwd()).repoRoot,n=new V(t);if(e.includes("--clear")){let s=n.read()!==null;n.clear(),process.stdout.write(s?`dezycro: active feature cleared.
40
40
  `:`dezycro: no active feature to clear.
41
- `);return}let o=n.read();if(!o){process.stdout.write(`dezycro: no active feature bound (run /dezycro:work to bind one).
42
- `);return}let i=o.featureName?`${o.featureName} (${o.featureId})`:o.featureId;process.stdout.write(`dezycro feature: ${i}
43
- active this session: ${o.active?"yes":"no"}
44
- `)}};var $e=(a=>(a.Run="run",a.Setup="setup",a.ResolveAuth="resolve-auth",a.Completion="completion",a.Metrics="metrics",a.AwaitEvent="await-event",a.Feature="feature",a))($e||{}),Ze=new Set(["help","-h","--help"]),De=`dezycro \u2014 Dezycro for AI coding agents
41
+ `);return}let r=n.read();if(!r){process.stdout.write(`dezycro: no active feature bound (run /dezycro:work to bind one).
42
+ `);return}let i=r.featureName?`${r.featureName} (${r.featureId})`:r.featureId;process.stdout.write(`dezycro feature: ${i}
43
+ active this session: ${r.active?"yes":"no"}
44
+ `)}};var We=p(require("crypto")),h=p(require("fs")),O=p(require("os")),g=p(require("path")),Ze=p(require("readline"));var he=class{constructor(e,t){this.apiUrl=e;this.token=t}apiUrl;token;async listWorkspaces(){let e=await this.get(`${this.apiUrl}/api/v1/workspaces`);return(Array.isArray(e?.data)?e.data:[]).map(n=>({id:String(n.id??""),name:String(n.name??"")})).filter(n=>n.id.length>0)}async fetchManifest(e,t){let n=`${this.apiUrl}/api/v1/workspaces/${e}/skills/sync-manifest`,r={Authorization:`Bearer ${this.token}`};t&&(r["If-None-Match"]=t);let i=await fetch(n,{headers:r});if(i.status===304)return{kind:"not-modified"};if(i.status===403||i.status===404)return{kind:"unavailable"};if(!i.ok)throw new Error(`GET ${n} \u2192 HTTP ${i.status}`);return{kind:"ok",manifest:await i.json(),etag:i.headers.get("etag")}}async get(e){let t=await fetch(e,{headers:{Authorization:`Bearer ${this.token}`}});if(!t.ok)throw new Error(`GET ${e} \u2192 HTTP ${t.status}`);return await t.json()}},Le=/^[a-z0-9][a-z0-9-]*$/,ct=500,lt=10;function ut(){return{stateDir:g.join(O.homedir(),".dezycro"),commandsDir:g.join(O.homedir(),".claude","commands")}}var me=class{constructor(e,t,n=r=>console.log(r)){this.gateway=e;this.paths=t;this.log=n}gateway;paths;log;configPath(){return g.join(this.paths.stateDir,"skills.json")}statePath(){return g.join(this.paths.stateDir,"skills-state.json")}readConfig(){let e=X(this.configPath());if(!e||typeof e!="object")return null;let t=e.workspaces&&typeof e.workspaces=="object"?e.workspaces:{};return{enabled:e.enabled!==!1,apiUrl:typeof e.apiUrl=="string"?e.apiUrl:null,workspaces:t}}writeConfig(e){h.mkdirSync(this.paths.stateDir,{recursive:!0}),K(this.configPath(),JSON.stringify(e,null,2))}readState(){let e=X(this.statePath());return e&&typeof e=="object"&&e.workspaces&&typeof e.workspaces=="object"?{version:1,workspaces:e.workspaces,lastSyncTs:typeof e.lastSyncTs=="string"?e.lastSyncTs:null}:{version:1,workspaces:{},lastSyncTs:null}}async sync(e){let t={added:0,updated:0,restored:0,removed:0,skipped:[],synced:!1},n=this.readConfig();if(!n||Object.keys(n.workspaces).length===0)return this.log("No workspaces configured for skills sync. Run `dezycro skills sync --configure` to pick."),t;if(!n.enabled)return this.log("Skills sync is disabled (~/.dezycro/skills.json enabled=false)."),t;if(!this.acquireLock())throw new Error("Another skills sync is already running (lock held). Retry in a moment.");try{let r=Object.keys(n.workspaces).filter(a=>a!=="*");if("*"in n.workspaces){let a=new Set(r);for(let u of await this.gateway.listWorkspaces())a.has(u.id)||r.push(u.id)}let i=this.readState(),s=new Map,l=new Map;for(let a of r){let u=e.force?void 0:this.cachedEtag(n,i,a),f=await this.gateway.fetchManifest(a,u);if(f.kind==="not-modified"&&this.hasLocalDrift(i,a)&&(f=await this.gateway.fetchManifest(a)),f.kind==="unavailable"){t.skipped.push({what:`workspace ${a}`,reason:"skills not available (flag off or no access)"});continue}f.kind!=="not-modified"&&(s.set(a,f.manifest),l.set(a,f.etag??null))}let c=new Map,m=new Map;for(let[a,u]of s){let d=n.workspaces[a]?.prefixAlias??u.prefix;c.set(a,d),m.set(d,[...m.get(d)??[],a])}for(let[a,u]of m)if(u.length>1){this.log(`\u26A0 prefix collision on '${a}' between workspaces ${u.join(", ")} \u2014 skipping all of them. Change a prefixAlias in ${this.configPath()} to resolve.`);for(let f of u)s.delete(f),t.skipped.push({what:`workspace ${f}`,reason:`prefix collision on '${a}'`})}for(let[a,u]of s)this.syncWorkspace(a,u,c.get(a)??u.prefix,i,e.force,t,l.get(a)??null);return i.lastSyncTs=new Date().toISOString(),h.mkdirSync(this.paths.stateDir,{recursive:!0}),K(this.statePath(),JSON.stringify(i,null,2)),t.synced=!0,t}finally{this.releaseLock()}}cachedEtag(e,t,n){let r=t.workspaces[n];if(!(!r?.etag||(e.workspaces[n]?.prefixAlias??r.canonicalPrefix)!==r.aliasUsed))return r.etag}syncWorkspace(e,t,n,r,i,s,l){if(!Le.test(n)){s.skipped.push({what:`workspace ${e}`,reason:`invalid prefix '${n}'`});return}let c=r.workspaces[e]??{canonicalPrefix:t.prefix,aliasUsed:n,skills:{}};if(c.aliasUsed!==n&&Object.keys(c.skills).length>0){for(let[a,u]of Object.entries(c.skills))this.removeOwned(u,s),delete c.skills[a];this.log(`Prefix changed for workspace ${e}: '${c.aliasUsed}' \u2192 '${n}'`)}c.canonicalPrefix=t.prefix,c.aliasUsed=n,c.etag=l;let m=new Set(t.skills.map(a=>a.id));for(let[a,u]of Object.entries(c.skills))m.has(a)||(this.removeOwned(u,s),delete c.skills[a]);for(let a of t.skills){if(!Le.test(a.name)){s.skipped.push({what:`skill '${a.name}'`,reason:"invalid name (not a slug)"});continue}let u=g.join(n,`${a.name}.md`),f=g.join(this.paths.commandsDir,u),d=c.skills[a.id],w=He(a.content);if(d&&d.version===a.version&&d.relPath===u){if(Je(f)===a.content){d.contentHash!==w&&(c.skills[a.id]={...d,contentHash:w});continue}h.mkdirSync(g.dirname(f),{recursive:!0}),K(f,a.content),c.skills[a.id]={name:a.name,version:a.version,relPath:u,contentHash:w},s.restored+=1,this.log(`restored /${n}:${a.name} \u2014 local edits to synced skills are overwritten; edit skills in the Dezycro platform`);continue}if(d&&d.relPath!==u&&(this.removeOwned(d,s),s.removed-=1),h.existsSync(f)&&!this.owns(r,f)&&!i){s.skipped.push({what:`skill '${a.name}'`,reason:`foreign file exists at ${f} \u2014 remove it or rerun with --force to take ownership`});continue}h.mkdirSync(g.dirname(f),{recursive:!0}),K(f,a.content),d?s.updated+=1:s.added+=1,c.skills[a.id]={name:a.name,version:a.version,relPath:u,contentHash:w}}r.workspaces[e]=c}hasLocalDrift(e,t){let n=e.workspaces[t];if(!n)return!1;for(let r of Object.values(n.skills)){if(r.contentHash===void 0)continue;let i=Je(g.join(this.paths.commandsDir,r.relPath));if(i===null||He(i)!==r.contentHash)return!0}return!1}owns(e,t){for(let n of Object.values(e.workspaces))for(let r of Object.values(n.skills))if(g.join(this.paths.commandsDir,r.relPath)===t)return!0;return!1}removeOwned(e,t){let n=g.join(this.paths.commandsDir,e.relPath);try{h.unlinkSync(n),t.removed+=1}catch{}}lockPath(){return g.join(this.paths.stateDir,".skills-state.lock")}acquireLock(){h.mkdirSync(this.paths.stateDir,{recursive:!0});let e=Date.now()+ct;for(;;)try{let t=h.openSync(this.lockPath(),"wx");return h.closeSync(t),!0}catch{if(Date.now()>=e)return!1;dt(lt)}}releaseLock(){try{h.unlinkSync(this.lockPath())}catch{}}},ge=class{async pick(e){if(e.length===0)return[];console.log(`
45
+ Workspaces you belong to:`),e.forEach((i,s)=>console.log(` ${s+1}. ${i.name} (${i.id})`));let n=(await ft('Sync skills from which workspaces? (e.g. "1,3", "all" = every workspace incl. future ones, empty = none): ')).trim().toLowerCase();if(n==="")return[];if(n==="all"||n==="*")return"*";let r=[];for(let i of n.split(",")){let s=Number.parseInt(i.trim(),10);Number.isInteger(s)&&s>=1&&s<=e.length&&r.push(e[s-1])}return r}},pt=`dezycro skills \u2014 sync workspace-authored skills to Claude Code
46
+
47
+ Usage:
48
+ dezycro skills sync Sync configured workspaces' skills to ~/.claude/commands/
49
+ dezycro skills sync --configure (Re)pick which workspaces to sync from
50
+ dezycro skills sync --force Also overwrite conflicting foreign files (takes ownership)
51
+
52
+ Synced skills appear as /<prefix>:<name> slash commands in Claude Code.
53
+ Config: ~/.dezycro/skills.json \xB7 Ledger: ~/.dezycro/skills-state.json`,Y=class{async run(e){let[t,...n]=e;if(t!=="sync"){console.log(pt),process.exitCode=t===void 0||t==="help"?0:2;return}let r=n.includes("--force"),i=n.includes("--configure"),s=this.resolveConnection();if("error"in s){console.error(`dezycro skills: not authenticated \u2014 ${s.error}`),console.error("Run `dezycro setup` to save your API key."),process.exitCode=1;return}let l=ut(),c=new he(s.apiUrl,s.token),m=new me(c,l);try{let a=m.readConfig();if(i||!a||Object.keys(a.workspaces).length===0){let f=await c.listWorkspaces();if(f.length===0){console.log("You are not a member of any workspace. Nothing to sync.");return}let d=await new ge().pick(f);if(d!=="*"&&d.length===0){console.log("No workspaces selected. Nothing to sync.");return}let w=a?.workspaces??{},ke=d==="*"?{...w,"*":w["*"]??{}}:Object.fromEntries(d.map(ve=>[ve.id,w[ve.id]??{}]));a={enabled:a?.enabled??!0,apiUrl:s.apiUrl,workspaces:ke},m.writeConfig(a),console.log(d==="*"?`Saved wildcard (all workspaces, resolved each sync) to ${m.configPath()}`:`Saved ${d.length} workspace(s) to ${m.configPath()}`)}let u=await m.sync({force:r});if(u.synced){console.log(`Synced skills: ${u.added} added, ${u.updated} updated, ${u.restored} restored, ${u.removed} removed, ${u.skipped.length} skipped`);for(let f of u.skipped)console.log(` skipped ${f.what}: ${f.reason}`);(process.env.CURSOR_TRACE_ID||process.env.TERM_PROGRAM==="cursor")&&console.log("Cursor skill delivery is coming in a future release. Skills are synced and ready for when support ships.")}}catch(a){console.error(`dezycro skills: sync failed \u2014 ${a instanceof Error?a.message:String(a)}`),console.error("No local files were modified. Check your connection and retry."),process.exitCode=1}}resolveConnection(){let e=k.findRepoRoot(process.cwd()),t=e?new k(e):null,n=process.env.DZ_TOKEN||t?.token||X(g.join(O.homedir(),".dezycro","token.json"))?.token;if(typeof n!="string"||n.length===0)return{error:"no token (DZ_TOKEN or ~/.dezycro/token.json)"};let r=X(g.join(O.homedir(),".dezycro","skills.json"));return{apiUrl:(process.env.DZ_API_URL||(typeof r?.apiUrl=="string"?r.apiUrl:null)||t?.apiUrl||"https://api.dezycro.ai").replace(/\/+$/,""),token:n}}};function X(o){try{return JSON.parse(h.readFileSync(o,"utf8"))}catch{return null}}function Je(o){try{return h.readFileSync(o,"utf8")}catch{return null}}function He(o){return We.createHash("sha256").update(o,"utf8").digest("hex")}function K(o,e){let t=`${o}.tmp-${process.pid}`;h.writeFileSync(t,e,"utf8"),h.renameSync(t,o)}function dt(o){let e=new SharedArrayBuffer(4);Atomics.wait(new Int32Array(e),0,0,o)}function ft(o){let e=Ze.default.createInterface({input:process.stdin,output:process.stdout});return new Promise(t=>e.question(o,n=>{e.close(),t(n)}))}var Ve=(c=>(c.Run="run",c.Setup="setup",c.ResolveAuth="resolve-auth",c.Completion="completion",c.Metrics="metrics",c.AwaitEvent="await-event",c.Feature="feature",c.Skills="skills",c))(Ve||{}),ht=new Set(["help","-h","--help"]),Ge=`dezycro \u2014 Dezycro for AI coding agents
45
54
 
46
55
  Usage: dezycro <command> [args]
47
56
 
@@ -55,7 +64,9 @@ Commands:
55
64
  await-event --event-types <csv> [--feature <id>] [--timeout <sec>] [--json]
56
65
  Block until the command bus pushes a matching event; prints it and exits (0 event / 5 timeout / 2 error)
57
66
  feature [--clear] Show the bound feature (and whether it's active this session); --clear wipes it (done with this feature)
67
+ skills sync [--configure] [--force]
68
+ Sync workspace-authored skills to ~/.claude/commands/ as /<prefix>:<name> slash commands
58
69
 
59
70
  Gateway control: dezycro run --status dezycro run --stop
60
- Custom port: dezycro run --port <n> <agent> (overrides DZ_PORT, default 13579)`,oe=class{handlers=new Map([["run",e=>{new O().run(e)}],["setup",()=>new M().run()],["resolve-auth",e=>new U().run(e[0])],["completion",e=>new B().run(e)],["metrics",e=>new N().run(e)],["await-event",e=>{new j().run(e)}],["feature",e=>new J().run(e)]]);run(e){let[t,...n]=e,o=this.handlers.get(t);if(o){o(n);return}if(t===void 0||Ze.has(t)){console.log(De);return}console.error(`dezycro: unknown command '${t}'
61
- `),console.error(De),process.exit(2)}};new oe().run(process.argv.slice(2));0&&(module.exports={Command});
71
+ Custom port: dezycro run --port <n> <agent> (overrides DZ_PORT, default 13579)`,ye=class{handlers=new Map([["run",e=>{new _().run(e)}],["setup",()=>new B().run()],["resolve-auth",e=>new L().run(e[0])],["completion",e=>new J().run(e)],["metrics",e=>new W().run(e)],["await-event",e=>{new G().run(e)}],["feature",e=>new q().run(e)],["skills",e=>{new Y().run(e)}]]);run(e){let[t,...n]=e,r=this.handlers.get(t);if(r){r(n);return}if(t===void 0||ht.has(t)){console.log(Ge);return}console.error(`dezycro: unknown command '${t}'
72
+ `),console.error(Ge),process.exit(2)}};new ye().run(process.argv.slice(2));0&&(module.exports={Command});
@@ -1,35 +1,35 @@
1
- "use strict";var ve=Object.create;var G=Object.defineProperty;var Ce=Object.getOwnPropertyDescriptor;var xe=Object.getOwnPropertyNames;var we=Object.getPrototypeOf,Re=Object.prototype.hasOwnProperty;var Ee=(n,e)=>{for(var t in e)G(n,t,{get:e[t],enumerable:!0})},ie=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of xe(e))!Re.call(n,o)&&o!==t&&G(n,o,{get:()=>e[o],enumerable:!(r=Ce(e,o))||r.enumerable});return n};var c=(n,e,t)=>(t=n!=null?ve(we(n)):{},ie(e||!n||!n.__esModule?G(t,"default",{value:n,enumerable:!0}):t,n)),Ae=n=>ie(G({},"__esModule",{value:!0}),n);var Pe={};Ee(Pe,{Gateway:()=>_,createGatewayServer:()=>Le});module.exports=Ae(Pe);var be=c(require("node:http")),F=c(require("node:fs")),z=c(require("node:path")),Se=require("node:stream");var H=c(require("fs")),ae=c(require("os")),g=c(require("path")),$=class n{constructor(e){this.repoRoot=e;this.config=n.readJson(g.join(e,".dezycro","config.json"))}repoRoot;config;static at(e=process.cwd()){return new n(n.findRepoRoot(e)??g.resolve(e))}get workspaceId(){return n.env("DZ_WORKSPACE_ID")??n.str(this.config?.workspaceId)}get tenantId(){return n.env("DZ_TENANT_ID")??n.str(this.config?.tenantId)}get projectId(){return n.str(this.config?.projectId)}agent(e){let t=this.agentNode(e);return{context1m:n.boolFlag("DZ_1M",t?.context1m),toolSearch:n.env("DZ_TOOL_SEARCH")??n.str(t?.toolSearch)}}agentNode(e){let t=this.config?.agents;if(!n.isObj(t))return null;let r=t[e];return n.isObj(r)?r:null}static boolFlag(e,t){let r=n.env(e);return r!==null?r==="1"||r.toLowerCase()==="true":t===!0}get apiUrl(){let e=n.env("DZ_API_URL");if(e)return n.trimSlash(e);let t=Array.isArray(this.config?.environments)?this.config?.environments:[],r=t.find(s=>n.isObj(s)&&s.default===!0)??t[0],o=n.isObj(r)?n.str(r.apiUrl):null;return o?n.trimSlash(o):null}get token(){let e=n.env("DZ_TOKEN");if(e)return e;let t=[g.join(this.repoRoot,".dezycro","companion-token.local.json"),g.join(ae.homedir(),".dezycro","token.json")];for(let r of t){let o=n.str(n.readJson(r)?.token);if(o)return o}return null}activeFeature(){let e=n.readJson(g.join(this.repoRoot,".dezycro","active-feature.json")),t=n.str(e?.featureId);if(!t)return null;let r=n.str(e?.workspaceId)??this.workspaceId;return r?{workspaceId:r,featureId:t,active:e?.active===!0}:null}connection(){let e=this.apiUrl;if(!e)return{error:"no apiUrl (set DZ_API_URL or .dezycro/config.json environments[].apiUrl)"};let t=this.workspaceId;if(!t)return{error:"no workspaceId (set DZ_WORKSPACE_ID or .dezycro/config.json workspaceId)"};let r=this.token;return r?{apiUrl:e,workspaceId:t,token:r}:{error:"no token (run `dezycro setup` or set DZ_TOKEN)"}}static findRepoRoot(e){let t=g.resolve(e||process.cwd());for(let r=0;r<64;r+=1){if(H.existsSync(g.join(t,".dezycro")))return t;let o=g.dirname(t);if(o===t)return null;t=o}return null}static readJson(e){try{return JSON.parse(H.readFileSync(e,"utf8"))}catch{return null}}static isObj(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}static str(e){return typeof e=="string"&&e.length>0?e:null}static env(e){let t=process.env[e];return t&&t.length>0?t:null}static trimSlash(e){return e.replace(/\/$/,"")}};var d={MessageStart:"message_start",ContentBlockStart:"content_block_start",ContentBlockDelta:"content_block_delta",ContentBlockStop:"content_block_stop",MessageDelta:"message_delta",MessageStop:"message_stop"},V={User:"user",Assistant:"assistant",System:"system"},y={Text:"text",ToolUse:"tool_use"},W={Text:"text_delta",InputJson:"input_json_delta"},x={ToolUse:"tool_use",EndTurn:"end_turn"};var le={UpdateTask:"update_task"},ce={Status:"status"},Q={Done:"DONE",Pushed:"PUSHED"};var b={SpecPublished:"specPublished",VerifiedRecently:"verifiedRecently"};var w={Anthropic:"anthropic",OpenAI:"openai"};var ee={Health:"/__dz/health",Stop:"/__dz/stop"},pe="last-opus-model",R={EventStream:"text/event-stream",Json:"application/json"};var k=class n{constructor(e){this.skills=e}skills;apply(e,t){let r=n.lastUserText(e),o=this.triggeredSkills(r),s=this.build(r);return s&&n.appendToLastUserMessage(e,t,s),{text:s,triggers:o}}triggeredSkills(e){let t=[];for(let r of Object.keys(this.skills??{}))e.includes(r)&&t.push(r);return t}static composition(e){return{toolDefChars:n.toolDefChars(e),systemChars:n.systemChars(e),...n.tailComposition(e)}}static emptyComposition(){return{toolDefChars:0,systemChars:0,tailResultChars:0,tailTextChars:0,freshToolChars:0}}static maxToolResultChars(e){return n.tailComposition(e).freshToolChars}static toolDefChars(e){return Array.isArray(e.tools)&&e.tools.length?JSON.stringify(e.tools).length:0}static systemChars(e){let t=e.system;if(typeof t=="string")return t.length;if(!Array.isArray(t))return 0;let r=0;for(let o of t)o&&typeof o.text=="string"&&(r+=o.text.length);return r}static tailComposition(e){let t=n.lastUserContent(e);if(typeof t=="string")return{tailResultChars:0,tailTextChars:t.length,freshToolChars:0};if(!Array.isArray(t))return{tailResultChars:0,tailTextChars:0,freshToolChars:0};let r=0,o=0,s=0;for(let i of t)if(i)if(i.type==="tool_result"){let a=n.textLength(i.content);r+=a,s=Math.max(s,a)}else typeof i.text=="string"&&(o+=i.text.length);return{tailResultChars:r,tailTextChars:o,freshToolChars:s}}static lastUserContent(e){let t=Array.isArray(e.messages)?e.messages:null;if(!t)return null;let r=t.length-1;for(;r>=0&&t[r].role!==V.User;)r-=1;if(r<0)return null;let o=t[r].content;return typeof o=="string"||Array.isArray(o)?o:null}static textLength(e){if(typeof e=="string")return e.length;if(!Array.isArray(e))return 0;let t=0;for(let r of e)r&&typeof r.text=="string"&&(t+=r.text.length);return t}build(e){let t=[];for(let[r,o]of Object.entries(this.skills??{}))e.includes(r)&&t.push(o);return t.join(`
2
- `)}static lastUserText(e){let t=Array.isArray(e.messages)?e.messages:null;if(!t)return"";for(let r=t.length-1;r>=0;r-=1){let o=t[r];if(o.role===V.User){if(typeof o.content=="string")return o.content;if(Array.isArray(o.content))return o.content.map(s=>typeof s=="string"?s:"text"in s&&typeof s.text=="string"?s.text:"").join(" ")}}return""}static appendToLastUserMessage(e,t,r){if(!r)return e;let o=Array.isArray(e.messages)?e.messages:null;if(!o||!o.length)return t===w.OpenAI&&typeof e.input=="string"&&(e.input+=`
1
+ "use strict";var Ae=Object.create;var V=Object.defineProperty;var De=Object.getOwnPropertyDescriptor;var Be=Object.getOwnPropertyNames;var Ie=Object.getPrototypeOf,Oe=Object.prototype.hasOwnProperty;var Ue=(n,e)=>{for(var t in e)V(n,t,{get:e[t],enumerable:!0})},ce=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of Be(e))!Oe.call(n,o)&&o!==t&&V(n,o,{get:()=>e[o],enumerable:!(r=De(e,o))||r.enumerable});return n};var c=(n,e,t)=>(t=n!=null?Ae(Ie(n)):{},ce(e||!n||!n.__esModule?V(t,"default",{value:n,enumerable:!0}):t,n)),Le=n=>ce(V({},"__esModule",{value:!0}),n);var Je={};Ue(Je,{Gateway:()=>$,createGatewayServer:()=>_e});module.exports=Le(Je);var Re=c(require("node:http")),J=c(require("node:fs")),G=c(require("node:path")),Ee=require("node:stream");var W=c(require("fs")),pe=c(require("os")),g=c(require("path")),B=class n{constructor(e){this.repoRoot=e;this.config=n.readJson(g.join(e,".dezycro","config.json"))}repoRoot;config;static at(e=process.cwd()){return new n(n.findRepoRoot(e)??g.resolve(e))}get workspaceId(){return n.env("DZ_WORKSPACE_ID")??n.str(this.config?.workspaceId)}get tenantId(){return n.env("DZ_TENANT_ID")??n.str(this.config?.tenantId)}get projectId(){return n.str(this.config?.projectId)}agent(e){let t=this.agentNode(e);return{context1m:n.boolFlag("DZ_1M",t?.context1m),toolSearch:n.env("DZ_TOOL_SEARCH")??n.str(t?.toolSearch)}}agentNode(e){let t=this.config?.agents;if(!n.isObj(t))return null;let r=t[e];return n.isObj(r)?r:null}static boolFlag(e,t){let r=n.env(e);return r!==null?r==="1"||r.toLowerCase()==="true":t===!0}get apiUrl(){let e=n.env("DZ_API_URL");if(e)return n.trimSlash(e);let t=Array.isArray(this.config?.environments)?this.config?.environments:[],r=t.find(s=>n.isObj(s)&&s.default===!0)??t[0],o=n.isObj(r)?n.str(r.apiUrl):null;return o?n.trimSlash(o):null}get token(){let e=n.env("DZ_TOKEN");if(e)return e;let t=[g.join(this.repoRoot,".dezycro","companion-token.local.json"),g.join(pe.homedir(),".dezycro","token.json")];for(let r of t){let o=n.str(n.readJson(r)?.token);if(o)return o}return null}activeFeature(){let e=n.readJson(g.join(this.repoRoot,".dezycro","active-feature.json")),t=n.str(e?.featureId);if(!t)return null;let r=n.str(e?.workspaceId)??this.workspaceId;return r?{workspaceId:r,featureId:t,active:e?.active===!0}:null}connection(){let e=this.apiUrl;if(!e)return{error:"no apiUrl (set DZ_API_URL or .dezycro/config.json environments[].apiUrl)"};let t=this.workspaceId;if(!t)return{error:"no workspaceId (set DZ_WORKSPACE_ID or .dezycro/config.json workspaceId)"};let r=this.token;return r?{apiUrl:e,workspaceId:t,token:r}:{error:"no token (run `dezycro setup` or set DZ_TOKEN)"}}static findRepoRoot(e){let t=g.resolve(e||process.cwd());for(let r=0;r<64;r+=1){if(W.existsSync(g.join(t,".dezycro")))return t;let o=g.dirname(t);if(o===t)return null;t=o}return null}static readJson(e){try{return JSON.parse(W.readFileSync(e,"utf8"))}catch{return null}}static isObj(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}static str(e){return typeof e=="string"&&e.length>0?e:null}static env(e){let t=process.env[e];return t&&t.length>0?t:null}static trimSlash(e){return e.replace(/\/$/,"")}};var d={MessageStart:"message_start",ContentBlockStart:"content_block_start",ContentBlockDelta:"content_block_delta",ContentBlockStop:"content_block_stop",MessageDelta:"message_delta",MessageStop:"message_stop"},Z={User:"user",Assistant:"assistant",System:"system"},y={Text:"text",ToolUse:"tool_use"},Y={Text:"text_delta",InputJson:"input_json_delta"},w={ToolUse:"tool_use",EndTurn:"end_turn"};var ue={UpdateTask:"update_task"},de={Status:"status"},te={Done:"DONE",Pushed:"PUSHED"};var b={SpecPublished:"specPublished",VerifiedRecently:"verifiedRecently"};var R={Anthropic:"anthropic",OpenAI:"openai"};var re={Health:"/__dz/health",Stop:"/__dz/stop"},me="last-opus-model",E={EventStream:"text/event-stream",Json:"application/json"};var k=class n{constructor(e){this.skills=e}skills;apply(e,t){let r=n.lastUserText(e),o=this.triggeredSkills(r),s=this.build(r);return s&&n.appendToLastUserMessage(e,t,s),{text:s,triggers:o}}triggeredSkills(e){let t=[];for(let r of Object.keys(this.skills??{}))e.includes(r)&&t.push(r);return t}static composition(e){return{toolDefChars:n.toolDefChars(e),systemChars:n.systemChars(e),...n.tailComposition(e)}}static emptyComposition(){return{toolDefChars:0,systemChars:0,tailResultChars:0,tailTextChars:0,freshToolChars:0}}static maxToolResultChars(e){return n.tailComposition(e).freshToolChars}static toolDefChars(e){return Array.isArray(e.tools)&&e.tools.length?JSON.stringify(e.tools).length:0}static systemChars(e){let t=e.system;if(typeof t=="string")return t.length;if(!Array.isArray(t))return 0;let r=0;for(let o of t)o&&typeof o.text=="string"&&(r+=o.text.length);return r}static tailComposition(e){let t=n.lastUserContent(e);if(typeof t=="string")return{tailResultChars:0,tailTextChars:t.length,freshToolChars:0};if(!Array.isArray(t))return{tailResultChars:0,tailTextChars:0,freshToolChars:0};let r=0,o=0,s=0;for(let i of t)if(i)if(i.type==="tool_result"){let a=n.textLength(i.content);r+=a,s=Math.max(s,a)}else typeof i.text=="string"&&(o+=i.text.length);return{tailResultChars:r,tailTextChars:o,freshToolChars:s}}static lastUserContent(e){let t=Array.isArray(e.messages)?e.messages:null;if(!t)return null;let r=t.length-1;for(;r>=0&&t[r].role!==Z.User;)r-=1;if(r<0)return null;let o=t[r].content;return typeof o=="string"||Array.isArray(o)?o:null}static textLength(e){if(typeof e=="string")return e.length;if(!Array.isArray(e))return 0;let t=0;for(let r of e)r&&typeof r.text=="string"&&(t+=r.text.length);return t}build(e){let t=[];for(let[r,o]of Object.entries(this.skills??{}))e.includes(r)&&t.push(o);return t.join(`
2
+ `)}static lastUserText(e){let t=Array.isArray(e.messages)?e.messages:null;if(!t)return"";for(let r=t.length-1;r>=0;r-=1){let o=t[r];if(o.role===Z.User){if(typeof o.content=="string")return o.content;if(Array.isArray(o.content))return o.content.map(s=>typeof s=="string"?s:"text"in s&&typeof s.text=="string"?s.text:"").join(" ")}}return""}static appendToLastUserMessage(e,t,r){if(!r)return e;let o=Array.isArray(e.messages)?e.messages:null;if(!o||!o.length)return t===R.OpenAI&&typeof e.input=="string"&&(e.input+=`
3
3
 
4
- ${n.tagged(r)}`),e;let s=o.length-1;for(;s>=0&&o[s].role!==V.User;)s-=1;if(s<0)return e;let i=o[s],a=n.tagged(r);return typeof i.content=="string"?i.content+=`
4
+ ${n.tagged(r)}`),e;let s=o.length-1;for(;s>=0&&o[s].role!==Z.User;)s-=1;if(s<0)return e;let i=o[s],a=n.tagged(r);return typeof i.content=="string"?i.content+=`
5
5
 
6
6
  ${a}`:Array.isArray(i.content)&&i.content.push({type:y.Text,text:a}),e}static tagged(e){return`<dezycro-companion>
7
7
  ${e}
8
- </dezycro-companion>`}};var te={rules:[{nameIncludes:le.UpdateTask,inputField:ce.Status,inputValues:[Q.Done,Q.Pushed],requireAll:[b.SpecPublished,b.VerifiedRecently],reason:"Cannot mark a task DONE/PUSHED yet."}]},A=class n{constructor(e,t){this.policy=e;this.state=t}policy;state;static STATE_PREDICATES={[b.SpecPublished]:e=>!!e&&e.needsSpecPublish!==!0,[b.VerifiedRecently]:e=>!e||!e.lastVerifyCompletionTs?!1:e.lastEditTs?Date.parse(e.lastVerifyCompletionTs)>=Date.parse(e.lastEditTs):!0};static FAILED_HINT={[b.SpecPublished]:"spec not published (controller/API changed) \u2014 run /dezycro:publish-spec",[b.VerifiedRecently]:"no passing verify since last edit \u2014 run /dezycro:verify"};decide(e,t){for(let r of this.policy?.rules??[]){if(!e||!e.includes(r.nameIncludes))continue;if(r.inputField){let a=t?.[r.inputField],l=(r.inputValues??[]).map(u=>String(u).toUpperCase());if(a==null||!l.includes(String(a).toUpperCase()))continue}let o=r.requireAll??[];if(o.length===0)return this.blocked(e,r.reason);let s=o.filter(a=>!n.STATE_PREDICATES[a]?.(this.state));if(s.length===0)continue;let i=s.map(a=>n.FAILED_HINT[a]||a).join("; ");return this.blocked(e,`${r.reason||"Blocked by Dezycro policy."} ${i}.`)}return null}blocked(e,t){return{toolName:e||"?",detail:t||"Blocked by Dezycro policy."}}};var De=new Set([d.ContentBlockStart,d.ContentBlockDelta,d.ContentBlockStop]),m=class n{constructor(e,t){this.name=e;this.data=t}name;data;static parse(e){let t=null,r=null;for(let s of e.split(`
8
+ </dezycro-companion>`}};var ne={rules:[{nameIncludes:ue.UpdateTask,inputField:de.Status,inputValues:[te.Done,te.Pushed],requireAll:[b.SpecPublished,b.VerifiedRecently],reason:"Cannot mark a task DONE/PUSHED yet."}]},I=class n{constructor(e,t){this.policy=e;this.state=t}policy;state;static STATE_PREDICATES={[b.SpecPublished]:e=>!!e&&e.needsSpecPublish!==!0,[b.VerifiedRecently]:e=>!e||!e.lastVerifyCompletionTs?!1:e.lastEditTs?Date.parse(e.lastVerifyCompletionTs)>=Date.parse(e.lastEditTs):!0};static FAILED_HINT={[b.SpecPublished]:"spec not published (controller/API changed) \u2014 run /dezycro:publish-spec",[b.VerifiedRecently]:"no passing verify since last edit \u2014 run /dezycro:verify"};decide(e,t){for(let r of this.policy?.rules??[]){if(!e||!e.includes(r.nameIncludes))continue;if(r.inputField){let a=t?.[r.inputField],l=(r.inputValues??[]).map(u=>String(u).toUpperCase());if(a==null||!l.includes(String(a).toUpperCase()))continue}let o=r.requireAll??[];if(o.length===0)return this.blocked(e,r.reason);let s=o.filter(a=>!n.STATE_PREDICATES[a]?.(this.state));if(s.length===0)continue;let i=s.map(a=>n.FAILED_HINT[a]||a).join("; ");return this.blocked(e,`${r.reason||"Blocked by Dezycro policy."} ${i}.`)}return null}blocked(e,t){return{toolName:e||"?",detail:t||"Blocked by Dezycro policy."}}};var Pe=new Set([d.ContentBlockStart,d.ContentBlockDelta,d.ContentBlockStop]),m=class n{constructor(e,t){this.name=e;this.data=t}name;data;static parse(e){let t=null,r=null;for(let s of e.split(`
9
9
  `))s.startsWith("event:")?t=s.slice(6).trim():s.startsWith("data:")&&(r=s.slice(5).trim());let o=null;try{o=r?JSON.parse(r):null}catch{o=null}return o?new n(t??String(o.type??""),o):null}static parseStream(e){return e.split(`
10
10
 
11
- `).filter(t=>t.trim().length>0).map(t=>n.parse(t)).filter(t=>t!==null)}static of(e){return new n(e.type,e)}static toolUseOf(e){return e?.type===y.ToolUse?e:null}get type(){return String(this.data.type??"")}get index(){return Number(this.data.index)}is(e){return this.type===e}get isContentBlockEvent(){return De.has(this.type)}get toolUse(){return n.toolUseOf(this.data.content_block)}get inputJsonChunk(){let e=this.data.delta;return e?.type===W.InputJson?e.partial_json??"":""}get usage(){let e=this.data.message,t=this.data.usage??e?.usage;return t?{inputTokens:t.input_tokens,outputTokens:t.output_tokens,cacheReadTokens:t.cache_read_input_tokens,cacheCreationTokens:t.cache_creation_input_tokens}:null}get model(){return this.data.message?.model??null}withIndex(e){return new n(this.name,{...this.data,index:e})}withStopReasonEnded(){let e=JSON.parse(JSON.stringify(this.data));return e.delta?.stop_reason===x.ToolUse&&(e.delta.stop_reason=x.EndTurn),new n(this.name,e)}serialize(){return`event: ${this.name}
11
+ `).filter(t=>t.trim().length>0).map(t=>n.parse(t)).filter(t=>t!==null)}static of(e){return new n(e.type,e)}static toolUseOf(e){return e?.type===y.ToolUse?e:null}get type(){return String(this.data.type??"")}get index(){return Number(this.data.index)}is(e){return this.type===e}get isContentBlockEvent(){return Pe.has(this.type)}get toolUse(){return n.toolUseOf(this.data.content_block)}get inputJsonChunk(){let e=this.data.delta;return e?.type===Y.InputJson?e.partial_json??"":""}get usage(){let e=this.data.message,t=this.data.usage??e?.usage;return t?{inputTokens:t.input_tokens,outputTokens:t.output_tokens,cacheReadTokens:t.cache_read_input_tokens,cacheCreationTokens:t.cache_creation_input_tokens}:null}get model(){return this.data.message?.model??null}withIndex(e){return new n(this.name,{...this.data,index:e})}withStopReasonEnded(){let e=JSON.parse(JSON.stringify(this.data));return e.delta?.stop_reason===w.ToolUse&&(e.delta.stop_reason=w.EndTurn),new n(this.name,e)}serialize(){return`event: ${this.name}
12
12
  data: ${JSON.stringify(this.data)}
13
13
 
14
- `}};var D=class{constructor(e){this.reasons=e}reasons;get isEmpty(){return this.reasons.length===0}text(){let e=this.reasons.map(t=>`\u2022 ${t.toolName}: ${t.detail}`).join(`
14
+ `}};var O=class{constructor(e){this.reasons=e}reasons;get isEmpty(){return this.reasons.length===0}text(){let e=this.reasons.map(t=>`\u2022 ${t.toolName}: ${t.detail}`).join(`
15
15
  `);return`\u26D4 Dezycro blocked ${this.reasons.length} tool call(s):
16
16
  ${e}
17
- Do the required step, then retry.`}sseEvents(e){return[m.of({type:d.ContentBlockStart,index:e,content_block:{type:y.Text,text:""}}),m.of({type:d.ContentBlockDelta,index:e,delta:{type:W.Text,text:this.text()}}),m.of({type:d.ContentBlockStop,index:e})].map(t=>t.serialize()).join("")}};var E=class{next=0;mapped=new Map;take(e){let t=this.next++;return this.mapped.set(e,t),t}of(e){return this.mapped.get(e)??e}reserve(){return this.next++}};var T=class{constructor(e){this.engine=e}engine;blockedReasons=[];sawAllowedToolUse=!1;judge(e,t){let r=this.engine.decide(e,t);return r?(this.blockedReasons.push(r),!0):(this.sawAllowedToolUse=!0,!1)}parseToolInput(e){try{return e?JSON.parse(e):{}}catch{return{}}}get denial(){return new D(this.blockedReasons)}get blockedCount(){return this.blockedReasons.length}};var B=class extends T{rewrite(e){try{if(!e||!Array.isArray(e.content))return e;let t=e.content.filter(o=>{let s=m.toolUseOf(o);return s?!this.judge(s.name,s.input??{}):!0}),r=this.denial;return r.isEmpty||(t.push({type:y.Text,text:r.text()}),e.content=t,!this.sawAllowedToolUse&&e.stop_reason===x.ToolUse&&(e.stop_reason=x.EndTurn)),e}catch{return e}}};var I=class extends T{pending="";remapper=new E;openTool=null;denialEmitted=!1;push(e){this.pending+=e;let t="",r;for(;(r=this.pending.indexOf(`
17
+ Do the required step, then retry.`}sseEvents(e){return[m.of({type:d.ContentBlockStart,index:e,content_block:{type:y.Text,text:""}}),m.of({type:d.ContentBlockDelta,index:e,delta:{type:Y.Text,text:this.text()}}),m.of({type:d.ContentBlockStop,index:e})].map(t=>t.serialize()).join("")}};var A=class{next=0;mapped=new Map;take(e){let t=this.next++;return this.mapped.set(e,t),t}of(e){return this.mapped.get(e)??e}reserve(){return this.next++}};var T=class{constructor(e){this.engine=e}engine;blockedReasons=[];sawAllowedToolUse=!1;judge(e,t){let r=this.engine.decide(e,t);return r?(this.blockedReasons.push(r),!0):(this.sawAllowedToolUse=!0,!1)}parseToolInput(e){try{return e?JSON.parse(e):{}}catch{return{}}}get denial(){return new O(this.blockedReasons)}get blockedCount(){return this.blockedReasons.length}};var U=class extends T{rewrite(e){try{if(!e||!Array.isArray(e.content))return e;let t=e.content.filter(o=>{let s=m.toolUseOf(o);return s?!this.judge(s.name,s.input??{}):!0}),r=this.denial;return r.isEmpty||(t.push({type:y.Text,text:r.text()}),e.content=t,!this.sawAllowedToolUse&&e.stop_reason===w.ToolUse&&(e.stop_reason=w.EndTurn)),e}catch{return e}}};var L=class extends T{pending="";remapper=new A;openTool=null;denialEmitted=!1;push(e){this.pending+=e;let t="",r;for(;(r=this.pending.indexOf(`
18
18
 
19
19
  `))!==-1;){let o=this.pending.slice(0,r);this.pending=this.pending.slice(r+2),o.trim()&&(t+=this.handle(o))}return t}flush(){let e="";return this.pending.trim()&&(e+=this.handle(this.pending),this.pending=""),e+this.emitDenialIfNeeded()}handle(e){let t=m.parse(e);if(!t)return`${e}
20
20
 
21
- `;switch(t.type){case d.ContentBlockStart:return this.onBlockStart(t);case d.ContentBlockDelta:return this.onBlockDelta(t);case d.ContentBlockStop:return this.onBlockStop(t);case d.MessageDelta:return this.emitDenialIfNeeded()+(this.sawAllowedToolUse?t:t.withStopReasonEnded()).serialize();default:return t.serialize()}}onBlockStart(e){let t=e.toolUse;return t?(this.openTool={originalIndex:e.index,toolName:t.name,inputJson:"",bufferedEvents:[e]},""):e.withIndex(this.remapper.take(e.index)).serialize()}onBlockDelta(e){return this.openTool&&e.index===this.openTool.originalIndex?(this.openTool.inputJson+=e.inputJsonChunk,this.openTool.bufferedEvents.push(e),""):e.withIndex(this.remapper.of(e.index)).serialize()}onBlockStop(e){return this.openTool&&e.index===this.openTool.originalIndex?this.closeOpenTool(e):e.withIndex(this.remapper.of(e.index)).serialize()}closeOpenTool(e){let t=this.openTool;if(this.openTool=null,this.judge(t.toolName,this.parseToolInput(t.inputJson)))return"";let r=this.remapper.take(t.originalIndex);return t.bufferedEvents.map(s=>s.withIndex(r).serialize()).join("")+e.withIndex(r).serialize()}emitDenialIfNeeded(){let e=this.denial;return e.isEmpty||this.denialEmitted?"":(this.denialEmitted=!0,e.sseEvents(this.remapper.reserve()))}};var de=require("node:util"),me=require("tslog");var Z=c(require("node:fs")),ue=c(require("node:os")),S=c(require("node:path")),Be="DZ_LOG_DIR",O=class n{stream;constructor(e){this.stream=n.open(e)}static dir(){let e=process.env[Be];if(e)return e;let t=ue.homedir();switch(process.platform){case"win32":return S.join(process.env.LOCALAPPDATA||S.join(t,"AppData","Local"),"Dezycro","logs");case"darwin":return S.join(t,"Library","Logs","Dezycro");default:return S.join(process.env.XDG_STATE_HOME||S.join(t,".local","state"),"dezycro","logs")}}write(e){(this.stream??process.stderr).write(e)}static open(e){try{let t=n.dir();return Z.mkdirSync(t,{recursive:!0}),Z.createWriteStream(S.join(t,`${e}.log`),{flags:"a"})}catch{return null}}};var re={silly:0,trace:1,debug:2,info:3,warn:4,error:5,fatal:6},Y=3,ne="DZ_LOG_LEVEL";var Ie="{{dateIsoStr}} {{logLevelName}} [{{name}}] ",U=class n{constructor(e,t=new O(e),r=n.resolveMinLevel(process.env[ne])){this.tag=e;this.impl=new me.Logger({name:e,type:"pretty",minLevel:r,stylePrettyLogs:!1,prettyLogTimeZone:"UTC",prettyLogTemplate:Ie,hideLogPositionForProduction:!0,overwrite:{transportFormatted:(o,s,i)=>t.write(n.format(o,s,i))}})}tag;impl;silly(...e){this.impl.silly(...e)}trace(...e){this.impl.trace(...e)}debug(...e){this.impl.debug(...e)}info(...e){this.impl.info(...e)}warn(...e){this.impl.warn(...e)}error(...e){this.impl.error(...e)}fatal(...e){this.impl.fatal(...e)}static resolveMinLevel(e){return e?re[e.trim().toLowerCase()]??Y:Y}static format(e,t,r){return`${[e.trimEnd(),...t.map(n.stringify),...r].join(" ")}
22
- `}static stringify(e){return typeof e=="string"?e:(0,de.inspect)(e,{depth:3,breakLength:1/0})}};var q=c(require("node:fs")),he=c(require("node:path"));var fe=c(require("node:fs")),v=c(require("node:path")),K=class n{static MAX_DEPTH=6;static dir(){let e=__dirname;for(let t=0;t<n.MAX_DEPTH;t+=1){if(fe.existsSync(v.join(e,"package.json")))return e;e=v.dirname(e)}return v.resolve(__dirname,"..","..")}static file(...e){return v.join(n.dir(),...e)}};var L=class n{constructor(e=n.defaultDir()){this.dir=e}dir;load(){return Promise.resolve(n.scan(this.dir))}static defaultDir(){return K.file("skills")}static scan(e){let t={},r;try{r=q.readdirSync(e,{withFileTypes:!0})}catch{return t}for(let o of r){if(!o.isDirectory())continue;let s=he.join(e,o.name,"SKILL.md"),i;try{i=q.readFileSync(s,"utf8")}catch{continue}let{name:a,body:l}=n.parseSkill(i),u=a||o.name;t[`/dezycro:${u}`]=n.instructionFor(u,l)}return t}static parseSkill(e){if(e.startsWith("---")){let t=e.indexOf(`
21
+ `;switch(t.type){case d.ContentBlockStart:return this.onBlockStart(t);case d.ContentBlockDelta:return this.onBlockDelta(t);case d.ContentBlockStop:return this.onBlockStop(t);case d.MessageDelta:return this.emitDenialIfNeeded()+(this.sawAllowedToolUse?t:t.withStopReasonEnded()).serialize();default:return t.serialize()}}onBlockStart(e){let t=e.toolUse;return t?(this.openTool={originalIndex:e.index,toolName:t.name,inputJson:"",bufferedEvents:[e]},""):e.withIndex(this.remapper.take(e.index)).serialize()}onBlockDelta(e){return this.openTool&&e.index===this.openTool.originalIndex?(this.openTool.inputJson+=e.inputJsonChunk,this.openTool.bufferedEvents.push(e),""):e.withIndex(this.remapper.of(e.index)).serialize()}onBlockStop(e){return this.openTool&&e.index===this.openTool.originalIndex?this.closeOpenTool(e):e.withIndex(this.remapper.of(e.index)).serialize()}closeOpenTool(e){let t=this.openTool;if(this.openTool=null,this.judge(t.toolName,this.parseToolInput(t.inputJson)))return"";let r=this.remapper.take(t.originalIndex);return t.bufferedEvents.map(s=>s.withIndex(r).serialize()).join("")+e.withIndex(r).serialize()}emitDenialIfNeeded(){let e=this.denial;return e.isEmpty||this.denialEmitted?"":(this.denialEmitted=!0,e.sseEvents(this.remapper.reserve()))}};var he=require("node:util"),ge=require("tslog");var K=c(require("node:fs")),fe=c(require("node:os")),S=c(require("node:path")),Ne="DZ_LOG_DIR",P=class n{stream;constructor(e){this.stream=n.open(e)}static dir(){let e=process.env[Ne];if(e)return e;let t=fe.homedir();switch(process.platform){case"win32":return S.join(process.env.LOCALAPPDATA||S.join(t,"AppData","Local"),"Dezycro","logs");case"darwin":return S.join(t,"Library","Logs","Dezycro");default:return S.join(process.env.XDG_STATE_HOME||S.join(t,".local","state"),"dezycro","logs")}}write(e){(this.stream??process.stderr).write(e)}static open(e){try{let t=n.dir();return K.mkdirSync(t,{recursive:!0}),K.createWriteStream(S.join(t,`${e}.log`),{flags:"a"})}catch{return null}}};var oe={silly:0,trace:1,debug:2,info:3,warn:4,error:5,fatal:6},q=3,se="DZ_LOG_LEVEL";var je="{{dateIsoStr}} {{logLevelName}} [{{name}}] ",N=class n{constructor(e,t=new P(e),r=n.resolveMinLevel(process.env[se])){this.tag=e;this.impl=new ge.Logger({name:e,type:"pretty",minLevel:r,stylePrettyLogs:!1,prettyLogTimeZone:"UTC",prettyLogTemplate:je,hideLogPositionForProduction:!0,overwrite:{transportFormatted:(o,s,i)=>t.write(n.format(o,s,i))}})}tag;impl;silly(...e){this.impl.silly(...e)}trace(...e){this.impl.trace(...e)}debug(...e){this.impl.debug(...e)}info(...e){this.impl.info(...e)}warn(...e){this.impl.warn(...e)}error(...e){this.impl.error(...e)}fatal(...e){this.impl.fatal(...e)}static resolveMinLevel(e){return e?oe[e.trim().toLowerCase()]??q:q}static format(e,t,r){return`${[e.trimEnd(),...t.map(n.stringify),...r].join(" ")}
22
+ `}static stringify(e){return typeof e=="string"?e:(0,he.inspect)(e,{depth:3,breakLength:1/0})}};var X=c(require("node:fs")),ke=c(require("node:path"));var ye=c(require("node:fs")),v=c(require("node:path")),D=class n{static MAX_DEPTH=6;static dir(){let e=__dirname;for(let t=0;t<n.MAX_DEPTH;t+=1){if(ye.existsSync(v.join(e,"package.json")))return e;e=v.dirname(e)}return v.resolve(__dirname,"..","..")}static file(...e){return v.join(n.dir(),...e)}};var j=class n{constructor(e=n.defaultDir()){this.dir=e}dir;load(){return Promise.resolve(n.scan(this.dir))}static defaultDir(){return D.file("skills")}static scan(e){let t={},r;try{r=X.readdirSync(e,{withFileTypes:!0})}catch{return t}for(let o of r){if(!o.isDirectory())continue;let s=ke.join(e,o.name,"SKILL.md"),i;try{i=X.readFileSync(s,"utf8")}catch{continue}let{name:a,body:l}=n.parseSkill(i),u=a||o.name;t[`/dezycro:${u}`]=n.instructionFor(u,l)}return t}static parseSkill(e){if(e.startsWith("---")){let t=e.indexOf(`
23
23
  ---`,3);if(t!==-1){let r=e.slice(3,t),o=e.slice(t+4).trimStart();return{name:n.frontmatterName(r),body:o}}}return{name:null,body:e}}static frontmatterName(e){let t="name:";for(let r of e.split(`
24
24
  `)){let o=r.trim();if(o.startsWith(t))return o.slice(t.length).trim()||null}return null}static instructionFor(e,t){return`The user invoked the Dezycro \`/dezycro:${e}\` command. Your harness may not support it natively \u2014 follow these instructions:
25
25
 
26
- ${t}`}};var P=class{constructor(e){this.sources=e}sources;async load(){return(await Promise.all(this.sources.map(t=>t.load()))).reduce((t,r)=>({...t,...r}),{})}};var X=c(require("node:fs")),ge=c(require("node:os")),C=c(require("node:path")),Oe="DZ_METRICS_DIR",Ue="DZ_METRICS",h=class n{file;constructor(e="gateway"){this.file=n.prepare(e)}static enabled(){return process.env[Ue]!=="0"}static dir(){let e=process.env[Oe];if(e)return e;let t=ge.homedir();switch(process.platform){case"win32":return C.join(process.env.LOCALAPPDATA||C.join(t,"AppData","Local"),"Dezycro","metrics");case"darwin":return C.join(t,"Library","Application Support","Dezycro","metrics");default:return C.join(process.env.XDG_STATE_HOME||C.join(t,".local","state"),"dezycro","metrics")}}record(e){if(this.file)try{X.appendFileSync(this.file,`${JSON.stringify({ts:new Date().toISOString(),...e})}
27
- `)}catch{}}static prepare(e){if(!n.enabled())return null;try{let t=n.dir();return X.mkdirSync(t,{recursive:!0}),C.join(t,`${e}.jsonl`)}catch{return null}}};var M=class{pending="";modelId=null;totals={inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreationTokens:0};feed(e){this.pending+=e;let t=this.pending.indexOf(`
26
+ ${t}`}};var M=class{constructor(e){this.sources=e}sources;async load(){return(await Promise.all(this.sources.map(t=>t.load()))).reduce((t,r)=>({...t,...r}),{})}};var Q=c(require("node:fs")),Te=c(require("node:os")),C=c(require("node:path")),Me="DZ_METRICS_DIR",Fe="DZ_METRICS",h=class n{file;constructor(e="gateway"){this.file=n.prepare(e)}static enabled(){return process.env[Fe]!=="0"}static dir(){let e=process.env[Me];if(e)return e;let t=Te.homedir();switch(process.platform){case"win32":return C.join(process.env.LOCALAPPDATA||C.join(t,"AppData","Local"),"Dezycro","metrics");case"darwin":return C.join(t,"Library","Application Support","Dezycro","metrics");default:return C.join(process.env.XDG_STATE_HOME||C.join(t,".local","state"),"dezycro","metrics")}}record(e){if(this.file)try{Q.appendFileSync(this.file,`${JSON.stringify({ts:new Date().toISOString(),...e})}
27
+ `)}catch{}}static prepare(e){if(!n.enabled())return null;try{let t=n.dir();return Q.mkdirSync(t,{recursive:!0}),C.join(t,`${e}.jsonl`)}catch{return null}}};var F=class{pending="";modelId=null;totals={inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreationTokens:0};feed(e){this.pending+=e;let t=this.pending.indexOf(`
28
28
 
29
29
  `);for(;t!==-1;)this.observe(this.pending.slice(0,t)),this.pending=this.pending.slice(t+2),t=this.pending.indexOf(`
30
30
 
31
- `)}get model(){return this.modelId}get usage(){return{...this.totals}}observe(e){let t=m.parse(e);if(!t)return;this.modelId??=t.model;let r=t.usage;r&&(r.inputTokens!=null&&(this.totals.inputTokens=r.inputTokens),r.outputTokens!=null&&(this.totals.outputTokens=r.outputTokens),r.cacheReadTokens!=null&&(this.totals.cacheReadTokens=r.cacheReadTokens),r.cacheCreationTokens!=null&&(this.totals.cacheCreationTokens=r.cacheCreationTokens))}};var ye=c(require("node:fs")),ke=c(require("node:path"));var N=class n{static BIG_TOOL_CHARS=4e3;static defaultFile(){return ke.join(h.dir(),"gateway.jsonl")}static fromFile(e=n.defaultFile()){let t;try{t=ye.readFileSync(e,"utf8")}catch{return n.empty()}return n.aggregate(t.split(`
32
- `))}static aggregate(e){let t=n.empty();for(let r of e){let o=n.parse(r);o&&(o.type==="turn"?n.addTurn(t,o):o.type==="compaction"&&n.addCompaction(t,o),n.trackTime(t,o.ts))}return t}static addTurn(e,t){e.turns+=1,e.inputTokens+=n.num(t.inputTokens),e.outputTokens+=n.num(t.outputTokens),e.cacheReadTokens+=n.num(t.cacheReadTokens),e.cacheCreationTokens+=n.num(t.cacheCreationTokens),e.totalDurationMs+=n.num(t.durationMs),e.gateBlocks+=n.num(t.blockedToolCalls);let r=n.num(t.freshToolChars);if(e.maxFreshToolChars=Math.max(e.maxFreshToolChars,r),r>n.BIG_TOOL_CHARS&&(e.bigToolTurns+=1),e.maxToolDefChars=Math.max(e.maxToolDefChars,n.num(t.toolDefChars)),e.maxSystemChars=Math.max(e.maxSystemChars,n.num(t.systemChars)),e.tailResultChars+=n.num(t.tailResultChars),e.tailTextChars+=n.num(t.tailTextChars),e.maxCacheCreationTokens=Math.max(e.maxCacheCreationTokens,n.num(t.cacheCreationTokens)),typeof t.status=="number"&&t.status!==200&&(e.errorTurns+=1),n.bump(e.models,typeof t.model=="string"?t.model:"(unknown)"),Array.isArray(t.skillsTriggered))for(let o of t.skillsTriggered)n.bump(e.skillsTriggered,String(o))}static addCompaction(e,t){e.compactions+=1,e.charsSaved+=n.num(t.charsSaved),e.estTokensSaved+=n.num(t.estTokensSaved),e.linesElided+=n.num(t.elidedLines),n.bump(e.commands,typeof t.command=="string"?t.command:"(unknown)")}static trackTime(e,t){typeof t=="string"&&((!e.firstTs||t<e.firstTs)&&(e.firstTs=t),(!e.lastTs||t>e.lastTs)&&(e.lastTs=t))}static parse(e){if(!e.trim())return null;try{return JSON.parse(e)}catch{return null}}static bump(e,t){e[t]=(e[t]??0)+1}static num(e){return typeof e=="number"&&Number.isFinite(e)?e:0}static empty(){return{turns:0,inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreationTokens:0,totalDurationMs:0,errorTurns:0,gateBlocks:0,maxFreshToolChars:0,bigToolTurns:0,maxToolDefChars:0,maxSystemChars:0,tailResultChars:0,tailTextChars:0,maxCacheCreationTokens:0,models:{},skillsTriggered:{},compactions:0,charsSaved:0,estTokensSaved:0,linesElided:0,commands:{},firstTs:null,lastTs:null}}};var f=c(require("node:fs")),Te=c(require("node:path")),j=class n{apiUrl;workspaceId;token;metricsFile;cursorFile;batchSize;timeoutMs;fetchImpl;constructor(e){this.apiUrl=e.apiUrl,this.workspaceId=e.workspaceId,this.token=e.token,this.metricsFile=e.metricsFile,this.cursorFile=e.cursorFile,this.batchSize=e.batchSize??1e3,this.timeoutMs=e.timeoutMs??15e3,this.fetchImpl=e.fetchImpl??fetch}async upload({dryRun:e=!1}={}){let{lines:t,newOffset:r}=this.readNewLines(),o=n.toBatch(t),s=o.turns.length+o.compactions.length,i={turns:o.turns.length,compactions:o.compactions.length,newOffset:r,sent:!1,dryRun:e};return e?i:s===0?(r!==this.readCursor()&&this.writeCursor(r),i):(await this.postAll(o),this.writeCursor(r),{...i,sent:!0})}static toBatch(e){let t=[],r=[];for(let o of e){let s;try{s=JSON.parse(o)}catch{continue}typeof s.ts=="string"&&(s.type==="turn"?t.push(n.mapTurn(s)):s.type==="compaction"&&r.push(n.mapCompaction(s)))}return{turns:t,compactions:r}}static mapTurn(e){return{timestamp:e.ts,provider:e.provider??null,model:e.model??null,status:e.status??null,durationMs:e.durationMs??null,inputTokens:e.inputTokens??null,outputTokens:e.outputTokens??null,cacheReadTokens:e.cacheReadTokens??null,cacheCreationTokens:e.cacheCreationTokens??null,toolDefChars:e.toolDefChars??null,systemChars:e.systemChars??null,freshToolChars:e.freshToolChars??null,tailResultChars:e.tailResultChars??null,tailTextChars:e.tailTextChars??null,blockedToolCalls:e.blockedToolCalls??null,skillsFired:Array.isArray(e.skillsTriggered)?e.skillsTriggered:[]}}static mapCompaction(e){return{timestamp:e.ts,command:e.command??null,originalChars:e.originalChars??null,outputChars:e.outputChars??null,charsSaved:e.charsSaved??null,estTokensSaved:e.estTokensSaved??null,originalLines:e.originalLines??null,outputLines:e.outputLines??null,elidedLines:e.elidedLines??null}}readNewLines(){let e=this.readCursor(),t;try{t=f.statSync(this.metricsFile).size}catch{return{lines:[],newOffset:e}}if(e>t&&(e=0),e===t)return{lines:[],newOffset:e};let r=f.openSync(this.metricsFile,"r");try{let o=t-e,s=Buffer.alloc(o);f.readSync(r,s,0,o,e);let i=s.toString("utf8"),a=i.lastIndexOf(`
31
+ `)}get model(){return this.modelId}get usage(){return{...this.totals}}observe(e){let t=m.parse(e);if(!t)return;this.modelId??=t.model;let r=t.usage;r&&(r.inputTokens!=null&&(this.totals.inputTokens=r.inputTokens),r.outputTokens!=null&&(this.totals.outputTokens=r.outputTokens),r.cacheReadTokens!=null&&(this.totals.cacheReadTokens=r.cacheReadTokens),r.cacheCreationTokens!=null&&(this.totals.cacheCreationTokens=r.cacheCreationTokens))}};var be=c(require("node:fs")),Se=c(require("node:path"));var z=class n{static BIG_TOOL_CHARS=4e3;static defaultFile(){return Se.join(h.dir(),"gateway.jsonl")}static fromFile(e=n.defaultFile()){let t;try{t=be.readFileSync(e,"utf8")}catch{return n.empty()}return n.aggregate(t.split(`
32
+ `))}static aggregate(e){let t=n.empty();for(let r of e){let o=n.parse(r);o&&(o.type==="turn"?n.addTurn(t,o):o.type==="compaction"&&n.addCompaction(t,o),n.trackTime(t,o.ts))}return t}static addTurn(e,t){e.turns+=1,e.inputTokens+=n.num(t.inputTokens),e.outputTokens+=n.num(t.outputTokens),e.cacheReadTokens+=n.num(t.cacheReadTokens),e.cacheCreationTokens+=n.num(t.cacheCreationTokens),e.totalDurationMs+=n.num(t.durationMs),e.gateBlocks+=n.num(t.blockedToolCalls);let r=n.num(t.freshToolChars);if(e.maxFreshToolChars=Math.max(e.maxFreshToolChars,r),r>n.BIG_TOOL_CHARS&&(e.bigToolTurns+=1),e.maxToolDefChars=Math.max(e.maxToolDefChars,n.num(t.toolDefChars)),e.maxSystemChars=Math.max(e.maxSystemChars,n.num(t.systemChars)),e.tailResultChars+=n.num(t.tailResultChars),e.tailTextChars+=n.num(t.tailTextChars),e.maxCacheCreationTokens=Math.max(e.maxCacheCreationTokens,n.num(t.cacheCreationTokens)),typeof t.status=="number"&&t.status!==200&&(e.errorTurns+=1),n.bump(e.models,typeof t.model=="string"?t.model:"(unknown)"),Array.isArray(t.skillsTriggered))for(let o of t.skillsTriggered)n.bump(e.skillsTriggered,String(o))}static addCompaction(e,t){e.compactions+=1,e.charsSaved+=n.num(t.charsSaved),e.estTokensSaved+=n.num(t.estTokensSaved),e.linesElided+=n.num(t.elidedLines),n.bump(e.commands,typeof t.command=="string"?t.command:"(unknown)")}static trackTime(e,t){typeof t=="string"&&((!e.firstTs||t<e.firstTs)&&(e.firstTs=t),(!e.lastTs||t>e.lastTs)&&(e.lastTs=t))}static parse(e){if(!e.trim())return null;try{return JSON.parse(e)}catch{return null}}static bump(e,t){e[t]=(e[t]??0)+1}static num(e){return typeof e=="number"&&Number.isFinite(e)?e:0}static empty(){return{turns:0,inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreationTokens:0,totalDurationMs:0,errorTurns:0,gateBlocks:0,maxFreshToolChars:0,bigToolTurns:0,maxToolDefChars:0,maxSystemChars:0,tailResultChars:0,tailTextChars:0,maxCacheCreationTokens:0,models:{},skillsTriggered:{},compactions:0,charsSaved:0,estTokensSaved:0,linesElided:0,commands:{},firstTs:null,lastTs:null}}};var f=c(require("node:fs")),ve=c(require("node:path")),_=class n{apiUrl;workspaceId;token;metricsFile;cursorFile;batchSize;timeoutMs;fetchImpl;constructor(e){this.apiUrl=e.apiUrl,this.workspaceId=e.workspaceId,this.token=e.token,this.metricsFile=e.metricsFile,this.cursorFile=e.cursorFile,this.batchSize=e.batchSize??1e3,this.timeoutMs=e.timeoutMs??15e3,this.fetchImpl=e.fetchImpl??fetch}async upload({dryRun:e=!1}={}){let{lines:t,newOffset:r}=this.readNewLines(),o=n.toBatch(t),s=o.turns.length+o.compactions.length,i={turns:o.turns.length,compactions:o.compactions.length,newOffset:r,sent:!1,dryRun:e};return e?i:s===0?(r!==this.readCursor()&&this.writeCursor(r),i):(await this.postAll(o),this.writeCursor(r),{...i,sent:!0})}static toBatch(e){let t=[],r=[];for(let o of e){let s;try{s=JSON.parse(o)}catch{continue}typeof s.ts=="string"&&(s.type==="turn"?t.push(n.mapTurn(s)):s.type==="compaction"&&r.push(n.mapCompaction(s)))}return{turns:t,compactions:r}}static mapTurn(e){return{timestamp:e.ts,provider:e.provider??null,model:e.model??null,status:e.status??null,durationMs:e.durationMs??null,inputTokens:e.inputTokens??null,outputTokens:e.outputTokens??null,cacheReadTokens:e.cacheReadTokens??null,cacheCreationTokens:e.cacheCreationTokens??null,toolDefChars:e.toolDefChars??null,systemChars:e.systemChars??null,freshToolChars:e.freshToolChars??null,tailResultChars:e.tailResultChars??null,tailTextChars:e.tailTextChars??null,blockedToolCalls:e.blockedToolCalls??null,skillsFired:Array.isArray(e.skillsTriggered)?e.skillsTriggered:[]}}static mapCompaction(e){return{timestamp:e.ts,command:e.command??null,originalChars:e.originalChars??null,outputChars:e.outputChars??null,charsSaved:e.charsSaved??null,estTokensSaved:e.estTokensSaved??null,originalLines:e.originalLines??null,outputLines:e.outputLines??null,elidedLines:e.elidedLines??null}}readNewLines(){let e=this.readCursor(),t;try{t=f.statSync(this.metricsFile).size}catch{return{lines:[],newOffset:e}}if(e>t&&(e=0),e===t)return{lines:[],newOffset:e};let r=f.openSync(this.metricsFile,"r");try{let o=t-e,s=Buffer.alloc(o);f.readSync(r,s,0,o,e);let i=s.toString("utf8"),a=i.lastIndexOf(`
33
33
  `);if(a<0)return{lines:[],newOffset:e};let l=i.slice(0,a+1),u=e+Buffer.byteLength(l,"utf8");return{lines:l.split(`
34
- `).filter(J=>J.trim().length>0),newOffset:u}}finally{f.closeSync(r)}}readCursor(){try{let e=JSON.parse(f.readFileSync(this.cursorFile,"utf8"));return typeof e.offset=="number"&&e.offset>=0?e.offset:0}catch{return 0}}writeCursor(e){try{f.mkdirSync(Te.dirname(this.cursorFile),{recursive:!0}),f.writeFileSync(this.cursorFile,JSON.stringify({offset:e,uploadedAt:new Date().toISOString()}))}catch{}}async postAll(e){for(let t=0;t<e.turns.length;t+=this.batchSize)await this.postBatch({turns:e.turns.slice(t,t+this.batchSize),compactions:[]});for(let t=0;t<e.compactions.length;t+=this.batchSize)await this.postBatch({turns:[],compactions:e.compactions.slice(t,t+this.batchSize)})}async postBatch(e){let t=`${this.apiUrl}/api/v1/workspaces/${this.workspaceId}/usage-telemetry`,r=new AbortController,o=setTimeout(()=>r.abort(),this.timeoutMs);try{let s=await this.fetchImpl(t,{method:"POST",headers:{Authorization:`Bearer ${this.token}`,"content-type":"application/json",accept:"application/json"},body:JSON.stringify(e),signal:r.signal});if(!s.ok)throw new Error(`usage-telemetry upload returned ${s.status}`)}finally{clearTimeout(o)}}};var _=class n{constructor(e,t){this.config=e;this.deps=t;this.injector=new k(t.skills)}config;deps;static UPSTREAMS=[{match:e=>e.startsWith("/v1/messages"),base:"https://api.anthropic.com",provider:w.Anthropic},{match:e=>e.startsWith("/v1/chat/completions")||e.startsWith("/v1/responses")||e.startsWith("/v1/completions"),base:"https://api.openai.com",provider:w.OpenAI}];static HOP_BY_HOP_REQUEST=["host","content-length","connection","accept-encoding","transfer-encoding"];static HOP_BY_HOP_RESPONSE=["content-encoding","content-length","transfer-encoding","connection"];injector;logger=new U("gateway");metrics=new h("gateway");telemetryTimer=null;telemetryUploader=null;static async fromEnv(){let e={port:Number(process.env.PORT||13579),inject:process.env.INJECT==="1",gate:process.env.GATE==="1",capture:process.env.CAPTURE==="1",stateFile:process.env.DZ_STATE_FILE||null},t={skills:await n.loadSkills(),policy:n.loadJson(process.env.DZ_POLICY_FILE)||te,loadState:()=>n.loadJson(e.stateFile)};return new n(e,t)}static loadSkills(){return new P(n.skillSources()).load()}static skillSources(){return[new L]}createServer(){return be.createServer((e,t)=>this.handle(e,t))}listen(){let e=this.createServer();return e.on("error",t=>{t.code==="EADDRINUSE"&&(this.logger.warn(`port ${this.config.port} already in use \u2014 a gateway is probably running.`),process.exit(1)),this.logger.error(`server error: ${t.message}`),process.exit(1)}),e.listen(this.config.port,"127.0.0.1",()=>{this.logger.info(`listening on http://127.0.0.1:${this.config.port} (inject=${this.config.inject} gate=${this.config.gate}) skills=${Object.keys(this.deps.skills).length}`)}),this.startTelemetry(),e}startTelemetry(){if(!h.enabled()||process.env.DZ_TELEMETRY==="0")return;let e=$.at(process.cwd()).connection();if("error"in e){this.logger.debug(`telemetry: off (${e.error})`);return}this.telemetryUploader=new j({...e,metricsFile:N.defaultFile(),cursorFile:z.join(h.dir(),"gateway.upload-cursor.json")});let t=Math.max(3e4,Number(process.env.DZ_TELEMETRY_INTERVAL_MS)||3e5),r=()=>{this.flushTelemetry()};this.telemetryTimer=setInterval(r,t),this.telemetryTimer.unref?.(),setTimeout(r,15e3).unref?.(),this.logger.info(`telemetry: background upload every ${Math.round(t/1e3)}s \u2192 ws ${e.workspaceId}`)}async flushTelemetry(){if(this.telemetryUploader)try{let e=await this.telemetryUploader.upload();e.sent&&this.logger.info(`telemetry: uploaded ${e.turns} turn + ${e.compactions} compaction event(s)`)}catch(e){this.logger.warn(`telemetry upload failed: ${e.message}`)}}handle(e,t){if(this.handleControlPlane(e,t))return;let r=n.pickUpstream(e.url||""),o=[];e.on("data",s=>o.push(s)),e.on("end",()=>this.forward(e,t,r,o))}handleControlPlane(e,t){return e.url===ee.Health?(t.writeHead(200,{"content-type":R.Json}),t.end(JSON.stringify({ok:!0,dz:!0,pid:process.pid,port:this.config.port,inject:this.config.inject,gate:this.config.gate})),!0):e.url===ee.Stop?(t.writeHead(200,{"content-type":R.Json}),t.end(JSON.stringify({ok:!0,stopping:!0})),this.telemetryTimer&&clearInterval(this.telemetryTimer),this.flushTelemetry().finally(()=>setTimeout(()=>process.exit(0),50)),setTimeout(()=>process.exit(0),3e3).unref?.(),!0):!1}async forward(e,t,r,o){let s=Date.now(),i=Buffer.concat(o),a=[],l=k.emptyComposition();if(this.config.inject&&r&&e.method==="POST"&&i.length){let p=this.injectBody(i.toString("utf8"),r.provider);i=Buffer.from(p.bodyText,"utf8"),a=p.triggers,l=p.composition,this.persistOpusModel(p.model)}let u=process.env.FORCE_UPSTREAM||(r?r.base:"https://api.anthropic.com");try{let p=await fetch(u+e.url,{method:e.method,headers:n.buildForwardHeaders(e.headers),body:["GET","HEAD"].includes(e.method||"")?void 0:i,redirect:"manual"}),J=(p.headers.get("content-type")||"").toLowerCase();if(r?.provider===w.Anthropic&&p.ok&&p.body&&(this.config.gate||h.enabled())){let oe=this.config.gate?new A(this.deps.policy,this.deps.loadState()):null,se={startedAt:s,provider:r.provider,status:p.status,triggers:a,...l};if(J.includes(R.EventStream))return this.streamResponse(p,t,oe,se);if(J.includes(R.Json))return this.jsonResponse(p,t,oe,se)}t.writeHead(p.status,n.filterResponseHeaders(p.headers)),p.body?n.webToNode(p.body).pipe(t):t.end()}catch(p){this.logger.error(`upstream error: ${p.message}`),t.writeHead(502,{"content-type":R.Json}),t.end(JSON.stringify({error:"gateway upstream failed",detail:String(p)}))}}injectBody(e,t){try{let r=JSON.parse(e),o=typeof r.model=="string"?r.model:null,{text:s,triggers:i}=this.injector.apply(r,t),a=h.enabled()?k.composition(r):k.emptyComposition();return s?(this.logger.debug(`-> ${t} injected(${s.split(`
35
- `).length} line(s))`),{bodyText:JSON.stringify(r),triggers:i,composition:a,model:o}):{bodyText:e,triggers:i,composition:a,model:o}}catch{return{bodyText:e,triggers:[],composition:k.emptyComposition(),model:null}}}persistOpusModel(e){if(!(!e||!/opus/i.test(e)||!this.config.stateFile))try{let t=e.replace(/\[.*$/,""),r=z.join(z.dirname(this.config.stateFile),pe);if(n.readFileTrim(r)===t)return;F.writeFileSync(r,t)}catch{}}static readFileTrim(e){try{return F.readFileSync(e,"utf8").trim()}catch{return null}}streamResponse(e,t,r,o){t.writeHead(e.status,n.filterResponseHeaders(e.headers));let s=r?new I(r):null,i=h.enabled()?new M:null,a=n.webToNode(e.body);a.setEncoding("utf8"),a.on("data",l=>{if(i&&i.feed(l),!s){t.write(l);return}try{let u=s.push(l);u&&t.write(u)}catch{t.write(l)}}),a.on("end",()=>{if(s)try{let l=s.flush();l&&t.write(l)}catch{}t.end(),this.recordTurn(o,i,s)}),a.on("error",()=>{try{t.end()}catch{}this.recordTurn(o,i,s)})}async jsonResponse(e,t,r,o){let s=await e.text(),i=s,a=null,l={model:null,usage:{}};try{let u=JSON.parse(s);l={model:u.model??null,usage:n.usageOf(u)},r&&(a=new B(r),i=JSON.stringify(a.rewrite(u)))}catch{i=s}a&&i!==s&&this.logger.info("\u26D4 gate rewrote response (blocked tool_use)"),t.writeHead(e.status,n.filterResponseHeaders(e.headers)),t.end(i),this.recordTurn(o,l,a)}recordTurn(e,t,r){let o=t?.usage??{};this.metrics.record({type:"turn",provider:e.provider,model:t?.model??null,durationMs:Date.now()-e.startedAt,status:e.status,inputTokens:o.inputTokens,outputTokens:o.outputTokens,cacheReadTokens:o.cacheReadTokens,cacheCreationTokens:o.cacheCreationTokens,skillsTriggered:e.triggers.length?e.triggers:void 0,blockedToolCalls:r?r.blockedCount:void 0,gated:r?r.blockedCount>0:void 0,freshToolChars:e.freshToolChars||void 0,toolDefChars:e.toolDefChars||void 0,systemChars:e.systemChars||void 0,tailResultChars:e.tailResultChars||void 0,tailTextChars:e.tailTextChars||void 0})}static usageOf(e){let t=e.usage;return t?{inputTokens:t.input_tokens,outputTokens:t.output_tokens,cacheReadTokens:t.cache_read_input_tokens,cacheCreationTokens:t.cache_creation_input_tokens}:{}}static pickUpstream(e){return n.UPSTREAMS.find(t=>t.match(e))??null}static buildForwardHeaders(e){let t={};for(let[r,o]of Object.entries(e))n.HOP_BY_HOP_REQUEST.includes(r.toLowerCase())||(typeof o=="string"?t[r]=o:Array.isArray(o)&&(t[r]=o.join(", ")));return t}static filterResponseHeaders(e){let t={};return e.forEach((r,o)=>{n.HOP_BY_HOP_RESPONSE.includes(o.toLowerCase())||(t[o]=r)}),t}static webToNode(e){return Se.Readable.fromWeb(e)}static loadJson(e){try{return e?JSON.parse(F.readFileSync(e,"utf8")):null}catch{return null}}};function Le(n,e){return new _(n,e).createServer()}require.main===module&&_.fromEnv().then(n=>n.listen());0&&(module.exports={Gateway,createGatewayServer});
34
+ `).filter(H=>H.trim().length>0),newOffset:u}}finally{f.closeSync(r)}}readCursor(){try{let e=JSON.parse(f.readFileSync(this.cursorFile,"utf8"));return typeof e.offset=="number"&&e.offset>=0?e.offset:0}catch{return 0}}writeCursor(e){try{f.mkdirSync(ve.dirname(this.cursorFile),{recursive:!0}),f.writeFileSync(this.cursorFile,JSON.stringify({offset:e,uploadedAt:new Date().toISOString()}))}catch{}}async postAll(e){for(let t=0;t<e.turns.length;t+=this.batchSize)await this.postBatch({turns:e.turns.slice(t,t+this.batchSize),compactions:[]});for(let t=0;t<e.compactions.length;t+=this.batchSize)await this.postBatch({turns:[],compactions:e.compactions.slice(t,t+this.batchSize)})}async postBatch(e){let t=`${this.apiUrl}/api/v1/workspaces/${this.workspaceId}/usage-telemetry`,r=new AbortController,o=setTimeout(()=>r.abort(),this.timeoutMs);try{let s=await this.fetchImpl(t,{method:"POST",headers:{Authorization:`Bearer ${this.token}`,"content-type":"application/json",accept:"application/json"},body:JSON.stringify(e),signal:r.signal});if(!s.ok)throw new Error(`usage-telemetry upload returned ${s.status}`)}finally{clearTimeout(o)}}};var Ce=require("node:child_process"),ee=c(require("fs")),xe=c(require("os")),x=c(require("path")),ze=240*1e3;function we(n,e){let t=x.join(xe.homedir(),".dezycro"),r=ie(x.join(t,"skills.json"));if(!r||r.enabled===!1||!r.workspaces||typeof r.workspaces!="object"||Object.keys(r.workspaces).length===0)return!1;let s=ie(x.join(n,".dezycro","config.json"))?.skills;if(s&&typeof s=="object"&&s.enabled===!1)return!1;let i=ie(x.join(t,"skills-state.json")),a=typeof i?.lastSyncTs=="string"?Date.parse(i.lastSyncTs):NaN;if(Number.isFinite(a)&&Date.now()-a<ze)return!1;let l=e??x.resolve(__dirname,"..","..","cli","dezycro.js");if(!ee.existsSync(l))return!1;try{return(0,Ce.spawn)(process.execPath,[l,"skills","sync"],{detached:!0,stdio:"ignore"}).unref(),!0}catch{return!1}}function ie(n){try{return JSON.parse(ee.readFileSync(n,"utf8"))}catch{return null}}var $=class n{constructor(e,t){this.config=e;this.deps=t;this.injector=new k(t.skills)}config;deps;static UPSTREAMS=[{match:e=>e.startsWith("/v1/messages"),base:"https://api.anthropic.com",provider:R.Anthropic},{match:e=>e.startsWith("/v1/chat/completions")||e.startsWith("/v1/responses")||e.startsWith("/v1/completions"),base:"https://api.openai.com",provider:R.OpenAI}];static HOP_BY_HOP_REQUEST=["host","content-length","connection","accept-encoding","transfer-encoding"];static HOP_BY_HOP_RESPONSE=["content-encoding","content-length","transfer-encoding","connection"];injector;logger=new N("gateway");metrics=new h("gateway");telemetryTimer=null;telemetryUploader=null;skillsSyncTimer=null;static async fromEnv(){let e={port:Number(process.env.PORT||13579),inject:process.env.INJECT==="1",gate:process.env.GATE==="1",capture:process.env.CAPTURE==="1",stateFile:process.env.DZ_STATE_FILE||null},t={skills:await n.loadSkills(),policy:n.loadJson(process.env.DZ_POLICY_FILE)||ne,loadState:()=>n.loadJson(e.stateFile)};return new n(e,t)}static loadSkills(){return new M(n.skillSources()).load()}static skillSources(){return[new j]}createServer(){return Re.createServer((e,t)=>this.handle(e,t))}listen(){let e=this.createServer();return e.on("error",t=>{t.code==="EADDRINUSE"&&(this.logger.warn(`port ${this.config.port} already in use \u2014 a gateway is probably running.`),process.exit(1)),this.logger.error(`server error: ${t.message}`),process.exit(1)}),e.listen(this.config.port,"127.0.0.1",()=>{this.logger.info(`listening on http://127.0.0.1:${this.config.port} (inject=${this.config.inject} gate=${this.config.gate}) skills=${Object.keys(this.deps.skills).length}`)}),this.startTelemetry(),this.startSkillsSync(),e}startSkillsSync(){let e=B.findRepoRoot(process.cwd())??process.cwd(),t=D.file("dist","cli","dezycro.js"),r=()=>{we(e,t)&&this.logger.debug("skills: spawned background sync")};this.skillsSyncTimer=setInterval(r,300*1e3),this.skillsSyncTimer.unref?.()}startTelemetry(){if(!h.enabled()||process.env.DZ_TELEMETRY==="0")return;let e=B.at(process.cwd()).connection();if("error"in e){this.logger.debug(`telemetry: off (${e.error})`);return}this.telemetryUploader=new _({...e,metricsFile:z.defaultFile(),cursorFile:G.join(h.dir(),"gateway.upload-cursor.json")});let t=Math.max(3e4,Number(process.env.DZ_TELEMETRY_INTERVAL_MS)||3e5),r=()=>{this.flushTelemetry()};this.telemetryTimer=setInterval(r,t),this.telemetryTimer.unref?.(),setTimeout(r,15e3).unref?.(),this.logger.info(`telemetry: background upload every ${Math.round(t/1e3)}s \u2192 ws ${e.workspaceId}`)}async flushTelemetry(){if(this.telemetryUploader)try{let e=await this.telemetryUploader.upload();e.sent&&this.logger.info(`telemetry: uploaded ${e.turns} turn + ${e.compactions} compaction event(s)`)}catch(e){this.logger.warn(`telemetry upload failed: ${e.message}`)}}handle(e,t){if(this.handleControlPlane(e,t))return;let r=n.pickUpstream(e.url||""),o=[];e.on("data",s=>o.push(s)),e.on("end",()=>this.forward(e,t,r,o))}handleControlPlane(e,t){return e.url===re.Health?(t.writeHead(200,{"content-type":E.Json}),t.end(JSON.stringify({ok:!0,dz:!0,pid:process.pid,port:this.config.port,inject:this.config.inject,gate:this.config.gate})),!0):e.url===re.Stop?(t.writeHead(200,{"content-type":E.Json}),t.end(JSON.stringify({ok:!0,stopping:!0})),this.telemetryTimer&&clearInterval(this.telemetryTimer),this.skillsSyncTimer&&clearInterval(this.skillsSyncTimer),this.flushTelemetry().finally(()=>setTimeout(()=>process.exit(0),50)),setTimeout(()=>process.exit(0),3e3).unref?.(),!0):!1}async forward(e,t,r,o){let s=Date.now(),i=Buffer.concat(o),a=[],l=k.emptyComposition();if(this.config.inject&&r&&e.method==="POST"&&i.length){let p=this.injectBody(i.toString("utf8"),r.provider);i=Buffer.from(p.bodyText,"utf8"),a=p.triggers,l=p.composition,this.persistOpusModel(p.model)}let u=process.env.FORCE_UPSTREAM||(r?r.base:"https://api.anthropic.com");try{let p=await fetch(u+e.url,{method:e.method,headers:n.buildForwardHeaders(e.headers),body:["GET","HEAD"].includes(e.method||"")?void 0:i,redirect:"manual"}),H=(p.headers.get("content-type")||"").toLowerCase();if(r?.provider===R.Anthropic&&p.ok&&p.body&&(this.config.gate||h.enabled())){let ae=this.config.gate?new I(this.deps.policy,this.deps.loadState()):null,le={startedAt:s,provider:r.provider,status:p.status,triggers:a,...l};if(H.includes(E.EventStream))return this.streamResponse(p,t,ae,le);if(H.includes(E.Json))return this.jsonResponse(p,t,ae,le)}t.writeHead(p.status,n.filterResponseHeaders(p.headers)),p.body?n.webToNode(p.body).pipe(t):t.end()}catch(p){this.logger.error(`upstream error: ${p.message}`),t.writeHead(502,{"content-type":E.Json}),t.end(JSON.stringify({error:"gateway upstream failed",detail:String(p)}))}}injectBody(e,t){try{let r=JSON.parse(e),o=typeof r.model=="string"?r.model:null,{text:s,triggers:i}=this.injector.apply(r,t),a=h.enabled()?k.composition(r):k.emptyComposition();return s?(this.logger.debug(`-> ${t} injected(${s.split(`
35
+ `).length} line(s))`),{bodyText:JSON.stringify(r),triggers:i,composition:a,model:o}):{bodyText:e,triggers:i,composition:a,model:o}}catch{return{bodyText:e,triggers:[],composition:k.emptyComposition(),model:null}}}persistOpusModel(e){if(!(!e||!/opus/i.test(e)||!this.config.stateFile))try{let t=e.replace(/\[.*$/,""),r=G.join(G.dirname(this.config.stateFile),me);if(n.readFileTrim(r)===t)return;J.writeFileSync(r,t)}catch{}}static readFileTrim(e){try{return J.readFileSync(e,"utf8").trim()}catch{return null}}streamResponse(e,t,r,o){t.writeHead(e.status,n.filterResponseHeaders(e.headers));let s=r?new L(r):null,i=h.enabled()?new F:null,a=n.webToNode(e.body);a.setEncoding("utf8"),a.on("data",l=>{if(i&&i.feed(l),!s){t.write(l);return}try{let u=s.push(l);u&&t.write(u)}catch{t.write(l)}}),a.on("end",()=>{if(s)try{let l=s.flush();l&&t.write(l)}catch{}t.end(),this.recordTurn(o,i,s)}),a.on("error",()=>{try{t.end()}catch{}this.recordTurn(o,i,s)})}async jsonResponse(e,t,r,o){let s=await e.text(),i=s,a=null,l={model:null,usage:{}};try{let u=JSON.parse(s);l={model:u.model??null,usage:n.usageOf(u)},r&&(a=new U(r),i=JSON.stringify(a.rewrite(u)))}catch{i=s}a&&i!==s&&this.logger.info("\u26D4 gate rewrote response (blocked tool_use)"),t.writeHead(e.status,n.filterResponseHeaders(e.headers)),t.end(i),this.recordTurn(o,l,a)}recordTurn(e,t,r){let o=t?.usage??{};this.metrics.record({type:"turn",provider:e.provider,model:t?.model??null,durationMs:Date.now()-e.startedAt,status:e.status,inputTokens:o.inputTokens,outputTokens:o.outputTokens,cacheReadTokens:o.cacheReadTokens,cacheCreationTokens:o.cacheCreationTokens,skillsTriggered:e.triggers.length?e.triggers:void 0,blockedToolCalls:r?r.blockedCount:void 0,gated:r?r.blockedCount>0:void 0,freshToolChars:e.freshToolChars||void 0,toolDefChars:e.toolDefChars||void 0,systemChars:e.systemChars||void 0,tailResultChars:e.tailResultChars||void 0,tailTextChars:e.tailTextChars||void 0})}static usageOf(e){let t=e.usage;return t?{inputTokens:t.input_tokens,outputTokens:t.output_tokens,cacheReadTokens:t.cache_read_input_tokens,cacheCreationTokens:t.cache_creation_input_tokens}:{}}static pickUpstream(e){return n.UPSTREAMS.find(t=>t.match(e))??null}static buildForwardHeaders(e){let t={};for(let[r,o]of Object.entries(e))n.HOP_BY_HOP_REQUEST.includes(r.toLowerCase())||(typeof o=="string"?t[r]=o:Array.isArray(o)&&(t[r]=o.join(", ")));return t}static filterResponseHeaders(e){let t={};return e.forEach((r,o)=>{n.HOP_BY_HOP_RESPONSE.includes(o.toLowerCase())||(t[o]=r)}),t}static webToNode(e){return Ee.Readable.fromWeb(e)}static loadJson(e){try{return e?JSON.parse(J.readFileSync(e,"utf8")):null}catch{return null}}};function _e(n,e){return new $(n,e).createServer()}require.main===module&&$.fromEnv().then(n=>n.listen());0&&(module.exports={Gateway,createGatewayServer});
@@ -1,15 +1,15 @@
1
- "use strict";var lt=Object.create;var F=Object.defineProperty;var dt=Object.getOwnPropertyDescriptor;var pt=Object.getOwnPropertyNames;var ft=Object.getPrototypeOf,gt=Object.prototype.hasOwnProperty;var mt=(t,e)=>{for(var n in e)F(t,n,{get:e[n],enumerable:!0})},ce=(t,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of pt(e))!gt.call(t,r)&&r!==n&&F(t,r,{get:()=>e[r],enumerable:!(o=dt(e,r))||o.enumerable});return t};var S=(t,e,n)=>(n=t!=null?lt(ft(t)):{},ce(e||!t||!t.__esModule?F(n,"default",{value:t,enumerable:!0}):n,t)),ht=t=>ce(F({},"__esModule",{value:!0}),t);var xn={};mt(xn,{QUALITY_GATE_REASON_PREFIX:()=>Qe,TRIVIAL_TURN_CHAR_DEFAULT_EXPORT:()=>Pn,_extractEditedPaths:()=>yn,_handlePostEdit:()=>Sn,_handleSessionStart:()=>Cn,_handleStop:()=>bn,_handleUpdateTaskGate:()=>kn,_handleUserPromptSubmit:()=>Tn,_handleWorkbookMutation:()=>vn,_readActiveFeatureId:()=>wn,_transcriptCharsSinceLastStop:()=>In,dispatch:()=>sn,shouldSurfaceSpecReminder:()=>st});module.exports=ht(xn);var U=S(require("fs")),Ze=S(require("path"));var B=S(require("fs")),R=S(require("path")),ue=Object.freeze({conservative:{decision:.85,issue:.8,assumption:.9,taskStatus:.9},balanced:{decision:.75,issue:.7,assumption:.8,taskStatus:.85},aggressive:{decision:.6,issue:.55,assumption:.7,taskStatus:.75}}),D=Object.freeze({companion:{autoVerify:{preTaskDone:!0,prePush:!0,postEditPulse:{enabled:!0,threshold:5},autoPublishSpecOnControllerChange:!0,controllerPaths:["**/controllers/**/*.{kt,java,py,go,ts,tsx,js,rs,rb,cs}","**/api/**/*.{kt,java,py,go,ts,tsx,js,rs,rb,cs}"],verifyTimeoutSeconds:300,publishSpecTimeoutSeconds:300},autoWorkbookUpdates:{enabled:!0,preset:"conservative",maxAutoLogsPerTurn:2,maxAutoLogsPerSession:10,undoWindow:"next-turn",thresholds:{decision:null,issue:null,assumption:null,taskStatus:null},trivialTurnCharCutoff:600,hashRingBufferSize:32},coverageNudges:{enabled:!0,staleBaselineDays:14,fetchMode:"async"},authoring:{defaultDepth:"adaptive",existingWorkScan:!0,qualityGate:{enabled:!0},minViableContextGate:{enabled:!0}},safeMode:!1}});function Y(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)}function le(t,e){if(!Y(e))return e===void 0?t:e;let n=Array.isArray(t)?[...t]:{...t};for(let o of Object.keys(e)){let r=n[o],s=e[o];Y(r)&&Y(s)?n[o]=le(r,s):n[o]=s}return n}function de(t){let e=R.join(t,".dezycro","config.json"),n;try{n=B.readFileSync(e,"utf8")}catch(r){if(r.code==="ENOENT")return JSON.parse(JSON.stringify(D));throw r}let o;try{o=JSON.parse(n)}catch{return JSON.parse(JSON.stringify(D))}return le(JSON.parse(JSON.stringify(D)),o)}function pe(t){let e=t?.companion?.autoWorkbookUpdates||{},n=e.preset||"conservative",o=ue[n]||ue.conservative,r=e.thresholds||{};return{decision:typeof r.decision=="number"?r.decision:o.decision,issue:typeof r.issue=="number"?r.issue:o.issue,assumption:typeof r.assumption=="number"?r.assumption:o.assumption,taskStatus:typeof r.taskStatus=="number"?r.taskStatus:o.taskStatus}}function w(t){let e=R.resolve(t||process.cwd());for(let n=0;n<64;n+=1){if(B.existsSync(R.join(e,".dezycro")))return e;let o=R.dirname(e);if(o===e)return null;e=o}return null}var m=S(require("fs")),N=S(require("path")),St=500,kt=10,Z=32,fe=64,ge=Object.freeze({autoDecisionLogs:0,autoIssueLogs:0,autoAssumptionLogs:0,autoTaskStatusLogs:0,undoCount:0,verifyAutoRuns:0,verifyAutoFailures:0,qualityGateFailures:0,contextGateFailures:0,lockContention:0,forceApprovals:0}),bt=Object.freeze({schemaVersion:1,currentSessionId:null,authoringMode:!1,authoringDocId:null,authoringDocType:null,authoringTranscriptPath:null,authoringStartedTs:null,pulseCount:0,lastEditTs:null,needsSpecPublish:!1,controllerEditsSinceLastPublish:[],coverageSnapshot:null,coverageSnapshotTs:null,coverageFetchPending:!1,lastVerifyCompletionTs:null,recentDecisionHashes:[],recentIssueHashes:[],recentAssumptionHashes:[],lastAutoWorkbookMutation:null,sessionAutoLogCount:0,turnAutoLogCount:0,currentTurnId:0,suppressWorkbookUpdatesUntil:null,pendingWorkbookSweep:!1,pendingWorkbookSweepReason:null,lastSeenVerifyCompletionTs:null,commandBus:{cursor:null,lastPollTs:null,lastAttemptTs:null,consecutiveFailures:0,recentEventIds:[]},metrics:ge});function me(t){return N.join(t,".dezycro","companion-state.json")}function he(t){return N.join(t,".dezycro",".companion-state.lock")}function Tt(t){return N.join(t,".dezycro",".companion-state.json.tmp")}function vt(t){m.mkdirSync(N.join(t,".dezycro"),{recursive:!0})}function Q(){return JSON.parse(JSON.stringify(bt))}function k(t){let e=me(t);try{let n=m.readFileSync(e,"utf8"),o=JSON.parse(n);return{...Q(),...o,metrics:{...ge,...o.metrics||{}}}}catch(n){let o=n;return o&&o.code==="ENOENT",Q()}}function Ct(t){let e=he(t),n=Date.now();for(;Date.now()-n<St;){try{let r=m.openSync(e,"wx");return m.writeSync(r,String(process.pid)),m.closeSync(r),!0}catch(r){if(r.code!=="EEXIST")throw r}let o=Date.now()+kt;for(;Date.now()<o;);}return!1}function wt(t){try{m.unlinkSync(he(t))}catch{}}function It(t,e){return e?{...t,...e}:t}function ee(t,e,n){if(!e||t.includes(e))return t;let o=t.concat([e]);return o.length>n?o.slice(o.length-n):o}function l(t,e){if(vt(t),!Ct(t))return null;try{let n=k(t),o=e||{},r={...n};for(let a of Object.keys(o)){let c=o[a];if(a==="metrics")r.metrics=It(n.metrics,c);else if(a==="pushDecisionHash")r.recentDecisionHashes=ee(n.recentDecisionHashes,c,Z);else if(a==="pushIssueHash")r.recentIssueHashes=ee(n.recentIssueHashes,c,Z);else if(a==="pushAssumptionHash")r.recentAssumptionHashes=ee(n.recentAssumptionHashes,c,Z);else if(a==="pushControllerEdit"){let u=c,p=n.controllerEditsSinceLastPublish.filter(h=>h.path!==u.path).concat([u]);r.controllerEditsSinceLastPublish=p.length>fe?p.slice(p.length-fe):p}else r[a]=c}let s=Tt(t),i=m.openSync(s,"w");return m.writeSync(i,JSON.stringify(r,null,2)),m.fsyncSync(i),m.closeSync(i),m.renameSync(s,me(t)),r}finally{wt(t)}}function E(t,e,n=1){let o=k(t).metrics[e]||0;return l(t,{metrics:{[e]:o+n}})}var j=S(require("fs")),ne=S(require("path")),Pt="companion.log";function ye(t,e){try{let n=ne.join(t,".dezycro");if(!j.existsSync(n))return;let o=JSON.stringify({ts:new Date().toISOString(),...e})+`
2
- `;j.appendFileSync(ne.join(n,Pt),o,{encoding:"utf8"})}catch{}}function g(t,e,n){ye(t,{level:"info",event:e,...n||{}})}function $(t,e,n){ye(t,{level:"warn",event:e,...n||{}})}var ke=S(require("fs")),be=S(require("path"));function Te(t){let e=t||{},n=e.repoRoot,o=typeof e.maxAgeMinutes=="number"?e.maxAgeMinutes:60;if(!n)return!1;let r=be.join(n,".dezycro","state.json"),s;try{let d=ke.readFileSync(r,"utf8");s=JSON.parse(d)}catch{return!1}if(!s||typeof s!="object")return!1;let i=s.lastVerifyTs,a=s.lastVerifyRunId;if(!i||!a)return!1;let c=Date.parse(i);if(!Number.isFinite(c))return!1;let u=Date.now()-c;return!(u<0||u>o*60*1e3)}function ve(t){let e=t||{},n=e.paths||[],o=e.status||"DONE",r=n.length>0?n.join(","):"<paths>";return{reason:"Cannot mark task "+o+" \u2014 no recent passing verifier run found for the linked paths. Run `/dezycro:verify --path "+r+"` first to attach passing verifier evidence, then retry."}}function Ce(t){if(!t)return{needed:!1,controllerPaths:[],lastEditTs:null};let e=k(t),n=Array.isArray(e.controllerEditsSinceLastPublish)?e.controllerEditsSinceLastPublish:[],o=n.map(i=>i&&i.path).filter(Boolean),r=n.length>0&&n[n.length-1].editedTs||null;return{needed:!!e.needsSpecPublish&&o.length>0,controllerPaths:o,lastEditTs:r}}function Rt(t){return String(t||"").replace(/\\/g,"/")}function Ie(t){let e=t.match(/\{([^{}]+)\}/);if(!e||e.index===void 0)return[t];let n=e[0],r=e[1].split(","),s=[];for(let i of r){let a=t.slice(0,e.index)+i+t.slice(e.index+n.length);s.push(...Ie(a))}return s}function _t(t){let e="^";for(let n=0;n<t.length;n+=1){let o=t[n];o==="*"?t[n+1]==="*"?(n+=1,t[n+1]==="/"?(n+=1,e+="(?:.*/)?"):e+=".*"):e+="[^/]*":o==="?"?e+="[^/]":".+^$()|[]\\".indexOf(o)!==-1?e+="\\"+o:e+=o}return e+="$",new RegExp(e)}var we=new Map;function Et(t){let e=we.get(t);if(e)return e;let n=Ie(t).map(_t);return we.set(t,n),n}function Ot(t,e){if(!t||!Array.isArray(e)||e.length===0)return!1;let n=Rt(t);for(let o of e){if(typeof o!="string"||o.length===0)continue;let r=Et(o);for(let s of r)if(s.test(n))return!0}return!1}function Pe(t,e){return Array.isArray(t)?t.filter(n=>Ot(n,e)):[]}var b=S(require("fs")),V=S(require("path"));var xe=Object.freeze({IDLE:"IDLE",DETECT_INTENT:"DETECT_INTENT",SCAN_EXISTING_WORK:"SCAN_EXISTING_WORK",CLARIFY:"CLARIFY",PROPOSE_PLACEMENT:"PROPOSE_PLACEMENT",CREATE_FEATURE:"CREATE_FEATURE",TRD_DISCOVERY:"TRD_DISCOVERY",MIN_CONTEXT_GATE:"MIN_CONTEXT_GATE",DRAFT:"DRAFT",ITERATE:"ITERATE",QUALITY_GATE:"QUALITY_GATE",FINALIZE:"FINALIZE"}),Ae=Object.freeze({PRD:"PRD",TRD:"TRD"}),Rn=Object.freeze(["skip","draft it","just write it","next"]),_n=Object.freeze([xe.MIN_CONTEXT_GATE,xe.QUALITY_GATE]),Nt="authoring-transcript.json",Lt="authoring-current-draft.json";function Re(t){return V.join(t,".dezycro",Nt)}function _e(t){return V.join(t,".dezycro",Lt)}function Mt(t){b.mkdirSync(V.join(t,".dezycro"),{recursive:!0})}function Ht(t,e){let n=`${t}.tmp`,o=b.openSync(n,"w");try{b.writeSync(o,JSON.stringify(e,null,2)),b.fsyncSync(o)}finally{b.closeSync(o)}b.renameSync(n,t)}function Ee(t){try{let e=b.readFileSync(t,"utf8");return JSON.parse(e)}catch{return null}}function Oe(t,e){if(!e||!e.docId||!e.docType)throw new Error("authoring.enter: docId and docType required");if(e.docType!==Ae.PRD&&e.docType!==Ae.TRD)throw new Error(`authoring.enter: invalid docType ${e.docType}`);Mt(t);let n=Re(t),o=new Date().toISOString(),r=Ee(n),s=r&&r.docId===e.docId?r:{docId:e.docId,docType:e.docType,featureId:e.featureId||null,seed:e.seed||null,startedTs:o,discoverySummary:null,qaTurns:[],draftHistory:[],qualityGateHistory:[]};return Ht(n,s),l(t,{authoringMode:!0,authoringDocId:e.docId,authoringDocType:e.docType,authoringTranscriptPath:n,authoringStartedTs:o}),s}function De(t){l(t,{authoringMode:!1,authoringDocId:null,authoringDocType:null,authoringTranscriptPath:null,authoringStartedTs:null});for(let e of[Re(t),_e(t)])try{b.unlinkSync(e)}catch{}}function Ne(t){let e=k(t);return{active:!!(e&&e.authoringMode),docId:e?e.authoringDocId:null,docType:e?e.authoringDocType:null,transcriptPath:e?e.authoringTranscriptPath:null,startedTs:e?e.authoringStartedTs:null}}function Le(t){return Ee(_e(t))}var En=Object.freeze(["architecture","data model","data-model","datamodel","api contract","api-contract","apicontract","migration","auth","queue","verifier-behavior","verifier behavior"]),Ft=Object.freeze([{id:"architecture",pattern:/^##\s+architecture\b/im},{id:"data_model",pattern:/^##\s+data\s+model\b/im},{id:"api_contracts",pattern:/^##\s+api\s+contracts?\b/im},{id:"failure_modes",pattern:/^##\s+failure\s+modes?/im},{id:"migration",pattern:/^##\s+migration\b/im},{id:"open_questions",pattern:/^##\s+open\s+questions?\b/im}]),Bt=/\b(TBD|TODO|FIXME|fill in later|to be determined)\b/i,Me=1e3,He=/^##\s+Pillar\s+\d+\s*:\s*(.+?)\s*$/gim,On=Object.freeze(["data_model","api_contracts","state_or_persistence","failure_modes","migration_or_rollout"]);function jt(t){if(!t||typeof t!="string")return[];let e=[];He.lastIndex=0;let n;for(;(n=He.exec(t))!==null;){let o=n[1].trim();o&&e.push(o)}return e}function Ue(t){let{trdContent:e,prdContent:n}=t,o=[],r=(e||"").toString(),s=r.trim().length;s<Me&&o.push({section:"document",issue:`TRD is too short (${s} chars; minimum ${Me}).`,required_fix:"Expand the draft with concrete technical detail."});for(let c of Ft)c.pattern.test(r)||o.push({section:c.id,issue:"Required H2 section is missing.",required_fix:`Add a "## ${c.id.replace(/_/g," ")}" section grounded in the PRD + discovery.`});let i=r.match(Bt);i&&o.push({section:"placeholders",issue:`Placeholder token "${i[0]}" present \u2014 TRDs must not ship with unresolved placeholders.`,required_fix:"Resolve or move the open item into the ## Open Questions section."});let a=jt(n);if(a.length>0){let c=[],u=r.toLowerCase();for(let d of a)u.includes(d.toLowerCase())||c.push(d);c.length>0&&o.push({section:"pillar_coverage",issue:`TRD does not mention PRD pillar(s): ${c.join(", ")}.`,required_fix:"Add a per-pillar technical-design section, or reference each pillar by name in the relevant TRD section."})}return{passed:o.length===0,blockingIssues:o,deterministic:!0}}var Fe=S(require("crypto"));function Be(t,e,n){let o=String(t||"").toLowerCase().trim(),r=String(e||"").toLowerCase().trim().replace(/\s+/g," "),s=String(n||"").toLowerCase().trim();return Fe.createHash("sha1").update(`${o}::${r}::${s}`).digest("hex").slice(0,16)}var G=Object.freeze({decision:"Logging decision: {{summary}} \u2014 reason: {{reason}}. Say 'undo that' if wrong.",issue:"Logging issue ({{severity}}): {{summary}}. Say 'undo that' if wrong.",assumption:"Logging assumption: {{summary}} \u2014 rationale: {{rationale}}. Say 'undo that' if wrong.",task_status:"Marking task {{task_id}} as {{proposed_status}} \u2014 rationale: {{rationale}}. Say 'undo that' if wrong."}),Gt="Auto-logging paused \u2014 {{count}} entries logged this session. Type /dezycro:sync to log manually.";function je(t){let e=t.thresholds||{},n=Math.max(0,Number(t.perTurnSlotsLeft)||0),o=Math.max(0,Number(t.perSessionSlotsLeft)||0);if(n<=0||o<=0)return Gt.replace("{{count}}",o<=0?"session-cap":"turn-cap");let r=Math.min(n,o),s=(t.recentHashes||[]).filter(Boolean),i=["[Dezycro Companion]",`Feature: ${Jt(t.activeFeatureName,t.activeFeatureId)}. Slots: ${r} (${n}/turn, ${o}/session).`,"","Scan since your last message for committed signals to log:",` - Decision (conf \u2265 ${e.decision??.85}) \u2014 committed technical/product choice`,` - Issue (conf \u2265 ${e.issue??.8}) \u2014 current blocker/bug`,` - Assumption (conf \u2265 ${e.assumption??.9}) \u2014 unverified belief affecting impl`,` - Task-status (conf \u2265 ${e.taskStatus??.9}) \u2014 task is now DONE/PUSHED`,"","Skip: hypotheticals, brainstorming, implementation-obvious details, ephemeral content, intent statements while authoring a PRD/TRD."];return s.length>0&&i.push(`Already-logged hashes (skip dupes): ${s.join(", ")}`),i.push("","For each surviving candidate (max "+r+"): print the announce line verbatim and call the matching MCP tool.",' decision: "'+G.decision+'" \u2192 mcp__dezycro(-dev)?__add_decision',' issue: "'+G.issue+'" \u2192 mcp__dezycro(-dev)?__add_issue',' assumption: "'+G.assumption+'" \u2192 mcp__dezycro(-dev)?__add_assumption',' task_status: "'+G.task_status+'" \u2192 mcp__dezycro(-dev)?__update_task',"","If nothing meets the bar, simply continue with whatever the user asked next \u2014 do NOT output any acknowledgment line. The hook will silence itself for trivial turns."),i.join(`
3
- `)}function Jt(t,e){if(!e)return"(none \u2014 skip if no feature is active)";let n=String(e).slice(0,8);return t?`${t} (${n}\u2026)`:n+"\u2026"}function $e(t){return typeof t!="string"?null:/add_decision$/.test(t)?"decision":/add_issue$/.test(t)?"issue":/add_assumption$/.test(t)?"assumption":/update_task$/.test(t)?"task-status":null}var Ve=Object.freeze(["undo that","no, wrong","revert that","undo the last log","wrong, undo"]);function Ge(t){if(typeof t!="string")return!1;let e=t.toLowerCase().trim();if(!e)return!1;for(let n=0;n<Ve.length;n+=1)if(e.includes(Ve[n]))return!0;return!1}function Je(t){if(!t||!t.tool||!t.entityId)return null;let e=t.entityId;switch(t.tool){case"add_decision":return{tool:"update_decision",args:{id:e,status:"INVALIDATED",reason:"undo by user"},humanLine:`Reverting the last auto-logged decision (${e.slice(0,8)}\u2026) \u2014 marking INVALIDATED.`};case"add_issue":return{tool:"update_issue",args:{id:e,status:"CANCELLED",reason:"undo by user"},humanLine:`Reverting the last auto-logged issue (${e.slice(0,8)}\u2026) \u2014 marking CANCELLED.`};case"add_assumption":return{tool:"update_issue",args:{id:e,status:"INVALIDATED",kind:"ASSUMPTION",reason:"undo by user"},humanLine:`Reverting the last auto-logged assumption (${e.slice(0,8)}\u2026) \u2014 marking INVALIDATED.`};case"update_task":return t.priorStatus?{tool:"update_task",args:{id:e,status:t.priorStatus},humanLine:`Reverting the last auto-task-status change \u2014 restoring ${e.slice(0,8)}\u2026 to ${t.priorStatus}.`}:null;default:return null}}function ze(t,e){return!t||typeof t.createdInTurnId!="number"?!1:e===t.createdInTurnId+1}function oe(t,e){return`${t} ${e}${t===1?"":"s"}`}function re(t){return!t||typeof t!="object"?null:{uncoveredPaths:Number(t.uncoveredPaths??0)||0,endpointsNotInSpec:Number(t.endpointsNotInSpec??0)||0,staleBaselines:Number(t.staleBaselines??0)||0}}function J(t){let e=re(t);if(!e)return null;let n=[];return e.uncoveredPaths>0&&n.push(`${oe(e.uncoveredPaths,"path")} uncovered`),e.endpointsNotInSpec>0&&n.push(`${oe(e.endpointsNotInSpec,"endpoint")} not in spec`),e.staleBaselines>0&&n.push(`${oe(e.staleBaselines,"baseline")} stale`),n.length===0?null:`Coverage: ${n.join(", ")}`}function We(t,e){let n=re(t),o=re(e);if(!o)return null;if(!n)return J(e);let s=[["paths uncovered",o.uncoveredPaths-n.uncoveredPaths],["endpoints not in spec",o.endpointsNotInSpec-n.endpointsNotInSpec],["baselines stale",o.staleBaselines-n.staleBaselines]].filter(([,a])=>a!==0);return s.length===0?null:`Coverage delta since last verify: ${s.map(([a,c])=>`${c>0?"+":""}${c} ${a}`).join(", ")}`}var L={HaltAndReplan:"HALT_AND_REPLAN",AcknowledgeAndContinue:"ACKNOWLEDGE_AND_CONTINUE",SilentLog:"SILENT_LOG"};var W=S(require("fs")),qe=S(require("os")),I=S(require("path")),z=class t{constructor(e){this.repoRoot=e;this.config=t.readJson(I.join(e,".dezycro","config.json"))}repoRoot;config;static at(e=process.cwd()){return new t(t.findRepoRoot(e)??I.resolve(e))}get workspaceId(){return t.env("DZ_WORKSPACE_ID")??t.str(this.config?.workspaceId)}get tenantId(){return t.env("DZ_TENANT_ID")??t.str(this.config?.tenantId)}get projectId(){return t.str(this.config?.projectId)}agent(e){let n=this.agentNode(e);return{context1m:t.boolFlag("DZ_1M",n?.context1m),toolSearch:t.env("DZ_TOOL_SEARCH")??t.str(n?.toolSearch)}}agentNode(e){let n=this.config?.agents;if(!t.isObj(n))return null;let o=n[e];return t.isObj(o)?o:null}static boolFlag(e,n){let o=t.env(e);return o!==null?o==="1"||o.toLowerCase()==="true":n===!0}get apiUrl(){let e=t.env("DZ_API_URL");if(e)return t.trimSlash(e);let n=Array.isArray(this.config?.environments)?this.config?.environments:[],o=n.find(s=>t.isObj(s)&&s.default===!0)??n[0],r=t.isObj(o)?t.str(o.apiUrl):null;return r?t.trimSlash(r):null}get token(){let e=t.env("DZ_TOKEN");if(e)return e;let n=[I.join(this.repoRoot,".dezycro","companion-token.local.json"),I.join(qe.homedir(),".dezycro","token.json")];for(let o of n){let r=t.str(t.readJson(o)?.token);if(r)return r}return null}activeFeature(){let e=t.readJson(I.join(this.repoRoot,".dezycro","active-feature.json")),n=t.str(e?.featureId);if(!n)return null;let o=t.str(e?.workspaceId)??this.workspaceId;return o?{workspaceId:o,featureId:n,active:e?.active===!0}:null}connection(){let e=this.apiUrl;if(!e)return{error:"no apiUrl (set DZ_API_URL or .dezycro/config.json environments[].apiUrl)"};let n=this.workspaceId;if(!n)return{error:"no workspaceId (set DZ_WORKSPACE_ID or .dezycro/config.json workspaceId)"};let o=this.token;return o?{apiUrl:e,workspaceId:n,token:o}:{error:"no token (run `dezycro setup` or set DZ_TOKEN)"}}static findRepoRoot(e){let n=I.resolve(e||process.cwd());for(let o=0;o<64;o+=1){if(W.existsSync(I.join(n,".dezycro")))return n;let r=I.dirname(n);if(r===n)return null;n=r}return null}static readJson(e){try{return JSON.parse(W.readFileSync(e,"utf8"))}catch{return null}}static isObj(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}static str(e){return typeof e=="string"&&e.length>0?e:null}static env(e){let n=process.env[e];return n&&n.length>0?n:null}static trimSlash(e){return e.replace(/\/$/,"")}};var Kt=["sessionStart","userPromptSubmit","stop","postToolUse"],Xt=60,Yt=L.HaltAndReplan,Ke=200,Zt=100,Qt=3,en=1800,se=class t{async pollIfDue(e,n,o,r){let s=this.resolveConfig(o),i=s.reactionMode,a={events:[],statePatch:null,reactionMode:i};if(!s.enabled||!s.hooks.has(n)&&n!=="sessionStart")return a;let c=r.commandBus,u=c.lastAttemptTs??c.lastPollTs;if(n!=="sessionStart"&&u){let f=Date.parse(u);if(!Number.isNaN(f)){let C=t.backoffSeconds(s.minPollIntervalSeconds,c.consecutiveFailures??0,s.circuitBreakerThreshold,s.maxBackoffSeconds);if((Date.now()-f)/1e3<C)return a}}let d=new z(e),p=d.activeFeature();if(!p||!p.active)return a;let h=d.apiUrl;if(!h)return a;let v=d.token;if(!v)return g(e,"command_bus_skip_no_token",{hookEvent:n}),a;let y=n==="sessionStart"?null:c.cursor,P=new Date().toISOString();try{let f=await this.fetchInbox(h,p,v,y),C=new Set(c.recentEventIds??[]),T=f.events.filter(O=>!C.has(O.eventId)),_=this.trimRecent([...c.recentEventIds??[],...T.map(O=>O.eventId)]),X={commandBus:{cursor:f.nextCursor||c.cursor,lastPollTs:f.serverTs,lastAttemptTs:P,consecutiveFailures:0,recentEventIds:_}};return T.length>0&&g(e,"command_bus_events_received",{hookEvent:n,count:T.length,types:T.map(O=>O.eventType)}),{events:T,statePatch:X,reactionMode:i}}catch(f){let C=(c.consecutiveFailures??0)+1,T={commandBus:{...c,lastAttemptTs:P,consecutiveFailures:C}},_=t.backoffSeconds(s.minPollIntervalSeconds,C,s.circuitBreakerThreshold,s.maxBackoffSeconds);return g(e,"command_bus_poll_failed",{hookEvent:n,error:f instanceof Error?f.message:String(f),consecutiveFailures:C,nextPollAfterSeconds:_,backoff:C>=s.circuitBreakerThreshold}),{events:[],statePatch:T,reactionMode:i}}}static backoffSeconds(e,n,o,r){if(n<o)return e;let s=n-o+1;return Math.min(e*Math.pow(2,s),r)}resolveConfig(e){let n=e?.enabled!==!1,o=new Set(e?.pollHooks??Kt);o.add("sessionStart");let r=Math.max(0,e?.minPollIntervalSeconds??Xt),s=e?.reactionMode??Yt,i=Math.max(1,e?.circuitBreakerThreshold??Qt),a=Math.max(r,e?.maxBackoffSeconds??en);return{enabled:n,hooks:o,minPollIntervalSeconds:r,reactionMode:s,circuitBreakerThreshold:i,maxBackoffSeconds:a}}async fetchInbox(e,n,o,r){let s=new URL(`${e}/api/v1/workspaces/${n.workspaceId}/features/${n.featureId}/inbox`);r&&s.searchParams.set("since",r),s.searchParams.set("limit",String(Zt));let i=new AbortController,a=setTimeout(()=>i.abort(),5e3);try{let c=await fetch(s.toString(),{method:"GET",headers:{Authorization:`Bearer ${o}`,Accept:"application/json"},signal:i.signal});if(!c.ok)throw new Error(`inbox poll returned ${c.status}`);let u=await c.json();if(!u||typeof u.serverTs!="string"||typeof u.nextCursor!="string")throw new Error("inbox poll response missing serverTs/nextCursor");return{serverTs:u.serverTs,nextCursor:u.nextCursor,events:Array.isArray(u.events)?u.events:[]}}finally{clearTimeout(a)}}trimRecent(e){return e.length<=Ke?e:e.slice(e.length-Ke)}},ie=class{renderForAgent(e,n){if(e.length===0)return"";let o=[`\u26A0\uFE0F Dezycro Command Bus: ${e.length} new event(s) on this feature.`,""];for(let r of e)o.push(this.renderEvent(r));return e.some(r=>r.urgency==="BLOCKING")&&o.push("",this.reactionGuidance(n)),o.join(`
4
- `)}renderEvent(e){if(e.eventType==="INVALIDATION"){let r=this.stringField(e.payload,"artifactSummary")??"(no summary)",s=this.stringField(e.payload,"reason")??"(no reason)",i=this.stringField(e.payload,"previousStatus")??"?",a=`${e.sourceType.toLowerCase()} ${e.sourceId??"?"}`;return`- INVALIDATION (${e.urgency}): ${a} ("${this.truncate(r,120)}") \u2014 reason: "${this.truncate(s,200)}" \u2014 was ${i}.`}let n=this.summarizePayload(e.payload),o=`${e.sourceType.toLowerCase()}${e.sourceId?` ${e.sourceId}`:""}`;return`- [Dezycro] ${e.eventType} (${e.urgency}) from ${o}${n?`: ${n}`:""}.`}summarizePayload(e){let n=[];for(let[o,r]of Object.entries(e??{}))r!=null&&(Array.isArray(r)?n.push(`${o}=${r.join("|")}`):typeof r!="object"&&n.push(`${o}=${String(r)}`));return this.truncate(n.join(", "),240)}reactionGuidance(e){switch(e){case L.HaltAndReplan:return"Reaction mode: HALT_AND_REPLAN \u2014 pause your current line of work, acknowledge in chat, and re-plan around these invalidations before touching code again.";case L.AcknowledgeAndContinue:return"Reaction mode: ACKNOWLEDGE_AND_CONTINUE \u2014 surface this in your reply but continue current work; do not silently reuse invalidated artifacts.";case L.SilentLog:return"Reaction mode: SILENT_LOG \u2014 recorded for the session transcript; no chat interruption.";default:return""}}stringField(e,n){let o=e?.[n];return typeof o=="string"?o:null}truncate(e,n){return e.length<=n?e:e.slice(0,n-1)+"\u2026"}},M=new se,q=new ie;var x=S(require("fs")),Xe=S(require("path"));function Ye(t){switch(t){case"resume":return!0;case"compact":return null;case"startup":case"clear":return!1;default:return!1}}var H=class{file;constructor(e){this.file=Xe.join(e,".dezycro","active-feature.json")}read(){try{let e=JSON.parse(x.readFileSync(this.file,"utf8")),n=typeof e.featureId=="string"?e.featureId:null;return n?{featureId:n,featureName:typeof e.featureName=="string"?e.featureName:void 0,workspaceId:typeof e.workspaceId=="string"?e.workspaceId:void 0,selectedAt:typeof e.selectedAt=="string"?e.selectedAt:void 0,active:e.active===!0}:null}catch{return null}}setActive(e){let n=this.read();if(!n||(n.active??!1)===e)return!1;try{return x.writeFileSync(this.file,`${JSON.stringify({...n,active:e},null,2)}
5
- `),!0}catch{return!1}}clear(){try{if(x.existsSync(this.file))return x.unlinkSync(this.file),!0}catch{}return!1}};var nn=new Set(["DONE","PUSHED"]),on=["/dezycro:sync","log what we did","log this","save progress","save what we did","wrap up","we're done","we are done","end of session","sweep the workbook"];function rn(t){if(typeof t!="string")return null;let e=t.toLowerCase().trim();if(!e)return null;for(let n of on)if(e.includes(n))return n;return null}var Qe="TRD quality gate (deterministic layer) blocked APPROVED transition: ";async function sn(t){let{event:e,route:n,payload:o,flags:r}=t;return e==="state"?un(r||{}):e==="authoring-state"?ln(r||{}):e==="pre-tool-use"&&n==="update_task"?et(o||{}):e==="pre-tool-use"&&n==="change_document_status"?cn(o||{}):e==="post-tool-use"&&n==="edit"?tt(o||{}):e==="post-tool-use"&&n==="workbook_mutation"?ut(o||{}):e==="session-start"?ot(o||{}):e==="session-end"?pn(o||{}):e==="user-prompt-submit"?rt(o||{}):e==="stop"?it(o||{}):0}function et(t){let e=w(process.cwd());if(!e)return process.stdout.write("{}"),0;let n=t&&t.tool_input||{},o=String(n.status||"").toUpperCase();if(!nn.has(o))return process.stdout.write("{}"),0;let r=n.pathRefs,s=Array.isArray(r)?r.filter(Boolean):[];if(s.length===0)return process.stdout.write("{}"),0;let i=A(e);if((i&&i.companion&&i.companion.autoVerify||{}).preTaskDone===!1)return process.stdout.write("{}"),0;if(Te({repoRoot:e,paths:s})){if(g(e,"pre_done_gate_pass",{status:o,pathRefs:s}),o==="PUSHED"){let p=an(e,i);if(p){g(e,"pre_push_coverage_nudge",{line:p});let h={systemMessage:p};return process.stdout.write(JSON.stringify(h)),0}}return process.stdout.write("{}"),0}E(e,"verifyAutoRuns"),E(e,"verifyAutoFailures"),$(e,"pre_done_gate_block",{status:o,pathRefs:s});let u=ve({paths:s,status:o}).reason,d={decision:"block",reason:u,hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:u}};return process.stdout.write(JSON.stringify(d)),0}function an(t,e){if((e&&e.companion&&e.companion.coverageNudges||{}).enabled===!1)return null;let o=k(t).coverageSnapshot,r=J(o);return r?`[Companion] ${r} \u2014 verify passed, but consider checking these before push.`:null}function cn(t){let n=t&&t.tool_input||{},o=n.status||n.newStatus||null,r=n.docId||n.documentId||n.id||null;if(o!=="APPROVED")return process.stdout.write("{}"),0;let s=w(process.cwd());if(!s)return process.stdout.write("{}"),0;let i=Le(s);if(!i||r&&i.docId&&i.docId!==r)return $(s,"quality_gate_degraded_permit",{reason:i?"docId_mismatch":"sidecar_missing",docId:r,sidecarDocId:i?i.docId:null}),process.stdout.write("{}"),0;if(i.docType!=="TRD")return process.stdout.write("{}"),0;let a=Ue({trdContent:i.content,prdContent:i.prdContent||""});if(a.passed)return g(s,"quality_gate_deterministic_pass",{docId:i.docId}),process.stdout.write("{}"),0;E(s,"qualityGateFailures");let c=a.blockingIssues.map(p=>`[${p.section}] ${p.issue}`).slice(0,5).join(" | "),u=`${Qe}${c||"unspecified failure"}. Fix and try again.`;$(s,"quality_gate_blocked",{docId:i.docId,issues:a.blockingIssues});let d={decision:"block",reason:u,hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:u}};return process.stdout.write(JSON.stringify(d)),0}async function tt(t){let e=w(process.cwd());if(!e)return 0;{let y=A(e),P=k(e),f=await M.pollIfDue(e,"postToolUse",y?.companion?.commandBus,P);f.statePatch&&l(e,f.statePatch);let C=q.renderForAgent(f.events,f.reactionMode);if(C){let T={hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:C}};return process.stdout.write(JSON.stringify(T)),0}}let n=t&&t.tool_input||{},o=nt(n);if(o.length===0)return 0;let r=A(e),s=r&&r.companion&&r.companion.autoVerify||{},i=Array.isArray(s.controllerPaths)?s.controllerPaths:[],a=s.postEditPulse&&s.postEditPulse.enabled!==!1,c=s.postEditPulse&&Number(s.postEditPulse.threshold)||5,u=Pe(o,i),d=new Date().toISOString(),h=(k(e).pulseCount||0)+1,v=a&&h>=c&&u.length>0;if(u.length===0)l(e,{pulseCount:h,lastEditTs:d});else for(let y=0;y<u.length;y+=1){let P=y===u.length-1,f={pushControllerEdit:{path:u[y],editedTs:d}};P&&(f.needsSpecPublish=!0,f.lastEditTs=d,f.pulseCount=v?0:h),l(e,f)}if(g(e,"post_edit",{edited:o.length,matchedControllers:u.length,pulseCount:v?0:h,nudged:v}),v){let y="[Companion] "+h+" edits since last verify and controller paths were touched ("+u.join(", ")+"). Consider running `/dezycro:verify` to catch regressions early.";process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:y}}))}return 0}function nt(t){if(!t||typeof t!="object")return[];let e=t.file_path;if(typeof e=="string")return[e];let n=t.path;if(typeof n=="string")return[n];let o=t.edits;if(Array.isArray(o)){let r=new Set;for(let s of o)s&&typeof s.file_path=="string"&&r.add(s.file_path);return Array.from(r)}return[]}function un(t){let e=w(process.cwd())||process.cwd();if(t.get){let n=k(e);return process.stdout.write(JSON.stringify(n,null,2)),0}if(typeof t.patch=="string"){let n;try{n=JSON.parse(t.patch)}catch(r){let s=r.message;return process.stdout.write(JSON.stringify({ok:!1,error:"invalid JSON for --patch: "+s})),1}return n===null||typeof n!="object"||Array.isArray(n)?(process.stdout.write(JSON.stringify({ok:!1,error:"--patch must be a JSON object"})),1):l(e,n)===null?(process.stdout.write(JSON.stringify({ok:!1,error:"lock contention \u2014 state not patched"})),1):(process.stdout.write(JSON.stringify({ok:!0})),0)}return process.stdout.write(JSON.stringify({ok:!1,error:"state requires one of --get or --patch=<json>"})),1}function ln(t){let e=w(process.cwd())||process.cwd(),n=t.set||(t.get?"get":null);if(n==="enter"){let o=t["doc-id"],r=t["doc-type"];if(!o||!r)return process.stdout.write(JSON.stringify({ok:!1,error:"--doc-id and --doc-type are required for --set=enter"})),1;try{let s=Oe(e,{docId:o,docType:r,featureId:t["feature-id"]||null,seed:t.seed||null});return process.stdout.write(JSON.stringify({ok:!0,transcript:s})),0}catch(s){return process.stdout.write(JSON.stringify({ok:!1,error:s.message})),1}}if(n==="exit")try{return De(e),process.stdout.write(JSON.stringify({ok:!0})),0}catch(o){return process.stdout.write(JSON.stringify({ok:!1,error:o.message})),1}if(n==="get"){let o=Ne(e);return process.stdout.write(JSON.stringify({ok:!0,...o})),0}return process.stdout.write(JSON.stringify({ok:!1,error:"authoring-state requires one of --set=enter, --set=exit, --get"})),1}var dn=600;async function ot(t){let e=w(process.cwd());if(!e)return 0;l(e,{sessionAutoLogCount:0,turnAutoLogCount:0,currentTurnId:0,lastAutoWorkbookMutation:null,coverageFetchPending:!1});let n=Ye(t.source);n!==null&&new H(e).setActive(n),g(e,"session_start",{source:t.source??null,active:n});let o=A(e),r=k(e),s=await M.pollIfDue(e,"sessionStart",o?.companion?.commandBus,r);return s.statePatch&&l(e,s.statePatch),0}async function pn(t){let e=w(process.cwd());if(!e)return 0;let n=new H(e).setActive(!1);return g(e,"session_end",{reason:t.reason??null,deactivated:n}),0}async function rt(t){let e=w(process.cwd());if(!e)return 0;let n=k(e),o=(n.currentTurnId||0)+1;l(e,{currentTurnId:o,turnAutoLogCount:0});{let i=A(e),a=await M.pollIfDue(e,"userPromptSubmit",i?.companion?.commandBus,n);a.statePatch&&l(e,a.statePatch);let c=q.renderForAgent(a.events,a.reactionMode);if(c){let u={hookSpecificOutput:{hookEventName:"UserPromptSubmit",additionalContext:c}};return process.stdout.write(JSON.stringify(u)),0}}let r=String(t?.prompt??t?.user_message??t?.message??"").trim(),s=rn(r);if(s&&(l(e,{pendingWorkbookSweep:!0,pendingWorkbookSweepReason:"wrap_phrase:"+s}),g(e,"wrap_phrase_detected",{phrase:s})),r&&Ge(r)){let i=n.lastAutoWorkbookMutation;if(i&&ze(i,o)){let a=Je(i);if(a){E(e,"undoCount"),l(e,{lastAutoWorkbookMutation:null}),g(e,"undo_directive",{tool:a.tool,entityId:i.entityId});let u={hookSpecificOutput:{hookEventName:"UserPromptSubmit",additionalContext:"[Dezycro Companion \u2014 undo] "+a.humanLine+`
1
+ "use strict";var ht=Object.create;var j=Object.defineProperty;var yt=Object.getOwnPropertyDescriptor;var St=Object.getOwnPropertyNames;var kt=Object.getPrototypeOf,bt=Object.prototype.hasOwnProperty;var Tt=(t,e)=>{for(var n in e)j(t,n,{get:e[n],enumerable:!0})},de=(t,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of St(e))!bt.call(t,r)&&r!==n&&j(t,r,{get:()=>e[r],enumerable:!(o=yt(e,r))||o.enumerable});return t};var m=(t,e,n)=>(n=t!=null?ht(kt(t)):{},de(e||!t||!t.__esModule?j(n,"default",{value:t,enumerable:!0}):n,t)),vt=t=>de(j({},"__esModule",{value:!0}),t);var Dn={};Tt(Dn,{QUALITY_GATE_REASON_PREFIX:()=>st,TRIVIAL_TURN_CHAR_DEFAULT_EXPORT:()=>Nn,_extractEditedPaths:()=>Cn,_handlePostEdit:()=>Pn,_handleSessionStart:()=>_n,_handleStop:()=>xn,_handleUpdateTaskGate:()=>In,_handleUserPromptSubmit:()=>An,_handleWorkbookMutation:()=>Rn,_readActiveFeatureId:()=>En,_transcriptCharsSinceLastStop:()=>On,dispatch:()=>fn,shouldSurfaceSpecReminder:()=>dt});module.exports=vt(Dn);var F=m(require("fs")),rt=m(require("path"));var B=m(require("fs")),R=m(require("path")),pe=Object.freeze({conservative:{decision:.85,issue:.8,assumption:.9,taskStatus:.9},balanced:{decision:.75,issue:.7,assumption:.8,taskStatus:.85},aggressive:{decision:.6,issue:.55,assumption:.7,taskStatus:.75}}),D=Object.freeze({companion:{autoVerify:{preTaskDone:!0,prePush:!0,postEditPulse:{enabled:!0,threshold:5},autoPublishSpecOnControllerChange:!0,controllerPaths:["**/controllers/**/*.{kt,java,py,go,ts,tsx,js,rs,rb,cs}","**/api/**/*.{kt,java,py,go,ts,tsx,js,rs,rb,cs}"],verifyTimeoutSeconds:300,publishSpecTimeoutSeconds:300},autoWorkbookUpdates:{enabled:!0,preset:"conservative",maxAutoLogsPerTurn:2,maxAutoLogsPerSession:10,undoWindow:"next-turn",thresholds:{decision:null,issue:null,assumption:null,taskStatus:null},trivialTurnCharCutoff:600,hashRingBufferSize:32},coverageNudges:{enabled:!0,staleBaselineDays:14,fetchMode:"async"},authoring:{defaultDepth:"adaptive",existingWorkScan:!0,qualityGate:{enabled:!0},minViableContextGate:{enabled:!0}},safeMode:!1}});function Q(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)}function fe(t,e){if(!Q(e))return e===void 0?t:e;let n=Array.isArray(t)?[...t]:{...t};for(let o of Object.keys(e)){let r=n[o],s=e[o];Q(r)&&Q(s)?n[o]=fe(r,s):n[o]=s}return n}function ge(t){let e=R.join(t,".dezycro","config.json"),n;try{n=B.readFileSync(e,"utf8")}catch(r){if(r.code==="ENOENT")return JSON.parse(JSON.stringify(D));throw r}let o;try{o=JSON.parse(n)}catch{return JSON.parse(JSON.stringify(D))}return fe(JSON.parse(JSON.stringify(D)),o)}function me(t){let e=t?.companion?.autoWorkbookUpdates||{},n=e.preset||"conservative",o=pe[n]||pe.conservative,r=e.thresholds||{};return{decision:typeof r.decision=="number"?r.decision:o.decision,issue:typeof r.issue=="number"?r.issue:o.issue,assumption:typeof r.assumption=="number"?r.assumption:o.assumption,taskStatus:typeof r.taskStatus=="number"?r.taskStatus:o.taskStatus}}function C(t){let e=R.resolve(t||process.cwd());for(let n=0;n<64;n+=1){if(B.existsSync(R.join(e,".dezycro")))return e;let o=R.dirname(e);if(o===e)return null;e=o}return null}var h=m(require("fs")),L=m(require("path")),Ct=500,Pt=10,ee=32,he=64,ye=Object.freeze({autoDecisionLogs:0,autoIssueLogs:0,autoAssumptionLogs:0,autoTaskStatusLogs:0,undoCount:0,verifyAutoRuns:0,verifyAutoFailures:0,qualityGateFailures:0,contextGateFailures:0,lockContention:0,forceApprovals:0}),It=Object.freeze({schemaVersion:1,currentSessionId:null,authoringMode:!1,authoringDocId:null,authoringDocType:null,authoringTranscriptPath:null,authoringStartedTs:null,pulseCount:0,lastEditTs:null,needsSpecPublish:!1,controllerEditsSinceLastPublish:[],coverageSnapshot:null,coverageSnapshotTs:null,coverageFetchPending:!1,lastVerifyCompletionTs:null,recentDecisionHashes:[],recentIssueHashes:[],recentAssumptionHashes:[],lastAutoWorkbookMutation:null,sessionAutoLogCount:0,turnAutoLogCount:0,currentTurnId:0,suppressWorkbookUpdatesUntil:null,pendingWorkbookSweep:!1,pendingWorkbookSweepReason:null,lastSeenVerifyCompletionTs:null,commandBus:{cursor:null,lastPollTs:null,lastAttemptTs:null,consecutiveFailures:0,recentEventIds:[]},metrics:ye});function Se(t){return L.join(t,".dezycro","companion-state.json")}function ke(t){return L.join(t,".dezycro",".companion-state.lock")}function xt(t){return L.join(t,".dezycro",".companion-state.json.tmp")}function At(t){h.mkdirSync(L.join(t,".dezycro"),{recursive:!0})}function te(){return JSON.parse(JSON.stringify(It))}function k(t){let e=Se(t);try{let n=h.readFileSync(e,"utf8"),o=JSON.parse(n);return{...te(),...o,metrics:{...ye,...o.metrics||{}}}}catch(n){let o=n;return o&&o.code==="ENOENT",te()}}function Rt(t){let e=ke(t),n=Date.now();for(;Date.now()-n<Ct;){try{let r=h.openSync(e,"wx");return h.writeSync(r,String(process.pid)),h.closeSync(r),!0}catch(r){if(r.code!=="EEXIST")throw r}let o=Date.now()+Pt;for(;Date.now()<o;);}return!1}function _t(t){try{h.unlinkSync(ke(t))}catch{}}function Et(t,e){return e?{...t,...e}:t}function ne(t,e,n){if(!e||t.includes(e))return t;let o=t.concat([e]);return o.length>n?o.slice(o.length-n):o}function l(t,e){if(At(t),!Rt(t))return null;try{let n=k(t),o=e||{},r={...n};for(let a of Object.keys(o)){let c=o[a];if(a==="metrics")r.metrics=Et(n.metrics,c);else if(a==="pushDecisionHash")r.recentDecisionHashes=ne(n.recentDecisionHashes,c,ee);else if(a==="pushIssueHash")r.recentIssueHashes=ne(n.recentIssueHashes,c,ee);else if(a==="pushAssumptionHash")r.recentAssumptionHashes=ne(n.recentAssumptionHashes,c,ee);else if(a==="pushControllerEdit"){let u=c,f=n.controllerEditsSinceLastPublish.filter(y=>y.path!==u.path).concat([u]);r.controllerEditsSinceLastPublish=f.length>he?f.slice(f.length-he):f}else r[a]=c}let s=xt(t),i=h.openSync(s,"w");return h.writeSync(i,JSON.stringify(r,null,2)),h.fsyncSync(i),h.closeSync(i),h.renameSync(s,Se(t)),r}finally{_t(t)}}function O(t,e,n=1){let o=k(t).metrics[e]||0;return l(t,{metrics:{[e]:o+n}})}var $=m(require("fs")),re=m(require("path")),Ot="companion.log";function be(t,e){try{let n=re.join(t,".dezycro");if(!$.existsSync(n))return;let o=JSON.stringify({ts:new Date().toISOString(),...e})+`
2
+ `;$.appendFileSync(re.join(n,Ot),o,{encoding:"utf8"})}catch{}}function p(t,e,n){be(t,{level:"info",event:e,...n||{}})}function V(t,e,n){be(t,{level:"warn",event:e,...n||{}})}var ve=m(require("fs")),we=m(require("path"));function Ce(t){let e=t||{},n=e.repoRoot,o=typeof e.maxAgeMinutes=="number"?e.maxAgeMinutes:60;if(!n)return!1;let r=we.join(n,".dezycro","state.json"),s;try{let d=ve.readFileSync(r,"utf8");s=JSON.parse(d)}catch{return!1}if(!s||typeof s!="object")return!1;let i=s.lastVerifyTs,a=s.lastVerifyRunId;if(!i||!a)return!1;let c=Date.parse(i);if(!Number.isFinite(c))return!1;let u=Date.now()-c;return!(u<0||u>o*60*1e3)}function Pe(t){let e=t||{},n=e.paths||[],o=e.status||"DONE",r=n.length>0?n.join(","):"<paths>";return{reason:"Cannot mark task "+o+" \u2014 no recent passing verifier run found for the linked paths. Run `/dezycro:verify --path "+r+"` first to attach passing verifier evidence, then retry."}}function Ie(t){if(!t)return{needed:!1,controllerPaths:[],lastEditTs:null};let e=k(t),n=Array.isArray(e.controllerEditsSinceLastPublish)?e.controllerEditsSinceLastPublish:[],o=n.map(i=>i&&i.path).filter(Boolean),r=n.length>0&&n[n.length-1].editedTs||null;return{needed:!!e.needsSpecPublish&&o.length>0,controllerPaths:o,lastEditTs:r}}function Lt(t){return String(t||"").replace(/\\/g,"/")}function Ae(t){let e=t.match(/\{([^{}]+)\}/);if(!e||e.index===void 0)return[t];let n=e[0],r=e[1].split(","),s=[];for(let i of r){let a=t.slice(0,e.index)+i+t.slice(e.index+n.length);s.push(...Ae(a))}return s}function Mt(t){let e="^";for(let n=0;n<t.length;n+=1){let o=t[n];o==="*"?t[n+1]==="*"?(n+=1,t[n+1]==="/"?(n+=1,e+="(?:.*/)?"):e+=".*"):e+="[^/]*":o==="?"?e+="[^/]":".+^$()|[]\\".indexOf(o)!==-1?e+="\\"+o:e+=o}return e+="$",new RegExp(e)}var xe=new Map;function Ht(t){let e=xe.get(t);if(e)return e;let n=Ae(t).map(Mt);return xe.set(t,n),n}function Ut(t,e){if(!t||!Array.isArray(e)||e.length===0)return!1;let n=Lt(t);for(let o of e){if(typeof o!="string"||o.length===0)continue;let r=Ht(o);for(let s of r)if(s.test(n))return!0}return!1}function Re(t,e){return Array.isArray(t)?t.filter(n=>Ut(n,e)):[]}var b=m(require("fs")),J=m(require("path"));var _e=Object.freeze({IDLE:"IDLE",DETECT_INTENT:"DETECT_INTENT",SCAN_EXISTING_WORK:"SCAN_EXISTING_WORK",CLARIFY:"CLARIFY",PROPOSE_PLACEMENT:"PROPOSE_PLACEMENT",CREATE_FEATURE:"CREATE_FEATURE",TRD_DISCOVERY:"TRD_DISCOVERY",MIN_CONTEXT_GATE:"MIN_CONTEXT_GATE",DRAFT:"DRAFT",ITERATE:"ITERATE",QUALITY_GATE:"QUALITY_GATE",FINALIZE:"FINALIZE"}),Ee=Object.freeze({PRD:"PRD",TRD:"TRD"}),Mn=Object.freeze(["skip","draft it","just write it","next"]),Hn=Object.freeze([_e.MIN_CONTEXT_GATE,_e.QUALITY_GATE]),jt="authoring-transcript.json",Bt="authoring-current-draft.json";function Oe(t){return J.join(t,".dezycro",jt)}function Ne(t){return J.join(t,".dezycro",Bt)}function $t(t){b.mkdirSync(J.join(t,".dezycro"),{recursive:!0})}function Vt(t,e){let n=`${t}.tmp`,o=b.openSync(n,"w");try{b.writeSync(o,JSON.stringify(e,null,2)),b.fsyncSync(o)}finally{b.closeSync(o)}b.renameSync(n,t)}function De(t){try{let e=b.readFileSync(t,"utf8");return JSON.parse(e)}catch{return null}}function Le(t,e){if(!e||!e.docId||!e.docType)throw new Error("authoring.enter: docId and docType required");if(e.docType!==Ee.PRD&&e.docType!==Ee.TRD)throw new Error(`authoring.enter: invalid docType ${e.docType}`);$t(t);let n=Oe(t),o=new Date().toISOString(),r=De(n),s=r&&r.docId===e.docId?r:{docId:e.docId,docType:e.docType,featureId:e.featureId||null,seed:e.seed||null,startedTs:o,discoverySummary:null,qaTurns:[],draftHistory:[],qualityGateHistory:[]};return Vt(n,s),l(t,{authoringMode:!0,authoringDocId:e.docId,authoringDocType:e.docType,authoringTranscriptPath:n,authoringStartedTs:o}),s}function Me(t){l(t,{authoringMode:!1,authoringDocId:null,authoringDocType:null,authoringTranscriptPath:null,authoringStartedTs:null});for(let e of[Oe(t),Ne(t)])try{b.unlinkSync(e)}catch{}}function He(t){let e=k(t);return{active:!!(e&&e.authoringMode),docId:e?e.authoringDocId:null,docType:e?e.authoringDocType:null,transcriptPath:e?e.authoringTranscriptPath:null,startedTs:e?e.authoringStartedTs:null}}function Ue(t){return De(Ne(t))}var Un=Object.freeze(["architecture","data model","data-model","datamodel","api contract","api-contract","apicontract","migration","auth","queue","verifier-behavior","verifier behavior"]),zt=Object.freeze([{id:"architecture",pattern:/^##\s+architecture\b/im},{id:"data_model",pattern:/^##\s+data\s+model\b/im},{id:"api_contracts",pattern:/^##\s+api\s+contracts?\b/im},{id:"failure_modes",pattern:/^##\s+failure\s+modes?/im},{id:"migration",pattern:/^##\s+migration\b/im},{id:"open_questions",pattern:/^##\s+open\s+questions?\b/im}]),Gt=/\b(TBD|TODO|FIXME|fill in later|to be determined)\b/i,Fe=1e3,je=/^##\s+Pillar\s+\d+\s*:\s*(.+?)\s*$/gim,Fn=Object.freeze(["data_model","api_contracts","state_or_persistence","failure_modes","migration_or_rollout"]);function Wt(t){if(!t||typeof t!="string")return[];let e=[];je.lastIndex=0;let n;for(;(n=je.exec(t))!==null;){let o=n[1].trim();o&&e.push(o)}return e}function Be(t){let{trdContent:e,prdContent:n}=t,o=[],r=(e||"").toString(),s=r.trim().length;s<Fe&&o.push({section:"document",issue:`TRD is too short (${s} chars; minimum ${Fe}).`,required_fix:"Expand the draft with concrete technical detail."});for(let c of zt)c.pattern.test(r)||o.push({section:c.id,issue:"Required H2 section is missing.",required_fix:`Add a "## ${c.id.replace(/_/g," ")}" section grounded in the PRD + discovery.`});let i=r.match(Gt);i&&o.push({section:"placeholders",issue:`Placeholder token "${i[0]}" present \u2014 TRDs must not ship with unresolved placeholders.`,required_fix:"Resolve or move the open item into the ## Open Questions section."});let a=Wt(n);if(a.length>0){let c=[],u=r.toLowerCase();for(let d of a)u.includes(d.toLowerCase())||c.push(d);c.length>0&&o.push({section:"pillar_coverage",issue:`TRD does not mention PRD pillar(s): ${c.join(", ")}.`,required_fix:"Add a per-pillar technical-design section, or reference each pillar by name in the relevant TRD section."})}return{passed:o.length===0,blockingIssues:o,deterministic:!0}}var $e=m(require("crypto"));function Ve(t,e,n){let o=String(t||"").toLowerCase().trim(),r=String(e||"").toLowerCase().trim().replace(/\s+/g," "),s=String(n||"").toLowerCase().trim();return $e.createHash("sha1").update(`${o}::${r}::${s}`).digest("hex").slice(0,16)}var z=Object.freeze({decision:"Logging decision: {{summary}} \u2014 reason: {{reason}}. Say 'undo that' if wrong.",issue:"Logging issue ({{severity}}): {{summary}}. Say 'undo that' if wrong.",assumption:"Logging assumption: {{summary}} \u2014 rationale: {{rationale}}. Say 'undo that' if wrong.",task_status:"Marking task {{task_id}} as {{proposed_status}} \u2014 rationale: {{rationale}}. Say 'undo that' if wrong."}),Xt="Auto-logging paused \u2014 {{count}} entries logged this session. Type /dezycro:sync to log manually.";function Je(t){let e=t.thresholds||{},n=Math.max(0,Number(t.perTurnSlotsLeft)||0),o=Math.max(0,Number(t.perSessionSlotsLeft)||0);if(n<=0||o<=0)return Xt.replace("{{count}}",o<=0?"session-cap":"turn-cap");let r=Math.min(n,o),s=(t.recentHashes||[]).filter(Boolean),i=["[Dezycro Companion]",`Feature: ${Yt(t.activeFeatureName,t.activeFeatureId)}. Slots: ${r} (${n}/turn, ${o}/session).`,"","Scan since your last message for committed signals to log:",` - Decision (conf \u2265 ${e.decision??.85}) \u2014 committed technical/product choice`,` - Issue (conf \u2265 ${e.issue??.8}) \u2014 current blocker/bug`,` - Assumption (conf \u2265 ${e.assumption??.9}) \u2014 unverified belief affecting impl`,` - Task-status (conf \u2265 ${e.taskStatus??.9}) \u2014 task is now DONE/PUSHED`,"","Skip: hypotheticals, brainstorming, implementation-obvious details, ephemeral content, intent statements while authoring a PRD/TRD."];return s.length>0&&i.push(`Already-logged hashes (skip dupes): ${s.join(", ")}`),i.push("","For each surviving candidate (max "+r+"): print the announce line verbatim and call the matching MCP tool.",' decision: "'+z.decision+'" \u2192 mcp__dezycro(-dev)?__add_decision',' issue: "'+z.issue+'" \u2192 mcp__dezycro(-dev)?__add_issue',' assumption: "'+z.assumption+'" \u2192 mcp__dezycro(-dev)?__add_assumption',' task_status: "'+z.task_status+'" \u2192 mcp__dezycro(-dev)?__update_task',"","If nothing meets the bar, simply continue with whatever the user asked next \u2014 do NOT output any acknowledgment line. The hook will silence itself for trivial turns."),i.join(`
3
+ `)}function Yt(t,e){if(!e)return"(none \u2014 skip if no feature is active)";let n=String(e).slice(0,8);return t?`${t} (${n}\u2026)`:n+"\u2026"}function ze(t){return typeof t!="string"?null:/add_decision$/.test(t)?"decision":/add_issue$/.test(t)?"issue":/add_assumption$/.test(t)?"assumption":/update_task$/.test(t)?"task-status":null}var Ge=Object.freeze(["undo that","no, wrong","revert that","undo the last log","wrong, undo"]);function We(t){if(typeof t!="string")return!1;let e=t.toLowerCase().trim();if(!e)return!1;for(let n=0;n<Ge.length;n+=1)if(e.includes(Ge[n]))return!0;return!1}function qe(t){if(!t||!t.tool||!t.entityId)return null;let e=t.entityId;switch(t.tool){case"add_decision":return{tool:"update_decision",args:{id:e,status:"INVALIDATED",reason:"undo by user"},humanLine:`Reverting the last auto-logged decision (${e.slice(0,8)}\u2026) \u2014 marking INVALIDATED.`};case"add_issue":return{tool:"update_issue",args:{id:e,status:"CANCELLED",reason:"undo by user"},humanLine:`Reverting the last auto-logged issue (${e.slice(0,8)}\u2026) \u2014 marking CANCELLED.`};case"add_assumption":return{tool:"update_issue",args:{id:e,status:"INVALIDATED",kind:"ASSUMPTION",reason:"undo by user"},humanLine:`Reverting the last auto-logged assumption (${e.slice(0,8)}\u2026) \u2014 marking INVALIDATED.`};case"update_task":return t.priorStatus?{tool:"update_task",args:{id:e,status:t.priorStatus},humanLine:`Reverting the last auto-task-status change \u2014 restoring ${e.slice(0,8)}\u2026 to ${t.priorStatus}.`}:null;default:return null}}function Ke(t,e){return!t||typeof t.createdInTurnId!="number"?!1:e===t.createdInTurnId+1}function se(t,e){return`${t} ${e}${t===1?"":"s"}`}function ie(t){return!t||typeof t!="object"?null:{uncoveredPaths:Number(t.uncoveredPaths??0)||0,endpointsNotInSpec:Number(t.endpointsNotInSpec??0)||0,staleBaselines:Number(t.staleBaselines??0)||0}}function G(t){let e=ie(t);if(!e)return null;let n=[];return e.uncoveredPaths>0&&n.push(`${se(e.uncoveredPaths,"path")} uncovered`),e.endpointsNotInSpec>0&&n.push(`${se(e.endpointsNotInSpec,"endpoint")} not in spec`),e.staleBaselines>0&&n.push(`${se(e.staleBaselines,"baseline")} stale`),n.length===0?null:`Coverage: ${n.join(", ")}`}function Xe(t,e){let n=ie(t),o=ie(e);if(!o)return null;if(!n)return G(e);let s=[["paths uncovered",o.uncoveredPaths-n.uncoveredPaths],["endpoints not in spec",o.endpointsNotInSpec-n.endpointsNotInSpec],["baselines stale",o.staleBaselines-n.staleBaselines]].filter(([,a])=>a!==0);return s.length===0?null:`Coverage delta since last verify: ${s.map(([a,c])=>`${c>0?"+":""}${c} ${a}`).join(", ")}`}var M={HaltAndReplan:"HALT_AND_REPLAN",AcknowledgeAndContinue:"ACKNOWLEDGE_AND_CONTINUE",SilentLog:"SILENT_LOG"};var q=m(require("fs")),Ye=m(require("os")),P=m(require("path")),W=class t{constructor(e){this.repoRoot=e;this.config=t.readJson(P.join(e,".dezycro","config.json"))}repoRoot;config;static at(e=process.cwd()){return new t(t.findRepoRoot(e)??P.resolve(e))}get workspaceId(){return t.env("DZ_WORKSPACE_ID")??t.str(this.config?.workspaceId)}get tenantId(){return t.env("DZ_TENANT_ID")??t.str(this.config?.tenantId)}get projectId(){return t.str(this.config?.projectId)}agent(e){let n=this.agentNode(e);return{context1m:t.boolFlag("DZ_1M",n?.context1m),toolSearch:t.env("DZ_TOOL_SEARCH")??t.str(n?.toolSearch)}}agentNode(e){let n=this.config?.agents;if(!t.isObj(n))return null;let o=n[e];return t.isObj(o)?o:null}static boolFlag(e,n){let o=t.env(e);return o!==null?o==="1"||o.toLowerCase()==="true":n===!0}get apiUrl(){let e=t.env("DZ_API_URL");if(e)return t.trimSlash(e);let n=Array.isArray(this.config?.environments)?this.config?.environments:[],o=n.find(s=>t.isObj(s)&&s.default===!0)??n[0],r=t.isObj(o)?t.str(o.apiUrl):null;return r?t.trimSlash(r):null}get token(){let e=t.env("DZ_TOKEN");if(e)return e;let n=[P.join(this.repoRoot,".dezycro","companion-token.local.json"),P.join(Ye.homedir(),".dezycro","token.json")];for(let o of n){let r=t.str(t.readJson(o)?.token);if(r)return r}return null}activeFeature(){let e=t.readJson(P.join(this.repoRoot,".dezycro","active-feature.json")),n=t.str(e?.featureId);if(!n)return null;let o=t.str(e?.workspaceId)??this.workspaceId;return o?{workspaceId:o,featureId:n,active:e?.active===!0}:null}connection(){let e=this.apiUrl;if(!e)return{error:"no apiUrl (set DZ_API_URL or .dezycro/config.json environments[].apiUrl)"};let n=this.workspaceId;if(!n)return{error:"no workspaceId (set DZ_WORKSPACE_ID or .dezycro/config.json workspaceId)"};let o=this.token;return o?{apiUrl:e,workspaceId:n,token:o}:{error:"no token (run `dezycro setup` or set DZ_TOKEN)"}}static findRepoRoot(e){let n=P.resolve(e||process.cwd());for(let o=0;o<64;o+=1){if(q.existsSync(P.join(n,".dezycro")))return n;let r=P.dirname(n);if(r===n)return null;n=r}return null}static readJson(e){try{return JSON.parse(q.readFileSync(e,"utf8"))}catch{return null}}static isObj(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}static str(e){return typeof e=="string"&&e.length>0?e:null}static env(e){let n=process.env[e];return n&&n.length>0?n:null}static trimSlash(e){return e.replace(/\/$/,"")}};var tn=["sessionStart","userPromptSubmit","stop","postToolUse"],nn=60,on=M.HaltAndReplan,Ze=200,rn=100,sn=3,an=1800,ae=class t{async pollIfDue(e,n,o,r){let s=this.resolveConfig(o),i=s.reactionMode,a={events:[],statePatch:null,reactionMode:i};if(!s.enabled||!s.hooks.has(n)&&n!=="sessionStart")return a;let c=r.commandBus,u=c.lastAttemptTs??c.lastPollTs;if(n!=="sessionStart"&&u){let g=Date.parse(u);if(!Number.isNaN(g)){let w=t.backoffSeconds(s.minPollIntervalSeconds,c.consecutiveFailures??0,s.circuitBreakerThreshold,s.maxBackoffSeconds);if((Date.now()-g)/1e3<w)return a}}let d=new W(e),f=d.activeFeature();if(!f||!f.active)return a;let y=d.apiUrl;if(!y)return a;let v=d.token;if(!v)return p(e,"command_bus_skip_no_token",{hookEvent:n}),a;let S=n==="sessionStart"?null:c.cursor,I=new Date().toISOString();try{let g=await this.fetchInbox(y,f,v,S),w=new Set(c.recentEventIds??[]),T=g.events.filter(N=>!w.has(N.eventId)),E=this.trimRecent([...c.recentEventIds??[],...T.map(N=>N.eventId)]),Z={commandBus:{cursor:g.nextCursor||c.cursor,lastPollTs:g.serverTs,lastAttemptTs:I,consecutiveFailures:0,recentEventIds:E}};return T.length>0&&p(e,"command_bus_events_received",{hookEvent:n,count:T.length,types:T.map(N=>N.eventType)}),{events:T,statePatch:Z,reactionMode:i}}catch(g){let w=(c.consecutiveFailures??0)+1,T={commandBus:{...c,lastAttemptTs:I,consecutiveFailures:w}},E=t.backoffSeconds(s.minPollIntervalSeconds,w,s.circuitBreakerThreshold,s.maxBackoffSeconds);return p(e,"command_bus_poll_failed",{hookEvent:n,error:g instanceof Error?g.message:String(g),consecutiveFailures:w,nextPollAfterSeconds:E,backoff:w>=s.circuitBreakerThreshold}),{events:[],statePatch:T,reactionMode:i}}}static backoffSeconds(e,n,o,r){if(n<o)return e;let s=n-o+1;return Math.min(e*Math.pow(2,s),r)}resolveConfig(e){let n=e?.enabled!==!1,o=new Set(e?.pollHooks??tn);o.add("sessionStart");let r=Math.max(0,e?.minPollIntervalSeconds??nn),s=e?.reactionMode??on,i=Math.max(1,e?.circuitBreakerThreshold??sn),a=Math.max(r,e?.maxBackoffSeconds??an);return{enabled:n,hooks:o,minPollIntervalSeconds:r,reactionMode:s,circuitBreakerThreshold:i,maxBackoffSeconds:a}}async fetchInbox(e,n,o,r){let s=new URL(`${e}/api/v1/workspaces/${n.workspaceId}/features/${n.featureId}/inbox`);r&&s.searchParams.set("since",r),s.searchParams.set("limit",String(rn));let i=new AbortController,a=setTimeout(()=>i.abort(),5e3);try{let c=await fetch(s.toString(),{method:"GET",headers:{Authorization:`Bearer ${o}`,Accept:"application/json"},signal:i.signal});if(!c.ok)throw new Error(`inbox poll returned ${c.status}`);let u=await c.json();if(!u||typeof u.serverTs!="string"||typeof u.nextCursor!="string")throw new Error("inbox poll response missing serverTs/nextCursor");return{serverTs:u.serverTs,nextCursor:u.nextCursor,events:Array.isArray(u.events)?u.events:[]}}finally{clearTimeout(a)}}trimRecent(e){return e.length<=Ze?e:e.slice(e.length-Ze)}},ce=class{renderForAgent(e,n){if(e.length===0)return"";let o=[`\u26A0\uFE0F Dezycro Command Bus: ${e.length} new event(s) on this feature.`,""];for(let r of e)o.push(this.renderEvent(r));return e.some(r=>r.urgency==="BLOCKING")&&o.push("",this.reactionGuidance(n)),o.join(`
4
+ `)}renderEvent(e){if(e.eventType==="INVALIDATION"){let r=this.stringField(e.payload,"artifactSummary")??"(no summary)",s=this.stringField(e.payload,"reason")??"(no reason)",i=this.stringField(e.payload,"previousStatus")??"?",a=`${e.sourceType.toLowerCase()} ${e.sourceId??"?"}`;return`- INVALIDATION (${e.urgency}): ${a} ("${this.truncate(r,120)}") \u2014 reason: "${this.truncate(s,200)}" \u2014 was ${i}.`}let n=this.summarizePayload(e.payload),o=`${e.sourceType.toLowerCase()}${e.sourceId?` ${e.sourceId}`:""}`;return`- [Dezycro] ${e.eventType} (${e.urgency}) from ${o}${n?`: ${n}`:""}.`}summarizePayload(e){let n=[];for(let[o,r]of Object.entries(e??{}))r!=null&&(Array.isArray(r)?n.push(`${o}=${r.join("|")}`):typeof r!="object"&&n.push(`${o}=${String(r)}`));return this.truncate(n.join(", "),240)}reactionGuidance(e){switch(e){case M.HaltAndReplan:return"Reaction mode: HALT_AND_REPLAN \u2014 pause your current line of work, acknowledge in chat, and re-plan around these invalidations before touching code again.";case M.AcknowledgeAndContinue:return"Reaction mode: ACKNOWLEDGE_AND_CONTINUE \u2014 surface this in your reply but continue current work; do not silently reuse invalidated artifacts.";case M.SilentLog:return"Reaction mode: SILENT_LOG \u2014 recorded for the session transcript; no chat interruption.";default:return""}}stringField(e,n){let o=e?.[n];return typeof o=="string"?o:null}truncate(e,n){return e.length<=n?e:e.slice(0,n-1)+"\u2026"}},H=new ae,K=new ce;var Qe=require("node:child_process"),X=m(require("fs")),et=m(require("os")),_=m(require("path")),un=240*1e3;function tt(t,e){let n=_.join(et.homedir(),".dezycro"),o=ue(_.join(n,"skills.json"));if(!o||o.enabled===!1||!o.workspaces||typeof o.workspaces!="object"||Object.keys(o.workspaces).length===0)return!1;let s=ue(_.join(t,".dezycro","config.json"))?.skills;if(s&&typeof s=="object"&&s.enabled===!1)return!1;let i=ue(_.join(n,"skills-state.json")),a=typeof i?.lastSyncTs=="string"?Date.parse(i.lastSyncTs):NaN;if(Number.isFinite(a)&&Date.now()-a<un)return!1;let c=e??_.resolve(__dirname,"..","..","cli","dezycro.js");if(!X.existsSync(c))return!1;try{return(0,Qe.spawn)(process.execPath,[c,"skills","sync"],{detached:!0,stdio:"ignore"}).unref(),!0}catch{return!1}}function ue(t){try{return JSON.parse(X.readFileSync(t,"utf8"))}catch{return null}}var x=m(require("fs")),nt=m(require("path"));function ot(t){switch(t){case"resume":return!0;case"compact":return null;case"startup":case"clear":return!1;default:return!1}}var U=class{file;constructor(e){this.file=nt.join(e,".dezycro","active-feature.json")}read(){try{let e=JSON.parse(x.readFileSync(this.file,"utf8")),n=typeof e.featureId=="string"?e.featureId:null;return n?{featureId:n,featureName:typeof e.featureName=="string"?e.featureName:void 0,workspaceId:typeof e.workspaceId=="string"?e.workspaceId:void 0,selectedAt:typeof e.selectedAt=="string"?e.selectedAt:void 0,active:e.active===!0}:null}catch{return null}}setActive(e){let n=this.read();if(!n||(n.active??!1)===e)return!1;try{return x.writeFileSync(this.file,`${JSON.stringify({...n,active:e},null,2)}
5
+ `),!0}catch{return!1}}clear(){try{if(x.existsSync(this.file))return x.unlinkSync(this.file),!0}catch{}return!1}};var ln=new Set(["DONE","PUSHED"]),dn=["/dezycro:sync","log what we did","log this","save progress","save what we did","wrap up","we're done","we are done","end of session","sweep the workbook"];function pn(t){if(typeof t!="string")return null;let e=t.toLowerCase().trim();if(!e)return null;for(let n of dn)if(e.includes(n))return n;return null}var st="TRD quality gate (deterministic layer) blocked APPROVED transition: ";async function fn(t){let{event:e,route:n,payload:o,flags:r}=t;return e==="state"?hn(r||{}):e==="authoring-state"?yn(r||{}):e==="pre-tool-use"&&n==="update_task"?it(o||{}):e==="pre-tool-use"&&n==="change_document_status"?mn(o||{}):e==="post-tool-use"&&n==="edit"?at(o||{}):e==="post-tool-use"&&n==="workbook_mutation"?mt(o||{}):e==="session-start"?ut(o||{}):e==="session-end"?kn(o||{}):e==="user-prompt-submit"?lt(o||{}):e==="stop"?pt(o||{}):0}function it(t){let e=C(process.cwd());if(!e)return process.stdout.write("{}"),0;let n=t&&t.tool_input||{},o=String(n.status||"").toUpperCase();if(!ln.has(o))return process.stdout.write("{}"),0;let r=n.pathRefs,s=Array.isArray(r)?r.filter(Boolean):[];if(s.length===0)return process.stdout.write("{}"),0;let i=A(e);if((i&&i.companion&&i.companion.autoVerify||{}).preTaskDone===!1)return process.stdout.write("{}"),0;if(Ce({repoRoot:e,paths:s})){if(p(e,"pre_done_gate_pass",{status:o,pathRefs:s}),o==="PUSHED"){let f=gn(e,i);if(f){p(e,"pre_push_coverage_nudge",{line:f});let y={systemMessage:f};return process.stdout.write(JSON.stringify(y)),0}}return process.stdout.write("{}"),0}O(e,"verifyAutoRuns"),O(e,"verifyAutoFailures"),V(e,"pre_done_gate_block",{status:o,pathRefs:s});let u=Pe({paths:s,status:o}).reason,d={decision:"block",reason:u,hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:u}};return process.stdout.write(JSON.stringify(d)),0}function gn(t,e){if((e&&e.companion&&e.companion.coverageNudges||{}).enabled===!1)return null;let o=k(t).coverageSnapshot,r=G(o);return r?`[Companion] ${r} \u2014 verify passed, but consider checking these before push.`:null}function mn(t){let n=t&&t.tool_input||{},o=n.status||n.newStatus||null,r=n.docId||n.documentId||n.id||null;if(o!=="APPROVED")return process.stdout.write("{}"),0;let s=C(process.cwd());if(!s)return process.stdout.write("{}"),0;let i=Ue(s);if(!i||r&&i.docId&&i.docId!==r)return V(s,"quality_gate_degraded_permit",{reason:i?"docId_mismatch":"sidecar_missing",docId:r,sidecarDocId:i?i.docId:null}),process.stdout.write("{}"),0;if(i.docType!=="TRD")return process.stdout.write("{}"),0;let a=Be({trdContent:i.content,prdContent:i.prdContent||""});if(a.passed)return p(s,"quality_gate_deterministic_pass",{docId:i.docId}),process.stdout.write("{}"),0;O(s,"qualityGateFailures");let c=a.blockingIssues.map(f=>`[${f.section}] ${f.issue}`).slice(0,5).join(" | "),u=`${st}${c||"unspecified failure"}. Fix and try again.`;V(s,"quality_gate_blocked",{docId:i.docId,issues:a.blockingIssues});let d={decision:"block",reason:u,hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:u}};return process.stdout.write(JSON.stringify(d)),0}async function at(t){let e=C(process.cwd());if(!e)return 0;{let S=A(e),I=k(e),g=await H.pollIfDue(e,"postToolUse",S?.companion?.commandBus,I);g.statePatch&&l(e,g.statePatch);let w=K.renderForAgent(g.events,g.reactionMode);if(w){let T={hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:w}};return process.stdout.write(JSON.stringify(T)),0}}let n=t&&t.tool_input||{},o=ct(n);if(o.length===0)return 0;let r=A(e),s=r&&r.companion&&r.companion.autoVerify||{},i=Array.isArray(s.controllerPaths)?s.controllerPaths:[],a=s.postEditPulse&&s.postEditPulse.enabled!==!1,c=s.postEditPulse&&Number(s.postEditPulse.threshold)||5,u=Re(o,i),d=new Date().toISOString(),y=(k(e).pulseCount||0)+1,v=a&&y>=c&&u.length>0;if(u.length===0)l(e,{pulseCount:y,lastEditTs:d});else for(let S=0;S<u.length;S+=1){let I=S===u.length-1,g={pushControllerEdit:{path:u[S],editedTs:d}};I&&(g.needsSpecPublish=!0,g.lastEditTs=d,g.pulseCount=v?0:y),l(e,g)}if(p(e,"post_edit",{edited:o.length,matchedControllers:u.length,pulseCount:v?0:y,nudged:v}),v){let S="[Companion] "+y+" edits since last verify and controller paths were touched ("+u.join(", ")+"). Consider running `/dezycro:verify` to catch regressions early.";process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:S}}))}return 0}function ct(t){if(!t||typeof t!="object")return[];let e=t.file_path;if(typeof e=="string")return[e];let n=t.path;if(typeof n=="string")return[n];let o=t.edits;if(Array.isArray(o)){let r=new Set;for(let s of o)s&&typeof s.file_path=="string"&&r.add(s.file_path);return Array.from(r)}return[]}function hn(t){let e=C(process.cwd())||process.cwd();if(t.get){let n=k(e);return process.stdout.write(JSON.stringify(n,null,2)),0}if(typeof t.patch=="string"){let n;try{n=JSON.parse(t.patch)}catch(r){let s=r.message;return process.stdout.write(JSON.stringify({ok:!1,error:"invalid JSON for --patch: "+s})),1}return n===null||typeof n!="object"||Array.isArray(n)?(process.stdout.write(JSON.stringify({ok:!1,error:"--patch must be a JSON object"})),1):l(e,n)===null?(process.stdout.write(JSON.stringify({ok:!1,error:"lock contention \u2014 state not patched"})),1):(process.stdout.write(JSON.stringify({ok:!0})),0)}return process.stdout.write(JSON.stringify({ok:!1,error:"state requires one of --get or --patch=<json>"})),1}function yn(t){let e=C(process.cwd())||process.cwd(),n=t.set||(t.get?"get":null);if(n==="enter"){let o=t["doc-id"],r=t["doc-type"];if(!o||!r)return process.stdout.write(JSON.stringify({ok:!1,error:"--doc-id and --doc-type are required for --set=enter"})),1;try{let s=Le(e,{docId:o,docType:r,featureId:t["feature-id"]||null,seed:t.seed||null});return process.stdout.write(JSON.stringify({ok:!0,transcript:s})),0}catch(s){return process.stdout.write(JSON.stringify({ok:!1,error:s.message})),1}}if(n==="exit")try{return Me(e),process.stdout.write(JSON.stringify({ok:!0})),0}catch(o){return process.stdout.write(JSON.stringify({ok:!1,error:o.message})),1}if(n==="get"){let o=He(e);return process.stdout.write(JSON.stringify({ok:!0,...o})),0}return process.stdout.write(JSON.stringify({ok:!1,error:"authoring-state requires one of --set=enter, --set=exit, --get"})),1}var Sn=600;async function ut(t){let e=C(process.cwd());if(!e)return 0;l(e,{sessionAutoLogCount:0,turnAutoLogCount:0,currentTurnId:0,lastAutoWorkbookMutation:null,coverageFetchPending:!1});let n=ot(t.source);n!==null&&new U(e).setActive(n),p(e,"session_start",{source:t.source??null,active:n}),tt(e)&&p(e,"skills_autosync_spawned",{});let r=A(e),s=k(e),i=await H.pollIfDue(e,"sessionStart",r?.companion?.commandBus,s);return i.statePatch&&l(e,i.statePatch),0}async function kn(t){let e=C(process.cwd());if(!e)return 0;let n=new U(e).setActive(!1);return p(e,"session_end",{reason:t.reason??null,deactivated:n}),0}async function lt(t){let e=C(process.cwd());if(!e)return 0;let n=k(e),o=(n.currentTurnId||0)+1;l(e,{currentTurnId:o,turnAutoLogCount:0});{let i=A(e),a=await H.pollIfDue(e,"userPromptSubmit",i?.companion?.commandBus,n);a.statePatch&&l(e,a.statePatch);let c=K.renderForAgent(a.events,a.reactionMode);if(c){let u={hookSpecificOutput:{hookEventName:"UserPromptSubmit",additionalContext:c}};return process.stdout.write(JSON.stringify(u)),0}}let r=String(t?.prompt??t?.user_message??t?.message??"").trim(),s=pn(r);if(s&&(l(e,{pendingWorkbookSweep:!0,pendingWorkbookSweepReason:"wrap_phrase:"+s}),p(e,"wrap_phrase_detected",{phrase:s})),r&&We(r)){let i=n.lastAutoWorkbookMutation;if(i&&Ke(i,o)){let a=qe(i);if(a){O(e,"undoCount"),l(e,{lastAutoWorkbookMutation:null}),p(e,"undo_directive",{tool:a.tool,entityId:i.entityId});let u={hookSpecificOutput:{hookEventName:"UserPromptSubmit",additionalContext:"[Dezycro Companion \u2014 undo] "+a.humanLine+`
6
6
 
7
7
  Call `+a.tool+` with arguments:
8
8
  `+JSON.stringify(a.args,null,2)+`
9
9
 
10
- Then confirm the reversal in one short sentence.`}};return process.stdout.write(JSON.stringify(u)),0}}}{let i=fn(e);if(i){let a=n.specReminderSurfacedCount??0;if(st(i.count,a)){l(e,{specReminderSurfacedCount:i.count}),g(e,"publish_spec_nudge",{paths:i.count});let c={hookSpecificOutput:{hookEventName:"UserPromptSubmit",additionalContext:i.text}};return process.stdout.write(JSON.stringify(c)),0}}else n.specReminderSurfacedCount&&l(e,{specReminderSurfacedCount:0})}return gn(e,n),process.stdout.write("{}"),0}function st(t,e){return t>e}function fn(t){let e=A(t);if((e&&e.companion&&e.companion.autoVerify||{}).autoPublishSpecOnControllerChange===!1)return null;let o=Ce(t);if(!o.needed||o.controllerPaths.length===0)return null;let r=8,s=o.controllerPaths,i=s.slice(0,r).map(c=>" - "+c);return s.length>r&&i.push(" - \u2026and "+(s.length-r)+" more"),{text:"[Dezycro Companion] "+s.length+` controller/API file(s) changed since the last published spec:
10
+ Then confirm the reversal in one short sentence.`}};return process.stdout.write(JSON.stringify(u)),0}}}{let i=bn(e);if(i){let a=n.specReminderSurfacedCount??0;if(dt(i.count,a)){l(e,{specReminderSurfacedCount:i.count}),p(e,"publish_spec_nudge",{paths:i.count});let c={hookSpecificOutput:{hookEventName:"UserPromptSubmit",additionalContext:i.text}};return process.stdout.write(JSON.stringify(c)),0}}else n.specReminderSurfacedCount&&l(e,{specReminderSurfacedCount:0})}return Tn(e,n),process.stdout.write("{}"),0}function dt(t,e){return t>e}function bn(t){let e=A(t);if((e&&e.companion&&e.companion.autoVerify||{}).autoPublishSpecOnControllerChange===!1)return null;let o=Ie(t);if(!o.needed||o.controllerPaths.length===0)return null;let r=8,s=o.controllerPaths,i=s.slice(0,r).map(c=>" - "+c);return s.length>r&&i.push(" - \u2026and "+(s.length-r)+" more"),{text:"[Dezycro Companion] "+s.length+` controller/API file(s) changed since the last published spec:
11
11
  `+i.join(`
12
- `)+"\n\nNew or changed endpoints are NOT yet reflected in the published OpenAPI spec, so verification cannot see them. Before continuing, run `/dezycro:publish-spec` to upload the updated spec (this regenerates the JEP), then `/dezycro:verify` to check the feature against it. This reminder shows once per change and again only if more controller/API files change; it clears after `/dezycro:publish-spec`.",count:s.length}}function gn(t,e){if(A(t)?.companion?.coverageNudges?.enabled===!1)return;let o=e.coverageSnapshot;if(!o||o.surfacedTs)return;let r=J(o);if(!r)return;l(t,{coverageSnapshot:{...o,surfacedTs:new Date().toISOString()}}),g(t,"coverage_one_liner",{line:r});let s={hookSpecificOutput:{hookEventName:"UserPromptSubmit",additionalContext:"[Companion] "+r}};process.stdout.write(JSON.stringify(s))}async function it(t){let e=w(process.cwd());if(!e)return 0;let n=A(e);{let C=k(e),T=await M.pollIfDue(e,"stop",n?.companion?.commandBus,C);T.statePatch&&l(e,T.statePatch);let _=q.renderForAgent(T.events,T.reactionMode);if(_){let X={systemMessage:_};return process.stdout.write(JSON.stringify(X)),0}}let o=n?.companion?.autoWorkbookUpdates||{};if(n?.companion?.safeMode===!0||o.enabled===!1)return ae();let r=k(e),s=!!(r.lastVerifyCompletionTs&&r.lastVerifyCompletionTs!==r.lastSeenVerifyCompletionTs);if(!!!(r.pendingWorkbookSweep||s))return g(e,"stop_skip",{reason:"no_trigger"}),s&&l(e,{lastSeenVerifyCompletionTs:r.lastVerifyCompletionTs}),K(e,r);if(r.authoringMode)return g(e,"stop_skip",{reason:"authoring_mode"}),l(e,{pendingWorkbookSweep:!1,pendingWorkbookSweepReason:null,lastSeenVerifyCompletionTs:r.lastVerifyCompletionTs||r.lastSeenVerifyCompletionTs}),K(e,r);let a=Number(o.maxAutoLogsPerTurn??2),c=Number(o.maxAutoLogsPerSession??10),u=Math.max(0,a-(r.turnAutoLogCount||0)),d=Math.max(0,c-(r.sessionAutoLogCount||0));if(u===0||d===0)return g(e,"stop_skip",{reason:"caps_exhausted",perTurnSlotsLeft:u,perSessionSlotsLeft:d}),l(e,{pendingWorkbookSweep:!1,pendingWorkbookSweepReason:null,lastSeenVerifyCompletionTs:r.lastVerifyCompletionTs||r.lastSeenVerifyCompletionTs}),K(e,r);if(r.suppressWorkbookUpdatesUntil&&Date.now()<new Date(r.suppressWorkbookUpdatesUntil).getTime())return g(e,"stop_skip",{reason:"suppressed"}),l(e,{pendingWorkbookSweep:!1,pendingWorkbookSweepReason:null,lastSeenVerifyCompletionTs:r.lastVerifyCompletionTs||r.lastSeenVerifyCompletionTs}),K(e,r);l(e,{pendingWorkbookSweep:!1,pendingWorkbookSweepReason:null,lastSeenVerifyCompletionTs:r.lastVerifyCompletionTs||r.lastSeenVerifyCompletionTs});let p=pe(n),h=[...r.recentDecisionHashes||[],...r.recentIssueHashes||[],...r.recentAssumptionHashes||[]],v=ct(e),y=je({thresholds:p,perTurnSlotsLeft:u,perSessionSlotsLeft:d,recentHashes:h,activeFeatureId:v.featureId,activeFeatureName:v.featureName});g(e,"stop_sweep_directive",{perTurnSlotsLeft:u,perSessionSlotsLeft:d,recentHashCount:h.length});let P=at(e,r);P&&(y+=`
12
+ `)+"\n\nNew or changed endpoints are NOT yet reflected in the published OpenAPI spec, so verification cannot see them. Before continuing, run `/dezycro:publish-spec` to upload the updated spec (this regenerates the JEP), then `/dezycro:verify` to check the feature against it. This reminder shows once per change and again only if more controller/API files change; it clears after `/dezycro:publish-spec`.",count:s.length}}function Tn(t,e){if(A(t)?.companion?.coverageNudges?.enabled===!1)return;let o=e.coverageSnapshot;if(!o||o.surfacedTs)return;let r=G(o);if(!r)return;l(t,{coverageSnapshot:{...o,surfacedTs:new Date().toISOString()}}),p(t,"coverage_one_liner",{line:r});let s={hookSpecificOutput:{hookEventName:"UserPromptSubmit",additionalContext:"[Companion] "+r}};process.stdout.write(JSON.stringify(s))}async function pt(t){let e=C(process.cwd());if(!e)return 0;let n=A(e);{let w=k(e),T=await H.pollIfDue(e,"stop",n?.companion?.commandBus,w);T.statePatch&&l(e,T.statePatch);let E=K.renderForAgent(T.events,T.reactionMode);if(E){let Z={systemMessage:E};return process.stdout.write(JSON.stringify(Z)),0}}let o=n?.companion?.autoWorkbookUpdates||{};if(n?.companion?.safeMode===!0||o.enabled===!1)return le();let r=k(e),s=!!(r.lastVerifyCompletionTs&&r.lastVerifyCompletionTs!==r.lastSeenVerifyCompletionTs);if(!!!(r.pendingWorkbookSweep||s))return p(e,"stop_skip",{reason:"no_trigger"}),s&&l(e,{lastSeenVerifyCompletionTs:r.lastVerifyCompletionTs}),Y(e,r);if(r.authoringMode)return p(e,"stop_skip",{reason:"authoring_mode"}),l(e,{pendingWorkbookSweep:!1,pendingWorkbookSweepReason:null,lastSeenVerifyCompletionTs:r.lastVerifyCompletionTs||r.lastSeenVerifyCompletionTs}),Y(e,r);let a=Number(o.maxAutoLogsPerTurn??2),c=Number(o.maxAutoLogsPerSession??10),u=Math.max(0,a-(r.turnAutoLogCount||0)),d=Math.max(0,c-(r.sessionAutoLogCount||0));if(u===0||d===0)return p(e,"stop_skip",{reason:"caps_exhausted",perTurnSlotsLeft:u,perSessionSlotsLeft:d}),l(e,{pendingWorkbookSweep:!1,pendingWorkbookSweepReason:null,lastSeenVerifyCompletionTs:r.lastVerifyCompletionTs||r.lastSeenVerifyCompletionTs}),Y(e,r);if(r.suppressWorkbookUpdatesUntil&&Date.now()<new Date(r.suppressWorkbookUpdatesUntil).getTime())return p(e,"stop_skip",{reason:"suppressed"}),l(e,{pendingWorkbookSweep:!1,pendingWorkbookSweepReason:null,lastSeenVerifyCompletionTs:r.lastVerifyCompletionTs||r.lastSeenVerifyCompletionTs}),Y(e,r);l(e,{pendingWorkbookSweep:!1,pendingWorkbookSweepReason:null,lastSeenVerifyCompletionTs:r.lastVerifyCompletionTs||r.lastSeenVerifyCompletionTs});let f=me(n),y=[...r.recentDecisionHashes||[],...r.recentIssueHashes||[],...r.recentAssumptionHashes||[]],v=gt(e),S=Je({thresholds:f,perTurnSlotsLeft:u,perSessionSlotsLeft:d,recentHashes:y,activeFeatureId:v.featureId,activeFeatureName:v.featureName});p(e,"stop_sweep_directive",{perTurnSlotsLeft:u,perSessionSlotsLeft:d,recentHashCount:y.length});let I=ft(e,r);I&&(S+=`
13
13
 
14
- `+P);let f={decision:"block",reason:y};return process.stdout.write(JSON.stringify(f)),0}function K(t,e){let n=at(t,e);if(!n)return ae();g(t,"stop_coverage_delta",{delta:n});let o={systemMessage:n};return process.stdout.write(JSON.stringify(o)),0}function at(t,e){let n=e.lastVerifyCompletionTs,o=e.coverageSnapshot;if(!n||!o)return null;let r=o.surfacedTs?new Date(o.surfacedTs).getTime():0;if(new Date(n).getTime()<=r)return null;let i=We(o.previous||null,o);return i?(l(t,{coverageSnapshot:{...o,surfacedTs:new Date().toISOString()}}),"[Companion] "+i):null}function ae(){return process.stdout.write("{}"),0}function mn(t){try{let e=t?.transcript_path;if(!e||typeof e!="string"||!U.existsSync(e))return null;let o=U.readFileSync(e,"utf8").split(`
15
- `).filter(Boolean),r=0,s=!1;for(let i=o.length-1;i>=0;i-=1){let a;try{a=JSON.parse(o[i])}catch{continue}let c=a?.message?.role??a?.role;if(c==="assistant"){let u=JSON.stringify(a.message?.content??a.content??"");r+=u.length}else if(c==="user"){if(s)break;s=!0;let u=JSON.stringify(a.message?.content??a.content??"");r+=u.length}if(r>1e6)break}return r}catch{return null}}function hn(t){return ct(t).featureId}function ct(t){try{let e=U.readFileSync(Ze.join(t,".dezycro","active-feature.json"),"utf8"),n=JSON.parse(e);return{featureId:n?.featureId||null,featureName:n?.featureName||null}}catch{return{featureId:null,featureName:null}}}function A(t){try{return de(t)}catch{return JSON.parse(JSON.stringify(D))}}function ut(t){let e=w(process.cwd());if(!e)return 0;let n=t?.tool_name||"",o=$e(n);if(!o)return 0;let r=t?.tool_input||{},s=t?.tool_response||t?.response||{},i=String(r.summary||r.title||r.description||r.statement||""),a=r.taskId||r.linkedTaskId||null,c=Be(o,i,a),u=s.data,d=s.id||(u?u.id:void 0)||r.taskId||null,p=k(e),h=(p.turnAutoLogCount||0)+1,v=(p.sessionAutoLogCount||0)+1,y={turnAutoLogCount:h,sessionAutoLogCount:v,lastAutoWorkbookMutation:{tool:n.split("__").pop()||"",entityId:d,createdInTurnId:p.currentTurnId||0,createdTs:new Date().toISOString(),priorStatus:r.priorStatus||null}};return o==="decision"&&(y.pushDecisionHash=c),o==="issue"&&(y.pushIssueHash=c),o==="assumption"&&(y.pushAssumptionHash=c),l(e,y),E(e,o==="decision"?"autoDecisionLogs":o==="issue"?"autoIssueLogs":o==="assumption"?"autoAssumptionLogs":"autoTaskStatusLogs"),g(e,"workbook_mutation",{kind:o,entityId:d,turnAutoLogCount:h,sessionAutoLogCount:v}),process.stdout.write("{}"),0}var yn=nt,Sn=tt,kn=et,bn=it,Tn=rt,vn=ut,Cn=ot,wn=hn,In=mn,Pn=dn;0&&(module.exports={QUALITY_GATE_REASON_PREFIX,TRIVIAL_TURN_CHAR_DEFAULT_EXPORT,_extractEditedPaths,_handlePostEdit,_handleSessionStart,_handleStop,_handleUpdateTaskGate,_handleUserPromptSubmit,_handleWorkbookMutation,_readActiveFeatureId,_transcriptCharsSinceLastStop,dispatch,shouldSurfaceSpecReminder});
14
+ `+I);let g={decision:"block",reason:S};return process.stdout.write(JSON.stringify(g)),0}function Y(t,e){let n=ft(t,e);if(!n)return le();p(t,"stop_coverage_delta",{delta:n});let o={systemMessage:n};return process.stdout.write(JSON.stringify(o)),0}function ft(t,e){let n=e.lastVerifyCompletionTs,o=e.coverageSnapshot;if(!n||!o)return null;let r=o.surfacedTs?new Date(o.surfacedTs).getTime():0;if(new Date(n).getTime()<=r)return null;let i=Xe(o.previous||null,o);return i?(l(t,{coverageSnapshot:{...o,surfacedTs:new Date().toISOString()}}),"[Companion] "+i):null}function le(){return process.stdout.write("{}"),0}function vn(t){try{let e=t?.transcript_path;if(!e||typeof e!="string"||!F.existsSync(e))return null;let o=F.readFileSync(e,"utf8").split(`
15
+ `).filter(Boolean),r=0,s=!1;for(let i=o.length-1;i>=0;i-=1){let a;try{a=JSON.parse(o[i])}catch{continue}let c=a?.message?.role??a?.role;if(c==="assistant"){let u=JSON.stringify(a.message?.content??a.content??"");r+=u.length}else if(c==="user"){if(s)break;s=!0;let u=JSON.stringify(a.message?.content??a.content??"");r+=u.length}if(r>1e6)break}return r}catch{return null}}function wn(t){return gt(t).featureId}function gt(t){try{let e=F.readFileSync(rt.join(t,".dezycro","active-feature.json"),"utf8"),n=JSON.parse(e);return{featureId:n?.featureId||null,featureName:n?.featureName||null}}catch{return{featureId:null,featureName:null}}}function A(t){try{return ge(t)}catch{return JSON.parse(JSON.stringify(D))}}function mt(t){let e=C(process.cwd());if(!e)return 0;let n=t?.tool_name||"",o=ze(n);if(!o)return 0;let r=t?.tool_input||{},s=t?.tool_response||t?.response||{},i=String(r.summary||r.title||r.description||r.statement||""),a=r.taskId||r.linkedTaskId||null,c=Ve(o,i,a),u=s.data,d=s.id||(u?u.id:void 0)||r.taskId||null,f=k(e),y=(f.turnAutoLogCount||0)+1,v=(f.sessionAutoLogCount||0)+1,S={turnAutoLogCount:y,sessionAutoLogCount:v,lastAutoWorkbookMutation:{tool:n.split("__").pop()||"",entityId:d,createdInTurnId:f.currentTurnId||0,createdTs:new Date().toISOString(),priorStatus:r.priorStatus||null}};return o==="decision"&&(S.pushDecisionHash=c),o==="issue"&&(S.pushIssueHash=c),o==="assumption"&&(S.pushAssumptionHash=c),l(e,S),O(e,o==="decision"?"autoDecisionLogs":o==="issue"?"autoIssueLogs":o==="assumption"?"autoAssumptionLogs":"autoTaskStatusLogs"),p(e,"workbook_mutation",{kind:o,entityId:d,turnAutoLogCount:y,sessionAutoLogCount:v}),process.stdout.write("{}"),0}var Cn=ct,Pn=at,In=it,xn=pt,An=lt,Rn=mt,_n=ut,En=wn,On=vn,Nn=Sn;0&&(module.exports={QUALITY_GATE_REASON_PREFIX,TRIVIAL_TURN_CHAR_DEFAULT_EXPORT,_extractEditedPaths,_handlePostEdit,_handleSessionStart,_handleStop,_handleUpdateTaskGate,_handleUserPromptSubmit,_handleWorkbookMutation,_readActiveFeatureId,_transcriptCharsSinceLastStop,dispatch,shouldSurfaceSpecReminder});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dezycro-ai/agent-plugin",
3
- "version": "2.5.2",
3
+ "version": "2.6.1",
4
4
  "description": "Official Dezycro plugin for AI coding agents (Claude Code today, more to come) — reverse-engineer your codebase into features, PRDs, TRDs, and live verification.",
5
5
  "bin": {
6
6
  "dezycro": "./dist/cli/dezycro.js"
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: verify
3
- description: Pull the latest verifier binary, run it against the local (or remote) environment, and upload results to the workbook. Requires an active feature (run /dezycro:work first). Accepts optional --env argument to select environment.
3
+ description: Pull the latest verifier binary and run it against the local (or remote) environment; the binary self-reports results to the workbook. Requires an active feature (run /dezycro:work first). Accepts optional --env argument to select environment.
4
4
  ---
5
5
 
6
6
  # Run Dezycro Verification
7
7
 
8
- You are pulling the verifier binary for the active feature, running it against the configured environment, and uploading results.
8
+ You are pulling the verifier binary for the active feature and running it against the configured environment. The binary self-reports results to the workbook — no manual upload.
9
9
 
10
10
  ## Step 0: Check for Pending Spec Publish (Pillar 2)
11
11
 
@@ -27,7 +27,13 @@ Parse the JSON. If `needsSpecPublish === true` AND `controllerEditsSinceLastPubl
27
27
 
28
28
  2. Invoke `/dezycro:publish-spec` programmatically before continuing.
29
29
 
30
- 3. Poll the feature's `verifyPending` flag every 5 seconds (call `get_workbook(workspaceId, featureId)` and read the feature record) until it clears or 5 minutes elapse — this matches the verifier compile SLA.
30
+ 3. Wait for the rebuilt verifier via the bundled CLI (don't poll the workbook):
31
+
32
+ ```bash
33
+ dezycro await-event --event-types VERIFIER_STATUS_UPDATE --feature "$FEATURE" --timeout 300
34
+ ```
35
+
36
+ Handle exit codes and `payload.status` exactly as in Step 4.3.
31
37
 
32
38
  4. After publish-spec returns control, clear the flags so the same edits don't re-trigger on the next verify:
33
39
 
@@ -160,15 +166,57 @@ If the binary fails to execute (non-zero exit with no JSON output), show stderr
160
166
  > - Check that the binary matches your platform
161
167
  > - Confirm the expected `AUTH_*` env vars from `.dezycro/auth.local.json` are set
162
168
 
163
- ## Step 7: Upload Results
169
+ ## Step 6b: Fix Loop (until pass or triaged)
170
+
171
+ A failing test is an intent violation until triaged otherwise. Do not stop at the first failing run — loop **triage → fix → re-run**, up to 5 iterations.
172
+
173
+ ### Register the loop (Stop-hook enforced)
174
+
175
+ On the first failing run, register the loop so the Companion Stop hook blocks premature stops until every failure is fixed or triaged:
176
+
177
+ ```bash
178
+ companion-runner state --patch='{"verifyLoop":{"active":true,"iteration":1,"maxIterations":5,"failingUntriaged":["<testCaseId>","..."],"blocksIssued":0,"startedTs":"<iso-8601-now>"}}'
179
+ ```
180
+
181
+ After each iteration, patch `iteration` and `failingUntriaged` (remove ids that now pass or were triaged as non-intent). When the loop exits, clear it:
182
+
183
+ ```bash
184
+ companion-runner state --patch='{"verifyLoop":null}'
185
+ ```
186
+
187
+ (Skip the state patches if `companion-runner` isn't installed — V1 repos. The loop procedure still applies, just unenforced.)
188
+
189
+ ### Triage each failing test BEFORE touching code
190
+
191
+ 1. Call `explain_test_failure(testCaseId)` via MCP. It returns, per failed step: provenance kind, the cited PRD section's heading + live text, `predicateText` for oracle steps, and a `drifted` flag. (The tool may not be deployed yet — if it's missing or errors, fall back to the `Intent:` reference in the verifier JSON output and go straight to the judge in step 3.)
192
+ 2. Branch on provenance:
193
+ - **`INFRASTRUCTURE` or no provenance** (spec-derived baseline tests): NOT an intent violation. This is an environment or spec-contract issue — re-check setup, health check, and the `AUTH_*` env vars. Triage as `infra`; remove the id from `failingUntriaged`.
194
+ - **`drifted: true`**: the PRD changed since the test was generated — likely a stale test, not a code bug. Surface it to the user with the section heading and suggest `/dezycro:publish-spec` to regenerate the JEP. Do NOT "fix" code to satisfy a drifted test. Triage as `stale`; remove from `failingUntriaged`.
195
+ - **`PRD_SECTION` / `ORACLE`, not drifted**: run the no-context judge (step 3).
196
+ 3. **No-context judge.** Dispatch a fresh subagent (Task tool) whose prompt contains ONLY the evidence — no repo access, no mention of your changes:
197
+ - the quoted live PRD section text and heading,
198
+ - the `predicateText` (for oracle steps),
199
+ - the failed step trace: request sent, expected vs actual response.
200
+
201
+ Ask: *"Does the observed behavior violate the quoted requirement, or does the test assert something the requirement does not say? Answer INTENT_VIOLATION, BAD_TEST, or UNCLEAR with a one-line reason."* The isolation is the point — the judge never sees the implementation or your diff, so it can only compare citation against behavior.
202
+ 4. Act on the verdict:
203
+ - **INTENT_VIOLATION** → fix the code. Keep the id in `failingUntriaged`.
204
+ - **BAD_TEST** → log it via `add_issue` ("test asserts beyond PRD §<heading>: <reason>"); remove from `failingUntriaged`.
205
+ - **UNCLEAR** → surface to the user; remove from `failingUntriaged` (don't loop on it).
206
+
207
+ ### Re-run
208
+
209
+ After applying fixes for this iteration, re-enter at Step 0. If your fixes touched controller paths, the edit hook already flipped `needsSpecPublish` and Step 0 re-publishes the spec — in that case gate on the rebuilt binary with the Step 4.3 `dezycro await-event` block (same exit-code handling) and re-pull before running. Otherwise re-run Step 6 directly with the cached binary. Bump `iteration` in the state patch.
210
+
211
+ ### Exit conditions (first hit wins)
212
+
213
+ - **All tests pass** → clear `verifyLoop`, continue to Step 7.
214
+ - **Every remaining failure is triaged non-intent** (infra / stale / bad-test / unclear) → clear `verifyLoop`, report the triage table, continue to Step 7.
215
+ - **Iteration cap (5), or two consecutive runs with an identical failing set** → clear `verifyLoop`, report honestly what still fails and why. Do NOT claim success.
164
216
 
165
- Take the JSON output from Step 6 and call `record_verifier_run` via MCP:
166
- - `workspaceId`: from config
167
- - `featureId`: from active feature
168
- - `results`: the full JSON output as a string
169
- - `specHash`, `flowVersion`, `jepVersion`: extract from the verifier JSON output's metadata section if present, otherwise omit
217
+ ## Step 7: Results Upload (Automatic)
170
218
 
171
- This automatically updates journey statuses in the workbook and may trigger lifecycle transitions (IN_PROGRESS -> VERIFIED if all enforced journeys pass and all journey-linked tasks are PUSHED).
219
+ The verifier binary self-reports each run to the registry/workbook do NOT call `record_verifier_run`. Journey status updates and lifecycle transitions (IN_PROGRESS -> VERIFIED) happen server-side from the binary's own report.
172
220
 
173
221
  ## Step 8: Display Results
174
222
 
@@ -192,7 +240,7 @@ SKIPPED:
192
240
 
193
241
  ## Step 9: Surface Coverage Gaps
194
242
 
195
- The verifier run also feeds the coverage projection. Call `get_coverage_report(workspaceId, featureId)` once the upload from Step 7 has settled. If the response carries a `null` report, skip this step — the projection materializes asynchronously and may not be ready on the first call.
243
+ The verifier run also feeds the coverage projection. Call `get_coverage_report(workspaceId, featureId)` after the run completes (the binary's self-report needs a moment to settle). If the response carries a `null` report, skip this step — the projection materializes asynchronously and may not be ready on the first call.
196
244
 
197
245
  If the report is present, take the top 3 entries (in this priority order) from `decisiveFailures` first, then `observedButInconclusive` to fill the remaining slots, and surface them with action hints derived from `verdict`:
198
246
 
@@ -244,7 +292,8 @@ If yes, run it via Bash.
244
292
 
245
293
  ## Next Steps
246
294
 
247
- Based on results:
295
+ Based on results (the Step 6b loop has already run by this point):
248
296
  - **All journeys passing**: "All journeys passing! The feature may auto-transition to VERIFIED."
249
- - **Some failures**: "Fix the failing journeys and run `/dezycro:verify` again. Focus on: <list top 2-3 failing journeys>."
250
- - **No journeys passing**: "No journeys passing yet. Review the failing assertions and start implementing the feature endpoints."
297
+ - **Remaining failures triaged non-intent**: show the triage table (infra / stale / bad-test / unclear) with the recommended action per bucket (fix env, re-publish spec, review flagged issue).
298
+ - **Loop cap hit with confirmed intent violations left**: report what still fails, the PRD sections violated, and what was attempted. Do NOT claim success.
299
+ - **No journeys passing on a fresh feature**: "No journeys passing yet. Review the failing assertions and start implementing the feature endpoints."