@devflow-tools/cli 0.16.9 → 0.16.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/commands/doctor.d.ts.map +1 -1
  2. package/dist/commands/doctor.js +354 -13
  3. package/dist/commands/doctor.js.map +1 -1
  4. package/dist/commands/setup.d.ts +25 -2
  5. package/dist/commands/setup.d.ts.map +1 -1
  6. package/dist/commands/setup.js +71 -63
  7. package/dist/commands/setup.js.map +1 -1
  8. package/dist/index.js +2 -2
  9. package/dist/index.js.map +1 -1
  10. package/dist/plugin-files/.claude-plugin/plugin.json +1 -1
  11. package/dist/plugin-files/dist/command-registry.json +39 -13
  12. package/dist/plugin-files/dist/hooks/hook-client.js +6 -6
  13. package/dist/plugin-files/dist/hooks/hook-daemon.js +14 -13
  14. package/dist/plugin-files/dist/hooks/post-tool-use-failure.js +1 -1
  15. package/dist/plugin-files/dist/hooks/post-tool-use.js +2 -2
  16. package/dist/plugin-files/dist/hooks/pre-compact.js +1 -1
  17. package/dist/plugin-files/dist/hooks/pre-tool-use.js +2 -2
  18. package/dist/plugin-files/dist/hooks/session-end.js +2 -2
  19. package/dist/plugin-files/dist/hooks/stop.js +2 -2
  20. package/dist/plugin-files/dist/hooks/user-prompt-submit.js +4 -3
  21. package/dist/plugin-files/dist/skills/devflow:animation/SKILL.md +3 -1
  22. package/dist/plugin-files/dist/skills/devflow:css/SKILL.md +3 -1
  23. package/dist/plugin-files/dist/skills/devflow:docker/SKILL.md +3 -1
  24. package/dist/plugin-files/dist/skills/devflow:electron/SKILL.md +3 -1
  25. package/dist/plugin-files/dist/skills/devflow:git/SKILL.md +3 -1
  26. package/dist/plugin-files/dist/skills/devflow:graphql/SKILL.md +3 -1
  27. package/dist/plugin-files/dist/skills/devflow:nest/SKILL.md +3 -1
  28. package/dist/plugin-files/dist/skills/devflow:nextjs/SKILL.md +3 -1
  29. package/dist/plugin-files/dist/skills/devflow:performance/SKILL.md +3 -1
  30. package/dist/plugin-files/dist/skills/devflow:react/SKILL.md +6 -4
  31. package/dist/plugin-files/dist/skills/devflow:tailwind/SKILL.md +3 -1
  32. package/dist/plugin-files/dist/skills/devflow:taro/SKILL.md +3 -1
  33. package/dist/plugin-files/dist/skills/devflow:ui-layout/SKILL.md +3 -1
  34. package/dist/plugin-files/dist/skills/devflow:vue/SKILL.md +3 -1
  35. package/dist/plugin-files/hooks/hooks.json +3 -2
  36. package/dist/plugin-files/hooks/session-start +54 -67
  37. package/dist/plugin-files/hooks/stop +6 -4
  38. package/dist/plugin-files/package.json +7 -7
  39. package/dist/plugin-files/skills/animation/SKILL.md +3 -1
  40. package/dist/plugin-files/skills/css/SKILL.md +3 -1
  41. package/dist/plugin-files/skills/docker/SKILL.md +3 -1
  42. package/dist/plugin-files/skills/doctor/SKILL.md +1 -1
  43. package/dist/plugin-files/skills/electron/SKILL.md +3 -1
  44. package/dist/plugin-files/skills/git/SKILL.md +3 -1
  45. package/dist/plugin-files/skills/graphql/SKILL.md +3 -1
  46. package/dist/plugin-files/skills/nest/SKILL.md +3 -1
  47. package/dist/plugin-files/skills/nextjs/SKILL.md +3 -1
  48. package/dist/plugin-files/skills/performance/SKILL.md +3 -1
  49. package/dist/plugin-files/skills/react/SKILL.md +6 -4
  50. package/dist/plugin-files/skills/tailwind/SKILL.md +3 -1
  51. package/dist/plugin-files/skills/taro/SKILL.md +3 -1
  52. package/dist/plugin-files/skills/ui-layout/SKILL.md +3 -1
  53. package/dist/plugin-files/skills/vue/SKILL.md +3 -1
  54. package/package.json +27 -27
@@ -1,14 +1,15 @@
1
- import {createServer,createConnection}from'net';import {join,dirname}from'path';import {mkdirSync,lstatSync,openSync,writeFileSync,fstatSync,closeSync,readFileSync,unlinkSync,existsSync,rmSync,renameSync,readdirSync,appendFileSync as appendFileSync$1}from'fs';import {getLocalApiKey,getDaemonSocketPath,getDaemonPidPath,loadConfig,getProjectStateDir,getDaemonRegistryPath,getProjectHash}from'@devflow-tools/sdk';import {request}from'http';import {homedir}from'os';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {fileURLToPath}from'url';import {spawn}from'child_process';import {mkdirSync as mkdirSync$1,appendFileSync,readdirSync as readdirSync$1,renameSync as renameSync$1,unlinkSync as unlinkSync$1,writeFileSync as writeFileSync$1,readFileSync as readFileSync$1,openSync as openSync$1,closeSync as closeSync$1,rmSync as rmSync$1,statSync,existsSync as existsSync$1}from'node:fs';import {dirname as dirname$1,basename,join as join$1}from'node:path';import {MemoryGate}from'@devflow-tools/memory-engine';import {randomUUID,createHash}from'crypto';import {createHash as createHash$1}from'node:crypto';import {homedir as homedir$1}from'node:os';var le=1e4,et=3e4,x=500;function tt(r,e,t){return new Promise(n=>{try{let o=new URL(r),s=request({hostname:o.hostname,port:o.port||80,path:o.pathname+o.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":t,"Content-Length":Buffer.byteLength(e)},timeout:5e3},i=>{let a=[];i.on("data",c=>a.push(c)),i.on("end",()=>{let c=Buffer.concat(a).toString();n(i.statusCode!=null&&i.statusCode>=200&&i.statusCode<300?c:null);});});s.on("error",()=>n(null)),s.on("timeout",()=>{s.destroy(),n(null);}),s.write(e),s.end();}catch{n(null);}})}var b=class{constructor(e){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=e?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=e?.apiKey??getLocalApiKey(),this.cacheDir=e?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=e?.legacyCacheDir??(e?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=e?.database??null,this.ownsDatabase=!e?.database;}async sendEvent(e){let t={...e,input:this.truncateInput(e.input)};return this.commitOrCache("tool_call",t)?(this.postHttp("/api/telemetry/tool-call",t),e.eventId):null}async sendExecutionStart(e,t,n,o,s=process.env.CLAUDE_PROJECT_DIR??process.cwd(),i=[]){let a={executionId:e,sessionId:t,skillName:n,startedAt:o,projectRoot:s,requiredMcpTools:i};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(e,t="completed",n=Date.now(),o){let s={executionId:e,status:t,finishedAt:n,metadata:o},i=this.commitOrCache("execution_complete",s);return i&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",s),i}async sendSessionStart(e,t,n){let o={id:e,projectRoot:t,startedAt:n,label:t.split("/").pop()??"unknown"},s=this.commitOrCache("session_start",o);return s&&this.postHttp("/api/telemetry/sessions",o),s}async completeEvent(e){let t=this.commitOrCache("complete_event",e);return t&&(this.postHttp("/api/telemetry/tool-call/output",e),this.scheduleMetricAggregation()),t}async endSession(e,t=Date.now()){let n={sessionId:e,finishedAt:t},o=this.commitOrCache("session_end",n);return o&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(e)}/close`,{finishedAt:t})),o}async flushCache(){try{let n=this.getDatabase(),o=n.listTelemetryFailures({unresolvedOnly:!0,limit:x}).reverse();for(let s of o)try{this.applyOperation(s.operation,s.payload),n.resolveTelemetryFailure(s.id);}catch{}n.trimTelemetryFailures(x);}catch{}let t=[...new Set([this.cacheDir,this.legacyCacheDir].filter(n=>!!n))].filter(n=>existsSync(n)).flatMap(n=>readdirSync(n).filter(o=>o.endsWith(".json")).map(o=>{let s=join(n,o);try{return {cacheFile:s,envelope:nt(JSON.parse(readFileSync(s,"utf8")),o)}}catch{return null}})).filter(n=>n!==null).sort((n,o)=>n.envelope.timestamp-o.envelope.timestamp||ue(n.envelope.operation)-ue(o.envelope.operation));for(let{cacheFile:n,envelope:o}of t)try{let s=this.getDatabase();s.insertTelemetryFailure({id:o.id,operation:o.operation,payload:o.payload,error:o.failure,createdAt:o.timestamp}),this.applyOperation(o.operation,o.payload),s.resolveTelemetryFailure(o.id),unlinkSync(n);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(e,t){try{return this.applyOperation(e,t),!0}catch(n){return this.cacheOperation(e,t,n),false}}applyOperation(e,t){let n=this.getDatabase();switch(e){case "tool_call":this.ensureParentSession(n,t),n.insertToolCallEvent(t);return;case "execution_start":this.ensureParentSession(n,t),n.insertSkillExecution({executionId:t.executionId,sessionId:t.sessionId,skillName:t.skillName,startedAt:t.startedAt,status:"running",requiredMcpTools:t.requiredMcpTools});return;case "execution_complete":n.reconcileSkillExecution(t.executionId,t.status,t.finishedAt,t.metadata);return;case "session_start":n.ensureSession(t),n.insertRun({id:de(t.id),source:"hook",tool:"session",input:{projectRoot:t.projectRoot},status:"active",startedAt:t.startedAt,tokenUsed:0,metadata:{sessionId:t.id,projectRoot:t.projectRoot}});return;case "complete_event":if(!n.updateToolCallEvent(t.eventId,{output:t.output===void 0?void 0:JSON.stringify(t.output),error:t.error,duration:t.duration}))throw new Error(`Tool call event ${t.eventId} is not available for completion`);return;case "session_end":n.closeSession(t.sessionId,t.finishedAt),n.updateRun(de(t.sessionId),{status:"completed",finishedAt:t.finishedAt});return}}ensureParentSession(e,t){let n=typeof t.sessionId=="string"?t.sessionId.trim():"";if(!n)throw new Error("Canonical session ID is required for telemetry");let o=typeof t.projectRoot=="string"&&t.projectRoot.trim()?t.projectRoot:typeof t.input?.projectRoot=="string"&&t.input.projectRoot.trim()?t.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";e.ensureSession({id:n,projectRoot:o,label:o==="unknown"?void 0:o.split("/").pop(),startedAt:Number(t.startedAt??t.timestamp??Date.now())});}cacheOperation(e,t,n){let o=Date.now(),s={id:`failure:${o}:${Math.random().toString(36).slice(2,11)}`,operation:e,payload:t,failure:n instanceof Error?n.message:String(n),timestamp:o};try{let i=this.getDatabase();i.insertTelemetryFailure({id:s.id,operation:e,payload:t,error:s.failure,createdAt:o}),i.trimTelemetryFailures(x),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let i=join(this.cacheDir,`${o}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(i,JSON.stringify(s,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let e=readdirSync(this.cacheDir).filter(t=>t.endsWith(".json")).sort();for(let t of e.slice(0,Math.max(0,e.length-x)))unlinkSync(join(this.cacheDir,t));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},et).unref());}scheduleMetricAggregation(){if(this.metricAggregationScheduled||process.env.DEVFLOW_HOOK_DEGRADED==="1")return;this.metricAggregationScheduled=true,setTimeout(()=>{this.metricAggregationScheduled=false;try{this.getDatabase().aggregatePendingToolMetrics(50)===50&&this.scheduleMetricAggregation();}catch{}},25).unref();}postHttp(e,t){tt(`${this.apiUrl}${e}`,JSON.stringify(t),this.apiKey);}truncateInput(e){let t=JSON.stringify(e);return t===void 0||t.length<=le?e:{_truncated:true,_original_size:t.length,_preview:`${t.substring(0,le)}...`}}};function ue(r){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(r)}function de(r){return `hook-run:${r}`}function nt(r,e){if(!r||typeof r!="object")throw new Error("Invalid telemetry cache envelope");let t=r;if(typeof t.operation=="string"&&t.payload!==void 0)return t;let n=t.type==="tool_call"?"tool_call":t.type==="execution_start"?"execution_start":null;if(!n)throw new Error("Unknown legacy telemetry cache operation");let o=t.payload??{},s=Number(o.timestamp??o.startedAt??Date.now());return {id:`legacy-cache:${e}`,operation:n,payload:o,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:s}}function $(r){return r??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function T(r){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir($(r))}var ct=2,lt=1;function ut(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join(dirname(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function me(r=ut()){let e=[join(r,"dist","command-registry.json"),join(r,"command-registry.json")];for(let t of e)try{if(!existsSync(t))continue;let n=JSON.parse(readFileSync(t,"utf8"));if(n.version!==ct&&n.version!==lt||!dt(n.commands)){console.error(`[devflow] command registry contract mismatch: ${t}`);continue}return n.commands}catch(n){console.error(`[devflow] command registry load failed: ${n.message}`);}return null}function dt(r){return !r||typeof r!="object"||Array.isArray(r)?false:Object.values(r).every(e=>{if(!e||typeof e!="object"||Array.isArray(e))return false;let t=e;return pe(t.mcpTools)&&pe(t.blockedNative)&&(t.source===void 0||t.source==="core"||t.source==="plugin")&&(t.requiresContext===void 0||typeof t.requiresContext=="boolean")&&(t.evidenceMode===void 0||["static","runtime","mixed"].includes(t.evidenceMode))})}function pe(r){return Array.isArray(r)&&r.every(e=>typeof e=="string"&&e.length>0)}function he(r){let e=me();return e?e[r]?.mcpTools??[]:[]}var G=join(homedir(),".devflow"),J=join(G,"server-refs.json"),vt="http://127.0.0.1:13337/api/health",_t=600*1e3,wt=500;function St(){existsSync(G)||mkdirSync(G,{recursive:true});}function C(){try{if(existsSync(J))return JSON.parse(readFileSync(J,"utf-8"))}catch{}return {sessions:[],lastActivity:0}}function fe(r){St(),writeFileSync(J,JSON.stringify(r));}var P=class{constructor(e){this.process=null;this.idleTimer=null;this.onShutdown=null;this.projectRoot=e;}setOnShutdown(e){this.onShutdown=e;}addRef(e){let t=C();t.sessions.includes(e)||t.sessions.push(e),t.lastActivity=Date.now(),fe(t),this.resetIdleTimer();}removeRef(e){let t=C();t.sessions=t.sessions.filter(n=>n!==e),t.lastActivity=Date.now(),fe(t),t.sessions.length===0&&this.resetIdleTimer();}get activeSessions(){return C().sessions.length}async ensureRunning(){return await this.healthCheck()?true:(await this.startServer(),this.waitForReady())}get isRunning(){return this.process!==null&&!this.process.killed}async stop(){this.idleTimer&&clearTimeout(this.idleTimer),this.process&&(this.process.kill("SIGTERM"),await new Promise(e=>{let t=setTimeout(()=>{this.process&&!this.process.killed&&this.process.kill("SIGKILL"),e();},5e3);this.process?this.process.on("exit",()=>{clearTimeout(t),e();}):(clearTimeout(t),e());}),this.process=null);}async startServer(){let e=join(this.projectRoot,"apps","server","dist","main.js"),t=join(this.projectRoot,"node_modules","@devflow-tools","server","dist","main.js"),n=existsSync(e)?e:t;this.process=spawn("node",[n],{cwd:this.projectRoot,env:{...process.env,NODE_ENV:process.env.NODE_ENV||"development"},stdio:["ignore","pipe","pipe"]}),this.process.stdout?.on("data",o=>{}),this.process.stderr?.on("data",o=>{}),this.process.on("exit",o=>{this.process=null,this.onShutdown&&this.onShutdown();}),this.process.on("error",()=>{this.process=null;});}async waitForReady(){let e=Date.now()+3e4;for(;Date.now()<e;){if(await this.healthCheck())return true;await new Promise(t=>setTimeout(t,wt));}return false}healthCheck(){return new Promise(e=>{let t=new URL(vt),n=request({hostname:t.hostname,port:t.port,path:t.pathname,method:"GET",timeout:2e3},o=>{e(o.statusCode===200);});n.on("error",()=>e(false)),n.on("timeout",()=>{n.destroy(),e(false);}),n.end();})}resetIdleTimer(){this.idleTimer&&clearTimeout(this.idleTimer),!(C().sessions.length>0)&&(this.idleTimer=setTimeout(()=>{this.stop();},_t).unref());}};function ge(r,e,t){try{let n=openGlobalDevFlowDatabase();try{n.insertEvent({kind:r,timestamp:Date.now(),duration:t,success:e.success!==!1,metadata:e});}finally{n.close();}}catch{}}var V=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",xt=getLocalApiKey(),Ct=8,Mt=6e4,ye=new Map;function Pt(r){let e=new URL(r,V).pathname,t=ye.get(e);return t||(t={failures:0,lastFailure:0,openUntil:0,status:"closed"},ye.set(e,t)),t}function z(r,e,t){if(e.status===t)return;let n=e.status;e.status=t,ge("http_circuit_transition",{path:new URL(r,V).pathname,previous:n,status:t,failures:e.failures,openUntil:e.openUntil});}function H(r,e){e.failures++,e.lastFailure=Date.now(),e.failures>=Ct&&(e.openUntil=Date.now()+Mt,z(r,e,"open"));}function Ot(r,e){let t=e.status!=="closed";e.failures=0,e.openUntil=0,t&&z(r,e,"closed");}var B=join(homedir(),".devflow","errors");function ve(r,e){try{existsSync(B)||mkdirSync(B,{recursive:!0});let t=`${new Date().toISOString()} | ${r} | ${e?.message??String(e)}
2
- `;appendFileSync$1(join(B,"http-errors.log"),t);}catch{}}function we(r,e,t={}){let n=t.timeout??5e3,o=t.maxRetries??2,s=t.circuitBreaker??true,i=Pt(r);if(s&&i.openUntil>Date.now())return Promise.resolve();s&&i.status==="open"&&z(r,i,"half_open");let a=c=>new Promise(u=>{let l=JSON.stringify(e),m=new URL(r,V),p=request({hostname:m.hostname,port:m.port,path:m.pathname+m.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":xt,"Content-Length":Buffer.byteLength(l)},timeout:n},f=>{if(f.resume(),f.statusCode&&f.statusCode>=400){let d=new Error(`HTTP ${f.statusCode}`);if(ve(r,d),c>0){let h=Math.min(1e3*Math.pow(2,o-c),8e3);setTimeout(()=>{a(c-1).then(u);},h);}else H(r,i),u();return}Ot(r,i),u();});p.on("error",f=>{if(ve(r,f),c>0){let d=Math.min(1e3*Math.pow(2,o-c),8e3);setTimeout(()=>{a(c-1).then(u);},d);}else H(r,i),u();}),p.on("timeout",()=>{p.destroy(),c>0?a(c-1).then(u):(H(r,i),u());}),p.write(l),p.end();});return a(o)}function O(r){if(!r||typeof r!="object"||Array.isArray(r))return {};let e=r,t={};for(let n of ["lastMcpCall","bypassCount"])if(n in e){let o=e[n];if(o==null)continue;t[n]=typeof o=="number"&&Number.isFinite(o)&&o>=0?o:0;}return t}function Ft(r,e){return r.lastMcpCall===e.lastMcpCall&&r.bypassCount===e.bypassCount}function Nt(r){try{return existsSync(r)?O(JSON.parse(readFileSync(r,"utf-8"))):{}}catch{return {}}}function Lt(r,e){let t=e instanceof Error?e.message:String(e);console.error(`[devflow] Receipt ${r} skipped: ${t}`);}function Se(r){let e=T(r);for(let t of ["receipt.json","receipt-lock.sqlite","receipt-lock.sqlite-shm","receipt-lock.sqlite-wal"])try{existsSync(join(e,t))&&unlinkSync(join(e,t));}catch{}}function Ut(r,e){if(r.getHookReceipt(e)){Se(e);return}let t=join(T(e),"receipt.json");if(!existsSync(t))return;let n=Nt(t);r.updateHookReceipt(e,()=>n),Se(e);}function be(r,e,t){let n;try{n=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),Ut(n,r);let o={},s=n.updateHookReceipt(r,a=>(o=O(a),O(e(o)))),i=O(s);return {receipt:i,applied:!0,changed:!Ft(o,i)}}catch(o){return Lt(t,o),{receipt:{},applied:false,changed:false}}finally{try{n?.close();}catch{}}}function A(r,e){return be(r,()=>e,"write").applied}function X(r,e){return be(r,e,"update")}var Ee={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",Bash:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function $t(r){switch(r){case "WebSearch":case "WebFetch":return 1;case "Agent":return 3;case "Grep":case "Glob":return 3;case "Bash":return 5;default:return 2}}var k=class{constructor(e,t,n){this.projectRoot=e;this.sessionId=t;this.executionId=n;}getPhase(){if(!this.sessionId||!this.executionId)return "idle";let e;try{e=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250});let t=e.getContextReceipt(this.projectRoot,this.sessionId,this.executionId);if(t&&t.expiresAt>Date.now())return "context_ready";t&&e.deleteContextReceipt(this.projectRoot,this.sessionId,this.executionId);}catch{}finally{try{e?.close();}catch{}}return "context_gathering"}evaluate(e,t){let n=this.getPhase();if(t||e==="Skill")return {permissionDecision:"allow"};if(n==="context_gathering"&&Ee[e])return {permissionDecision:"deny",reason:`DevFlow context required. Call mcp__devflow__get_project_context, then retry ${e}.`};if(n==="context_ready")return {permissionDecision:"allow"};let o=Ee[e];if(!o)return {permissionDecision:"allow"};let s=X(this.projectRoot,c=>({...c,bypassCount:(c.bypassCount??0)+1}));if(!s.applied)return {permissionDecision:"allow"};let i=s.receipt.bypassCount??0,a=$t(e);return i>=a?{permissionDecision:"allow",additionalContext:`\u5DF2 ${i} \u6B21\u76F4\u63A5\u4F7F\u7528 ${e}\uFF0C\u5EFA\u8BAE\u7528 mcp__devflow__${o} \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`}:{permissionDecision:"allow"}}recordMcpCall(){A(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}};var Bt=10,Vt=5e3,zt=3e4,Kt=new Int32Array(new SharedArrayBuffer(4)),Ce=0;function Yt(r){if(typeof r!="object"||r===null)return false;let e=r;return typeof e.rootPath=="string"&&e.rootPath.length>0&&typeof e.projectHash=="string"&&e.projectHash.length>0&&typeof e.pid=="number"&&Number.isInteger(e.pid)&&e.pid>0&&typeof e.startedAt=="number"&&Number.isFinite(e.startedAt)&&e.startedAt>=0}function Me(r){if(!existsSync$1(r))return {entries:[],needsRepair:false};try{let e=JSON.parse(readFileSync$1(r,"utf8"));if(!Array.isArray(e))return {entries:[],needsRepair:!0};let t=e.filter(Yt);return {entries:t,needsRepair:t.length!==e.length}}catch{return {entries:[],needsRepair:true}}}function Pe(r,e){mkdirSync$1(dirname$1(r),{recursive:true});let t=`${r}.${process.pid}.${Date.now()}.${Ce++}.tmp`,n;try{n=openSync$1(t,"wx"),writeFileSync$1(n,JSON.stringify(e,null,2)),closeSync$1(n),n=void 0,renameSync$1(t,r);}finally{try{n!==void 0&&closeSync$1(n);}finally{rmSync$1(t,{force:true});}}}function Oe(r){try{return process.kill(r,0),!0}catch(e){return e.code!=="ESRCH"}}function Ae(r){if(typeof r!="object"||r===null)return false;let e=r;return typeof e.pid=="number"&&Number.isInteger(e.pid)&&e.pid>0&&typeof e.createdAt=="number"&&Number.isFinite(e.createdAt)&&typeof e.token=="string"&&e.token.length>0}function Te(r,e){try{return readFileSync$1(r,"utf8")!==e?!1:(rmSync$1(r),!0)}catch(t){return t.code==="ENOENT"}}function Xt(r){try{let e=readFileSync$1(r,"utf8"),t;try{let o=JSON.parse(e);Ae(o)&&(t=o);}catch{}return t?Oe(t.pid)?!1:Te(r,e):Date.now()-statSync(r).mtimeMs>zt&&Te(r,e)}catch(e){return e.code==="ENOENT"}}function Qt(r){mkdirSync$1(dirname$1(r),{recursive:true});let e=`${r}.lock`,t=Date.now()+Vt;for(;;){let n=`${process.pid}:${Date.now()}:${Ce++}`,o;try{return o=openSync$1(e,"wx"),writeFileSync$1(o,JSON.stringify({pid:process.pid,createdAt:Date.now(),token:n})),closeSync$1(o),o=void 0,{path:e,token:n}}catch(s){if(o!==void 0)try{closeSync$1(o);}finally{rmSync$1(e,{force:true});}if(s.code!=="EEXIST")throw s}if(!Xt(e)){if(Date.now()>=t)throw new Error(`Timed out acquiring daemon registry lock: ${e}`);Atomics.wait(Kt,0,0,Bt);}}}function Zt(r){try{let e=readFileSync$1(r.path,"utf8"),t=JSON.parse(e);Ae(t)&&t.token===r.token&&rmSync$1(r.path);}catch(e){if(e.code!=="ENOENT")throw e}}function je(r){let e=getDaemonRegistryPath(),t=Qt(e);try{return r(e)}finally{Zt(t);}}function Fe(r,e){je(t=>{let n=Me(t).entries.filter(o=>o.rootPath!==r&&Oe(o.pid));n.push({rootPath:r,projectHash:getProjectHash(r),pid:e,startedAt:Date.now()}),Pe(t,n);});}function Ne(r,e){je(t=>{let n=Me(t).entries.filter(o=>o.rootPath!==r||e!==void 0&&o.pid!==e);Pe(t,n);});}async function L(r){let e=!r.memory,t=r.memory??new MemoryGate(r.projectRoot),n=0,o=0;try{await t.forceWarmUp(),r.trigger==="session_end"&&(n=await t.releasePendingDistillLeases(r.sessionId)),o=t.getPendingEventCount();}finally{e&&t.close();}let s={pendingEvents:o,releasedLeases:n,trigger:r.trigger,sessionId:r.sessionId};try{let i=openGlobalDevFlowDatabase();try{let a=Date.now(),c=`distill:${a}:${Math.random().toString(36).slice(2,10)}`;i.recordMemoryDistillCheckpoint({id:c,projectRoot:r.projectRoot,sessionId:r.sessionId,trigger:r.trigger,pendingEvents:o,releasedLeases:n,createdAt:a}),i.insertEvent({kind:"memory_distill_requested",timestamp:a,success:!0,metadata:{...s,projectRoot:r.projectRoot}});}finally{i.close();}}catch{}return s}function Q(r){if(r.pendingEvents===0)return "";let e=r.sessionId?`\uFF0CsessionId=${r.sessionId}`:"";return `Memory distill checkpoint: ${r.pendingEvents} \u4E2A\u4E8B\u4EF6\u5F85\u63D0\u70BC${e}\u3002\u4E0A\u4E0B\u6587\u538B\u7F29\u5B8C\u6210\u540E\uFF0C\u8C03\u7528 mcp__devflow__memory_request_distill\uFF1B\u6309\u8FD4\u56DE\u7684 nextPage \u5206\u9875\u8BFB\u53D6\u5168\u90E8\u4E8B\u4EF6\uFF0C\u63D0\u70BC observations\uFF0C\u518D\u8C03\u7528 mcp__devflow__memory_save_distilled \u5E76\u786E\u8BA4\u4FDD\u5B58\u7ED3\u679C\u3002`}function un(r){return Buffer.from(r,"utf8").toString("base64url")}var I=class{constructor(e){this.projectRoot=e;this.sessions=new Map;this.sessionsDir=join(T(e),"hook-sessions");}registerSession(e){let t=e.trim();if(!t)throw new Error("session_id_required");let n=this.get(t);if(n)return n.lastActivityAt=Date.now(),this.persist(n),n;let o=Date.now(),s={sessionId:t,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:o,lastActivityAt:o};return this.sessions.set(t,s),this.persist(s),s}startExecution(e,t,n){let o=this.registerSession(e);return (!o.executionId||o.skillName!==t)&&(o.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),o.skillName=t,o.requiredMcpTools=[...new Set(n)],o.lastActivityAt=Date.now(),this.persist(o),o}get(e){let t=e.trim();if(!t)return null;let n=this.sessions.get(t);if(n)return n;let o=this.snapshotPath(t);if(!existsSync(o))return null;try{let s=JSON.parse(readFileSync(o,"utf8"));return s.sessionId!==t||s.projectRoot!==this.projectRoot?null:(s.requiredMcpTools=Array.isArray(s.requiredMcpTools)?s.requiredMcpTools:[],this.sessions.set(t,s),s)}catch{return null}}completeExecution(e){let t=this.get(e);if(!t)return null;let n={...t,requiredMcpTools:[...t.requiredMcpTools]};return delete t.executionId,delete t.skillName,t.requiredMcpTools=[],t.lastActivityAt=Date.now(),this.persist(t),n}removeSession(e){let t=e.trim();t&&(this.sessions.delete(t),rmSync(this.snapshotPath(t),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(e){return join(this.sessionsDir,`${un(e)}.json`)}persist(e){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let t=this.snapshotPath(e.sessionId),n=`${t}.${process.pid}.tmp`;writeFileSync(n,JSON.stringify(e),{mode:384}),renameSync(n,t);}};var hn=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",fn=getLocalApiKey();function gn(r,e){return new Promise(t=>{let n=JSON.stringify(e),o=new URL(r,hn),s=request({hostname:o.hostname,port:o.port,path:o.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":fn,"Content-Length":Buffer.byteLength(n)},timeout:5e3},()=>t());s.on("error",()=>t()),s.write(n),s.end();})}async function Z(r,e=$(),t=true,n){let o;try{o=JSON.parse(r);}catch{return}let s=o.session_id?.trim();if(!s)return;let i=new I(e),a=i.get(s),c=n?.telemetry??new b,u=!n?.telemetry;try{try{await c.flushAndAggregate();}catch{}let l;try{l=openGlobalDevFlowDatabase();let m=l.listToolCallEventsBySession(s),p=a?.executionId;if(p){let f=m.filter(S=>S.executionId===p),d=f.length,h=f.filter(S=>S.isMcpTool).length,g=d-h,y=f.filter(S=>S.toolType==="subagent").length,w=Date.now(),E=l.getSkillExecution(p),v=E?.startedAt!=null?Math.max(0,w-E.startedAt):0,_=l.getExecutionObligationCompliance(p);try{l.updateSkillExecution(p,{status:"completed",finishedAt:w,totalToolCalls:d,mcpToolCalls:h,directToolCalls:g,subagentCount:y,totalDuration:v,mcpComplianceRate:_.rate}),await gn("/api/telemetry/skill-execution/complete",{executionId:p,finishedAt:w,status:"completed",summary:{totalToolCalls:d,mcpToolCalls:h,directToolCalls:g,subagentCount:y,totalDuration:v,mcpComplianceRate:_.rate}});}catch{}}t&&l.deleteHookReceipt(e),l.deleteContextReceipt(e,s);}catch{}finally{l?.close();}try{if(await L({projectRoot:e,sessionId:s,trigger:"session_end",memory:n?.memory}),n?.memory)await n.memory.closeSession(s);else {let m=new(await import('@devflow-tools/memory-engine')).MemoryGate(e);try{await m.closeSession(s);}finally{m.close();}}}catch{}try{await c.endSession(s),await c.flushAndAggregate();}catch{}}finally{u&&c.close(),i.removeSession(s);}}if(process.argv[1]?.endsWith("session-end")||process.argv[1]?.endsWith("session-end.js")){let r="";process.stdin.setEncoding("utf8"),process.stdin.on("data",e=>{r+=e;}),process.stdin.on("end",async()=>{await Z(r.trim()||process.argv[2]||""),process.exit(0);}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),1e4).unref();}var Tn=/(?:^|[.!?。!?]\s*)(?:(?:please\s+)?(?:remember|memorize)\b|(?:请记|(?:请)?记住))/iu,In=/\b(?:(?:do\s+not|don't|dont|never|not)(?:\s+need\s+to)?|no\s+need\s+to)\s+(?:please\s+)?(?:remember|memorize)\b|(?:不要|别|不用|无需|不必|不需要)(?:再)?(?:记住|记|记忆)/iu;function qe(r){let e=r.trim();return e&&Tn.test(e)&&!In.test(e)?e:null}function Ge(r){let e=createHash$1("sha256").update(r).digest("hex").slice(0,16),t=process.env.DEVFLOW_STATE_DIR||join$1(homedir$1(),".devflow","state",e);return join$1(t,"memory-intents.jsonl")}function kn(r){try{return readFileSync$1(r,"utf8").split(`
3
- `).filter(Boolean).flatMap(e=>{try{let t=JSON.parse(e);return typeof t.content=="string"&&typeof t.createdAt=="number"?[t]:[]}catch{return []}})}catch{return []}}function Je(r,e){let t=Ge(r);mkdirSync$1(dirname$1(t),{recursive:true}),appendFileSync(t,`${JSON.stringify(e)}
4
- `,{mode:384});}function We(r){let e=Ge(r),t=`${e}.claim-`,n=`${basename(e)}.claim-`,o=(()=>{try{return readdirSync$1(dirname$1(e)).filter(a=>a.startsWith(n)&&!a.includes(".tmp-")).sort()[0]}catch{return}})(),s=o?join$1(dirname$1(e),o):`${t}${Date.now()}-${process.pid}`;if(!o)try{renameSync$1(e,s);}catch{return null}let i=kn(s);if(i.length===0){try{unlinkSync$1(s);}catch{}return null}return {path:s,intents:i}}function He(r){if(r.intents.shift(),r.intents.length===0){try{unlinkSync$1(r.path);}catch{}return}let e=`${r.path}.tmp-${process.pid}`;writeFileSync$1(e,r.intents.map(t=>JSON.stringify(t)).join(`
1
+ import {createServer,createConnection}from'net';import {createHash,randomUUID as randomUUID$1}from'node:crypto';import {join,dirname}from'path';import {mkdirSync,lstatSync,openSync,writeFileSync,fstatSync,closeSync,readFileSync,unlinkSync,existsSync as existsSync$1,readdirSync,rmSync,renameSync as renameSync$1,appendFileSync}from'fs';import {getLocalApiKey,getDaemonSocketPath,getDaemonPidPath,formatSkillRuntimeOverlay,createSkillRuntimeOverlay,loadConfig,selectHostAdapter,getProjectStateDir,getDaemonRegistryPath,getProjectHash}from'@devflow-tools/sdk';import {request}from'http';import {homedir}from'os';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {fileURLToPath}from'url';import {spawn}from'child_process';import {mkdirSync as mkdirSync$1,existsSync,writeFileSync as writeFileSync$1,renameSync,unlinkSync as unlinkSync$1,readdirSync as readdirSync$1,readFileSync as readFileSync$1,rmSync as rmSync$1,openSync as openSync$1,closeSync as closeSync$1,statSync}from'node:fs';import {join as join$1,basename,resolve,relative,sep,dirname as dirname$1,isAbsolute}from'node:path';import {MemoryGate}from'@devflow-tools/memory-engine';import {randomUUID,createHash as createHash$1}from'crypto';import {homedir as homedir$1}from'node:os';var Ce=1e4,to=3e4,M=500;function oo(n,e,t){return new Promise(o=>{try{let r=new URL(n),i=request({hostname:r.hostname,port:r.port||80,path:r.pathname+r.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":t,"Content-Length":Buffer.byteLength(e)},timeout:5e3},s=>{let a=[];s.on("data",c=>a.push(c)),s.on("end",()=>{let c=Buffer.concat(a).toString();o(s.statusCode!=null&&s.statusCode>=200&&s.statusCode<300?c:null);});});i.on("error",()=>o(null)),i.on("timeout",()=>{i.destroy(),o(null);}),i.write(e),i.end();}catch{o(null);}})}var b=class{constructor(e){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=e?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=e?.apiKey??getLocalApiKey(),this.cacheDir=e?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=e?.legacyCacheDir??(e?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=e?.database??null,this.ownsDatabase=!e?.database;let t=e?.busyTimeoutMs??Number(process.env.DEVFLOW_TELEMETRY_DB_BUSY_TIMEOUT_MS);this.busyTimeoutMs=Number.isSafeInteger(t)&&t>=0?t:100;}async sendEvent(e){let t={...e,input:this.truncateInput(e.input)};return this.commitOrCache("tool_call",t)?(this.postHttp("/api/telemetry/tool-call",t),e.eventId):null}async sendExecutionStart(e,t,o,r,i=process.env.CLAUDE_PROJECT_DIR??process.cwd(),s=[]){let a={executionId:e,sessionId:t,skillName:o,startedAt:r,projectRoot:i,requiredMcpTools:s};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(e,t="completed",o=Date.now(),r){let i={executionId:e,status:t,finishedAt:o,metadata:r},s=this.commitOrCache("execution_complete",i);return s&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",i),s}async sendSessionStart(e,t,o){let r={id:e,projectRoot:t,startedAt:o,label:t.split("/").pop()??"unknown"},i=this.commitOrCache("session_start",r);return i&&this.postHttp("/api/telemetry/sessions",r),i}async completeEvent(e){let t=this.commitOrCache("complete_event",e);return t&&(this.postHttp("/api/telemetry/tool-call/output",e),this.scheduleMetricAggregation()),t}async endSession(e,t=Date.now()){let o={sessionId:e,finishedAt:t},r=this.commitOrCache("session_end",o);return r&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(e)}/close`,{finishedAt:t})),r}async flushCache(){try{let o=this.getDatabase(),r=o.listTelemetryFailures({unresolvedOnly:!0,limit:M}).reverse();for(let i of r)try{this.applyOperation(i.operation,i.payload),o.resolveTelemetryFailure(i.id);}catch{}o.trimTelemetryFailures(M);}catch{}let t=[...new Set([this.cacheDir,this.legacyCacheDir].filter(o=>!!o))].filter(o=>existsSync$1(o)).flatMap(o=>readdirSync(o).filter(r=>r.endsWith(".json")).map(r=>{let i=join(o,r);try{return {cacheFile:i,envelope:no(JSON.parse(readFileSync(i,"utf8")),r)}}catch{return null}})).filter(o=>o!==null).sort((o,r)=>o.envelope.timestamp-r.envelope.timestamp||Pe(o.envelope.operation)-Pe(r.envelope.operation));for(let{cacheFile:o,envelope:r}of t)try{let i=this.getDatabase();i.insertTelemetryFailure({id:r.id,operation:r.operation,payload:r.payload,error:r.failure,createdAt:r.timestamp}),this.applyOperation(r.operation,r.payload),i.resolveTelemetryFailure(r.id),unlinkSync(o);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:this.busyTimeoutMs}),this.database}commitOrCache(e,t){try{return this.applyOperation(e,t),!0}catch(o){return this.cacheOperation(e,t,o),false}}applyOperation(e,t){let o=this.getDatabase();switch(e){case "tool_call":this.ensureParentSession(o,t),o.insertToolCallEvent(t);return;case "execution_start":this.ensureParentSession(o,t),o.insertSkillExecution({executionId:t.executionId,sessionId:t.sessionId,skillName:t.skillName,startedAt:t.startedAt,status:"running",requiredMcpTools:t.requiredMcpTools});return;case "execution_complete":o.reconcileSkillExecution(t.executionId,t.status,t.finishedAt,t.metadata);return;case "session_start":o.ensureSession(t),o.insertRun({id:Oe(t.id),source:"hook",tool:"session",input:{projectRoot:t.projectRoot},status:"active",startedAt:t.startedAt,tokenUsed:0,metadata:{sessionId:t.id,projectRoot:t.projectRoot}});return;case "complete_event":if(!o.updateToolCallEvent(t.eventId,{output:t.output===void 0?void 0:JSON.stringify(t.output),error:t.error,duration:t.duration}))throw new Error(`Tool call event ${t.eventId} is not available for completion`);return;case "session_end":o.closeSession(t.sessionId,t.finishedAt),o.updateRun(Oe(t.sessionId),{status:"completed",finishedAt:t.finishedAt});return}}ensureParentSession(e,t){let o=typeof t.sessionId=="string"?t.sessionId.trim():"";if(!o)throw new Error("Canonical session ID is required for telemetry");let r=typeof t.projectRoot=="string"&&t.projectRoot.trim()?t.projectRoot:typeof t.input?.projectRoot=="string"&&t.input.projectRoot.trim()?t.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";e.ensureSession({id:o,projectRoot:r,label:r==="unknown"?void 0:r.split("/").pop(),startedAt:Number(t.startedAt??t.timestamp??Date.now())});}cacheOperation(e,t,o){let r=Date.now(),i={id:`failure:${r}:${Math.random().toString(36).slice(2,11)}`,operation:e,payload:t,failure:o instanceof Error?o.message:String(o),timestamp:r};try{let s=this.getDatabase();s.insertTelemetryFailure({id:i.id,operation:e,payload:t,error:i.failure,createdAt:r}),s.trimTelemetryFailures(M),this.scheduleRetry();return}catch{}try{existsSync$1(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let s=join(this.cacheDir,`${r}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(s,JSON.stringify(i,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let e=readdirSync(this.cacheDir).filter(t=>t.endsWith(".json")).sort();for(let t of e.slice(0,Math.max(0,e.length-M)))unlinkSync(join(this.cacheDir,t));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},to).unref());}scheduleMetricAggregation(){if(this.metricAggregationScheduled||process.env.DEVFLOW_HOOK_DEGRADED==="1")return;this.metricAggregationScheduled=true,setTimeout(()=>{this.metricAggregationScheduled=false;try{this.getDatabase().aggregatePendingToolMetrics(50)===50&&this.scheduleMetricAggregation();}catch{}},25).unref();}postHttp(e,t){oo(`${this.apiUrl}${e}`,JSON.stringify(t),this.apiKey);}truncateInput(e){let t=JSON.stringify(e);return t===void 0||t.length<=Ce?e:{_truncated:true,_original_size:t.length,_preview:`${t.substring(0,Ce)}...`}}};function Pe(n){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(n)}function Oe(n){return `hook-run:${n}`}function no(n,e){if(!n||typeof n!="object")throw new Error("Invalid telemetry cache envelope");let t=n;if(typeof t.operation=="string"&&t.payload!==void 0)return t;let o=t.type==="tool_call"?"tool_call":t.type==="execution_start"?"execution_start":null;if(!o)throw new Error("Unknown legacy telemetry cache operation");let r=t.payload??{},i=Number(r.timestamp??r.startedAt??Date.now());return {id:`legacy-cache:${e}`,operation:o,payload:r,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:i}}function C(n){return n??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function _(n){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(C(n))}var lo=2,uo=1;function po(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join(dirname(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function Ae(n=po()){let e=[join(n,"dist","command-registry.json"),join(n,"command-registry.json")];for(let t of e)try{if(!existsSync$1(t))continue;let o=JSON.parse(readFileSync(t,"utf8"));if(o.version!==lo&&o.version!==uo||!mo(o.commands)){console.error(`[devflow] command registry contract mismatch: ${t}`);continue}return o.commands}catch(o){console.error(`[devflow] command registry load failed: ${o.message}`);}return null}function mo(n){return !n||typeof n!="object"||Array.isArray(n)?false:Object.values(n).every(e=>{if(!e||typeof e!="object"||Array.isArray(e))return false;let t=e;return V(t.mcpTools)&&V(t.blockedNative)&&(t.source===void 0||t.source==="core"||t.source==="plugin")&&(t.requiresContext===void 0||typeof t.requiresContext=="boolean")&&(t.runtimePackages===void 0||V(t.runtimePackages))&&(t.evidenceMode===void 0||["static","runtime","mixed"].includes(t.evidenceMode))})}function V(n){return Array.isArray(n)&&n.every(e=>typeof e=="string"&&e.length>0)}function je(n){let e=Ae();return e?e[n]?.mcpTools??[]:[]}var X=join(homedir(),".devflow"),Q=join(X,"server-refs.json"),vo="http://127.0.0.1:13337/api/health",wo=600*1e3,ko=500;function Ro(){existsSync$1(X)||mkdirSync(X,{recursive:true});}function P(){try{if(existsSync$1(Q))return JSON.parse(readFileSync(Q,"utf-8"))}catch{}return {sessions:[],lastActivity:0}}function Fe(n){Ro(),writeFileSync(Q,JSON.stringify(n));}var A=class{constructor(e){this.process=null;this.idleTimer=null;this.onShutdown=null;this.projectRoot=e;}setOnShutdown(e){this.onShutdown=e;}addRef(e){let t=P();t.sessions.includes(e)||t.sessions.push(e),t.lastActivity=Date.now(),Fe(t),this.resetIdleTimer();}removeRef(e){let t=P();t.sessions=t.sessions.filter(o=>o!==e),t.lastActivity=Date.now(),Fe(t),t.sessions.length===0&&this.resetIdleTimer();}get activeSessions(){return P().sessions.length}async ensureRunning(){return await this.healthCheck()?true:(await this.startServer(),this.waitForReady())}get isRunning(){return this.process!==null&&!this.process.killed}async stop(){this.idleTimer&&clearTimeout(this.idleTimer),this.process&&(this.process.kill("SIGTERM"),await new Promise(e=>{let t=setTimeout(()=>{this.process&&!this.process.killed&&this.process.kill("SIGKILL"),e();},5e3);this.process?this.process.on("exit",()=>{clearTimeout(t),e();}):(clearTimeout(t),e());}),this.process=null);}async startServer(){let e=join(this.projectRoot,"apps","server","dist","main.js"),t=join(this.projectRoot,"node_modules","@devflow-tools","server","dist","main.js"),o=existsSync$1(e)?e:t;this.process=spawn("node",[o],{cwd:this.projectRoot,env:{...process.env,NODE_ENV:process.env.NODE_ENV||"development"},stdio:["ignore","pipe","pipe"]}),this.process.stdout?.on("data",r=>{}),this.process.stderr?.on("data",r=>{}),this.process.on("exit",r=>{this.process=null,this.onShutdown&&this.onShutdown();}),this.process.on("error",()=>{this.process=null;});}async waitForReady(){let e=Date.now()+3e4;for(;Date.now()<e;){if(await this.healthCheck())return true;await new Promise(t=>setTimeout(t,ko));}return false}healthCheck(){return new Promise(e=>{let t=new URL(vo),o=request({hostname:t.hostname,port:t.port,path:t.pathname,method:"GET",timeout:2e3},r=>{e(r.statusCode===200);});o.on("error",()=>e(false)),o.on("timeout",()=>{o.destroy(),e(false);}),o.end();})}resetIdleTimer(){this.idleTimer&&clearTimeout(this.idleTimer),!(P().sessions.length>0)&&(this.idleTimer=setTimeout(()=>{this.stop();},wo).unref());}};function Ne(n,e,t){try{let o=openGlobalDevFlowDatabase();try{o.insertEvent({kind:n,timestamp:Date.now(),duration:t,success:e.success!==!1,metadata:e});}finally{o.close();}}catch{}}var oe=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",Co=getLocalApiKey(),Po=8,Oo=6e4,$e=new Map;function Ao(n){let e=new URL(n,oe).pathname,t=$e.get(e);return t||(t={failures:0,lastFailure:0,openUntil:0,status:"closed"},$e.set(e,t)),t}function ne(n,e,t){if(e.status===t)return;let o=e.status;e.status=t,Ne("http_circuit_transition",{path:new URL(n,oe).pathname,previous:o,status:t,failures:e.failures,openUntil:e.openUntil});}function ee(n,e){e.failures++,e.lastFailure=Date.now(),e.failures>=Po&&(e.openUntil=Date.now()+Oo,ne(n,e,"open"));}function jo(n,e){let t=e.status!=="closed";e.failures=0,e.openUntil=0,t&&ne(n,e,"closed");}var te=join(homedir(),".devflow","errors");function Le(n,e){try{existsSync$1(te)||mkdirSync(te,{recursive:!0});let t=`${new Date().toISOString()} | ${n} | ${e?.message??String(e)}
2
+ `;appendFileSync(join(te,"http-errors.log"),t);}catch{}}function He(n,e,t={}){let o=t.timeout??5e3,r=t.maxRetries??2,i=t.circuitBreaker??true,s=Ao(n);if(i&&s.openUntil>Date.now())return Promise.resolve();i&&s.status==="open"&&ne(n,s,"half_open");let a=c=>new Promise(l=>{let u=JSON.stringify(e),p=new URL(n,oe),m=request({hostname:p.hostname,port:p.port,path:p.pathname+p.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":Co,"Content-Length":Buffer.byteLength(u)},timeout:o},f=>{if(f.resume(),f.statusCode&&f.statusCode>=400){let d=new Error(`HTTP ${f.statusCode}`);if(Le(n,d),c>0){let y=Math.min(1e3*Math.pow(2,r-c),8e3);setTimeout(()=>{a(c-1).then(l);},y);}else ee(n,s),l();return}jo(n,s),l();});m.on("error",f=>{if(Le(n,f),c>0){let d=Math.min(1e3*Math.pow(2,r-c),8e3);setTimeout(()=>{a(c-1).then(l);},d);}else ee(n,s),l();}),m.on("timeout",()=>{m.destroy(),c>0?a(c-1).then(l):(ee(n,s),l());}),m.write(u),m.end();});return a(r)}function j(n){if(!n||typeof n!="object"||Array.isArray(n))return {};let e=n,t={};for(let o of ["lastMcpCall","bypassCount"])if(o in e){let r=e[o];if(r==null)continue;t[o]=typeof r=="number"&&Number.isFinite(r)&&r>=0?r:0;}return t}function $o(n,e){return n.lastMcpCall===e.lastMcpCall&&n.bypassCount===e.bypassCount}function Lo(n){try{return existsSync$1(n)?j(JSON.parse(readFileSync(n,"utf-8"))):{}}catch{return {}}}function Uo(n,e){let t=e instanceof Error?e.message:String(e);console.error(`[devflow] Receipt ${n} skipped: ${t}`);}function We(n){let e=_(n);for(let t of ["receipt.json","receipt-lock.sqlite","receipt-lock.sqlite-shm","receipt-lock.sqlite-wal"])try{existsSync$1(join(e,t))&&unlinkSync(join(e,t));}catch{}}function Ho(n,e){if(n.getHookReceipt(e)){We(e);return}let t=join(_(e),"receipt.json");if(!existsSync$1(t))return;let o=Lo(t);n.updateHookReceipt(e,()=>o),We(e);}function Je(n,e,t){let o;try{o=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),Ho(o,n);let r={},i=o.updateHookReceipt(n,a=>(r=j(a),j(e(r)))),s=j(i);return {receipt:s,applied:!0,changed:!$o(r,s)}}catch(r){return Uo(t,r),{receipt:{},applied:false,changed:false}}finally{try{o?.close();}catch{}}}function F(n,e){return Je(n,()=>e,"write").applied}function se(n,e){return Je(n,e,"update")}var qe={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",Bash:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function Wo(n){switch(n){case "WebSearch":case "WebFetch":return 1;case "Agent":return 3;case "Grep":case "Glob":return 3;case "Bash":return 5;default:return 2}}var E=class{constructor(e,t,o){this.projectRoot=e;this.sessionId=t;this.executionId=o;}getPhase(){if(!this.sessionId||!this.executionId)return "idle";let e;try{e=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250});let t=e.getContextReceipt(this.projectRoot,this.sessionId,this.executionId);if(t&&t.expiresAt>Date.now())return "context_ready";t&&e.deleteContextReceipt(this.projectRoot,this.sessionId,this.executionId);}catch{}finally{try{e?.close();}catch{}}return "context_gathering"}evaluate(e,t){let o=this.getPhase();if(t||e==="Skill")return {permissionDecision:"allow"};if(o==="context_gathering"&&qe[e])return {permissionDecision:"deny",reason:`DevFlow context required. Call mcp__devflow__get_project_context, then retry ${e}.`};if(o==="context_ready")return {permissionDecision:"allow"};let r=qe[e];if(!r)return {permissionDecision:"allow"};let i=se(this.projectRoot,c=>({...c,bypassCount:(c.bypassCount??0)+1}));if(!i.applied)return {permissionDecision:"allow"};let s=i.receipt.bypassCount??0,a=Wo(e);return s>=a?{permissionDecision:"allow",additionalContext:`\u5DF2 ${s} \u6B21\u76F4\u63A5\u4F7F\u7528 ${e}\uFF0C\u5EFA\u8BAE\u7528 mcp__devflow__${r} \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`}:{permissionDecision:"allow"}}recordMcpCall(){F(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}};var Ko=10,Vo=5e3,Yo=3e4,Xo=new Int32Array(new SharedArrayBuffer(4)),Xe=0;function Qo(n){if(typeof n!="object"||n===null)return false;let e=n;return typeof e.rootPath=="string"&&e.rootPath.length>0&&typeof e.projectHash=="string"&&e.projectHash.length>0&&typeof e.pid=="number"&&Number.isInteger(e.pid)&&e.pid>0&&typeof e.startedAt=="number"&&Number.isFinite(e.startedAt)&&e.startedAt>=0}function Qe(n){if(!existsSync(n))return {entries:[],needsRepair:false};try{let e=JSON.parse(readFileSync$1(n,"utf8"));if(!Array.isArray(e))return {entries:[],needsRepair:!0};let t=e.filter(Qo);return {entries:t,needsRepair:t.length!==e.length}}catch{return {entries:[],needsRepair:true}}}function Ze(n,e){mkdirSync$1(dirname$1(n),{recursive:true});let t=`${n}.${process.pid}.${Date.now()}.${Xe++}.tmp`,o;try{o=openSync$1(t,"wx"),writeFileSync$1(o,JSON.stringify(e,null,2)),closeSync$1(o),o=void 0,renameSync(t,n);}finally{try{o!==void 0&&closeSync$1(o);}finally{rmSync$1(t,{force:true});}}}function et(n){try{return process.kill(n,0),!0}catch(e){return e.code!=="ESRCH"}}function tt(n){if(typeof n!="object"||n===null)return false;let e=n;return typeof e.pid=="number"&&Number.isInteger(e.pid)&&e.pid>0&&typeof e.createdAt=="number"&&Number.isFinite(e.createdAt)&&typeof e.token=="string"&&e.token.length>0}function Ge(n,e){try{return readFileSync$1(n,"utf8")!==e?!1:(rmSync$1(n),!0)}catch(t){return t.code==="ENOENT"}}function Zo(n){try{let e=readFileSync$1(n,"utf8"),t;try{let r=JSON.parse(e);tt(r)&&(t=r);}catch{}return t?et(t.pid)?!1:Ge(n,e):Date.now()-statSync(n).mtimeMs>Yo&&Ge(n,e)}catch(e){return e.code==="ENOENT"}}function en(n){mkdirSync$1(dirname$1(n),{recursive:true});let e=`${n}.lock`,t=Date.now()+Vo;for(;;){let o=`${process.pid}:${Date.now()}:${Xe++}`,r;try{return r=openSync$1(e,"wx"),writeFileSync$1(r,JSON.stringify({pid:process.pid,createdAt:Date.now(),token:o})),closeSync$1(r),r=void 0,{path:e,token:o}}catch(i){if(r!==void 0)try{closeSync$1(r);}finally{rmSync$1(e,{force:true});}if(i.code!=="EEXIST")throw i}if(!Zo(e)){if(Date.now()>=t)throw new Error(`Timed out acquiring daemon registry lock: ${e}`);Atomics.wait(Xo,0,0,Ko);}}}function tn(n){try{let e=readFileSync$1(n.path,"utf8"),t=JSON.parse(e);tt(t)&&t.token===n.token&&rmSync$1(n.path);}catch(e){if(e.code!=="ENOENT")throw e}}function ot(n){let e=getDaemonRegistryPath(),t=en(e);try{return n(e)}finally{tn(t);}}function nt(n,e){ot(t=>{let o=Qe(t).entries.filter(r=>r.rootPath!==n&&et(r.pid));o.push({rootPath:n,projectHash:getProjectHash(n),pid:e,startedAt:Date.now()}),Ze(t,o);});}function rt(n,e){ot(t=>{let o=Qe(t).entries.filter(r=>r.rootPath!==n||e!==void 0&&r.pid!==e);Ze(t,o);});}async function x(n){let e=!n.memory,t=n.memory??new MemoryGate(n.projectRoot),o=0,r=0;try{await t.forceWarmUp(),n.trigger==="session_end"&&(o=await t.releasePendingDistillLeases(n.sessionId)),r=t.getPendingEventCount();}finally{e&&t.close();}let i={pendingEvents:r,releasedLeases:o,trigger:n.trigger,sessionId:n.sessionId};try{let s=openGlobalDevFlowDatabase();try{let a=Date.now(),c=`distill:${a}:${Math.random().toString(36).slice(2,10)}`;s.recordMemoryDistillCheckpoint({id:c,projectRoot:n.projectRoot,sessionId:n.sessionId,trigger:n.trigger,pendingEvents:r,releasedLeases:o,createdAt:a}),s.insertEvent({kind:"memory_distill_requested",timestamp:a,success:!0,metadata:{...i,projectRoot:n.projectRoot}});}finally{s.close();}}catch{}return i}function it(n){if(n.pendingEvents===0)return "";let e=n.sessionId?`\uFF0CsessionId=${n.sessionId}`:"";return `Memory distill checkpoint: ${n.pendingEvents} \u4E2A\u4E8B\u4EF6\u5F85\u63D0\u70BC${e}\u3002\u4E0A\u4E0B\u6587\u538B\u7F29\u5B8C\u6210\u540E\uFF0C\u8C03\u7528 mcp__devflow__memory_request_distill\uFF1B\u6309\u8FD4\u56DE\u7684 nextPage \u5206\u9875\u8BFB\u53D6\u5168\u90E8\u4E8B\u4EF6\uFF0C\u63D0\u70BC observations\uFF0C\u518D\u8C03\u7528 mcp__devflow__memory_save_distilled \u5E76\u786E\u8BA4\u4FDD\u5B58\u7ED3\u679C\u3002`}function pn(n){return Buffer.from(n,"utf8").toString("base64url")}var I=class{constructor(e){this.projectRoot=e;this.sessions=new Map;this.sessionsDir=join(_(e),"hook-sessions");}registerSession(e){let t=e.trim();if(!t)throw new Error("session_id_required");let o=this.get(t);if(o)return o.lastActivityAt=Date.now(),this.persist(o),o;let r=Date.now(),i={sessionId:t,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:r,lastActivityAt:r};return this.sessions.set(t,i),this.persist(i),i}startExecution(e,t,o){let r=this.registerSession(e);return (!r.executionId||r.skillName!==t)&&(r.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),r.skillName=t,r.requiredMcpTools=[...new Set(o)],r.lastActivityAt=Date.now(),this.persist(r),r}get(e){let t=e.trim();if(!t)return null;let o=this.sessions.get(t);if(o)return o;let r=this.snapshotPath(t);if(!existsSync$1(r))return null;try{let i=JSON.parse(readFileSync(r,"utf8"));return i.sessionId!==t||i.projectRoot!==this.projectRoot?null:(i.requiredMcpTools=Array.isArray(i.requiredMcpTools)?i.requiredMcpTools:[],this.sessions.set(t,i),i)}catch{return null}}completeExecution(e){let t=this.get(e);if(!t)return null;let o={...t,requiredMcpTools:[...t.requiredMcpTools]};return delete t.executionId,delete t.skillName,t.requiredMcpTools=[],t.lastActivityAt=Date.now(),this.persist(t),o}removeSession(e){let t=e.trim();t&&(this.sessions.delete(t),rmSync(this.snapshotPath(t),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(e){return join(this.sessionsDir,`${pn(e)}.json`)}persist(e){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let t=this.snapshotPath(e.sessionId),o=`${t}.${process.pid}.tmp`;writeFileSync(o,JSON.stringify(e),{mode:384}),renameSync$1(o,t);}};function lt(n,e){let t=mt(n);mkdirSync$1(t,{recursive:true,mode:448});let o=join$1(t,`${e.receiptId}.json`);if(existsSync(o))return e.receiptId;let r=`${o}.${process.pid}.tmp`;writeFileSync$1(r,JSON.stringify(e),{mode:384});try{renameSync(r,o);}catch(i){try{unlinkSync$1(r);}catch{}if(!existsSync(o))throw i}return e.receiptId}function ut(n){let e=mt(n);mkdirSync$1(e,{recursive:true,mode:448});let t=readdirSync$1(e).filter(s=>s.endsWith(".json")||/\.json\.claim-\d+$/u.test(s)).sort()[0];if(!t)return null;let o=join$1(e,t),r=/\.json\.claim-\d+$/u.test(t),i=r?o:`${o}.claim-${process.pid}`;if(!r)try{renameSync(o,i);}catch{return null}try{let s=JSON.parse(readFileSync$1(i,"utf8"));if(typeof s.receiptId!="string"||typeof s.inputJson!="string"||typeof s.sessionId!="string"||typeof s.cleanupSharedState!="boolean")throw unlinkSync$1(i),new Error("session_finalize_spool_payload_invalid");return {receiptId:basename(t).replace(/\.json(?:\.claim-\d+)?$/u,""),payload:s,path:i}}catch(s){try{unlinkSync$1(i);}catch{}throw s}}function dt(n){unlinkSync$1(n.path);}function pt(n){try{renameSync(n.path,n.path.replace(/\.claim-\d+$/u,""));}catch{}}function mt(n){return join$1(_(n),"session-finalize-pending")}var wn=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",kn=getLocalApiKey();function H(n,e=C(),t=true,o,r=true){let i;try{i=JSON.parse(n);}catch{return null}let s=i.session_id?.trim();if(!s)return null;let a=`session-close:${createHash("sha256").update(`${e}\0${s}`).digest("hex").slice(0,24)}`,c={inputJson:n,sessionId:s,cleanupSharedState:t,executionId:o},l;try{l=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:50});let u=l.requestSessionClosure({receiptId:a,projectRoot:e,sessionId:s,payload:c}),p=l.getWorkByIdempotencyKey(a);return {status:u.state==="closed"||u.state==="closed_with_pending_work"?"completed":"accepted",receiptId:a,workState:p?.state??"pending",closureState:u.state,pendingWorkCount:u.pendingWorkCount}}catch(u){if(!r)throw u;return lt(e,{receiptId:a,...c}),{status:"accepted",receiptId:a,workState:"spooled",closureState:"closing",pendingWorkCount:1}}finally{l?.close();}}function ht(n,e){return new Promise(t=>{let o=JSON.stringify(e),r=new URL(n,wn),i=request({hostname:r.hostname,port:r.port,path:r.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":kn,"Content-Length":Buffer.byteLength(o)},timeout:5e3},()=>t());i.on("error",()=>t()),i.write(o),i.end();})}async function gt(n,e=C(),t=true,o){let r;try{r=JSON.parse(n);}catch{return}let i=r.session_id?.trim();if(!i)return;let s=new I(e),a=s.get(i),c=o?.telemetry??new b,l=!o?.telemetry;try{try{await c.flushAndAggregate();}catch{}let u;try{u=openGlobalDevFlowDatabase();let p=a?.executionId;if(p){let d=Date.now(),y=u.listMemoryTurns(e,i,100);try{u.reconcileSkillExecution(p,"completed",d,{memoryReceiptIds:y.flatMap(g=>g.receiptId?[g.receiptId]:[]),missingMemoryDecisionCount:y.filter(g=>g.status==="pending").length});let h=u.getSkillExecution(p);await ht("/api/telemetry/skill-execution/complete",{executionId:p,finishedAt:d,status:"completed",summary:{totalToolCalls:h?.totalToolCalls??0,mcpToolCalls:h?.mcpToolCalls??0,directToolCalls:h?.directToolCalls??0,subagentCount:h?.subagentCount??0,totalDuration:h?.totalDuration??0,mcpComplianceRate:h?.mcpComplianceRate??0,missedTools:h?.missedMcpTools??[],actualFailureCount:h?.failedToolCalls??0,fallbackCount:h?.fallbackCount??0,fallbackReasons:h?.fallbackReasons??[]}});}catch{}}let m=u.listMemoryTurns(e,i,100),f=u.reconcileRunningSkillExecutionsForSession(i,Date.now(),{memoryReceiptIds:m.flatMap(d=>d.receiptId?[d.receiptId]:[]),missingMemoryDecisionCount:m.filter(d=>d.status==="pending").length,closureReason:"session_finalize"});for(let d of f)await ht("/api/telemetry/skill-execution/complete",{executionId:d.executionId,finishedAt:d.finishedAt,status:d.status,summary:{totalToolCalls:d.totalToolCalls??0,mcpToolCalls:d.mcpToolCalls??0,directToolCalls:d.directToolCalls??0,subagentCount:d.subagentCount??0,totalDuration:d.totalDuration??0,mcpComplianceRate:d.mcpComplianceRate??0,missedTools:d.missedMcpTools??[],actualFailureCount:d.failedToolCalls??0,fallbackCount:d.fallbackCount??0,fallbackReasons:d.fallbackReasons??[]}});if(t){u.deleteHookReceipt(e);let d=_(e),y=createHash("sha256").update(e).digest("hex").slice(0,12);for(let h of [`session-id-${y}`,"current-execution-id","current-skill.json","event-map.json"])rmSync$1(join$1(d,h),{force:!0});}u.deleteContextReceipt(e,i);}catch{}finally{u?.close();}try{if(await x({projectRoot:e,sessionId:i,trigger:"session_end",memory:o?.memory}),o?.memory)await o.memory.closeSession(i);else {let p=new(await import('@devflow-tools/memory-engine')).MemoryGate(e);try{await p.closeSession(i);}finally{p.close();}}}catch{}try{await c.endSession(i),await c.flushAndAggregate();}catch{}}finally{l&&c.close(),s.removeSession(i);}}if(process.argv[1]?.endsWith("session-end")||process.argv[1]?.endsWith("session-end.js")){let n=console.log.bind(console);console.log=console.error.bind(console),console.info=console.error.bind(console);let e="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{e+=t;}),process.stdin.on("end",async()=>{let t=H(e.trim()||process.argv[2]||"");t&&n(JSON.stringify(t)),process.exit(0);}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),1e4).unref();}var Dn=/^\/(?:devflow:)?[\w-]+\b\s*/iu,Mn=/\b(?:(?:do\s+not|don't|dont|never|not)(?:\s+need\s+to)?|no\s+need\s+to)\s+(?:please\s+)?(?:remember|memorize)\b|(?:不要|别|不用|无需|不必|不需要)(?:再)?(?:记住|记|记忆)/iu,Cn=/(?:^|[.!?]\s*)(?:(?:please|kindly)\s+)?(?:remember|memorize|save\s+this|store\s+this|note\s+that)(?:\s+(?:that|to))?[\s:,-]*(.+)$/iu,Pn=/(?:^|[。!?;,,]\s*)(?:(?:请|麻烦|务必|一定要|帮我|帮忙|需要)\s*)?(?:记住|记一下|记下来|记|记录一下|记录下来|保存一下|保存下来)(?:这(?:件事|一点|个信息|些内容))?[\s::,,]*(.+)$/iu;function W(n){let e=n.trim().replace(Dn,"").trim();return !e||Mn.test(e)?null:(Pn.exec(e)??Cn.exec(e))?.[1]?.trim().replace(/^[::,,\s]+/u,"")||null}function On(n){let e=createHash("sha256").update(n).digest("hex").slice(0,16),t=process.env.DEVFLOW_STATE_DIR||join$1(homedir$1(),".devflow","state",e);return join$1(t,"memory-intents.jsonl")}function An(n){try{return readFileSync$1(n,"utf8").split(`
3
+ `).filter(Boolean).flatMap(e=>{try{let t=JSON.parse(e);return typeof t.content=="string"&&typeof t.createdAt=="number"?[t]:[]}catch{return []}})}catch{return []}}function wt(n){let e=On(n),t=`${e}.claim-`,o=`${basename(e)}.claim-`,r=(()=>{try{return readdirSync$1(dirname$1(e)).filter(a=>a.startsWith(o)&&!a.includes(".tmp-")).sort()[0]}catch{return}})(),i=r?join$1(dirname$1(e),r):`${t}${Date.now()}-${process.pid}`;if(!r)try{renameSync(e,i);}catch{return null}let s=An(i);if(s.length===0){try{unlinkSync$1(i);}catch{}return null}return {path:i,intents:s}}function ue(n){if(n.intents.shift(),n.intents.length===0){try{unlinkSync$1(n.path);}catch{}return}let e=`${n.path}.tmp-${process.pid}`;writeFileSync$1(e,n.intents.map(t=>JSON.stringify(t)).join(`
5
4
  `)+`
6
- `,{mode:384}),renameSync$1(e,r.path);}function U(r){let e=r.trim().replace(/^\//,"");if(!e.startsWith("devflow:"))return null;let t=e.slice(8).replace(/^devflow-/,"");return /^[a-z0-9][a-z0-9-]*$/i.test(t)?`devflow:${t.toLowerCase()}`:null}function Be(r){let e=r.trimStart().match(/^\/(devflow:[a-z0-9][a-z0-9-]*)\b/i);if(!e)return null;let t=U(e[1]);return t?{rawName:e[1],skillName:t}:null}function D(r,e){return e?`evt_tool_${createHash("sha256").update(`${r}\0${e}`).digest("hex").slice(0,24)}`:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`}var Wn=600*1e3,Hn=2e3,Bn=250,Vn=2e3,zn=1e3,Ve=1024*1024,Kn=2*1024*1024,Yn=6e4,Xn=100,Qn={Agent:"get_project_context",Bash:"get_project_context",Glob:"get_project_context",Grep:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function Zn(r){switch(r){case "Read":case "Glob":return "file_read";case "Write":case "Edit":case "NotebookEdit":return "file_write";case "Bash":return "bash_command";case "Agent":return "subagent";case "Skill":return "skill_invoke";default:return "tool_use"}}function oe(r){return typeof r=="string"&&r.trim().length>0}function eo(r,e,t=process.env,n){if(!r.startsWith("mcp__devflow__"))return e;let o=e!==null&&typeof e=="object"&&!Array.isArray(e)?e:{};if(oe(o.projectRoot))return o;let s=oe(t.CLAUDE_PROJECT_DIR)?t.CLAUDE_PROJECT_DIR:n;return oe(s)&&(o.projectRoot=s),o}var re=class{constructor(e,t){this.socketIdentity=null;this.pidIdentity=null;this.server=null;this.activeSessionIds=new Set;this.eventMap=[];this.idleTimer=null;this.telemetryFlushTimer=null;this.successfulTelemetryOperations=0;this.shutdownPromise=null;this.explicitMemoryGate=null;this.memoryWarmup=null;this.explicitIntentFlush=null;this.projectRoot=e,this.socketPath=getDaemonSocketPath(e),this.pidPath=getDaemonPidPath(e),this.telemetry=new b,this.runtimeStore=new I(this.projectRoot),this.serverManager=new P(e),t&&this.handleSessionStart(t);}async start(){mkdirSync(dirname(this.socketPath),{recursive:true,mode:448}),await this.cleanStaleStartupResources(),this.server=createServer(e=>this.handleConnection(e));try{await this.listen(),this.writePidFile(),Fe(this.projectRoot,process.pid);}catch(e){throw await this.closeServer(),this.cleanupOwnedResources(),e}this.server.on("error",e=>{console.error("[devflow-daemon] Server error:",e.message);}),console.error("[devflow-daemon] Listening on",this.socketPath),await this.telemetry.flushAndAggregate(),this.telemetryFlushTimer=setInterval(()=>{this.flushTelemetry();},Yn),this.telemetryFlushTimer.unref(),this.resetIdleTimer();}handleConnection(e){let t="",n=false,o=false,s=0,i=Promise.resolve();e.setEncoding("utf8");let a=()=>{o||(o=true,clearTimeout(c),e.destroy());},c=setTimeout(()=>{a();},Hn).unref(),u=async l=>{if(!o){o=true,clearTimeout(c);try{await this.writeFrame(e,l);}catch{}finally{e.destroy();}}};e.on("data",l=>{if(o)return;t+=l;let m=t.split(`
7
- `);if(t=m.pop()??"",Buffer.byteLength(t,"utf8")>Ve){a();return}if(m.length===0)return;let p=0;for(let f of m){let d=Buffer.byteLength(f,"utf8");if(d>Ve){a();return}p+=d+1;}if(s+p>Kn){a();return}s+=p,i=i.then(async()=>{for(let f of m){if(o)return;if(!n){let d;try{d=JSON.parse(f);}catch{await u({error:"handshake_invalid"});return}if(d?.type!=="hello"||typeof d.rootPath!="string"){await u({error:"handshake_invalid"});return}if(d.rootPath!==this.projectRoot){await u({error:"handshake_root_mismatch",expected:this.projectRoot});return}n=true,clearTimeout(c),this.resetIdleTimer();continue}if(f.trim())try{let d=JSON.parse(f),h=await this.handleRequest(d);h!==null&&!o&&await this.writeFrame(e,h);}catch(d){o||await this.writeFrame(e,{error:d.message});}}}).catch(()=>{a();}).finally(()=>{s-=p;});}),e.on("close",()=>clearTimeout(c)),e.on("error",()=>{});}writeFrame(e,t){return new Promise((n,o)=>{if(e.destroyed||!e.writable){o(new Error("Socket is not writable"));return}let s=false,i=true,a=false,c=setTimeout(()=>{e.destroy(),l(new Error("Timed out writing daemon response"));},zn),u=()=>{clearTimeout(c),e.off("close",m),e.off("error",p),e.off("drain",f);},l=h=>{a||!h&&(!s||!i)||(a=true,u(),h?o(h):n());},m=()=>l(new Error("Socket closed during write")),p=h=>l(h),f=()=>{i=true,l();};e.once("close",m),e.once("error",p),e.write(`${JSON.stringify(t)}
8
- `,h=>{if(h){l(h);return}s=true,l();})||(i=false,e.once("drain",f));})}async cleanStaleStartupResources(){let e=this.getPathIdentity(this.socketPath);if(e){if(await this.isSocketAcceptingConnections())throw new Error(`Daemon socket already active for ${this.projectRoot}`);this.removePathIfOwned(this.socketPath,e,"stale socket");}let t=this.getPathIdentity(this.pidPath);t&&this.removePathIfOwned(this.pidPath,t,"stale PID file");}getPathIdentity(e){try{let t=lstatSync(e);return {dev:t.dev,ino:t.ino}}catch(t){if(t.code==="ENOENT")return null;throw t}}pathMatchesIdentity(e,t){if(!t)return false;try{let n=this.getPathIdentity(e);return n?.dev===t.dev&&n.ino===t.ino}catch(n){return console.error("[devflow-daemon] Failed to verify path ownership:",n.message),false}}writePidFile(){let e=openSync(this.pidPath,"wx",384);try{writeFileSync(e,`${process.pid}
9
- `);let t=fstatSync(e);this.pidIdentity={dev:t.dev,ino:t.ino};}finally{closeSync(e);}}removePathIfOwned(e,t,n,o){try{return !this.pathMatchesIdentity(e,t)||o!==void 0&&readFileSync(e,"utf8")!==o||!this.pathMatchesIdentity(e,t)?!1:(unlinkSync(e),!0)}catch(s){return s.code!=="ENOENT"&&console.error(`[devflow-daemon] Failed to remove ${n}:`,s.message),false}}isSocketAcceptingConnections(){return new Promise((e,t)=>{let n=createConnection(this.socketPath),o=setTimeout(()=>{n.destroy(),t(new Error(`Timed out probing daemon socket ${this.socketPath}`));},Bn),s=i=>{clearTimeout(o),n.destroy(),e(i);};n.once("connect",()=>s(true)),n.once("error",i=>{let a=i.code;a==="ENOENT"||a==="ECONNREFUSED"?s(false):(clearTimeout(o),t(i));});})}listen(){return new Promise((e,t)=>{let n=this.server;if(!n){t(new Error("Daemon server is not initialized"));return}let o=i=>{n.off("listening",s),t(i);},s=()=>{n.off("error",o);try{let i=this.getPathIdentity(this.socketPath);if(!i)throw new Error(`Daemon socket missing after listen: ${this.socketPath}`);this.socketIdentity=i,e();}catch(i){t(i);}};n.once("error",o),n.once("listening",s),n.listen(this.socketPath);})}resetIdleTimer(){this.idleTimer&&clearTimeout(this.idleTimer),this.idleTimer=null,!(this.activeSessionIds.size>0)&&(this.idleTimer=setTimeout(()=>{console.error("[devflow-daemon] Idle timeout, exiting"),this.shutdown();},Wn).unref());}async handleRequest(e){switch(e.type){case "pre-tool-use":return this.handlePreToolUse(e.input);case "post-tool-use":return this.handlePostToolUse(e.input);case "post-tool-use-failure":return this.handlePostToolUseFailure(e.input);case "stop":return await this.handleStop(e.input??""),{status:"ok"};case "memory-snapshot":return this.handleMemorySnapshot();case "session-start":return this.handleSessionStart(e.input??"");case "session-end":return await this.handleSessionEnd(e.input??""),{status:"ok"};case "user-prompt-submit":return this.handleUserPromptSubmit(e.input);case "pre-compact":return this.handlePreCompact(e.input);default:return {error:"unknown request type"}}}async handlePreToolUse(e){try{if(!e||e.trim()==="")return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}};let t=JSON.parse(e);t.tool_input=eo(t.tool_name,t.tool_input,process.env,this.projectRoot);let n=this.trackSession(t.session_id);if(t.tool_name==="Skill"){let g=t.tool_input?.skill,y=g?U(g):null;y&&this.startSkillExecution(n,y);}let o=this.runtimeStore.get(n);t.tool_name.startsWith("mcp__devflow__")&&(t.tool_input._devflow_session_id=n,t.tool_input._devflow_execution_id=o?.executionId??n,t.tool_use_id&&(t.tool_input._devflow_tool_use_id=t.tool_use_id));let s=t.tool_name.startsWith("mcp__"),i=s?t.tool_name.replace(/^mcp__[^_]+__/,""):void 0,a=new k(this.projectRoot,n,o?.executionId),c=a.getPhase(),u=a.evaluate(t.tool_name,s),l;u.permissionDecision==="deny"?l={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:u.reason??"MCP context required"}}:u.additionalContext?l={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:u.additionalContext}}:l={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}},t.tool_name.startsWith("mcp__devflow__")&&(l.hookSpecificOutput.updatedInput=t.tool_input);let m={...t.tool_input};delete m._devflow_session_id,delete m._devflow_execution_id,delete m._devflow_tool_use_id;let p=o?.executionId??n,d={eventId:D(n,t.tool_use_id),executionId:p,sessionId:n,projectRoot:this.projectRoot,toolUseId:t.tool_use_id,timestamp:Date.now(),toolName:t.tool_name,toolType:s?"mcp":t.tool_name==="Agent"?"subagent":"direct",isMcpTool:s,mcpToolName:i,mcpEnforced:s&&c==="context_gathering",mcpFallback:!s&&c==="context_gathering",kind:Zn(t.tool_name),input:m,duration:0,blocked:l&&l.hookSpecificOutput?.permissionDecision==="deny"},h=await this.telemetry.sendEvent({...d,blockReason:d.blocked?"DEVFLOW_CONTEXT_REQUIRED":void 0,error:d.blocked?"DEVFLOW_CONTEXT_REQUIRED":void 0});return h&&!d.blocked&&(this.eventMap.push({toolName:t.tool_name,toolUseId:t.tool_use_id,eventId:h,sessionId:n,timestamp:d.timestamp}),this.noteTelemetryOperation()),h&&d.blocked&&await this.telemetry.completeEvent({eventId:h,sessionId:n,error:"DEVFLOW_CONTEXT_REQUIRED",duration:Date.now()-d.timestamp,completedAt:Date.now()}),l}catch{return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}}}trackSession(e){let t=e?.trim()||(this.activeSessionIds.size===1?[...this.activeSessionIds][0]:"");if(!t)throw new Error("session_id_required");return this.registerSession(t),t}registerSession(e){this.activeSessionIds.has(e)||(this.activeSessionIds.size===0&&A(this.projectRoot,{bypassCount:0}),this.activeSessionIds.add(e),this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null),this.serverManager.addRef(e),this.telemetry.sendSessionStart(e,this.projectRoot,Date.now()).catch(()=>{})),this.runtimeStore.registerSession(e);}startSkillExecution(e,t){let n=this.runtimeStore.get(e)?.executionId,o=this.runtimeStore.startExecution(e,t,he(t));o.executionId&&o.executionId!==n&&this.telemetry.sendExecutionStart(o.executionId,e,t,o.lastActivityAt,this.projectRoot,o.requiredMcpTools).catch(()=>{});}async handleSessionStart(e){try{let t=JSON.parse(e);if(typeof t.session_id!="string"||!t.session_id.trim())return {error:"session_id_required"};let n=t.session_id.trim();this.registerSession(n);let o=await this.getMemoryGate();await o.ensureSession(n,this.projectRoot,"Claude Code session"),await o.releasePendingDistillLeases(n);let s=o.getPendingEventCount();return {status:"ok",...s>0?{additionalContext:Q({pendingEvents:s,releasedLeases:0,trigger:"session_end"})}:{}}}catch{return {error:"session_start_invalid"}}}popEventFromMap(e,t,n){let o=this.eventMap.filter(i=>i.toolName===e&&i.sessionId===t);if(o.length===0)return null;let s=n?o.find(i=>i.toolUseId===n):o.sort((i,a)=>i.timestamp-a.timestamp)[0];return s?(this.eventMap=this.eventMap.filter(i=>i.eventId!==s.eventId),s):null}enforcePostToolUse(e){if(!Qn[e])return null;let t=Math.floor(Date.now()/1e3),n=X(this.projectRoot,i=>t-(i.lastMcpCall??0)<30?i:{...i,bypassCount:(i.bypassCount??0)+1});if(!n.applied||!n.changed)return null;let o=n.receipt.bypassCount??0;if(o===1)return null;let s;switch(e){case "Agent":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u4F7F\u7528\u4E86 Agent \u5B50\u4EE3\u7406\u6267\u884C\u641C\u7D22\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Bash":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Bash \u547D\u4EE4\u641C\u7D22\u4EE3\u7801\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Glob":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Glob \u5339\u914D\u6587\u4EF6\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Grep":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Grep \u641C\u7D22\u4EE3\u7801\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "WebSearch":case "WebFetch":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86\u7F51\u7EDC\u641C\u7D22\u3002\u4E0B\u6B21\u8BF7\u7528 get_knowledge MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;default:s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u8BF7\u4F7F\u7528 DevFlow MCP \u5DE5\u5177\u66FF\u4EE3\u76F4\u63A5\u8C03\u7528\u3002`;}return o>=3?s=`${s} \u5DF2\u8FBE\u5230\u6700\u5927\u8FDD\u89C4\u6B21\u6570\uFF0C\u540E\u7EED\u7ED5\u8FC7\u5C06\u88AB\u786C\u963B\u6B62\u3002`:o>=2?s=`${s} \u4E0B\u6B21\u8FDD\u89C4\u5C06\u88AB\u963B\u6B62\u3002`:s=`${s} \u8BF7\u7ACB\u5373\u7EA0\u6B63\u3002`,s}async handlePostToolUse(e){if(!e)return null;let t;try{t=JSON.parse(e);}catch{return null}let n=t.tool_name||"",o=t.tool_input||{},s=t.tool_response,i=this.trackSession(t.session_id);if(n==="Skill"){let v=o?.skill,_=v?U(v):null;_&&(this.startSkillExecution(i,_),A(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0}));}n.startsWith("mcp__")&&new k(this.projectRoot).recordMcpCall();let a=this.popEventFromMap(n,i,t.tool_use_id);if(a){let v=typeof s=="string"?s:JSON.stringify(s),_=v.length>5e3?{_truncated:true,_originalSize:v.length,text:v.slice(0,5e3)}:s,S=Date.now()-a.timestamp;await this.telemetry.completeEvent({eventId:a.eventId,sessionId:i,output:_,duration:S,completedAt:Date.now()})&&this.noteTelemetryOperation();}let c=typeof s=="object"&&s!==null?s:null,u=c?.exitCode,l=c?.stderr,m=/^\s*(ls|cat|pwd|cd|echo|head|tail|wc|which|whoami|date|env|printenv|id|hostname|uname)\b/,p=o.command||"",f=JSON.stringify(s),d=Buffer.byteLength(f,"utf8"),h=this.countResults(s),g,y,w=true;if(n.startsWith("mcp__"))g="mcp_call",y={mcpTool:n.replace(/^mcp__[^_]+__/,""),query:o.query??null,resultCount:h,resultSizeBytes:d};else if(n==="Read"||n==="Write"||n==="Edit")g=n==="Read"?"file_read":"file_write",y={filePath:o.file_path??null,fileContentSize:d};else if(n==="Bash"){let v=m.test(p),_=u!==void 0&&u!==0;w=!v||_,g=_?"bash_error":"bash_command",y={command:p||n,exitCode:u??null,stderr:l??null};}else n==="Agent"?(g="subagent",y={subagentType:o.subagent_type??null,description:typeof o.description=="string"?o.description.slice(0,200):null}):n==="WebSearch"||n==="WebFetch"?(g=n==="WebSearch"?"web_search":"web_fetch",y={query:typeof o.query=="string"?o.query.slice(0,200):null}):(w=false,g="tool_use",y={});if(w){let v={id:t.tool_use_id?`memory:${D(i,t.tool_use_id)}`:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:i,tool:n,kind:g,payload:y,command:p||void 0,exitCode:u,stderr:l??void 0,durationMs:0,createdAt:Date.now()};this.postMemoryEvent(v);}let E=this.enforcePostToolUse(n);return E?(console.error("[devflow-daemon] Enforcement:",E),{hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:JSON.stringify(E)}}):null}async handlePostToolUseFailure(e){let t;try{t=JSON.parse(e);}catch{return null}let n=this.trackSession(t.session_id),o=t.tool_name??"unknown",s=t.error??`${o} failed`,i=this.popEventFromMap(o,n,t.tool_use_id);if(i)await this.telemetry.completeEvent({eventId:i.eventId,sessionId:n,error:s,duration:Date.now()-i.timestamp,completedAt:Date.now()})&&this.noteTelemetryOperation();else {let a=D(n,t.tool_use_id);await this.telemetry.sendEvent({eventId:a,executionId:this.runtimeStore.get(n)?.executionId??n,sessionId:n,projectRoot:this.projectRoot,toolUseId:t.tool_use_id,timestamp:Date.now(),toolName:o,toolType:o.startsWith("mcp__")?"mcp":"direct",isMcpTool:o.startsWith("mcp__"),mcpToolName:o.startsWith("mcp__")?o.replace(/^mcp__[^_]+__/,""):void 0,mcpEnforced:false,mcpFallback:false,kind:"error",input:t.tool_input??{},duration:0,error:s,blocked:false});}return this.postMemoryEvent({id:t.tool_use_id?`memory:${D(n,t.tool_use_id)}`:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:n,tool:o,command:typeof t.tool_input?.command=="string"?t.tool_input.command:o,exitCode:-1,stderr:s,durationMs:i?Date.now()-i.timestamp:0,createdAt:Date.now(),kind:o==="Bash"?"bash_error":"tool_use",payload:{command:t.tool_input?.command??o,exitCode:-1,stderr:s}}),null}countResults(e){if(!e)return 0;let t=e;if(typeof e=="string")try{t=JSON.parse(e);}catch{return 1}let n=t,o=["files","results","data","result","memories","nodes","chunks","findings","symbols"],s=Array.isArray(n.data)?n.data:n.structuredContent??n;if(Array.isArray(s))return s.length;if(typeof s=="object"&&s!==null){let i=s,a=0,c=false;for(let u of o)Array.isArray(i[u])&&(c=true,a+=i[u].length);for(let[,u]of Object.entries(i))u&&typeof u=="object"&&!Array.isArray(u)&&(a+=this.countResults(u));return a>0?a:c?0:Object.keys(i).length>0?1:0}return 0}async postMemoryEvent(e){we(`/api/memory/session-events?rootPath=${encodeURIComponent(this.projectRoot)}`,e);try{await(await this.getMemoryGate()).recordEvent(e);}catch(t){console.error("[devflow-daemon] Local memory event write failed:",t.message);}}async getMemoryGate(){let e=this.explicitMemoryGate??=new MemoryGate(this.projectRoot);return this.memoryWarmup??=e.forceWarmUp().catch(t=>{throw this.memoryWarmup=null,t}),await this.memoryWarmup,e}async handleUserPromptSubmit(e){try{let t=JSON.parse(e),n=typeof t.prompt=="string"?t.prompt:"",o=typeof t.session_id=="string"&&t.session_id.trim()?this.trackSession(t.session_id):null,s=Be(n);if(o&&s&&this.startSkillExecution(o,s.skillName),n.trim()){let i=qe(n);i&&Je(this.projectRoot,{content:i,sessionId:o??void 0,createdAt:Date.now()}),o&&await(await this.getMemoryGate()).recordUserMessage(n.slice(0,2e3),o,n.slice(0,200));}}catch{}return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}}}async handlePreCompact(e){let t;try{let i=JSON.parse(e);typeof i.session_id=="string"&&i.session_id.trim()&&(t=i.session_id.trim());}catch{}await this.flushTelemetry(),this.startExplicitIntentFlush(),await this.explicitIntentFlush;let n=await this.getMemoryGate(),o=await L({projectRoot:this.projectRoot,sessionId:t,trigger:"pre_compact",memory:n}),s=Q(o);return {hookSpecificOutput:{hookEventName:"PreCompact",...s?{additionalContext:s}:{}}}}noteTelemetryOperation(){this.successfulTelemetryOperations++,this.successfulTelemetryOperations>=Xn&&this.flushTelemetry();}async flushTelemetry(){this.successfulTelemetryOperations=0,await this.telemetry.flushAndAggregate();}startExplicitIntentFlush(){this.explicitIntentFlush||(this.explicitIntentFlush=this.flushExplicitMemoryIntents().finally(()=>{this.explicitIntentFlush=null;}));}async handleStop(e){let t=null;try{let n=JSON.parse(e);typeof n.session_id=="string"&&n.session_id.trim()&&(t=n.session_id.trim());}catch{}if(t??=this.activeSessionIds.size===1?[...this.activeSessionIds][0]:null,t){let n=this.runtimeStore.completeExecution(t);n?.executionId&&(await this.telemetry.sendExecutionComplete(n.executionId),this.deleteContextReceipt(t,n.executionId));}this.startExplicitIntentFlush(),await this.explicitIntentFlush;}async flushExplicitMemoryIntents(){this.flushTelemetry();let e=We(this.projectRoot);if(e)try{let t=this.explicitMemoryGate??=new MemoryGate(this.projectRoot);for(await t.forceWarmUp();e.intents.length>0;){let n=e.intents[0];await t.saveExplicitMemoryIntent(n.content,n.sessionId),He(e);}}catch(t){console.error("[devflow-daemon] Explicit memory intent save failed:",t.message);}}deleteContextReceipt(e,t){let n;try{n=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),n.deleteContextReceipt(this.projectRoot,e,t);}catch{}finally{n?.close();}}async handleMemorySnapshot(){let e=loadConfig(this.projectRoot).sessionStart?.injectMemories??true;if(e===false)return {status:"ok",enabled:false,markdown:""};let t=typeof e=="object"?e.topN:void 0,n=typeof e=="object"?e.budgetTokens:void 0,o=Number.isFinite(t)?Math.max(0,Math.floor(t)):10,s=Number.isFinite(n)?Math.max(1,Math.floor(n)):800;try{let i=this.explicitMemoryGate??=new MemoryGate(this.projectRoot),c={...await i.getAll({purpose:"session_bootstrap",budgetTokens:s,limit:o}),_devflow_unique:{memory_version:1,structured_storage:!0,project_context_included:!0},_accuracy:{data_freshness_ms:0,source_layer:"memory",degradation:typeof i.getObservationVectorCoverage=="function"?i.getObservationVectorCoverage().missing>0?"bm25_only":"none":"partial"}};if(c.memories.length===0)return {status:"ok",enabled:!0,markdown:`## Project memory
10
- \u672C\u9879\u76EE\u6682\u65E0\u8BB0\u5FC6`};let u=c.memories.map(l=>`- **${l.title||l.type}**: ${l.content} (confidence ${l.confidence.toFixed(2)}, ${l.source})`);return {status:"ok",enabled:!0,markdown:`## Project memory (auto-injected, ${c.memories.length} items, ${c.tokenCount} tokens)
11
- ${u.join(`
12
- `)}`}}catch(i){return console.error("[devflow-daemon] Memory snapshot failed:",i.message),{status:"degraded",enabled:true,markdown:`## Project memory
13
- \u672C\u9879\u76EE\u6682\u65E0\u8BB0\u5FC6`}}}async handleSessionEnd(e){let t=null;try{let i=JSON.parse(e);typeof i.session_id=="string"&&i.session_id.trim()&&(t=i.session_id.trim());}catch{}t??=this.activeSessionIds.size===1?[...this.activeSessionIds][0]:null;let n=t===null?this.activeSessionIds.size<=1:this.activeSessionIds.size<=1&&this.activeSessionIds.has(t);this.startExplicitIntentFlush(),await this.explicitIntentFlush;let o=await this.getMemoryGate(),s=t?this.runtimeStore.get(t)?.executionId:void 0;s&&await this.telemetry.sendExecutionComplete(s),t&&this.deleteContextReceipt(t,s),await Z(e,this.projectRoot,n,{telemetry:this.telemetry,memory:o}),t&&(this.serverManager.removeRef(t),this.activeSessionIds.delete(t),this.eventMap=this.eventMap.filter(i=>i.sessionId!==t),this.runtimeStore.removeSession(t)),this.activeSessionIds.size===0?setImmediate(()=>{this.shutdown();}):this.resetIdleTimer();}shutdown(){return this.shutdownPromise?this.shutdownPromise:(this.shutdownPromise=this.performShutdown(),this.shutdownPromise)}async performShutdown(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null),this.telemetryFlushTimer&&(clearInterval(this.telemetryFlushTimer),this.telemetryFlushTimer=null),await this.flushTelemetry(),await this.closeServer(),this.cleanupOwnedResources(),await this.serverManager.stop(),await this.explicitIntentFlush,this.explicitMemoryGate?.close(),this.explicitMemoryGate=null,this.telemetry.close(),process.exit(0);}closeServer(){let e=this.server;return !e||!e.listening||!this.pathMatchesIdentity(this.socketPath,this.socketIdentity)?Promise.resolve():(this.server=null,new Promise(t=>{let n=false,o=()=>{n||(n=true,clearTimeout(s),t());},s=setTimeout(o,Vn);e.close(o);}))}cleanupOwnedResources(){try{Ne(this.projectRoot,process.pid);}catch(e){console.error("[devflow-daemon] Failed to unregister:",e.message);}this.removePathIfOwned(this.socketPath,this.socketIdentity,"socket"),this.removePathIfOwned(this.pidPath,this.pidIdentity,"PID file",`${process.pid}
14
- `);}};if(process.env.DEVFLOW_HOOK_DAEMON_DISABLE_AUTOSTART!=="1"){let r=process.argv[2]||process.env.CLAUDE_PROJECT_DIR||process.cwd(),e=new re(r,process.argv[3]);process.once("SIGTERM",()=>{e.shutdown();}),process.once("SIGINT",()=>{e.shutdown();}),e.start().catch(t=>{console.error("[devflow-daemon] Failed to start:",t),process.exit(1);});}export{re as HookDaemon,eo as injectProjectRootIfMissing};
5
+ `,{mode:384}),renameSync(e,n.path);}function q(n){let e=n.trim().replace(/^\//,"");if(!e.startsWith("devflow:"))return null;let t=e.slice(8).replace(/^devflow-/,"");return /^[a-z0-9][a-z0-9-]*$/i.test(t)?`devflow:${t.toLowerCase()}`:null}function kt(n){let e=n.trimStart().match(/^\/(devflow:[a-z0-9][a-z0-9-]*)\b/i);if(!e)return null;let t=q(e[1]);return t?{rawName:e[1],skillName:t}:null}function D(n,e){return e?`evt_tool_${createHash$1("sha256").update(`${n}\0${e}`).digest("hex").slice(0,24)}`:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`}var Fn=/^(?:ok(?:ay)?|yes|no|thanks?|thank you|continue|好的?|可以|行|是|否|谢谢|继续|收到|明白了?)[.!。!\s]*$/iu;function Nn(n){let e=n.trim();return e.length>0&&!Fn.test(e)}function $n(n,e,t=Date.now()){let o=createHash("sha256").update(e.trim()).digest("hex"),r=createHash("sha256").update(`${n}\0${t}\0${o}`).digest("hex").slice(0,24);return {turnId:`turn:${r}`,eventId:`memory-turn:${r}`,promptHash:o,createdAt:t}}function me(n,e,t=[]){return `memory-receipt:${createHash("sha256").update(`${n}\0${e}\0${[...t].sort().join("\0")}`).digest("hex").slice(0,24)}`}function he(n){return `DevFlow memory decision required for ${n.turnId}. Before the final response, semantically decide whether this turn contains durable project knowledge. Call mcp__devflow__memory_commit_turn with evidence-bound candidates, or mcp__devflow__memory_skip_turn with a concrete reason. Do not claim memory success without a canonical receipt.`}function de(n){let e=n.memoryIds.length>0?n.memoryIds.join(","):"none",t=n.status==="committed"?"accepted":n.status,o=n.status==="committed"?"; indexStatus=pending":"";return `DevFlow canonical memory receipt: status=${t}; receiptId=${n.receiptId??"missing"}; turnId=${n.turnId}; memoryIds=${e}; source=${n.source??"unknown"}${o}.`}function Rt(n,e,t){if(t)return null;let o=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:50});try{let r=o.getPendingMemoryTurn(n,e);return !r||!o.markMemoryTurnStopPrompted(r.turnId)?null:{decision:"block",reason:he(r)}}finally{o.close();}}function fe(n){if(!Nn(n.prompt))return null;let e=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:50});try{let t=n.prompt.trim().slice(0,4e3),o=createHash("sha256").update(t).digest("hex"),r=e.listMemoryTurns(n.projectRoot,n.sessionId,5).find(l=>l.promptHash===o&&Date.now()-l.createdAt<1e4),i=r?{turnId:r.turnId,eventId:r.eventId,promptHash:r.promptHash,createdAt:r.createdAt}:$n(n.sessionId,t),s=r??e.beginMemoryTurn({...i,projectRoot:n.projectRoot,sessionId:n.sessionId});if(s.status!=="pending")return {turn:s,additionalContext:de(s),skipSkillRouting:s.source==="explicit_intent"};let a=W(t)??void 0,c=a?"commit_explicit":"capture_prompt";return e.enqueueWork({idempotencyKey:`memory-turn:${c}:${s.turnId}`,kind:a?"memory.explicit_commit":"memory.turn_capture",projectRoot:n.projectRoot,sessionId:n.sessionId,turnId:s.turnId,payload:{operation:c,turnId:s.turnId,eventId:s.eventId,sessionId:n.sessionId,prompt:t,explicitContent:a,createdAt:s.createdAt}}),{turn:s,explicitContent:a,additionalContext:a?`DevFlow explicit memory is durably queued for ${s.turnId}, but no canonical receipt exists yet. Do not claim that it was remembered.`:he(s),skipSkillRouting:!!a}}finally{e.close();}}async function ye(n){let{payload:e,memory:t,projectRoot:o}=n,r=openGlobalDevFlowDatabase();try{let i=r.getMemoryTurn(e.turnId);if(!i)throw new Error(`Memory turn ${e.turnId} does not exist`);if(i.status!=="pending")return {turn:i,additionalContext:de(i),skipSkillRouting:i.source==="explicit_intent"};if(await t.recordUserMessage(e.prompt,e.sessionId,e.prompt.slice(0,200)),await t.recordEvent({id:e.eventId,sessionId:e.sessionId,tool:"UserPromptSubmit",kind:"user_message",payload:{prompt:e.prompt,turnId:e.turnId},createdAt:e.createdAt}),e.operation==="capture_prompt"||!e.explicitContent)return {turn:i,additionalContext:he(i),skipSkillRouting:!1};let s=await t.saveExplicitMemoryIntent(e.explicitContent,e.sessionId,e.eventId),a=me(i.turnId,"committed",[s.id]),c=r.commitMemoryTurn({turnId:i.turnId,receiptId:a,memoryIds:[s.id],source:"explicit_intent",reason:"explicit_user_request"});return r.enqueueWork({idempotencyKey:`vector:${o}:${s.id}`,kind:"memory.vector_backfill",projectRoot:o,sessionId:e.sessionId,turnId:c.turnId,payload:{observationId:s.id},maxAttempts:20}),{turn:c,explicitContent:e.explicitContent,additionalContext:de(c),skipSkillRouting:!0}}finally{r.close();}}var Hn=new Set(["Write","Edit","NotebookEdit"]),bt="Claude native memory writes are disabled for this project. Use DevFlow explicit memory or mcp__devflow__memory_commit_turn so a canonical receipt is created.";function Dt(n){if(!Hn.has(n.toolName))return null;let e=Wn(n.toolInput);if(!e)return null;let t=qn(e,n.projectRoot),o=resolve(n.projectRoot,".claude");if(It(t,o)&&basename(t).toLocaleUpperCase("en-US")==="MEMORY.MD")return bt;let r=resolve(n.home??homedir$1(),".claude","projects");return It(t,r)&&relative(r,t).split(sep).some(s=>s.toLocaleLowerCase("en-US")==="memory")?bt:null}function Wn(n){for(let e of ["file_path","notebook_path","path"]){let t=n[e];if(typeof t=="string"&&t.trim())return t.trim()}return null}function qn(n,e){return resolve(isAbsolute(n)?n:resolve(e,n))}function It(n,e){let t=relative(e,n);return t===""||!t.startsWith(`..${sep}`)&&t!==".."&&!isAbsolute(t)}var Vn=1024*1024;function Pt(n){let e=join$1(getProjectStateDir(n),"session-start-pending");mkdirSync$1(e,{recursive:true,mode:448});let t=readdirSync$1(e).filter(s=>s.endsWith(".json")||/\.json\.claim-\d+$/u.test(s)).sort()[0];if(!t)return null;let o=join$1(e,t),r=/\.json\.claim-\d+$/u.test(t),i=r?o:`${o}.claim-${process.pid}`;if(!r)try{renameSync(o,i);}catch{return null}try{let s=readFileSync$1(i,"utf8");if(Buffer.byteLength(s,"utf8")>Vn)throw unlinkSync$1(i),new Error("session_start_spool_input_too_large");return {receiptId:basename(t).replace(/\.json(?:\.claim-\d+)?$/u,""),input:s,path:i}}catch(s){try{unlinkSync$1(i);}catch{}throw s}}function Ot(n){unlinkSync$1(n.path);}function At(n){let e=n.path.replace(/\.claim-\d+$/u,"");try{renameSync(n.path,e);}catch{}}function Ft(n,e){let t=Ut(n);mkdirSync$1(t,{recursive:true,mode:448});let o=`memory-turn-spool-${Date.now()}-${process.pid}-${randomUUID$1()}`,r=join$1(t,`${o}.json`),i=`${r}.tmp`;return writeFileSync$1(i,JSON.stringify({...e,prompt:e.prompt.trim().slice(0,4e3)}),{mode:384}),renameSync(i,r),o}function Nt(n){let e=Ut(n);mkdirSync$1(e,{recursive:true,mode:448});let t=readdirSync$1(e).filter(s=>s.endsWith(".json")||/\.json\.claim-\d+$/u.test(s)).sort()[0];if(!t)return null;let o=join$1(e,t),r=/\.json\.claim-\d+$/u.test(t),i=r?o:`${o}.claim-${process.pid}`;if(!r)try{renameSync(o,i);}catch{return null}try{let s=JSON.parse(readFileSync$1(i,"utf8"));if(typeof s.sessionId!="string"||typeof s.prompt!="string"||typeof s.createdAt!="number")throw unlinkSync$1(i),new Error("memory_turn_spool_payload_invalid");return {receiptId:basename(t).replace(/\.json(?:\.claim-\d+)?$/u,""),payload:s,path:i}}catch(s){try{unlinkSync$1(i);}catch{}throw s}}function $t(n){unlinkSync$1(n.path);}function Lt(n){try{renameSync(n.path,n.path.replace(/\.claim-\d+$/u,""));}catch{}}function Ut(n){return join$1(_(n),"memory-turn-pending")}var G=class extends Error{constructor(e,t){super(t),this.name="PermanentWorkError",this.category=e;}},nr=1e3,rr=3e4,ir=1,sr=5*6e4,B=class{constructor(e){this.timer=null;this.draining=null;this.stopped=true;this.projectRoot=e.projectRoot,this.handlers=e.handlers,this.pollIntervalMs=we(e.pollIntervalMs,nr),this.leaseMs=we(e.leaseMs,rr),this.batchSize=we(e.batchSize,ir),this.owner=e.owner??`hook-daemon:${process.pid}:${randomUUID$1()}`,this.now=e.now??Date.now,this.random=e.random??Math.random,this.openDatabase=e.openDatabase??(()=>openGlobalDevFlowDatabase());}start(){this.stopped&&(this.stopped=false,this.schedule(0));}stop(){this.stopped=true,this.timer&&clearTimeout(this.timer),this.timer=null;}drainOnce(){return this.draining?this.draining:(this.draining=this.performDrain().finally(()=>{this.draining=null;}),this.draining)}schedule(e){this.stopped||(this.timer&&clearTimeout(this.timer),this.timer=setTimeout(()=>{this.timer=null,this.drainOnce().catch(t=>console.error("[devflow-work] Drain failed:",Ht(t))).finally(()=>this.schedule(this.pollIntervalMs));},e),this.timer.unref());}async performDrain(){let e=Object.keys(this.handlers);if(e.length===0)return 0;let t=this.openDatabase();try{let o=this.now();t.recoverExpiredWork(this.projectRoot,o);let r=t.leaseWork({projectRoot:this.projectRoot,owner:this.owner,kinds:e,limit:this.batchSize,leaseMs:this.leaseMs,now:o});for(let i of r)await this.processItem(t,i);return r.length}finally{t.close();}}async processItem(e,t){let o=this.handlers[t.kind];if(!o){e.deadLetterWork(t.id,this.owner,{category:"unsupported_work_kind",message:`No work handler is registered for ${t.kind}`});return}try{if(await o({item:t}),!e.completeWork(t.id,this.owner,this.now()))throw new Error(`Work lease was lost before completion: ${t.id}`)}catch(r){let i=ar(r);if(r instanceof G){e.deadLetterWork(t.id,this.owner,i);return}e.retryWork(t.id,this.owner,i,this.now()+cr(t.attempts,this.random()));}}};function we(n,e){return Number.isSafeInteger(n)&&n>0?n:e}function ar(n){if(n instanceof G)return {category:n.category,message:n.message};let e=n&&typeof n=="object"?n:null;return {category:typeof e?.category=="string"?e.category:"runtime_error",message:Ht(n)}}function cr(n,e){let t=Math.min(sr,500*2**Math.max(0,n-1)),o=Math.floor(t*.2*Math.max(0,Math.min(1,e)));return t+o}function Ht(n){return n instanceof Error?n.message:String(n)}var mr=250;function zt(n,e=process.env){let t=qt(join$1(homedir$1(),".devflow","config.json")),o=qt(join$1(n,".devflow","config.json")),r=ke(e.DEVFLOW_HOOK_PROFILE)??ke(o.profile)??ke(t.profile)??"standard",i=Number(e.DEVFLOW_HOOK_ACK_BUDGET_MS??o.acknowledgementBudgetMs??t.acknowledgementBudgetMs),s=Number.isSafeInteger(i)&&i>0?Math.min(i,5e3):mr,a=new Set([...Re(t.disabledHandlers),...Re(o.disabledHandlers),...Re(e.DEVFLOW_DISABLED_HOOK_HANDLERS?.split(","))].map(gr).filter(c=>!!c));return {profile:r,acknowledgementBudgetMs:s,disabledHandlers:a}}async function K(n,e,t,o){let r,i=new Promise(s=>{r=setTimeout(()=>{console.error(`[devflow-hook] ${n} exceeded ${e}ms acknowledgement budget`),s(o());},e),r.unref();});try{return await Promise.race([Promise.resolve().then(t),i])}finally{r&&clearTimeout(r);}}async function Jt(n,e=process.env){let t=e.DEVFLOW_HOST_ADAPTER??(e.CLAUDE_PROJECT_DIR||e.CLAUDE_PLUGIN_ROOT||e.CLAUDE_CODE_ENTRYPOINT?"claude-code":void 0),o=await selectHostAdapter({projectRoot:n,env:e,explicitHost:t});if(o.status==="failed")throw new Error(`host_adapter_selection_failed:${o.code}:${o.reason}`);let r=await o.adapter.capabilities({projectRoot:n,env:e,explicitHost:t});return {hostId:o.adapter.id,capabilities:new Map(r.map(i=>[i.capability,i])),profiles:new Set(o.adapter.hooks?.profileSupport??[])}}function Gt(n,e,t){let o=yr(n,e.profile);if(!t.profiles.has(e.profile))return {enabled:false,policy:"fail_open",reason:`hook_profile_${e.profile}_unsupported_by_${t.hostId}`,fallback:"host_event_continues_without_devflow_handler"};if(e.disabledHandlers.has(n))return {enabled:false,policy:"fail_open",reason:"hook_handler_disabled",fallback:"host_event_continues_without_devflow_handler"};if(e.profile==="minimal"&&!hr.has(n))return {enabled:false,policy:"fail_open",reason:"hook_handler_not_in_minimal_profile",fallback:"critical_receipts_only"};let r=fr[n];if(!r)return {enabled:true,policy:o};let i=t.capabilities.get(r);return !i||i.status!=="supported"?{enabled:false,policy:"fail_open",reason:i?.reason??`${r}_unsupported_by_${t.hostId}`,fallback:i?.fallback??"host_event_continues_without_native_hook_processing"}:{enabled:true,policy:o}}var hr=new Set(["session-start","user-prompt-submit","stop","session-end","memory-snapshot"]),fr={"session-start":"hooks.session_start","user-prompt-submit":"hooks.prompt_submit","pre-tool-use":"hooks.pre_tool_use","post-tool-use":"hooks.post_tool_use","post-tool-use-failure":"hooks.post_tool_use",stop:"hooks.stop","session-end":"hooks.session_end"};function yr(n,e){return n==="user-prompt-submit"||n==="stop"||n==="session-end"?"enqueue_and_open":n==="pre-tool-use"&&e==="strict"?"fail_closed":"fail_open"}function qt(n){if(!existsSync(n))return {};try{let t=JSON.parse(readFileSync$1(n,"utf8")).hooks;return t&&typeof t=="object"&&!Array.isArray(t)?t:{}}catch{return {}}}function ke(n){return n==="minimal"||n==="standard"||n==="strict"?n:void 0}function Re(n){return Array.isArray(n)?n.filter(e=>typeof e=="string"):[]}function gr(n){let e=n.trim().replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/_/g,"-").toLowerCase();return ({sessionstart:"session-start",userpromptsubmit:"user-prompt-submit",pretooluse:"pre-tool-use",posttooluse:"post-tool-use",posttoolusefailure:"post-tool-use-failure",sessionend:"session-end",precompact:"pre-compact",memorysnapshot:"memory-snapshot"}[e.replace(/-/g,"")]??e)||void 0}var jr=600*1e3,Fr=2e3,Nr=250,$r=2e3,Lr=1e3,Bt=1024*1024,Ur=2*1024*1024,Hr=6e4,Wr=100,qr={Agent:"get_project_context",Bash:"get_project_context",Glob:"get_project_context",Grep:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function zr(n){switch(n){case "Read":case "Glob":return "file_read";case "Write":case "Edit":case "NotebookEdit":return "file_write";case "Bash":return "bash_command";case "Agent":return "subagent";case "Skill":return "skill_invoke";default:return "tool_use"}}function Ie(n){return typeof n=="string"&&n.trim().length>0}function Jr(n,e,t=process.env,o){if(!n.startsWith("mcp__devflow__"))return e;let r=e!==null&&typeof e=="object"&&!Array.isArray(e)?e:{};if(Ie(r.projectRoot))return r;let i=Ie(t.CLAUDE_PROJECT_DIR)?t.CLAUDE_PROJECT_DIR:o;return Ie(i)&&(r.projectRoot=i),r}var Te=class{constructor(e,t){this.socketIdentity=null;this.pidIdentity=null;this.server=null;this.activeSessionIds=new Set;this.eventMap=[];this.idleTimer=null;this.telemetryFlushTimer=null;this.successfulTelemetryOperations=0;this.shutdownPromise=null;this.explicitMemoryGate=null;this.memoryWarmup=null;this.explicitIntentFlush=null;this.hostRuntime=null;this.memoryTurnSpoolTimer=null;this.sessionFinalizeSpoolTimer=null;this.projectRoot=e,this.socketPath=getDaemonSocketPath(e),this.pidPath=getDaemonPidPath(e),this.telemetry=new b,this.runtimeStore=new I(this.projectRoot),this.serverManager=new A(e),this.hookRuntime=zt(e),this.initialSessionInput=t,this.workRuntime=new B({projectRoot:e,handlers:{"memory.explicit_commit":async({item:o})=>{await this.persistMemoryTurnItem(o.payload);},"memory.turn_capture":async({item:o})=>{await this.persistMemoryTurnItem(o.payload);},"session.bootstrap":async({item:o})=>{await this.bootstrapSessionWork(o.payload);},"memory.vector_backfill":async()=>{await(await this.getMemoryGate()).backfillObservationVectors({batchSize:32});},"memory.turn_distill":async({item:o})=>{await x({projectRoot:this.projectRoot,sessionId:o.sessionId,trigger:"session_end",memory:await this.getMemoryGate()});},"session.finalize":async({item:o})=>{await this.finalizeSessionWork(o.payload,o.id);},"telemetry.reconcile":async()=>{await this.flushTelemetry();}}});}async start(){this.hostRuntime=await Jt(this.projectRoot),mkdirSync(dirname(this.socketPath),{recursive:true,mode:448}),await this.cleanStaleStartupResources(),this.server=createServer(e=>this.handleConnection(e));try{await this.listen(),this.writePidFile(),nt(this.projectRoot,process.pid);}catch(e){throw await this.closeServer(),this.cleanupOwnedResources(),e}if(this.server.on("error",e=>{console.error("[devflow-daemon] Server error:",e.message);}),console.error("[devflow-daemon] Listening on",this.socketPath),await this.telemetry.flushAndAggregate(),this.telemetryFlushTimer=setInterval(()=>{this.flushTelemetry();},Hr),this.telemetryFlushTimer.unref(),this.workRuntime.start(),this.initialSessionInput){let e=this.initialSessionInput;this.initialSessionInput=void 0,this.handleRequest({type:"session-start",input:e});}this.drainSessionStartSpool(),this.scheduleMemoryTurnSpoolDrain(0),this.scheduleSessionFinalizeSpoolDrain(0),this.resetIdleTimer();}handleConnection(e){let t="",o=false,r=false,i=0,s=Promise.resolve();e.setEncoding("utf8");let a=()=>{r||(r=true,clearTimeout(c),e.destroy());},c=setTimeout(()=>{a();},Fr).unref(),l=async u=>{if(!r){r=true,clearTimeout(c);try{await this.writeFrame(e,u);}catch{}finally{e.destroy();}}};e.on("data",u=>{if(r)return;t+=u;let p=t.split(`
6
+ `);if(t=p.pop()??"",Buffer.byteLength(t,"utf8")>Bt){a();return}if(p.length===0)return;let m=0;for(let f of p){let d=Buffer.byteLength(f,"utf8");if(d>Bt){a();return}m+=d+1;}if(i+m>Ur){a();return}i+=m,s=s.then(async()=>{for(let f of p){if(r)return;if(!o){let d;try{d=JSON.parse(f);}catch{await l({error:"handshake_invalid"});return}if(d?.type!=="hello"||typeof d.rootPath!="string"){await l({error:"handshake_invalid"});return}if(d.rootPath!==this.projectRoot){await l({error:"handshake_root_mismatch",expected:this.projectRoot});return}o=true,clearTimeout(c),this.resetIdleTimer();continue}if(f.trim())try{let d=JSON.parse(f),y=await this.handleRequest(d);y!==null&&!r&&await this.writeFrame(e,y);}catch(d){r||await this.writeFrame(e,{error:d.message});}}}).catch(()=>{a();}).finally(()=>{i-=m;});}),e.on("close",()=>clearTimeout(c)),e.on("error",()=>{});}writeFrame(e,t){return new Promise((o,r)=>{if(e.destroyed||!e.writable){r(new Error("Socket is not writable"));return}let i=false,s=true,a=false,c=setTimeout(()=>{e.destroy(),u(new Error("Timed out writing daemon response"));},Lr),l=()=>{clearTimeout(c),e.off("close",p),e.off("error",m),e.off("drain",f);},u=y=>{a||!y&&(!i||!s)||(a=true,l(),y?r(y):o());},p=()=>u(new Error("Socket closed during write")),m=y=>u(y),f=()=>{s=true,u();};e.once("close",p),e.once("error",m),e.write(`${JSON.stringify(t)}
7
+ `,y=>{if(y){u(y);return}i=true,u();})||(s=false,e.once("drain",f));})}async cleanStaleStartupResources(){let e=this.getPathIdentity(this.socketPath);if(e){if(await this.isSocketAcceptingConnections())throw new Error(`Daemon socket already active for ${this.projectRoot}`);this.removePathIfOwned(this.socketPath,e,"stale socket");}let t=this.getPathIdentity(this.pidPath);t&&this.removePathIfOwned(this.pidPath,t,"stale PID file");}getPathIdentity(e){try{let t=lstatSync(e);return {dev:t.dev,ino:t.ino}}catch(t){if(t.code==="ENOENT")return null;throw t}}pathMatchesIdentity(e,t){if(!t)return false;try{let o=this.getPathIdentity(e);return o?.dev===t.dev&&o.ino===t.ino}catch(o){return console.error("[devflow-daemon] Failed to verify path ownership:",o.message),false}}writePidFile(){let e=openSync(this.pidPath,"wx",384);try{writeFileSync(e,`${process.pid}
8
+ `);let t=fstatSync(e);this.pidIdentity={dev:t.dev,ino:t.ino};}finally{closeSync(e);}}removePathIfOwned(e,t,o,r){try{return !this.pathMatchesIdentity(e,t)||r!==void 0&&readFileSync(e,"utf8")!==r||!this.pathMatchesIdentity(e,t)?!1:(unlinkSync(e),!0)}catch(i){return i.code!=="ENOENT"&&console.error(`[devflow-daemon] Failed to remove ${o}:`,i.message),false}}isSocketAcceptingConnections(){return new Promise((e,t)=>{let o=createConnection(this.socketPath),r=setTimeout(()=>{o.destroy(),t(new Error(`Timed out probing daemon socket ${this.socketPath}`));},Nr),i=s=>{clearTimeout(r),o.destroy(),e(s);};o.once("connect",()=>i(true)),o.once("error",s=>{let a=s.code;a==="ENOENT"||a==="ECONNREFUSED"?i(false):(clearTimeout(r),t(s));});})}listen(){return new Promise((e,t)=>{let o=this.server;if(!o){t(new Error("Daemon server is not initialized"));return}let r=s=>{o.off("listening",i),t(s);},i=()=>{o.off("error",r);try{let s=this.getPathIdentity(this.socketPath);if(!s)throw new Error(`Daemon socket missing after listen: ${this.socketPath}`);this.socketIdentity=s,e();}catch(s){t(s);}};o.once("error",r),o.once("listening",i),o.listen(this.socketPath);})}resetIdleTimer(){this.idleTimer&&clearTimeout(this.idleTimer),this.idleTimer=null,!(this.activeSessionIds.size>0)&&(this.idleTimer=setTimeout(()=>{console.error("[devflow-daemon] Idle timeout, exiting"),this.shutdown();},jr).unref());}async handleRequest(e){let t=this.hostRuntime;if(!t)return this.hookFailureResponse(e.type,{enabled:false,policy:"fail_open",reason:"host_adapter_not_ready",fallback:"host_event_continues_without_devflow_handler"});let o=Gt(e.type,this.hookRuntime,t);if(!o.enabled)return this.hookFailureResponse(e.type,o);try{switch(e.type){case "pre-tool-use":return this.handlePreToolUse(e.input);case "post-tool-use":return await this.handlePostToolUse(e.input)??{status:"ok"};case "post-tool-use-failure":return await this.handlePostToolUseFailure(e.input)??{status:"ok"};case "stop":return K("Stop",this.hookRuntime.acknowledgementBudgetMs,async()=>await this.handleStop(e.input??"")??{status:"ok"},()=>({status:"degraded",reason:"hook_budget_exceeded",fallback:"background_processing"}));case "memory-snapshot":return this.handleMemorySnapshot();case "session-start":return this.handleSessionStart(e.input??"");case "session-end":return K("SessionEnd",this.hookRuntime.acknowledgementBudgetMs,()=>this.handleSessionEnd(e.input??""),()=>({status:"degraded",reason:"hook_budget_exceeded",fallback:"durable_session_recovery"}));case "user-prompt-submit":return K("UserPromptSubmit",this.hookRuntime.acknowledgementBudgetMs,()=>this.handleUserPromptSubmit(e.input),()=>({hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",additionalContext:"DevFlow background processing exceeded its acknowledgement budget; continue in degraded mode and do not claim an unreceipted memory save."}}));case "pre-compact":return this.handlePreCompact(e.input);default:return {error:"unknown request type"}}}catch(r){return this.hookFailureResponse(e.type,o,r)}}async drainSessionStartSpool(){for(;;){let e=Pt(this.projectRoot);if(!e)return;try{let t=await this.handleRequest({type:"session-start",input:e.input});if(t&&"status"in t&&t.status==="degraded")throw new Error(`session_start_degraded:${String(t.reason??"unknown")}`);Ot(e),console.error(`[devflow-daemon] SessionStart accepted from durable spool: ${e.receiptId}`);}catch(t){At(e),console.error("[devflow-daemon] SessionStart spool deferred:",t.message);return}}}hookFailureResponse(e,t,o){let r=o instanceof Error?o.message:t.reason??"hook_handler_unavailable",i=t.fallback??(t.policy==="enqueue_and_open"?"durable_background_processing":"host_event_continues");return e==="pre-tool-use"?{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:t.policy==="fail_closed"?"deny":"allow",permissionDecisionReason:r,additionalContext:`DevFlow hook degraded: ${r}; fallback=${i}.`}}:e==="user-prompt-submit"?{hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",additionalContext:`DevFlow hook degraded: ${r}; fallback=${i}. Do not claim unreceipted work completed.`}}:{status:"degraded",reason:r,fallback:i}}async handlePreToolUse(e){try{if(!e||e.trim()==="")return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}};let t=JSON.parse(e);t.tool_input=Jr(t.tool_name,t.tool_input,process.env,this.projectRoot);let o=this.trackSession(t.session_id),r="";if(t.tool_name==="Skill"){let S=t.tool_input?.skill,w=S?q(S):null;w&&(r=this.startSkillExecution(o,w));}let i=this.runtimeStore.get(o);t.tool_name.startsWith("mcp__devflow__")&&(t.tool_input._devflow_session_id=o,t.tool_input._devflow_execution_id=i?.executionId??o,t.tool_use_id&&(t.tool_input._devflow_tool_use_id=t.tool_use_id));let s=t.tool_name.startsWith("mcp__"),a=s?t.tool_name.replace(/^mcp__[^_]+__/,""):void 0,c=new E(this.projectRoot,o,i?.executionId),l=c.getPhase(),u=Dt({toolName:t.tool_name,toolInput:t.tool_input,projectRoot:this.projectRoot}),p=u?{permissionDecision:"deny",reason:u}:c.evaluate(t.tool_name,s),m;if(p.permissionDecision==="deny"?m={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:p.reason??"MCP context required"}}:p.additionalContext?m={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:p.additionalContext}}:m={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}},r){let S=m.hookSpecificOutput,w=typeof S.additionalContext=="string"?S.additionalContext:"";S.additionalContext=w?`${w}
9
+ ${r}`:r;}t.tool_name.startsWith("mcp__devflow__")&&(m.hookSpecificOutput.updatedInput=t.tool_input);let f={...t.tool_input};delete f._devflow_session_id,delete f._devflow_execution_id,delete f._devflow_tool_use_id;let d=i?.executionId??o,h={eventId:D(o,t.tool_use_id),executionId:d,sessionId:o,projectRoot:this.projectRoot,toolUseId:t.tool_use_id,timestamp:Date.now(),toolName:t.tool_name,toolType:s?"mcp":t.tool_name==="Agent"?"subagent":"direct",isMcpTool:s,mcpToolName:a,mcpEnforced:s&&l==="context_gathering",mcpFallback:!s&&l==="context_gathering",kind:zr(t.tool_name),input:f,duration:0,blocked:m&&m.hookSpecificOutput?.permissionDecision==="deny"},g=await this.telemetry.sendEvent({...h,blockReason:h.blocked?u?"DEVFLOW_CANONICAL_MEMORY_REQUIRED":"DEVFLOW_CONTEXT_REQUIRED":void 0,error:h.blocked?u?"DEVFLOW_CANONICAL_MEMORY_REQUIRED":"DEVFLOW_CONTEXT_REQUIRED":void 0});return g&&!h.blocked&&(this.eventMap.push({toolName:t.tool_name,toolUseId:t.tool_use_id,eventId:g,sessionId:o,timestamp:h.timestamp}),this.noteTelemetryOperation()),g&&h.blocked&&await this.telemetry.completeEvent({eventId:g,sessionId:o,error:u?"DEVFLOW_CANONICAL_MEMORY_REQUIRED":"DEVFLOW_CONTEXT_REQUIRED",duration:Date.now()-h.timestamp,completedAt:Date.now()}),m}catch{return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}}}trackSession(e){let t=e?.trim()||(this.activeSessionIds.size===1?[...this.activeSessionIds][0]:"");if(!t)throw new Error("session_id_required");return this.registerSession(t),t}registerSession(e){this.activeSessionIds.has(e)||(this.activeSessionIds.size===0&&F(this.projectRoot,{bypassCount:0}),this.activeSessionIds.add(e),this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null),this.serverManager.addRef(e),this.telemetry.sendSessionStart(e,this.projectRoot,Date.now()).catch(()=>{})),this.runtimeStore.registerSession(e);}startSkillExecution(e,t){let o=this.runtimeStore.get(e)?.executionId,r=this.runtimeStore.startExecution(e,t,je(t));return r.executionId&&r.executionId!==o&&this.telemetry.sendExecutionStart(r.executionId,e,t,r.lastActivityAt,this.projectRoot,r.requiredMcpTools).catch(()=>{}),formatSkillRuntimeOverlay(createSkillRuntimeOverlay(t,this.projectRoot))}async handleSessionStart(e){try{let t=JSON.parse(e);if(typeof t.session_id!="string"||!t.session_id.trim())return {error:"session_id_required"};let o=t.session_id.trim();this.registerSession(o);let r=`session-start:${createHash("sha256").update(`${this.projectRoot}\0${o}`).digest("hex").slice(0,24)}`,i=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:50}),s="pending";try{s=i.enqueueWork({idempotencyKey:r,kind:"session.bootstrap",projectRoot:this.projectRoot,sessionId:o,payload:{sessionId:o}}).state;}finally{i.close();}return {status:s==="completed"?"completed":"accepted",receiptId:r,workState:s}}catch{return {error:"session_start_invalid"}}}popEventFromMap(e,t,o){let r=this.eventMap.filter(s=>s.toolName===e&&s.sessionId===t);if(r.length===0)return null;let i=o?r.find(s=>s.toolUseId===o):r.sort((s,a)=>s.timestamp-a.timestamp)[0];return i?(this.eventMap=this.eventMap.filter(s=>s.eventId!==i.eventId),i):null}enforcePostToolUse(e){if(!qr[e])return null;let t=Math.floor(Date.now()/1e3),o=se(this.projectRoot,s=>t-(s.lastMcpCall??0)<30?s:{...s,bypassCount:(s.bypassCount??0)+1});if(!o.applied||!o.changed)return null;let r=o.receipt.bypassCount??0;if(r===1)return null;let i;switch(e){case "Agent":i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u4F7F\u7528\u4E86 Agent \u5B50\u4EE3\u7406\u6267\u884C\u641C\u7D22\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Bash":i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Bash \u547D\u4EE4\u641C\u7D22\u4EE3\u7801\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Glob":i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Glob \u5339\u914D\u6587\u4EF6\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Grep":i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Grep \u641C\u7D22\u4EE3\u7801\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "WebSearch":case "WebFetch":i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86\u7F51\u7EDC\u641C\u7D22\u3002\u4E0B\u6B21\u8BF7\u7528 get_knowledge MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;default:i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u8BF7\u4F7F\u7528 DevFlow MCP \u5DE5\u5177\u66FF\u4EE3\u76F4\u63A5\u8C03\u7528\u3002`;}return r>=3?i=`${i} \u5DF2\u8FBE\u5230\u6700\u5927\u8FDD\u89C4\u6B21\u6570\uFF0C\u540E\u7EED\u7ED5\u8FC7\u5C06\u88AB\u786C\u963B\u6B62\u3002`:r>=2?i=`${i} \u4E0B\u6B21\u8FDD\u89C4\u5C06\u88AB\u963B\u6B62\u3002`:i=`${i} \u8BF7\u7ACB\u5373\u7EA0\u6B63\u3002`,i}async handlePostToolUse(e){if(!e)return null;let t;try{t=JSON.parse(e);}catch{return null}let o=t.tool_name||"",r=t.tool_input||{},i=t.tool_response,s=this.trackSession(t.session_id);if(o==="Skill"){let v=r?.skill,k=v?q(v):null;k&&(this.startSkillExecution(s,k),F(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0}));}o.startsWith("mcp__")&&new E(this.projectRoot).recordMcpCall();let a=this.popEventFromMap(o,s,t.tool_use_id);if(a){let v=typeof i=="string"?i:JSON.stringify(i),k=v.length>5e3?{_truncated:true,_originalSize:v.length,text:v.slice(0,5e3)}:i,Kt=Date.now()-a.timestamp;await this.telemetry.completeEvent({eventId:a.eventId,sessionId:s,output:k,duration:Kt,completedAt:Date.now()})&&this.noteTelemetryOperation();}let c=typeof i=="object"&&i!==null?i:null,l=c?.exitCode,u=c?.stderr,p=/^\s*(ls|cat|pwd|cd|echo|head|tail|wc|which|whoami|date|env|printenv|id|hostname|uname)\b/,m=r.command||"",f=JSON.stringify(i),d=Buffer.byteLength(f,"utf8"),y=this.countResults(i),h,g,S=true;if(o.startsWith("mcp__"))h="mcp_call",g={mcpTool:o.replace(/^mcp__[^_]+__/,""),query:r.query??null,resultCount:y,resultSizeBytes:d};else if(o==="Read"||o==="Write"||o==="Edit")h=o==="Read"?"file_read":"file_write",g={filePath:r.file_path??null,fileContentSize:d};else if(o==="Bash"){let v=p.test(m),k=l!==void 0&&l!==0;S=!v||k,h=k?"bash_error":"bash_command",g={command:m||o,exitCode:l??null,stderr:u??null};}else o==="Agent"?(h="subagent",g={subagentType:r.subagent_type??null,description:typeof r.description=="string"?r.description.slice(0,200):null}):o==="WebSearch"||o==="WebFetch"?(h=o==="WebSearch"?"web_search":"web_fetch",g={query:typeof r.query=="string"?r.query.slice(0,200):null}):(S=false,h="tool_use",g={});if(S){let v={id:t.tool_use_id?`memory:${D(s,t.tool_use_id)}`:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:s,tool:o,kind:h,payload:g,command:m||void 0,exitCode:l,stderr:u??void 0,durationMs:0,createdAt:Date.now()};this.postMemoryEvent(v);}let w=this.enforcePostToolUse(o);return w?(console.error("[devflow-daemon] Enforcement:",w),{hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:JSON.stringify(w)}}):null}async handlePostToolUseFailure(e){let t;try{t=JSON.parse(e);}catch{return null}let o=this.trackSession(t.session_id),r=t.tool_name??"unknown",i=t.error??`${r} failed`,s=this.popEventFromMap(r,o,t.tool_use_id);if(s)await this.telemetry.completeEvent({eventId:s.eventId,sessionId:o,error:i,duration:Date.now()-s.timestamp,completedAt:Date.now()})&&this.noteTelemetryOperation();else {let a=D(o,t.tool_use_id);await this.telemetry.sendEvent({eventId:a,executionId:this.runtimeStore.get(o)?.executionId??o,sessionId:o,projectRoot:this.projectRoot,toolUseId:t.tool_use_id,timestamp:Date.now(),toolName:r,toolType:r.startsWith("mcp__")?"mcp":"direct",isMcpTool:r.startsWith("mcp__"),mcpToolName:r.startsWith("mcp__")?r.replace(/^mcp__[^_]+__/,""):void 0,mcpEnforced:false,mcpFallback:false,kind:"error",input:t.tool_input??{},duration:0,error:i,blocked:false});}return this.postMemoryEvent({id:t.tool_use_id?`memory:${D(o,t.tool_use_id)}`:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:o,tool:r,command:typeof t.tool_input?.command=="string"?t.tool_input.command:r,exitCode:-1,stderr:i,durationMs:s?Date.now()-s.timestamp:0,createdAt:Date.now(),kind:r==="Bash"?"bash_error":"tool_use",payload:{command:t.tool_input?.command??r,exitCode:-1,stderr:i}}),null}countResults(e){if(!e)return 0;let t=e;if(typeof e=="string")try{t=JSON.parse(e);}catch{return 1}let o=t,r=["files","results","data","result","memories","nodes","chunks","findings","symbols"],i=Array.isArray(o.data)?o.data:o.structuredContent??o;if(Array.isArray(i))return i.length;if(typeof i=="object"&&i!==null){let s=i,a=0,c=false;for(let l of r)Array.isArray(s[l])&&(c=true,a+=s[l].length);for(let[,l]of Object.entries(s))l&&typeof l=="object"&&!Array.isArray(l)&&(a+=this.countResults(l));return a>0?a:c?0:Object.keys(s).length>0?1:0}return 0}async postMemoryEvent(e){He(`/api/memory/session-events?rootPath=${encodeURIComponent(this.projectRoot)}`,e);try{await(await this.getMemoryGate()).recordEvent(e);}catch(t){console.error("[devflow-daemon] Local memory event write failed:",t.message);}}async getMemoryGate(){let e=this.explicitMemoryGate??=new MemoryGate(this.projectRoot);return this.memoryWarmup??=e.forceWarmUp().catch(t=>{throw this.memoryWarmup=null,t}),await this.memoryWarmup,e}async handleUserPromptSubmit(e){let t=[];try{let o=JSON.parse(e),r=typeof o.prompt=="string"?o.prompt:"",i=typeof o.session_id=="string"&&o.session_id.trim()?this.trackSession(o.session_id):null,s=kt(r),a=!1;if(r.trim()&&i)try{let c=fe({projectRoot:this.projectRoot,sessionId:i,prompt:r});if(c){let l=c;if(c.explicitContent&&c.turn.status==="pending")try{l=await ye({projectRoot:this.projectRoot,payload:{operation:"commit_explicit",turnId:c.turn.turnId,eventId:c.turn.eventId,sessionId:i,prompt:r.trim().slice(0,4e3),explicitContent:c.explicitContent,createdAt:c.turn.createdAt},memory:await this.getMemoryGate()});}catch(u){console.error("[devflow-daemon] Explicit memory commit deferred:",u.message);}t.push(l.additionalContext),a=l.skipSkillRouting;}}catch(c){a=!!W(r);try{let u=Ft(this.projectRoot,{sessionId:i,prompt:r,createdAt:Date.now()});t.push(`DevFlow memory turn status=accepted; durable=true; receiptId=${u}; canonicalMemory=false; SQLite queue replay pending. Do not claim memory success without a canonical receipt.`),this.scheduleMemoryTurnSpoolDrain(250),console.error("[devflow-daemon] Memory turn spooled after database contention:",c.message);}catch(u){t.push("DevFlow memory turn status=degraded; durable=false; no canonical receipt exists. Do not claim memory success."),console.error("[devflow-daemon] Memory turn persistence unavailable:",u.message);}}if(i&&s&&!a){let c=this.startSkillExecution(i,s.skillName);c&&t.push(c);}}catch{}return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t.length?{additionalContext:t.join(`
10
+ `)}:{}}}}async handlePreCompact(e){let t;try{let s=JSON.parse(e);typeof s.session_id=="string"&&s.session_id.trim()&&(t=s.session_id.trim());}catch{}await this.flushTelemetry(),this.startExplicitIntentFlush(),await this.explicitIntentFlush;let o=await this.getMemoryGate(),r=await x({projectRoot:this.projectRoot,sessionId:t,trigger:"pre_compact",memory:o}),i=it(r);return {hookSpecificOutput:{hookEventName:"PreCompact",...i?{additionalContext:i}:{}}}}async persistMemoryTurnItem(e){if(!e||typeof e!="object")throw new Error("memory_turn_payload_invalid");let t=e;if(t.operation!=="capture_prompt"&&t.operation!=="commit_explicit"||typeof t.turnId!="string"||typeof t.eventId!="string"||typeof t.sessionId!="string"||typeof t.prompt!="string"||typeof t.createdAt!="number")throw new Error("memory_turn_payload_invalid");await ye({projectRoot:this.projectRoot,payload:t,memory:await this.getMemoryGate()});}scheduleMemoryTurnSpoolDrain(e){this.memoryTurnSpoolTimer||(this.memoryTurnSpoolTimer=setTimeout(()=>{this.memoryTurnSpoolTimer=null,this.drainMemoryTurnSpool();},e),this.memoryTurnSpoolTimer.unref());}async drainMemoryTurnSpool(){for(;;){let e=Nt(this.projectRoot);if(!e)return;try{fe({projectRoot:this.projectRoot,sessionId:e.payload.sessionId,prompt:e.payload.prompt}),$t(e),this.workRuntime.drainOnce();}catch(t){Lt(e),console.error("[devflow-daemon] Memory turn spool replay deferred:",t.message),this.scheduleMemoryTurnSpoolDrain(1e3);return}}}async bootstrapSessionWork(e){if(!e||typeof e!="object"||typeof e.sessionId!="string")throw new Error("session_bootstrap_payload_invalid");let t=e.sessionId,o=await this.getMemoryGate();await o.ensureSession(t,this.projectRoot,"Host session"),await o.releasePendingDistillLeases(t);}noteTelemetryOperation(){this.successfulTelemetryOperations++,this.successfulTelemetryOperations>=Wr&&this.flushTelemetry();}async flushTelemetry(){this.successfulTelemetryOperations=0,await this.telemetry.flushAndAggregate();}startExplicitIntentFlush(){this.explicitIntentFlush||(this.explicitIntentFlush=this.flushExplicitMemoryIntents().finally(()=>{this.explicitIntentFlush=null;}));}async handleStop(e){let t=null,o=false;try{let r=JSON.parse(e);typeof r.session_id=="string"&&r.session_id.trim()&&(t=r.session_id.trim()),o=r.stop_hook_active===!0;}catch{}if(t??=this.activeSessionIds.size===1?[...this.activeSessionIds][0]:null,this.startExplicitIntentFlush(),t){let r=Rt(this.projectRoot,t,o);if(r)return r;if(o)return null;let i=this.runtimeStore.completeExecution(t);if(i?.executionId){let s=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:50}),a={};try{let c=s.listMemoryTurns(this.projectRoot,t,100);a={memoryReceiptIds:c.flatMap(l=>l.receiptId?[l.receiptId]:[]),missingMemoryDecisionCount:c.filter(l=>l.status==="pending").length};}finally{s.close();}await this.telemetry.sendExecutionComplete(i.executionId,"completed",Date.now(),a),this.deleteContextReceipt(t,i.executionId);}}return null}async flushExplicitMemoryIntents(){this.flushTelemetry();let e=wt(this.projectRoot);if(e)try{let t=this.explicitMemoryGate??=new MemoryGate(this.projectRoot);for(await t.forceWarmUp();e.intents.length>0;){let o=e.intents[0];if(o.turnId){let i=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:50});try{let s=i.getMemoryTurn(o.turnId);if(!s||s.status!=="pending"){console.error(`[devflow-daemon] Dropping stale legacy memory intent for ${o.turnId}: ${s?.status??"missing"}`),ue(e);continue}}finally{i.close();}}let r=await t.saveExplicitMemoryIntent(o.content,o.sessionId,o.eventId);if(o.turnId){let i=openGlobalDevFlowDatabase();try{i.commitMemoryTurn({turnId:o.turnId,receiptId:me(o.turnId,"committed",[r.id]),memoryIds:[r.id],source:"explicit_intent",reason:"explicit_user_request_retry"});}finally{i.close();}}ue(e);}}catch(t){console.error("[devflow-daemon] Explicit memory intent save failed:",t.message);}}deleteContextReceipt(e,t){let o;try{o=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),o.deleteContextReceipt(this.projectRoot,e,t);}catch{}finally{o?.close();}}async handleMemorySnapshot(){let e=loadConfig(this.projectRoot).sessionStart?.injectMemories??true;if(e===false)return {status:"ok",enabled:false,markdown:""};let t=typeof e=="object"?e.topN:void 0,o=typeof e=="object"?e.budgetTokens:void 0,r=Number.isFinite(t)?Math.max(0,Math.floor(t)):10,i=Number.isFinite(o)?Math.max(1,Math.floor(o)):800;try{let s=this.explicitMemoryGate??=new MemoryGate(this.projectRoot),c={...await s.getAll({purpose:"session_bootstrap",budgetTokens:i,limit:r}),_devflow_unique:{memory_version:1,structured_storage:!0,project_context_included:!0},_accuracy:{data_freshness_ms:0,source_layer:"memory",degradation:typeof s.getObservationVectorCoverage=="function"?s.getObservationVectorCoverage().missing>0?"bm25_only":"none":"partial"}};if(c.memories.length===0)return {status:"ok",enabled:!0,markdown:`## Project memory
11
+ \u672C\u9879\u76EE\u6682\u65E0\u8BB0\u5FC6`};let l=c.memories.map(u=>`- **${u.title||u.type}**: ${u.content} (confidence ${u.confidence.toFixed(2)}, ${u.source})`);return {status:"ok",enabled:!0,markdown:`## Project memory (auto-injected, ${c.memories.length} items, ${c.tokenCount} tokens)
12
+ ${l.join(`
13
+ `)}`}}catch(s){return console.error("[devflow-daemon] Memory snapshot failed:",s.message),{status:"degraded",enabled:true,markdown:`## Project memory
14
+ \u672C\u9879\u76EE\u6682\u65E0\u8BB0\u5FC6`}}}async handleSessionEnd(e){let t=null;try{let s=JSON.parse(e);typeof s.session_id=="string"&&s.session_id.trim()&&(t=s.session_id.trim());}catch{}t??=this.activeSessionIds.size===1?[...this.activeSessionIds][0]:null;let o=t===null?this.activeSessionIds.size<=1:this.activeSessionIds.size<=1&&this.activeSessionIds.has(t),r=t?this.runtimeStore.get(t)?.executionId:void 0,i=H(e,this.projectRoot,o,r);return i?.workState==="spooled"&&this.scheduleSessionFinalizeSpoolDrain(250),t&&(this.serverManager.removeRef(t),this.activeSessionIds.delete(t),this.eventMap=this.eventMap.filter(s=>s.sessionId!==t)),this.activeSessionIds.size===0&&i?.workState!=="spooled"?setImmediate(()=>{this.workRuntime.drainOnce().finally(()=>{this.shutdown();});}):this.resetIdleTimer(),i??{status:"ok"}}async finalizeSessionWork(e,t){if(!e||typeof e!="object")throw new Error("session_finalize_payload_invalid");let o=e;if(typeof o.inputJson!="string"||typeof o.sessionId!="string")throw new Error("session_finalize_payload_invalid");typeof o.executionId=="string"&&o.executionId&&await this.telemetry.sendExecutionComplete(o.executionId);let r=await this.getMemoryGate();await gt(o.inputJson,this.projectRoot,o.cleanupSharedState!==false,{telemetry:this.telemetry,memory:r});let i=openGlobalDevFlowDatabase();try{i.completeSessionClosure(this.projectRoot,o.sessionId,t);}finally{i.close();}}scheduleSessionFinalizeSpoolDrain(e){this.sessionFinalizeSpoolTimer||(this.sessionFinalizeSpoolTimer=setTimeout(()=>{this.sessionFinalizeSpoolTimer=null,this.drainSessionFinalizeSpool();},e),this.sessionFinalizeSpoolTimer.unref());}async drainSessionFinalizeSpool(){for(;;){let e=ut(this.projectRoot);if(!e)return;try{if(!H(e.payload.inputJson,this.projectRoot,e.payload.cleanupSharedState,e.payload.executionId,!1))throw new Error("session_finalize_spool_rejected");dt(e),this.workRuntime.drainOnce();}catch(t){pt(e),console.error("[devflow-daemon] Session finalize spool replay deferred:",t.message),this.scheduleSessionFinalizeSpoolDrain(1e3);return}}}shutdown(){return this.shutdownPromise?this.shutdownPromise:(this.shutdownPromise=this.performShutdown(),this.shutdownPromise)}async performShutdown(){this.workRuntime.stop(),this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null),this.telemetryFlushTimer&&(clearInterval(this.telemetryFlushTimer),this.telemetryFlushTimer=null),this.memoryTurnSpoolTimer&&(clearTimeout(this.memoryTurnSpoolTimer),this.memoryTurnSpoolTimer=null),this.sessionFinalizeSpoolTimer&&(clearTimeout(this.sessionFinalizeSpoolTimer),this.sessionFinalizeSpoolTimer=null),await this.flushTelemetry(),await this.closeServer(),this.cleanupOwnedResources(),await this.serverManager.stop(),await this.explicitIntentFlush,this.explicitMemoryGate?.close(),this.explicitMemoryGate=null,this.telemetry.close(),process.exit(0);}closeServer(){let e=this.server;return !e||!e.listening||!this.pathMatchesIdentity(this.socketPath,this.socketIdentity)?Promise.resolve():(this.server=null,new Promise(t=>{let o=false,r=()=>{o||(o=true,clearTimeout(i),t());},i=setTimeout(r,$r);e.close(r);}))}cleanupOwnedResources(){try{rt(this.projectRoot,process.pid);}catch(e){console.error("[devflow-daemon] Failed to unregister:",e.message);}this.removePathIfOwned(this.socketPath,this.socketIdentity,"socket"),this.removePathIfOwned(this.pidPath,this.pidIdentity,"PID file",`${process.pid}
15
+ `);}};if(process.env.DEVFLOW_HOOK_DAEMON_DISABLE_AUTOSTART!=="1"){let n=process.argv[2]||process.env.CLAUDE_PROJECT_DIR||process.cwd(),e=new Te(n,process.argv[3]);process.once("SIGTERM",()=>{e.shutdown();}),process.once("SIGINT",()=>{e.shutdown();}),e.start().catch(t=>{console.error("[devflow-daemon] Failed to start:",t),process.exit(1);});}export{Te as HookDaemon,Jr as injectProjectRootIfMissing};
@@ -1,2 +1,2 @@
1
- import {request}from'http';import {getLocalApiKey}from'@devflow-tools/sdk/auth';import {classifyWorkflowFailure}from'@devflow-tools/workflow-engine/failure-classifier';import {existsSync,readFileSync,rmSync,mkdirSync,writeFileSync,renameSync,readdirSync,unlinkSync}from'fs';import {join}from'path';import {homedir}from'os';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {getLocalApiKey as getLocalApiKey$1,getProjectStateDir}from'@devflow-tools/sdk';import {randomUUID,createHash}from'crypto';var b=1e4,L=3e4,h=500;function N(i,t,e){return new Promise(r=>{try{let s=new URL(i),n=request({hostname:s.hostname,port:s.port||80,path:s.pathname+s.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(t)},timeout:5e3},o=>{let a=[];o.on("data",c=>a.push(c)),o.on("end",()=>{let c=Buffer.concat(a).toString();r(o.statusCode!=null&&o.statusCode>=200&&o.statusCode<300?c:null);});});n.on("error",()=>r(null)),n.on("timeout",()=>{n.destroy(),r(null);}),n.write(t),n.end();}catch{r(null);}})}var g=class{constructor(t){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??getLocalApiKey$1(),this.cacheDir=t?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=t?.legacyCacheDir??(t?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=t?.database??null,this.ownsDatabase=!t?.database;}async sendEvent(t){let e={...t,input:this.truncateInput(t.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),t.eventId):null}async sendExecutionStart(t,e,r,s,n=process.env.CLAUDE_PROJECT_DIR??process.cwd(),o=[]){let a={executionId:t,sessionId:e,skillName:r,startedAt:s,projectRoot:n,requiredMcpTools:o};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(t,e="completed",r=Date.now(),s){let n={executionId:t,status:e,finishedAt:r,metadata:s},o=this.commitOrCache("execution_complete",n);return o&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",n),o}async sendSessionStart(t,e,r){let s={id:t,projectRoot:e,startedAt:r,label:e.split("/").pop()??"unknown"},n=this.commitOrCache("session_start",s);return n&&this.postHttp("/api/telemetry/sessions",s),n}async completeEvent(t){let e=this.commitOrCache("complete_event",t);return e&&(this.postHttp("/api/telemetry/tool-call/output",t),this.scheduleMetricAggregation()),e}async endSession(t,e=Date.now()){let r={sessionId:t,finishedAt:e},s=this.commitOrCache("session_end",r);return s&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),s}async flushCache(){try{let r=this.getDatabase(),s=r.listTelemetryFailures({unresolvedOnly:!0,limit:h}).reverse();for(let n of s)try{this.applyOperation(n.operation,n.payload),r.resolveTelemetryFailure(n.id);}catch{}r.trimTelemetryFailures(h);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(r=>!!r))].filter(r=>existsSync(r)).flatMap(r=>readdirSync(r).filter(s=>s.endsWith(".json")).map(s=>{let n=join(r,s);try{return {cacheFile:n,envelope:$(JSON.parse(readFileSync(n,"utf8")),s)}}catch{return null}})).filter(r=>r!==null).sort((r,s)=>r.envelope.timestamp-s.envelope.timestamp||T(r.envelope.operation)-T(s.envelope.operation));for(let{cacheFile:r,envelope:s}of e)try{let n=this.getDatabase();n.insertTelemetryFailure({id:s.id,operation:s.operation,payload:s.payload,error:s.failure,createdAt:s.timestamp}),this.applyOperation(s.operation,s.payload),n.resolveTelemetryFailure(s.id),unlinkSync(r);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(t,e){try{return this.applyOperation(t,e),!0}catch(r){return this.cacheOperation(t,e,r),false}}applyOperation(t,e){let r=this.getDatabase();switch(t){case "tool_call":this.ensureParentSession(r,e),r.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(r,e),r.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running",requiredMcpTools:e.requiredMcpTools});return;case "execution_complete":r.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":r.ensureSession(e),r.insertRun({id:R(e.id),source:"hook",tool:"session",input:{projectRoot:e.projectRoot},status:"active",startedAt:e.startedAt,tokenUsed:0,metadata:{sessionId:e.id,projectRoot:e.projectRoot}});return;case "complete_event":if(!r.updateToolCallEvent(e.eventId,{output:e.output===void 0?void 0:JSON.stringify(e.output),error:e.error,duration:e.duration}))throw new Error(`Tool call event ${e.eventId} is not available for completion`);return;case "session_end":r.closeSession(e.sessionId,e.finishedAt),r.updateRun(R(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(t,e){let r=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!r)throw new Error("Canonical session ID is required for telemetry");let s=typeof e.projectRoot=="string"&&e.projectRoot.trim()?e.projectRoot:typeof e.input?.projectRoot=="string"&&e.input.projectRoot.trim()?e.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";t.ensureSession({id:r,projectRoot:s,label:s==="unknown"?void 0:s.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(t,e,r){let s=Date.now(),n={id:`failure:${s}:${Math.random().toString(36).slice(2,11)}`,operation:t,payload:e,failure:r instanceof Error?r.message:String(r),timestamp:s};try{let o=this.getDatabase();o.insertTelemetryFailure({id:n.id,operation:t,payload:e,error:n.failure,createdAt:s}),o.trimTelemetryFailures(h),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let o=join(this.cacheDir,`${s}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(o,JSON.stringify(n,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let t=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of t.slice(0,Math.max(0,t.length-h)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},L).unref());}scheduleMetricAggregation(){if(this.metricAggregationScheduled||process.env.DEVFLOW_HOOK_DEGRADED==="1")return;this.metricAggregationScheduled=true,setTimeout(()=>{this.metricAggregationScheduled=false;try{this.getDatabase().aggregatePendingToolMetrics(50)===50&&this.scheduleMetricAggregation();}catch{}},25).unref();}postHttp(t,e){N(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=b?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,b)}...`}}};function T(i){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(i)}function R(i){return `hook-run:${i}`}function $(i,t){if(!i||typeof i!="object")throw new Error("Invalid telemetry cache envelope");let e=i;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let r=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!r)throw new Error("Unknown legacy telemetry cache operation");let s=e.payload??{},n=Number(s.timestamp??s.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:r,payload:s,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:n}}function J(i){return i??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function I(i){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(J(i))}function Y(i){return Buffer.from(i,"utf8").toString("base64url")}var f=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(I(t),"hook-sessions");}registerSession(t){let e=t.trim();if(!e)throw new Error("session_id_required");let r=this.get(e);if(r)return r.lastActivityAt=Date.now(),this.persist(r),r;let s=Date.now(),n={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:s,lastActivityAt:s};return this.sessions.set(e,n),this.persist(n),n}startExecution(t,e,r){let s=this.registerSession(t);return (!s.executionId||s.skillName!==e)&&(s.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),s.skillName=e,s.requiredMcpTools=[...new Set(r)],s.lastActivityAt=Date.now(),this.persist(s),s}get(t){let e=t.trim();if(!e)return null;let r=this.sessions.get(e);if(r)return r;let s=this.snapshotPath(e);if(!existsSync(s))return null;try{let n=JSON.parse(readFileSync(s,"utf8"));return n.sessionId!==e||n.projectRoot!==this.projectRoot?null:(n.requiredMcpTools=Array.isArray(n.requiredMcpTools)?n.requiredMcpTools:[],this.sessions.set(e,n),n)}catch{return null}}completeExecution(t){let e=this.get(t);if(!e)return null;let r={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),r}removeSession(t){let e=t.trim();e&&(this.sessions.delete(e),rmSync(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(t){return join(this.sessionsDir,`${Y(t)}.json`)}persist(t){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(t.sessionId),r=`${e}.${process.pid}.tmp`;writeFileSync(r,JSON.stringify(t),{mode:384}),renameSync(r,e);}};function P(i,t){return t?`evt_tool_${createHash("sha256").update(`${i}\0${t}`).digest("hex").slice(0,24)}`:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`}var te=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",re=getLocalApiKey(),_=process.env.CLAUDE_PROJECT_DIR||process.cwd();function v(i,t){let e=Number(i);return Number.isInteger(e)&&e>=0?e:t}function se(i,t,e){return new Promise((r,s)=>{let n=JSON.stringify(t),o=new URL(i,te),a=false,c=l=>{a||(a=true,l?s(l):r());},u=request({hostname:o.hostname,port:o.port,path:o.pathname+o.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":re,"Content-Length":Buffer.byteLength(n)},timeout:e},l=>{l.on("error",c),l.on("end",()=>{let d=l.statusCode??0;if(d<200||d>=300){c(new Error(`HTTP ${d}`));return}c();}),l.resume();});u.on("error",c),u.on("timeout",()=>{u.destroy(new Error(`PostToolUseFailure request timed out after ${e}ms`));}),u.write(n),u.end();})}function ne(i){return new Promise(t=>setTimeout(t,i))}async function oe(i,t){let e=t.timeoutMs??v(process.env.DEVFLOW_FAILURE_TIMEOUT_MS,1500),r=t.maxRetries??v(process.env.DEVFLOW_FAILURE_MAX_RETRIES,2),s=t.retryDelayMs??v(process.env.DEVFLOW_FAILURE_RETRY_DELAY_MS,100),n=`/api/memory/session-events?rootPath=${encodeURIComponent(_)}`;for(let o=0;o<=r;o+=1)try{await se(n,i,e);return}catch(a){if(o===r)throw a;await ne(s*2**o);}}async function ie(i,t={}){let e;try{e=JSON.parse(i);}catch{return}let r=e.tool_name||"",s=e.tool_input||{},n=e.error||"",o=e.session_id?.trim();if(!o)return;let a=new f(_).registerSession(o),c=P(o,e.tool_use_id),u=classifyWorkflowFailure({stepId:r,toolName:r,error:new Error(n||`${r||"tool"} failed`),errorStack:n||void 0}),l=typeof s=="object"&&s.command?s.command:r,d={id:`memory:${c}`,sessionId:o,tool:r,command:l,exitCode:-1,stderr:n,durationMs:0,createdAt:Date.now(),kind:r==="Bash"?"bash_error":"tool_use",payload:{command:l,exitCode:-1,stderr:n,failureSource:"PostToolUseFailure",failureCategory:u.category}},A=!t.telemetryClient,y=t.telemetryClient??new g,p=r.startsWith("mcp__"),O={eventId:c,executionId:a.executionId??o,sessionId:o,projectRoot:_,toolUseId:e.tool_use_id,timestamp:d.createdAt,toolName:r,toolType:p?"mcp":"direct",isMcpTool:p,mcpToolName:p?r:void 0,mcpEnforced:p,mcpFallback:false,kind:"error",input:s,duration:0,error:n,blocked:false,failureCategory:u.category};try{await y.sendEvent(O),await y.completeEvent({eventId:c,sessionId:o,error:n,duration:0,completedAt:Date.now()});}finally{A&&y.close();}t.eventDelivery?await t.eventDelivery(d):await oe(d,t);}if(process.argv[1]?.endsWith("post-tool-use-failure")||process.argv[1]?.endsWith("post-tool-use-failure.js")){let i="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{i+=t;}),process.stdin.on("end",()=>{ie(i.trim()||process.argv[2]||"").catch(t=>{console.error("[devflow] PostToolUseFailure delivery failed:",t.message),process.exitCode=1;});}),process.stdin.on("error",t=>{console.error("[devflow] PostToolUseFailure input failed:",t.message),process.exitCode=1;});}
1
+ import {request}from'http';import {getLocalApiKey}from'@devflow-tools/sdk/auth';import {classifyWorkflowFailure}from'@devflow-tools/workflow-engine/failure-classifier';import {existsSync,readFileSync,rmSync,mkdirSync,writeFileSync,renameSync,readdirSync,unlinkSync}from'fs';import {join}from'path';import {homedir}from'os';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {getLocalApiKey as getLocalApiKey$1,getProjectStateDir}from'@devflow-tools/sdk';import {randomUUID,createHash}from'crypto';var S=1e4,j=3e4,h=500;function L(o,t,e){return new Promise(r=>{try{let s=new URL(o),n=request({hostname:s.hostname,port:s.port||80,path:s.pathname+s.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(t)},timeout:5e3},i=>{let a=[];i.on("data",c=>a.push(c)),i.on("end",()=>{let c=Buffer.concat(a).toString();r(i.statusCode!=null&&i.statusCode>=200&&i.statusCode<300?c:null);});});n.on("error",()=>r(null)),n.on("timeout",()=>{n.destroy(),r(null);}),n.write(t),n.end();}catch{r(null);}})}var g=class{constructor(t){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??getLocalApiKey$1(),this.cacheDir=t?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=t?.legacyCacheDir??(t?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=t?.database??null,this.ownsDatabase=!t?.database;let e=t?.busyTimeoutMs??Number(process.env.DEVFLOW_TELEMETRY_DB_BUSY_TIMEOUT_MS);this.busyTimeoutMs=Number.isSafeInteger(e)&&e>=0?e:100;}async sendEvent(t){let e={...t,input:this.truncateInput(t.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),t.eventId):null}async sendExecutionStart(t,e,r,s,n=process.env.CLAUDE_PROJECT_DIR??process.cwd(),i=[]){let a={executionId:t,sessionId:e,skillName:r,startedAt:s,projectRoot:n,requiredMcpTools:i};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(t,e="completed",r=Date.now(),s){let n={executionId:t,status:e,finishedAt:r,metadata:s},i=this.commitOrCache("execution_complete",n);return i&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",n),i}async sendSessionStart(t,e,r){let s={id:t,projectRoot:e,startedAt:r,label:e.split("/").pop()??"unknown"},n=this.commitOrCache("session_start",s);return n&&this.postHttp("/api/telemetry/sessions",s),n}async completeEvent(t){let e=this.commitOrCache("complete_event",t);return e&&(this.postHttp("/api/telemetry/tool-call/output",t),this.scheduleMetricAggregation()),e}async endSession(t,e=Date.now()){let r={sessionId:t,finishedAt:e},s=this.commitOrCache("session_end",r);return s&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),s}async flushCache(){try{let r=this.getDatabase(),s=r.listTelemetryFailures({unresolvedOnly:!0,limit:h}).reverse();for(let n of s)try{this.applyOperation(n.operation,n.payload),r.resolveTelemetryFailure(n.id);}catch{}r.trimTelemetryFailures(h);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(r=>!!r))].filter(r=>existsSync(r)).flatMap(r=>readdirSync(r).filter(s=>s.endsWith(".json")).map(s=>{let n=join(r,s);try{return {cacheFile:n,envelope:$(JSON.parse(readFileSync(n,"utf8")),s)}}catch{return null}})).filter(r=>r!==null).sort((r,s)=>r.envelope.timestamp-s.envelope.timestamp||w(r.envelope.operation)-w(s.envelope.operation));for(let{cacheFile:r,envelope:s}of e)try{let n=this.getDatabase();n.insertTelemetryFailure({id:s.id,operation:s.operation,payload:s.payload,error:s.failure,createdAt:s.timestamp}),this.applyOperation(s.operation,s.payload),n.resolveTelemetryFailure(s.id),unlinkSync(r);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:this.busyTimeoutMs}),this.database}commitOrCache(t,e){try{return this.applyOperation(t,e),!0}catch(r){return this.cacheOperation(t,e,r),false}}applyOperation(t,e){let r=this.getDatabase();switch(t){case "tool_call":this.ensureParentSession(r,e),r.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(r,e),r.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running",requiredMcpTools:e.requiredMcpTools});return;case "execution_complete":r.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":r.ensureSession(e),r.insertRun({id:R(e.id),source:"hook",tool:"session",input:{projectRoot:e.projectRoot},status:"active",startedAt:e.startedAt,tokenUsed:0,metadata:{sessionId:e.id,projectRoot:e.projectRoot}});return;case "complete_event":if(!r.updateToolCallEvent(e.eventId,{output:e.output===void 0?void 0:JSON.stringify(e.output),error:e.error,duration:e.duration}))throw new Error(`Tool call event ${e.eventId} is not available for completion`);return;case "session_end":r.closeSession(e.sessionId,e.finishedAt),r.updateRun(R(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(t,e){let r=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!r)throw new Error("Canonical session ID is required for telemetry");let s=typeof e.projectRoot=="string"&&e.projectRoot.trim()?e.projectRoot:typeof e.input?.projectRoot=="string"&&e.input.projectRoot.trim()?e.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";t.ensureSession({id:r,projectRoot:s,label:s==="unknown"?void 0:s.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(t,e,r){let s=Date.now(),n={id:`failure:${s}:${Math.random().toString(36).slice(2,11)}`,operation:t,payload:e,failure:r instanceof Error?r.message:String(r),timestamp:s};try{let i=this.getDatabase();i.insertTelemetryFailure({id:n.id,operation:t,payload:e,error:n.failure,createdAt:s}),i.trimTelemetryFailures(h),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let i=join(this.cacheDir,`${s}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(i,JSON.stringify(n,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let t=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of t.slice(0,Math.max(0,t.length-h)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},j).unref());}scheduleMetricAggregation(){if(this.metricAggregationScheduled||process.env.DEVFLOW_HOOK_DEGRADED==="1")return;this.metricAggregationScheduled=true,setTimeout(()=>{this.metricAggregationScheduled=false;try{this.getDatabase().aggregatePendingToolMetrics(50)===50&&this.scheduleMetricAggregation();}catch{}},25).unref();}postHttp(t,e){L(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=S?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,S)}...`}}};function w(o){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(o)}function R(o){return `hook-run:${o}`}function $(o,t){if(!o||typeof o!="object")throw new Error("Invalid telemetry cache envelope");let e=o;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let r=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!r)throw new Error("Unknown legacy telemetry cache operation");let s=e.payload??{},n=Number(s.timestamp??s.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:r,payload:s,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:n}}function J(o){return o??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function I(o){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(J(o))}function X(o){return Buffer.from(o,"utf8").toString("base64url")}var f=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(I(t),"hook-sessions");}registerSession(t){let e=t.trim();if(!e)throw new Error("session_id_required");let r=this.get(e);if(r)return r.lastActivityAt=Date.now(),this.persist(r),r;let s=Date.now(),n={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:s,lastActivityAt:s};return this.sessions.set(e,n),this.persist(n),n}startExecution(t,e,r){let s=this.registerSession(t);return (!s.executionId||s.skillName!==e)&&(s.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),s.skillName=e,s.requiredMcpTools=[...new Set(r)],s.lastActivityAt=Date.now(),this.persist(s),s}get(t){let e=t.trim();if(!e)return null;let r=this.sessions.get(e);if(r)return r;let s=this.snapshotPath(e);if(!existsSync(s))return null;try{let n=JSON.parse(readFileSync(s,"utf8"));return n.sessionId!==e||n.projectRoot!==this.projectRoot?null:(n.requiredMcpTools=Array.isArray(n.requiredMcpTools)?n.requiredMcpTools:[],this.sessions.set(e,n),n)}catch{return null}}completeExecution(t){let e=this.get(t);if(!e)return null;let r={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),r}removeSession(t){let e=t.trim();e&&(this.sessions.delete(e),rmSync(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(t){return join(this.sessionsDir,`${X(t)}.json`)}persist(t){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(t.sessionId),r=`${e}.${process.pid}.tmp`;writeFileSync(r,JSON.stringify(t),{mode:384}),renameSync(r,e);}};function O(o,t){return t?`evt_tool_${createHash("sha256").update(`${o}\0${t}`).digest("hex").slice(0,24)}`:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`}var te=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",re=getLocalApiKey(),_=process.env.CLAUDE_PROJECT_DIR||process.cwd();function v(o,t){let e=Number(o);return Number.isInteger(e)&&e>=0?e:t}function se(o,t,e){return new Promise((r,s)=>{let n=JSON.stringify(t),i=new URL(o,te),a=false,c=l=>{a||(a=true,l?s(l):r());},u=request({hostname:i.hostname,port:i.port,path:i.pathname+i.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":re,"Content-Length":Buffer.byteLength(n)},timeout:e},l=>{l.on("error",c),l.on("end",()=>{let d=l.statusCode??0;if(d<200||d>=300){c(new Error(`HTTP ${d}`));return}c();}),l.resume();});u.on("error",c),u.on("timeout",()=>{u.destroy(new Error(`PostToolUseFailure request timed out after ${e}ms`));}),u.write(n),u.end();})}function ne(o){return new Promise(t=>setTimeout(t,o))}async function oe(o,t){let e=t.timeoutMs??v(process.env.DEVFLOW_FAILURE_TIMEOUT_MS,1500),r=t.maxRetries??v(process.env.DEVFLOW_FAILURE_MAX_RETRIES,2),s=t.retryDelayMs??v(process.env.DEVFLOW_FAILURE_RETRY_DELAY_MS,100),n=`/api/memory/session-events?rootPath=${encodeURIComponent(_)}`;for(let i=0;i<=r;i+=1)try{await se(n,o,e);return}catch(a){if(i===r)throw a;await ne(s*2**i);}}async function ie(o,t={}){let e;try{e=JSON.parse(o);}catch{return}let r=e.tool_name||"",s=e.tool_input||{},n=e.error||"",i=e.session_id?.trim();if(!i)return;let a=new f(_).registerSession(i),c=O(i,e.tool_use_id),u=classifyWorkflowFailure({stepId:r,toolName:r,error:new Error(n||`${r||"tool"} failed`),errorStack:n||void 0}),l=typeof s=="object"&&s.command?s.command:r,d={id:`memory:${c}`,sessionId:i,tool:r,command:l,exitCode:-1,stderr:n,durationMs:0,createdAt:Date.now(),kind:r==="Bash"?"bash_error":"tool_use",payload:{command:l,exitCode:-1,stderr:n,failureSource:"PostToolUseFailure",failureCategory:u.category}},P=!t.telemetryClient,y=t.telemetryClient??new g,p=r.startsWith("mcp__"),A={eventId:c,executionId:a.executionId??i,sessionId:i,projectRoot:_,toolUseId:e.tool_use_id,timestamp:d.createdAt,toolName:r,toolType:p?"mcp":"direct",isMcpTool:p,mcpToolName:p?r:void 0,mcpEnforced:p,mcpFallback:false,kind:"error",input:s,duration:0,error:n,blocked:false,failureCategory:u.category};try{await y.sendEvent(A),await y.completeEvent({eventId:c,sessionId:i,error:n,duration:0,completedAt:Date.now()});}finally{P&&y.close();}t.eventDelivery?await t.eventDelivery(d):await oe(d,t);}if(process.argv[1]?.endsWith("post-tool-use-failure")||process.argv[1]?.endsWith("post-tool-use-failure.js")){let o=console.log.bind(console);console.log=console.error.bind(console),console.info=console.error.bind(console);let t="";process.stdin.setEncoding("utf8"),process.stdin.on("data",e=>{t+=e;}),process.stdin.on("end",async()=>{try{await ie(t.trim()||process.argv[2]||""),o(JSON.stringify({status:"ok"}));}catch(e){console.error("[devflow] PostToolUseFailure delivery failed:",e.message),o(JSON.stringify({status:"degraded",reason:"post_tool_failure_delivery_failed",fallback:"durable_retry_on_next_runtime"}));}}),process.stdin.on("error",e=>{console.error("[devflow] PostToolUseFailure input failed:",e.message),o(JSON.stringify({status:"degraded",reason:"post_tool_failure_input_failed",fallback:"host_event_continues"}));});}
2
2
  export{ie as handlePostToolUseFailure};
@@ -1,3 +1,3 @@
1
- import {existsSync,readFileSync,rmSync,mkdirSync,writeFileSync,renameSync,readdirSync,unlinkSync,appendFileSync}from'fs';import {join,dirname}from'path';import {getLogWriter}from'@devflow-tools/telemetry';import {request}from'http';import {homedir}from'os';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {randomUUID,createHash}from'crypto';import {fileURLToPath}from'url';function H(o,t,e){try{let n=openGlobalDevFlowDatabase();try{n.insertEvent({kind:o,timestamp:Date.now(),duration:e,success:t.success!==!1,metadata:t});}finally{n.close();}}catch{}}var M=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",_e=getLocalApiKey(),we=8,be=6e4,W=new Map;function Se(o){let t=new URL(o,M).pathname,e=W.get(t);return e||(e={failures:0,lastFailure:0,openUntil:0,status:"closed"},W.set(t,e)),e}function F(o,t,e){if(t.status===e)return;let n=t.status;t.status=e,H("http_circuit_transition",{path:new URL(o,M).pathname,previous:n,status:e,failures:t.failures,openUntil:t.openUntil});}function A(o,t){t.failures++,t.lastFailure=Date.now(),t.failures>=we&&(t.openUntil=Date.now()+be,F(o,t,"open"));}function Re(o,t){let e=t.status!=="closed";t.failures=0,t.openUntil=0,e&&F(o,t,"closed");}var O=join(homedir(),".devflow","errors");function J(o,t){try{existsSync(O)||mkdirSync(O,{recursive:!0});let e=`${new Date().toISOString()} | ${o} | ${t?.message??String(t)}
2
- `;appendFileSync(join(O,"http-errors.log"),e);}catch{}}function B(o,t,e={}){let n=e.timeout??5e3,r=e.maxRetries??2,s=e.circuitBreaker??true,i=Se(o);if(s&&i.openUntil>Date.now())return Promise.resolve();s&&i.status==="open"&&F(o,i,"half_open");let a=c=>new Promise(l=>{let u=JSON.stringify(t),d=new URL(o,M),f=request({hostname:d.hostname,port:d.port,path:d.pathname+d.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":_e,"Content-Length":Buffer.byteLength(u)},timeout:n},g=>{if(g.resume(),g.statusCode&&g.statusCode>=400){let y=new Error(`HTTP ${g.statusCode}`);if(J(o,y),c>0){let b=Math.min(1e3*Math.pow(2,r-c),8e3);setTimeout(()=>{a(c-1).then(l);},b);}else A(o,i),l();return}Re(o,i),l();});f.on("error",g=>{if(J(o,g),c>0){let y=Math.min(1e3*Math.pow(2,r-c),8e3);setTimeout(()=>{a(c-1).then(l);},y);}else A(o,i),l();}),f.on("timeout",()=>{f.destroy(),c>0?a(c-1).then(l):(A(o,i),l());}),f.write(u),f.end();});return a(r)}function j(o){return o??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function h(o){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(j(o))}function R(o){if(!o||typeof o!="object"||Array.isArray(o))return {};let t=o,e={};for(let n of ["lastMcpCall","bypassCount"])if(n in t){let r=t[n];if(r==null)continue;e[n]=typeof r=="number"&&Number.isFinite(r)&&r>=0?r:0;}return e}function Ee(o,t){return o.lastMcpCall===t.lastMcpCall&&o.bypassCount===t.bypassCount}function ke(o){try{return existsSync(o)?R(JSON.parse(readFileSync(o,"utf-8"))):{}}catch{return {}}}function Ie(o,t){let e=t instanceof Error?t.message:String(t);console.error(`[devflow] Receipt ${o} skipped: ${e}`);}function K(o){let t=h(o);for(let e of ["receipt.json","receipt-lock.sqlite","receipt-lock.sqlite-shm","receipt-lock.sqlite-wal"])try{existsSync(join(t,e))&&unlinkSync(join(t,e));}catch{}}function Te(o,t){if(o.getHookReceipt(t)){K(t);return}let e=join(h(t),"receipt.json");if(!existsSync(e))return;let n=ke(e);o.updateHookReceipt(t,()=>n),K(t);}function Y(o,t,e){let n;try{n=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),Te(n,o);let r={},s=n.updateHookReceipt(o,a=>(r=R(a),R(t(r)))),i=R(s);return {receipt:i,applied:!0,changed:!Ee(r,i)}}catch(r){return Ie(e,r),{receipt:{},applied:false,changed:false}}finally{try{n?.close();}catch{}}}function U(o,t){return Y(o,()=>t,"write").applied}function L(o,t){return Y(o,t,"update")}var z={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",Bash:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function Ae(o){switch(o){case "WebSearch":case "WebFetch":return 1;case "Agent":return 3;case "Grep":case "Glob":return 3;case "Bash":return 5;default:return 2}}var D=class{constructor(t,e,n){this.projectRoot=t;this.sessionId=e;this.executionId=n;}getPhase(){if(!this.sessionId||!this.executionId)return "idle";let t;try{t=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250});let e=t.getContextReceipt(this.projectRoot,this.sessionId,this.executionId);if(e&&e.expiresAt>Date.now())return "context_ready";e&&t.deleteContextReceipt(this.projectRoot,this.sessionId,this.executionId);}catch{}finally{try{t?.close();}catch{}}return "context_gathering"}evaluate(t,e){let n=this.getPhase();if(e||t==="Skill")return {permissionDecision:"allow"};if(n==="context_gathering"&&z[t])return {permissionDecision:"deny",reason:`DevFlow context required. Call mcp__devflow__get_project_context, then retry ${t}.`};if(n==="context_ready")return {permissionDecision:"allow"};let r=z[t];if(!r)return {permissionDecision:"allow"};let s=L(this.projectRoot,c=>({...c,bypassCount:(c.bypassCount??0)+1}));if(!s.applied)return {permissionDecision:"allow"};let i=s.receipt.bypassCount??0,a=Ae(t);return i>=a?{permissionDecision:"allow",additionalContext:`\u5DF2 ${i} \u6B21\u76F4\u63A5\u4F7F\u7528 ${t}\uFF0C\u5EFA\u8BAE\u7528 mcp__devflow__${r} \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`}:{permissionDecision:"allow"}}recordMcpCall(){U(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}};var te=1e4,Ue=3e4,C=500;function Le(o,t,e){return new Promise(n=>{try{let r=new URL(o),s=request({hostname:r.hostname,port:r.port||80,path:r.pathname+r.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(t)},timeout:5e3},i=>{let a=[];i.on("data",c=>a.push(c)),i.on("end",()=>{let c=Buffer.concat(a).toString();n(i.statusCode!=null&&i.statusCode>=200&&i.statusCode<300?c:null);});});s.on("error",()=>n(null)),s.on("timeout",()=>{s.destroy(),n(null);}),s.write(t),s.end();}catch{n(null);}})}var x=class{constructor(t){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??getLocalApiKey(),this.cacheDir=t?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=t?.legacyCacheDir??(t?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=t?.database??null,this.ownsDatabase=!t?.database;}async sendEvent(t){let e={...t,input:this.truncateInput(t.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),t.eventId):null}async sendExecutionStart(t,e,n,r,s=process.env.CLAUDE_PROJECT_DIR??process.cwd(),i=[]){let a={executionId:t,sessionId:e,skillName:n,startedAt:r,projectRoot:s,requiredMcpTools:i};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(t,e="completed",n=Date.now(),r){let s={executionId:t,status:e,finishedAt:n,metadata:r},i=this.commitOrCache("execution_complete",s);return i&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",s),i}async sendSessionStart(t,e,n){let r={id:t,projectRoot:e,startedAt:n,label:e.split("/").pop()??"unknown"},s=this.commitOrCache("session_start",r);return s&&this.postHttp("/api/telemetry/sessions",r),s}async completeEvent(t){let e=this.commitOrCache("complete_event",t);return e&&(this.postHttp("/api/telemetry/tool-call/output",t),this.scheduleMetricAggregation()),e}async endSession(t,e=Date.now()){let n={sessionId:t,finishedAt:e},r=this.commitOrCache("session_end",n);return r&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),r}async flushCache(){try{let n=this.getDatabase(),r=n.listTelemetryFailures({unresolvedOnly:!0,limit:C}).reverse();for(let s of r)try{this.applyOperation(s.operation,s.payload),n.resolveTelemetryFailure(s.id);}catch{}n.trimTelemetryFailures(C);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(n=>!!n))].filter(n=>existsSync(n)).flatMap(n=>readdirSync(n).filter(r=>r.endsWith(".json")).map(r=>{let s=join(n,r);try{return {cacheFile:s,envelope:$e(JSON.parse(readFileSync(s,"utf8")),r)}}catch{return null}})).filter(n=>n!==null).sort((n,r)=>n.envelope.timestamp-r.envelope.timestamp||ne(n.envelope.operation)-ne(r.envelope.operation));for(let{cacheFile:n,envelope:r}of e)try{let s=this.getDatabase();s.insertTelemetryFailure({id:r.id,operation:r.operation,payload:r.payload,error:r.failure,createdAt:r.timestamp}),this.applyOperation(r.operation,r.payload),s.resolveTelemetryFailure(r.id),unlinkSync(n);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(t,e){try{return this.applyOperation(t,e),!0}catch(n){return this.cacheOperation(t,e,n),false}}applyOperation(t,e){let n=this.getDatabase();switch(t){case "tool_call":this.ensureParentSession(n,e),n.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(n,e),n.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running",requiredMcpTools:e.requiredMcpTools});return;case "execution_complete":n.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":n.ensureSession(e),n.insertRun({id:re(e.id),source:"hook",tool:"session",input:{projectRoot:e.projectRoot},status:"active",startedAt:e.startedAt,tokenUsed:0,metadata:{sessionId:e.id,projectRoot:e.projectRoot}});return;case "complete_event":if(!n.updateToolCallEvent(e.eventId,{output:e.output===void 0?void 0:JSON.stringify(e.output),error:e.error,duration:e.duration}))throw new Error(`Tool call event ${e.eventId} is not available for completion`);return;case "session_end":n.closeSession(e.sessionId,e.finishedAt),n.updateRun(re(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(t,e){let n=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!n)throw new Error("Canonical session ID is required for telemetry");let r=typeof e.projectRoot=="string"&&e.projectRoot.trim()?e.projectRoot:typeof e.input?.projectRoot=="string"&&e.input.projectRoot.trim()?e.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";t.ensureSession({id:n,projectRoot:r,label:r==="unknown"?void 0:r.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(t,e,n){let r=Date.now(),s={id:`failure:${r}:${Math.random().toString(36).slice(2,11)}`,operation:t,payload:e,failure:n instanceof Error?n.message:String(n),timestamp:r};try{let i=this.getDatabase();i.insertTelemetryFailure({id:s.id,operation:t,payload:e,error:s.failure,createdAt:r}),i.trimTelemetryFailures(C),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let i=join(this.cacheDir,`${r}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(i,JSON.stringify(s,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let t=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of t.slice(0,Math.max(0,t.length-C)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},Ue).unref());}scheduleMetricAggregation(){if(this.metricAggregationScheduled||process.env.DEVFLOW_HOOK_DEGRADED==="1")return;this.metricAggregationScheduled=true,setTimeout(()=>{this.metricAggregationScheduled=false;try{this.getDatabase().aggregatePendingToolMetrics(50)===50&&this.scheduleMetricAggregation();}catch{}},25).unref();}postHttp(t,e){Le(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=te?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,te)}...`}}};function ne(o){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(o)}function re(o){return `hook-run:${o}`}function $e(o,t){if(!o||typeof o!="object")throw new Error("Invalid telemetry cache envelope");let e=o;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let n=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!n)throw new Error("Unknown legacy telemetry cache operation");let r=e.payload??{},s=Number(r.timestamp??r.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:n,payload:r,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:s}}function ze(o){return Buffer.from(o,"utf8").toString("base64url")}var E=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(h(t),"hook-sessions");}registerSession(t){let e=t.trim();if(!e)throw new Error("session_id_required");let n=this.get(e);if(n)return n.lastActivityAt=Date.now(),this.persist(n),n;let r=Date.now(),s={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:r,lastActivityAt:r};return this.sessions.set(e,s),this.persist(s),s}startExecution(t,e,n){let r=this.registerSession(t);return (!r.executionId||r.skillName!==e)&&(r.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),r.skillName=e,r.requiredMcpTools=[...new Set(n)],r.lastActivityAt=Date.now(),this.persist(r),r}get(t){let e=t.trim();if(!e)return null;let n=this.sessions.get(e);if(n)return n;let r=this.snapshotPath(e);if(!existsSync(r))return null;try{let s=JSON.parse(readFileSync(r,"utf8"));return s.sessionId!==e||s.projectRoot!==this.projectRoot?null:(s.requiredMcpTools=Array.isArray(s.requiredMcpTools)?s.requiredMcpTools:[],this.sessions.set(e,s),s)}catch{return null}}completeExecution(t){let e=this.get(t);if(!e)return null;let n={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),n}removeSession(t){let e=t.trim();e&&(this.sessions.delete(e),rmSync(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(t){return join(this.sessionsDir,`${ze(t)}.json`)}persist(t){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(t.sessionId),n=`${e}.${process.pid}.tmp`;writeFileSync(n,JSON.stringify(t),{mode:384}),renameSync(n,e);}};var Qe=2,et=1;function tt(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join(dirname(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function ie(o=tt()){let t=[join(o,"dist","command-registry.json"),join(o,"command-registry.json")];for(let e of t)try{if(!existsSync(e))continue;let n=JSON.parse(readFileSync(e,"utf8"));if(n.version!==Qe&&n.version!==et||!nt(n.commands)){console.error(`[devflow] command registry contract mismatch: ${e}`);continue}return n.commands}catch(n){console.error(`[devflow] command registry load failed: ${n.message}`);}return null}function nt(o){return !o||typeof o!="object"||Array.isArray(o)?false:Object.values(o).every(t=>{if(!t||typeof t!="object"||Array.isArray(t))return false;let e=t;return se(e.mcpTools)&&se(e.blockedNative)&&(e.source===void 0||e.source==="core"||e.source==="plugin")&&(e.requiresContext===void 0||typeof e.requiresContext=="boolean")&&(e.evidenceMode===void 0||["static","runtime","mixed"].includes(e.evidenceMode))})}function se(o){return Array.isArray(o)&&o.every(t=>typeof t=="string"&&t.length>0)}function ae(o){let t=ie();return t?t[o]?.mcpTools??[]:[]}function ce(o){let t=o.trim().replace(/^\//,"");if(!t.startsWith("devflow:"))return null;let e=t.slice(8).replace(/^devflow-/,"");return /^[a-z0-9][a-z0-9-]*$/i.test(e)?`devflow:${e.toLowerCase()}`:null}function le(o,t){return t?`evt_tool_${createHash("sha256").update(`${o}\0${t}`).digest("hex").slice(0,24)}`:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`}var ut=new Set(["Agent","Bash","Glob","Grep","WebSearch","WebFetch"]);function pt(o,t,e,n){try{let r=join(h(o),"event-map.json");if(!existsSync(r))return null;let s=JSON.parse(readFileSync(r,"utf-8")),i=s.filter(l=>l.toolName===e&&(!l.sessionId||l.sessionId===t));if(i.length===0)return null;let a=n?i.find(l=>l.toolUseId===n):i.sort((l,u)=>l.timestamp-u.timestamp)[0];if(!a)return null;let c=s.findIndex(l=>l.eventId===a.eventId);return c!==-1&&s.splice(c,1),writeFileSync(r,JSON.stringify(s)),{eventId:a.eventId,timestamp:a.timestamp}}catch{return null}}function ue(o){if(!o)return 0;let t=o;if(typeof o=="string")try{t=JSON.parse(o);}catch{return 1}let e=t,n=["files","results","data","result","memories","nodes","chunks","findings","symbols","keySymbols"],r=["reasoning","riskHints","nextActions"],s=Array.isArray(e.data)?e.data:e.structuredContent??e;if(Array.isArray(s))return s.length;if(typeof s=="object"&&s!==null){let i=s,a=0,c=false;for(let u of n)Array.isArray(i[u])&&(c=true,a+=i[u].length);for(let u of r)Array.isArray(i[u])&&(a+=i[u].length);for(let[,u]of Object.entries(i))u&&typeof u=="object"&&!Array.isArray(u)&&(a+=ue(u));let l=!!(i._devflow||i._devflow_unique||i.taskType);return a===0&&l&&!c?1:a>0?a:c?0:Object.keys(i).length>0?1:0}return 0}function dt(o,t){if(!ut.has(t))return null;let e=L(o,r=>({...r,bypassCount:(r.bypassCount??0)+1}));if(!e.applied)return null;let n=e.receipt.bypassCount??0;return n>20&&n%10===0?`MCP \u5DE5\u5177\u4F7F\u7528\u7387\u4F4E\uFF1A${n} \u6B21\u76F4\u63A5\u5DE5\u5177\u8C03\u7528\u3002\u5EFA\u8BAE\u4F7F\u7528 get_project_context \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`:null}async function mt(o){if(!o)return null;let t;try{t=JSON.parse(o);}catch{return null}let e=t.tool_name||"",n=j(),r=t.tool_input||{},s=t.tool_response||{},i=t.session_id?.trim()||null,a=new E(n);if(i&&a.registerSession(i),e==="Skill"){let p=typeof r=="object"&&r.skill?r.skill:"",m=ce(p);i&&m&&(a.startExecution(i,m,ae(m)),U(n,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0}));}e.startsWith("mcp__")&&new D(n).recordMcpCall();let c=i?a.get(i):null,l=i?pt(n,i,e,t.tool_use_id):null,u=i&&t.tool_use_id&&!l?(()=>{try{let p=openGlobalDevFlowDatabase();try{return p.getToolCallEventByToolUseId(i,t.tool_use_id)}finally{p.close();}}catch{return null}})():null,d=l??u,f=typeof s=="object"&&s!==null?s:null,g=JSON.stringify(s),y=Buffer.byteLength(g,"utf8"),b=ue(s);if(c?.executionId!==void 0||e.startsWith("mcp__devflow__")||e==="Skill"){let p=getLogWriter(n),m=c?.executionId??`auto-${i??Date.now()}`;p.toolCall({tool:e,runId:m,stepId:`step-${m}`,input:r,output:s,duration:d?Math.max(0,Date.now()-d.timestamp):0,success:!f?.error,resultCount:b,resultSizeBytes:y,error:f?.error});}if(d&&i){let p=typeof s=="string"?s:JSON.stringify(s),m=p.length>5e3?{_truncated:true,_originalSize:p.length,text:p.slice(0,5e3)}:s;await new x().completeEvent({eventId:d.eventId,sessionId:i,output:m,duration:d?Math.max(0,Date.now()-d.timestamp):0,completedAt:Date.now()});}let k=typeof r=="object"&&r.command?r.command:"",S=f?.exitCode,q=f?.stderr,pe=/^\s*(ls|cat|pwd|cd|echo|head|tail|wc|which|whoami|date|env|printenv|id|hostname|uname)\b/,v,_,I=true;if(e.startsWith("mcp__"))v="mcp_call",_={mcpTool:e.replace(/^mcp__[^_]+__/,""),query:typeof r=="object"&&r.query?r.query:null,resultCount:b,resultSizeBytes:y};else if(e==="Read"||e==="Write"||e==="Edit")v=e==="Read"?"file_read":"file_write",_={filePath:typeof r=="object"&&r.file_path?r.file_path:null,fileContentSize:y};else if(e==="Bash"){let p=pe.test(k),m=S!==void 0&&S!==0;I=!p||m,v=m?"bash_error":"bash_command",_={command:k||e,exitCode:S??null,stderr:q??null};}else e==="Agent"?(v="subagent",_={subagentType:typeof r=="object"&&r.subagent_type?r.subagent_type:null,description:typeof r=="object"&&r.description?String(r.description).slice(0,200):null}):e==="WebSearch"||e==="WebFetch"?(v=e==="WebSearch"?"web_search":"web_fetch",_={query:typeof r=="object"&&r.query?String(r.query).slice(0,200):null}):(I=false,v="tool_use",_={});if(I){let p={id:t.tool_use_id?`memory:${le(i??"",t.tool_use_id)}`:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:i??"",tool:e,kind:v,payload:_,command:k||void 0,exitCode:S,stderr:q??void 0,durationMs:d?Math.max(0,Date.now()-d.timestamp):0,createdAt:Date.now()};i&&B(`/api/memory/session-events?rootPath=${encodeURIComponent(n)}`,p);}let T=dt(n,e);return T&&console.error("[devflow] Enforcement:",T),T}if(process.argv[1]?.endsWith("post-tool-use")||process.argv[1]?.endsWith("post-tool-use.js")){let o="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{o+=t;}),process.stdin.on("end",async()=>{let t=await mt(o.trim()||process.argv[2]||"");if(t){let e=JSON.stringify(t);console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:e}}));}}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),5e3).unref();}
1
+ import {existsSync,readFileSync,rmSync,mkdirSync,writeFileSync,renameSync,readdirSync,unlinkSync,appendFileSync}from'fs';import {join,dirname}from'path';import {getLogWriter}from'@devflow-tools/telemetry';import {request}from'http';import {homedir}from'os';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {randomUUID,createHash}from'crypto';import {fileURLToPath}from'url';function H(o,t,e){try{let n=openGlobalDevFlowDatabase();try{n.insertEvent({kind:o,timestamp:Date.now(),duration:e,success:t.success!==!1,metadata:t});}finally{n.close();}}catch{}}var M=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",be=getLocalApiKey(),_e=8,we=6e4,J=new Map;function Se(o){let t=new URL(o,M).pathname,e=J.get(t);return e||(e={failures:0,lastFailure:0,openUntil:0,status:"closed"},J.set(t,e)),e}function F(o,t,e){if(t.status===e)return;let n=t.status;t.status=e,H("http_circuit_transition",{path:new URL(o,M).pathname,previous:n,status:e,failures:t.failures,openUntil:t.openUntil});}function A(o,t){t.failures++,t.lastFailure=Date.now(),t.failures>=_e&&(t.openUntil=Date.now()+we,F(o,t,"open"));}function Re(o,t){let e=t.status!=="closed";t.failures=0,t.openUntil=0,e&&F(o,t,"closed");}var O=join(homedir(),".devflow","errors");function B(o,t){try{existsSync(O)||mkdirSync(O,{recursive:!0});let e=`${new Date().toISOString()} | ${o} | ${t?.message??String(t)}
2
+ `;appendFileSync(join(O,"http-errors.log"),e);}catch{}}function K(o,t,e={}){let n=e.timeout??5e3,r=e.maxRetries??2,s=e.circuitBreaker??true,i=Se(o);if(s&&i.openUntil>Date.now())return Promise.resolve();s&&i.status==="open"&&F(o,i,"half_open");let a=c=>new Promise(l=>{let u=JSON.stringify(t),d=new URL(o,M),f=request({hostname:d.hostname,port:d.port,path:d.pathname+d.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":be,"Content-Length":Buffer.byteLength(u)},timeout:n},g=>{if(g.resume(),g.statusCode&&g.statusCode>=400){let y=new Error(`HTTP ${g.statusCode}`);if(B(o,y),c>0){let w=Math.min(1e3*Math.pow(2,r-c),8e3);setTimeout(()=>{a(c-1).then(l);},w);}else A(o,i),l();return}Re(o,i),l();});f.on("error",g=>{if(B(o,g),c>0){let y=Math.min(1e3*Math.pow(2,r-c),8e3);setTimeout(()=>{a(c-1).then(l);},y);}else A(o,i),l();}),f.on("timeout",()=>{f.destroy(),c>0?a(c-1).then(l):(A(o,i),l());}),f.write(u),f.end();});return a(r)}function j(o){return o??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function h(o){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(j(o))}function R(o){if(!o||typeof o!="object"||Array.isArray(o))return {};let t=o,e={};for(let n of ["lastMcpCall","bypassCount"])if(n in t){let r=t[n];if(r==null)continue;e[n]=typeof r=="number"&&Number.isFinite(r)&&r>=0?r:0;}return e}function Ee(o,t){return o.lastMcpCall===t.lastMcpCall&&o.bypassCount===t.bypassCount}function ke(o){try{return existsSync(o)?R(JSON.parse(readFileSync(o,"utf-8"))):{}}catch{return {}}}function Ie(o,t){let e=t instanceof Error?t.message:String(t);console.error(`[devflow] Receipt ${o} skipped: ${e}`);}function z(o){let t=h(o);for(let e of ["receipt.json","receipt-lock.sqlite","receipt-lock.sqlite-shm","receipt-lock.sqlite-wal"])try{existsSync(join(t,e))&&unlinkSync(join(t,e));}catch{}}function Te(o,t){if(o.getHookReceipt(t)){z(t);return}let e=join(h(t),"receipt.json");if(!existsSync(e))return;let n=ke(e);o.updateHookReceipt(t,()=>n),z(t);}function X(o,t,e){let n;try{n=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),Te(n,o);let r={},s=n.updateHookReceipt(o,a=>(r=R(a),R(t(r)))),i=R(s);return {receipt:i,applied:!0,changed:!Ee(r,i)}}catch(r){return Ie(e,r),{receipt:{},applied:false,changed:false}}finally{try{n?.close();}catch{}}}function U(o,t){return X(o,()=>t,"write").applied}function L(o,t){return X(o,t,"update")}var V={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",Bash:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function Ae(o){switch(o){case "WebSearch":case "WebFetch":return 1;case "Agent":return 3;case "Grep":case "Glob":return 3;case "Bash":return 5;default:return 2}}var D=class{constructor(t,e,n){this.projectRoot=t;this.sessionId=e;this.executionId=n;}getPhase(){if(!this.sessionId||!this.executionId)return "idle";let t;try{t=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250});let e=t.getContextReceipt(this.projectRoot,this.sessionId,this.executionId);if(e&&e.expiresAt>Date.now())return "context_ready";e&&t.deleteContextReceipt(this.projectRoot,this.sessionId,this.executionId);}catch{}finally{try{t?.close();}catch{}}return "context_gathering"}evaluate(t,e){let n=this.getPhase();if(e||t==="Skill")return {permissionDecision:"allow"};if(n==="context_gathering"&&V[t])return {permissionDecision:"deny",reason:`DevFlow context required. Call mcp__devflow__get_project_context, then retry ${t}.`};if(n==="context_ready")return {permissionDecision:"allow"};let r=V[t];if(!r)return {permissionDecision:"allow"};let s=L(this.projectRoot,c=>({...c,bypassCount:(c.bypassCount??0)+1}));if(!s.applied)return {permissionDecision:"allow"};let i=s.receipt.bypassCount??0,a=Ae(t);return i>=a?{permissionDecision:"allow",additionalContext:`\u5DF2 ${i} \u6B21\u76F4\u63A5\u4F7F\u7528 ${t}\uFF0C\u5EFA\u8BAE\u7528 mcp__devflow__${r} \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`}:{permissionDecision:"allow"}}recordMcpCall(){U(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}};var ne=1e4,Ue=3e4,C=500;function Le(o,t,e){return new Promise(n=>{try{let r=new URL(o),s=request({hostname:r.hostname,port:r.port||80,path:r.pathname+r.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(t)},timeout:5e3},i=>{let a=[];i.on("data",c=>a.push(c)),i.on("end",()=>{let c=Buffer.concat(a).toString();n(i.statusCode!=null&&i.statusCode>=200&&i.statusCode<300?c:null);});});s.on("error",()=>n(null)),s.on("timeout",()=>{s.destroy(),n(null);}),s.write(t),s.end();}catch{n(null);}})}var x=class{constructor(t){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??getLocalApiKey(),this.cacheDir=t?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=t?.legacyCacheDir??(t?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=t?.database??null,this.ownsDatabase=!t?.database;let e=t?.busyTimeoutMs??Number(process.env.DEVFLOW_TELEMETRY_DB_BUSY_TIMEOUT_MS);this.busyTimeoutMs=Number.isSafeInteger(e)&&e>=0?e:100;}async sendEvent(t){let e={...t,input:this.truncateInput(t.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),t.eventId):null}async sendExecutionStart(t,e,n,r,s=process.env.CLAUDE_PROJECT_DIR??process.cwd(),i=[]){let a={executionId:t,sessionId:e,skillName:n,startedAt:r,projectRoot:s,requiredMcpTools:i};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(t,e="completed",n=Date.now(),r){let s={executionId:t,status:e,finishedAt:n,metadata:r},i=this.commitOrCache("execution_complete",s);return i&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",s),i}async sendSessionStart(t,e,n){let r={id:t,projectRoot:e,startedAt:n,label:e.split("/").pop()??"unknown"},s=this.commitOrCache("session_start",r);return s&&this.postHttp("/api/telemetry/sessions",r),s}async completeEvent(t){let e=this.commitOrCache("complete_event",t);return e&&(this.postHttp("/api/telemetry/tool-call/output",t),this.scheduleMetricAggregation()),e}async endSession(t,e=Date.now()){let n={sessionId:t,finishedAt:e},r=this.commitOrCache("session_end",n);return r&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),r}async flushCache(){try{let n=this.getDatabase(),r=n.listTelemetryFailures({unresolvedOnly:!0,limit:C}).reverse();for(let s of r)try{this.applyOperation(s.operation,s.payload),n.resolveTelemetryFailure(s.id);}catch{}n.trimTelemetryFailures(C);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(n=>!!n))].filter(n=>existsSync(n)).flatMap(n=>readdirSync(n).filter(r=>r.endsWith(".json")).map(r=>{let s=join(n,r);try{return {cacheFile:s,envelope:$e(JSON.parse(readFileSync(s,"utf8")),r)}}catch{return null}})).filter(n=>n!==null).sort((n,r)=>n.envelope.timestamp-r.envelope.timestamp||re(n.envelope.operation)-re(r.envelope.operation));for(let{cacheFile:n,envelope:r}of e)try{let s=this.getDatabase();s.insertTelemetryFailure({id:r.id,operation:r.operation,payload:r.payload,error:r.failure,createdAt:r.timestamp}),this.applyOperation(r.operation,r.payload),s.resolveTelemetryFailure(r.id),unlinkSync(n);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:this.busyTimeoutMs}),this.database}commitOrCache(t,e){try{return this.applyOperation(t,e),!0}catch(n){return this.cacheOperation(t,e,n),false}}applyOperation(t,e){let n=this.getDatabase();switch(t){case "tool_call":this.ensureParentSession(n,e),n.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(n,e),n.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running",requiredMcpTools:e.requiredMcpTools});return;case "execution_complete":n.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":n.ensureSession(e),n.insertRun({id:oe(e.id),source:"hook",tool:"session",input:{projectRoot:e.projectRoot},status:"active",startedAt:e.startedAt,tokenUsed:0,metadata:{sessionId:e.id,projectRoot:e.projectRoot}});return;case "complete_event":if(!n.updateToolCallEvent(e.eventId,{output:e.output===void 0?void 0:JSON.stringify(e.output),error:e.error,duration:e.duration}))throw new Error(`Tool call event ${e.eventId} is not available for completion`);return;case "session_end":n.closeSession(e.sessionId,e.finishedAt),n.updateRun(oe(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(t,e){let n=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!n)throw new Error("Canonical session ID is required for telemetry");let r=typeof e.projectRoot=="string"&&e.projectRoot.trim()?e.projectRoot:typeof e.input?.projectRoot=="string"&&e.input.projectRoot.trim()?e.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";t.ensureSession({id:n,projectRoot:r,label:r==="unknown"?void 0:r.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(t,e,n){let r=Date.now(),s={id:`failure:${r}:${Math.random().toString(36).slice(2,11)}`,operation:t,payload:e,failure:n instanceof Error?n.message:String(n),timestamp:r};try{let i=this.getDatabase();i.insertTelemetryFailure({id:s.id,operation:t,payload:e,error:s.failure,createdAt:r}),i.trimTelemetryFailures(C),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let i=join(this.cacheDir,`${r}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(i,JSON.stringify(s,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let t=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of t.slice(0,Math.max(0,t.length-C)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},Ue).unref());}scheduleMetricAggregation(){if(this.metricAggregationScheduled||process.env.DEVFLOW_HOOK_DEGRADED==="1")return;this.metricAggregationScheduled=true,setTimeout(()=>{this.metricAggregationScheduled=false;try{this.getDatabase().aggregatePendingToolMetrics(50)===50&&this.scheduleMetricAggregation();}catch{}},25).unref();}postHttp(t,e){Le(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=ne?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,ne)}...`}}};function re(o){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(o)}function oe(o){return `hook-run:${o}`}function $e(o,t){if(!o||typeof o!="object")throw new Error("Invalid telemetry cache envelope");let e=o;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let n=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!n)throw new Error("Unknown legacy telemetry cache operation");let r=e.payload??{},s=Number(r.timestamp??r.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:n,payload:r,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:s}}function ze(o){return Buffer.from(o,"utf8").toString("base64url")}var E=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(h(t),"hook-sessions");}registerSession(t){let e=t.trim();if(!e)throw new Error("session_id_required");let n=this.get(e);if(n)return n.lastActivityAt=Date.now(),this.persist(n),n;let r=Date.now(),s={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:r,lastActivityAt:r};return this.sessions.set(e,s),this.persist(s),s}startExecution(t,e,n){let r=this.registerSession(t);return (!r.executionId||r.skillName!==e)&&(r.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),r.skillName=e,r.requiredMcpTools=[...new Set(n)],r.lastActivityAt=Date.now(),this.persist(r),r}get(t){let e=t.trim();if(!e)return null;let n=this.sessions.get(e);if(n)return n;let r=this.snapshotPath(e);if(!existsSync(r))return null;try{let s=JSON.parse(readFileSync(r,"utf8"));return s.sessionId!==e||s.projectRoot!==this.projectRoot?null:(s.requiredMcpTools=Array.isArray(s.requiredMcpTools)?s.requiredMcpTools:[],this.sessions.set(e,s),s)}catch{return null}}completeExecution(t){let e=this.get(t);if(!e)return null;let n={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),n}removeSession(t){let e=t.trim();e&&(this.sessions.delete(e),rmSync(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(t){return join(this.sessionsDir,`${ze(t)}.json`)}persist(t){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(t.sessionId),n=`${e}.${process.pid}.tmp`;writeFileSync(n,JSON.stringify(t),{mode:384}),renameSync(n,e);}};var Qe=2,et=1;function tt(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join(dirname(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function ie(o=tt()){let t=[join(o,"dist","command-registry.json"),join(o,"command-registry.json")];for(let e of t)try{if(!existsSync(e))continue;let n=JSON.parse(readFileSync(e,"utf8"));if(n.version!==Qe&&n.version!==et||!nt(n.commands)){console.error(`[devflow] command registry contract mismatch: ${e}`);continue}return n.commands}catch(n){console.error(`[devflow] command registry load failed: ${n.message}`);}return null}function nt(o){return !o||typeof o!="object"||Array.isArray(o)?false:Object.values(o).every(t=>{if(!t||typeof t!="object"||Array.isArray(t))return false;let e=t;return $(e.mcpTools)&&$(e.blockedNative)&&(e.source===void 0||e.source==="core"||e.source==="plugin")&&(e.requiresContext===void 0||typeof e.requiresContext=="boolean")&&(e.runtimePackages===void 0||$(e.runtimePackages))&&(e.evidenceMode===void 0||["static","runtime","mixed"].includes(e.evidenceMode))})}function $(o){return Array.isArray(o)&&o.every(t=>typeof t=="string"&&t.length>0)}function ae(o){let t=ie();return t?t[o]?.mcpTools??[]:[]}function ce(o){let t=o.trim().replace(/^\//,"");if(!t.startsWith("devflow:"))return null;let e=t.slice(8).replace(/^devflow-/,"");return /^[a-z0-9][a-z0-9-]*$/i.test(e)?`devflow:${e.toLowerCase()}`:null}function le(o,t){return t?`evt_tool_${createHash("sha256").update(`${o}\0${t}`).digest("hex").slice(0,24)}`:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`}var ut=new Set(["Agent","Bash","Glob","Grep","WebSearch","WebFetch"]);function pt(o,t,e,n){try{let r=join(h(o),"event-map.json");if(!existsSync(r))return null;let s=JSON.parse(readFileSync(r,"utf-8")),i=s.filter(l=>l.toolName===e&&(!l.sessionId||l.sessionId===t));if(i.length===0)return null;let a=n?i.find(l=>l.toolUseId===n):i.sort((l,u)=>l.timestamp-u.timestamp)[0];if(!a)return null;let c=s.findIndex(l=>l.eventId===a.eventId);return c!==-1&&s.splice(c,1),writeFileSync(r,JSON.stringify(s)),{eventId:a.eventId,timestamp:a.timestamp}}catch{return null}}function ue(o){if(!o)return 0;let t=o;if(typeof o=="string")try{t=JSON.parse(o);}catch{return 1}let e=t,n=["files","results","data","result","memories","nodes","chunks","findings","symbols","keySymbols"],r=["reasoning","riskHints","nextActions"],s=Array.isArray(e.data)?e.data:e.structuredContent??e;if(Array.isArray(s))return s.length;if(typeof s=="object"&&s!==null){let i=s,a=0,c=false;for(let u of n)Array.isArray(i[u])&&(c=true,a+=i[u].length);for(let u of r)Array.isArray(i[u])&&(a+=i[u].length);for(let[,u]of Object.entries(i))u&&typeof u=="object"&&!Array.isArray(u)&&(a+=ue(u));let l=!!(i._devflow||i._devflow_unique||i.taskType);return a===0&&l&&!c?1:a>0?a:c?0:Object.keys(i).length>0?1:0}return 0}function dt(o,t){if(!ut.has(t))return null;let e=L(o,r=>({...r,bypassCount:(r.bypassCount??0)+1}));if(!e.applied)return null;let n=e.receipt.bypassCount??0;return n>20&&n%10===0?`MCP \u5DE5\u5177\u4F7F\u7528\u7387\u4F4E\uFF1A${n} \u6B21\u76F4\u63A5\u5DE5\u5177\u8C03\u7528\u3002\u5EFA\u8BAE\u4F7F\u7528 get_project_context \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`:null}async function mt(o){if(!o)return null;let t;try{t=JSON.parse(o);}catch{return null}let e=t.tool_name||"",n=j(),r=t.tool_input||{},s=t.tool_response||{},i=t.session_id?.trim()||null,a=new E(n);if(i&&a.registerSession(i),e==="Skill"){let p=typeof r=="object"&&r.skill?r.skill:"",m=ce(p);i&&m&&(a.startExecution(i,m,ae(m)),U(n,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0}));}e.startsWith("mcp__")&&new D(n).recordMcpCall();let c=i?a.get(i):null,l=i?pt(n,i,e,t.tool_use_id):null,u=i&&t.tool_use_id&&!l?(()=>{try{let p=openGlobalDevFlowDatabase();try{return p.getToolCallEventByToolUseId(i,t.tool_use_id)}finally{p.close();}}catch{return null}})():null,d=l??u,f=typeof s=="object"&&s!==null?s:null,g=JSON.stringify(s),y=Buffer.byteLength(g,"utf8"),w=ue(s);if(c?.executionId!==void 0||e.startsWith("mcp__devflow__")||e==="Skill"){let p=getLogWriter(n),m=c?.executionId??`auto-${i??Date.now()}`;p.toolCall({tool:e,runId:m,stepId:`step-${m}`,input:r,output:s,duration:d?Math.max(0,Date.now()-d.timestamp):0,success:!f?.error,resultCount:w,resultSizeBytes:y,error:f?.error});}if(d&&i){let p=typeof s=="string"?s:JSON.stringify(s),m=p.length>5e3?{_truncated:true,_originalSize:p.length,text:p.slice(0,5e3)}:s;await new x().completeEvent({eventId:d.eventId,sessionId:i,output:m,duration:d?Math.max(0,Date.now()-d.timestamp):0,completedAt:Date.now()});}let k=typeof r=="object"&&r.command?r.command:"",S=f?.exitCode,W=f?.stderr,pe=/^\s*(ls|cat|pwd|cd|echo|head|tail|wc|which|whoami|date|env|printenv|id|hostname|uname)\b/,v,b,I=true;if(e.startsWith("mcp__"))v="mcp_call",b={mcpTool:e.replace(/^mcp__[^_]+__/,""),query:typeof r=="object"&&r.query?r.query:null,resultCount:w,resultSizeBytes:y};else if(e==="Read"||e==="Write"||e==="Edit")v=e==="Read"?"file_read":"file_write",b={filePath:typeof r=="object"&&r.file_path?r.file_path:null,fileContentSize:y};else if(e==="Bash"){let p=pe.test(k),m=S!==void 0&&S!==0;I=!p||m,v=m?"bash_error":"bash_command",b={command:k||e,exitCode:S??null,stderr:W??null};}else e==="Agent"?(v="subagent",b={subagentType:typeof r=="object"&&r.subagent_type?r.subagent_type:null,description:typeof r=="object"&&r.description?String(r.description).slice(0,200):null}):e==="WebSearch"||e==="WebFetch"?(v=e==="WebSearch"?"web_search":"web_fetch",b={query:typeof r=="object"&&r.query?String(r.query).slice(0,200):null}):(I=false,v="tool_use",b={});if(I){let p={id:t.tool_use_id?`memory:${le(i??"",t.tool_use_id)}`:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:i??"",tool:e,kind:v,payload:b,command:k||void 0,exitCode:S,stderr:W??void 0,durationMs:d?Math.max(0,Date.now()-d.timestamp):0,createdAt:Date.now()};i&&K(`/api/memory/session-events?rootPath=${encodeURIComponent(n)}`,p);}let T=dt(n,e);return T&&console.error("[devflow] Enforcement:",T),T}if(process.argv[1]?.endsWith("post-tool-use")||process.argv[1]?.endsWith("post-tool-use.js")){let o=console.log.bind(console);console.log=console.error.bind(console),console.info=console.error.bind(console);let t="";process.stdin.setEncoding("utf8"),process.stdin.on("data",e=>{t+=e;}),process.stdin.on("end",async()=>{let e=await mt(t.trim()||process.argv[2]||"");if(e){let n=JSON.stringify(e);o(JSON.stringify({hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:n}}));}else o(JSON.stringify({status:"ok"}));}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),5e3).unref();}
3
3
  export{mt as handlePostToolUse};