@devflow-tools/cli 0.16.5 → 0.16.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "devflow",
3
3
  "description": "DevFlow — Developer Intelligence Platform: project context, code graph, knowledge base, memory engine, and workflow automation with 14 domain plugins",
4
- "version": "0.16.5",
4
+ "version": "0.16.6",
5
5
  "author": {
6
6
  "name": "DevFlow"
7
7
  },
@@ -1,14 +1,14 @@
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 ce=1e4,et=3e4,D=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 R=class{constructor(e){this.retryScheduled=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.getDatabase().aggregatePendingToolMetrics(),this.postHttp("/api/telemetry/tool-call/output",e)),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:D}).reverse();for(let s of o)try{this.applyOperation(s.operation,s.payload),n.resolveTelemetryFailure(s.id);}catch{}n.trimTelemetryFailures(D);}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||le(n.envelope.operation)-le(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"});return;case "execution_complete":n.reconcileSkillExecution(t.executionId,t.status,t.finishedAt,t.metadata);return;case "session_start":n.ensureSession(t),n.insertRun({id:ue(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(ue(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(D),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-D)))unlinkSync(join(this.cacheDir,t));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},et).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<=ce?e:{_truncated:true,_original_size:t.length,_preview:`${t.substring(0,ce)}...`}}};function le(r){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(r)}function ue(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 L(r){return r??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function b(r){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(L(r))}var ct=1;function lt(){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 pe(r=lt()){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||!ut(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 ut(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 de(t.mcpTools)&&de(t.blockedNative)})}function de(r){return Array.isArray(r)&&r.every(e=>typeof e=="string"&&e.length>0)}function me(r){let e=pe();return e?e[r]?.mcpTools??[]:[]}var q=join(homedir(),".devflow"),J=join(q,"server-refs.json"),gt="http://127.0.0.1:13337/api/health",vt=600*1e3,wt=500;function _t(){existsSync(q)||mkdirSync(q,{recursive:true});}function x(){try{if(existsSync(J))return JSON.parse(readFileSync(J,"utf-8"))}catch{}return {sessions:[],lastActivity:0}}function he(r){_t(),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=x();t.sessions.includes(e)||t.sessions.push(e),t.lastActivity=Date.now(),he(t),this.resetIdleTimer();}removeRef(e){let t=x();t.sessions=t.sessions.filter(n=>n!==e),t.lastActivity=Date.now(),he(t),t.sessions.length===0&&this.resetIdleTimer();}get activeSessions(){return x().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(gt),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),!(x().sessions.length>0)&&(this.idleTimer=setTimeout(()=>{this.stop();},vt).unref());}};function fe(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 B=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",Dt=getLocalApiKey(),xt=8,Ct=6e4,ye=new Map;function Pt(r){let e=new URL(r,B).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,fe("http_circuit_transition",{path:new URL(r,B).pathname,previous:n,status:t,failures:e.failures,openUntil:e.openUntil});}function W(r,e){e.failures++,e.lastFailure=Date.now(),e.failures>=xt&&(e.openUntil=Date.now()+Ct,z(r,e,"open"));}function Mt(r,e){let t=e.status!=="closed";e.failures=0,e.openUntil=0,t&&z(r,e,"closed");}var H=join(homedir(),".devflow","errors");function ge(r,e){try{existsSync(H)||mkdirSync(H,{recursive:!0});let t=`${new Date().toISOString()} | ${r} | ${e?.message??String(e)}
2
- `;appendFileSync$1(join(H,"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(d=>{let l=JSON.stringify(e),m=new URL(r,B),p=request({hostname:m.hostname,port:m.port,path:m.pathname+m.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":Dt,"Content-Length":Buffer.byteLength(l)},timeout:n},f=>{if(f.resume(),f.statusCode&&f.statusCode>=400){let u=new Error(`HTTP ${f.statusCode}`);if(ge(r,u),c>0){let h=Math.min(1e3*Math.pow(2,o-c),8e3);setTimeout(()=>{a(c-1).then(d);},h);}else W(r,i),d();return}Mt(r,i),d();});p.on("error",f=>{if(ge(r,f),c>0){let u=Math.min(1e3*Math.pow(2,o-c),8e3);setTimeout(()=>{a(c-1).then(d);},u);}else W(r,i),d();}),p.on("timeout",()=>{p.destroy(),c>0?a(c-1).then(d):(W(r,i),d());}),p.write(l),p.end();});return a(o)}function M(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 jt(r,e){return r.lastMcpCall===e.lastMcpCall&&r.bypassCount===e.bypassCount}function Ft(r){try{return existsSync(r)?M(JSON.parse(readFileSync(r,"utf-8"))):{}}catch{return {}}}function Nt(r,e){let t=e instanceof Error?e.message:String(e);console.error(`[devflow] Receipt ${r} skipped: ${t}`);}function _e(r){let e=b(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)){_e(e);return}let t=join(b(e),"receipt.json");if(!existsSync(t))return;let n=Ft(t);r.updateHookReceipt(e,()=>n),_e(e);}function Re(r,e,t){let n;try{n=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),Ut(n,r);let o={},s=n.updateHookReceipt(r,a=>(o=M(a),M(e(o)))),i=M(s);return {receipt:i,applied:!0,changed:!jt(o,i)}}catch(o){return Nt(t,o),{receipt:{},applied:false,changed:false}}finally{try{n?.close();}catch{}}}function O(r,e){return Re(r,()=>e,"write").applied}function X(r,e){return Re(r,e,"update")}var Se={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",Bash:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function Lt(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 I=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"&&Se[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=Se[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=Lt(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(){O(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}};var Ht=10,Bt=5e3,zt=3e4,Kt=new Int32Array(new SharedArrayBuffer(4)),xe=0;function Vt(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 Ce(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(Vt);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()}.${xe++}.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 Me(r){try{return process.kill(r,0),!0}catch(e){return e.code!=="ESRCH"}}function Oe(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 be(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);Oe(o)&&(t=o);}catch{}return t?Me(t.pid)?!1:be(r,e):Date.now()-statSync(r).mtimeMs>zt&&be(r,e)}catch(e){return e.code==="ENOENT"}}function Yt(r){mkdirSync$1(dirname$1(r),{recursive:true});let e=`${r}.lock`,t=Date.now()+Bt;for(;;){let n=`${process.pid}:${Date.now()}:${xe++}`,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,Ht);}}}function Qt(r){try{let e=readFileSync$1(r.path,"utf8"),t=JSON.parse(e);Oe(t)&&t.token===r.token&&rmSync$1(r.path);}catch(e){if(e.code!=="ENOENT")throw e}}function Ae(r){let e=getDaemonRegistryPath(),t=Yt(e);try{return r(e)}finally{Qt(t);}}function je(r,e){Ae(t=>{let n=Ce(t).entries.filter(o=>o.rootPath!==r&&Me(o.pid));n.push({rootPath:r,projectHash:getProjectHash(r),pid:e,startedAt:Date.now()}),Pe(t,n);});}function Fe(r,e){Ae(t=>{let n=Ce(t).entries.filter(o=>o.rootPath!==r||e!==void 0&&o.pid!==e);Pe(t,n);});}async function N(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 Y(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 ln(r){return Buffer.from(r,"utf8").toString("base64url")}var T=class{constructor(e){this.projectRoot=e;this.sessions=new Map;this.sessionsDir=join(b(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,`${ln(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 mn=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",hn=getLocalApiKey();function fn(r,e){return new Promise(t=>{let n=JSON.stringify(e),o=new URL(r,mn),s=request({hostname:o.hostname,port:o.port,path:o.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":hn,"Content-Length":Buffer.byteLength(n)},timeout:5e3},()=>t());s.on("error",()=>t()),s.write(n),s.end();})}async function Q(r,e=L(),t=true,n){let o;try{o=JSON.parse(r);}catch{return}let s=o.session_id?.trim();if(!s)return;let i=new T(e),a=i.get(s),c=n?.telemetry??new R,d=!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(y=>y.executionId===p),u=f.length,h=f.filter(y=>y.isMcpTool).length,g=u-h,v=f.filter(y=>y.toolType==="subagent").length,_=f.map(y=>y.timestamp).filter(Boolean),S=_.length>=2?Math.max(..._)-Math.min(..._):0,w=l.getExecutionObligationCompliance(p);try{l.updateSkillExecution(p,{status:"completed",finishedAt:Date.now(),totalToolCalls:u,mcpToolCalls:h,directToolCalls:g,subagentCount:v,totalDuration:S,mcpComplianceRate:w.rate}),await fn("/api/telemetry/skill-execution/complete",{executionId:p,finishedAt:Date.now(),status:"completed",summary:{totalToolCalls:u,mcpToolCalls:h,directToolCalls:g,subagentCount:v,totalDuration:S,mcpComplianceRate:w.rate}});}catch{}}t&&l.deleteHookReceipt(e),l.deleteContextReceipt(e,s);}catch{}finally{l?.close();}try{if(await N({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{d&&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 Q(r.trim()||process.argv[2]||""),process.exit(0);}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),1e4).unref();}var bn=/(?:^|[.!?。!?]\s*)(?:(?:please\s+)?(?:remember|memorize)\b|(?:请记|(?:请)?记住))/iu,Tn=/\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 $e(r){let e=r.trim();return e&&bn.test(e)&&!Tn.test(e)?e:null}function qe(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 In(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=qe(r);mkdirSync$1(dirname$1(t),{recursive:true}),appendFileSync(t,`${JSON.stringify(e)}
4
- `,{mode:384});}function Ge(r){let e=qe(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=In(s);if(i.length===0){try{unlinkSync$1(s);}catch{}return null}return {path:s,intents:i}}function We(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 {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=1;function lt(){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=lt()){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||!ut(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 ut(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)})}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 J=join(homedir(),".devflow"),G=join(J,"server-refs.json"),yt="http://127.0.0.1:13337/api/health",vt=600*1e3,wt=500;function _t(){existsSync(J)||mkdirSync(J,{recursive:true});}function C(){try{if(existsSync(G))return JSON.parse(readFileSync(G,"utf-8"))}catch{}return {sessions:[],lastActivity:0}}function fe(r){_t(),writeFileSync(G,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(yt),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();},vt).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 z=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",Dt=getLocalApiKey(),xt=8,Ct=6e4,ye=new Map;function Mt(r){let e=new URL(r,z).pathname,t=ye.get(e);return t||(t={failures:0,lastFailure:0,openUntil:0,status:"closed"},ye.set(e,t)),t}function K(r,e,t){if(e.status===t)return;let n=e.status;e.status=t,ge("http_circuit_transition",{path:new URL(r,z).pathname,previous:n,status:t,failures:e.failures,openUntil:e.openUntil});}function H(r,e){e.failures++,e.lastFailure=Date.now(),e.failures>=xt&&(e.openUntil=Date.now()+Ct,K(r,e,"open"));}function Pt(r,e){let t=e.status!=="closed";e.failures=0,e.openUntil=0,t&&K(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 _e(r,e,t={}){let n=t.timeout??5e3,o=t.maxRetries??2,s=t.circuitBreaker??true,i=Mt(r);if(s&&i.openUntil>Date.now())return Promise.resolve();s&&i.status==="open"&&K(r,i,"half_open");let a=c=>new Promise(u=>{let l=JSON.stringify(e),m=new URL(r,z),p=request({hostname:m.hostname,port:m.port,path:m.pathname+m.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":Dt,"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}Pt(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 jt(r,e){return r.lastMcpCall===e.lastMcpCall&&r.bypassCount===e.bypassCount}function Ft(r){try{return existsSync(r)?O(JSON.parse(readFileSync(r,"utf-8"))):{}}catch{return {}}}function Nt(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 Lt(r,e){if(r.getHookReceipt(e)){Se(e);return}let t=join(T(e),"receipt.json");if(!existsSync(t))return;let n=Ft(t);r.updateHookReceipt(e,()=>n),Se(e);}function be(r,e,t){let n;try{n=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),Lt(n,r);let o={},s=n.updateHookReceipt(r,a=>(o=O(a),O(e(o)))),i=O(s);return {receipt:i,applied:!0,changed:!jt(o,i)}}catch(o){return Nt(t,o),{receipt:{},applied:false,changed:false}}finally{try{n?.close();}catch{}}}function A(r,e){return be(r,()=>e,"write").applied}function Y(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 Ut(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=Y(this.projectRoot,c=>({...c,bypassCount:(c.bypassCount??0)+1}));if(!s.applied)return {permissionDecision:"allow"};let i=s.receipt.bypassCount??0,a=Ut(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 Ht=10,Bt=5e3,zt=3e4,Kt=new Int32Array(new SharedArrayBuffer(4)),Ce=0;function Vt(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(Vt);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 Yt(r){mkdirSync$1(dirname$1(r),{recursive:true});let e=`${r}.lock`,t=Date.now()+Bt;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,Ht);}}}function Qt(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=Yt(e);try{return r(e)}finally{Qt(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 ln(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,`${ln(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 mn=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",hn=getLocalApiKey();function fn(r,e){return new Promise(t=>{let n=JSON.stringify(e),o=new URL(r,mn),s=request({hostname:o.hostname,port:o.port,path:o.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":hn,"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,_=Date.now(),E=l.getSkillExecution(p),v=E?.startedAt!=null?Math.max(0,_-E.startedAt):0,w=l.getExecutionObligationCompliance(p);try{l.updateSkillExecution(p,{status:"completed",finishedAt:_,totalToolCalls:d,mcpToolCalls:h,directToolCalls:g,subagentCount:y,totalDuration:v,mcpComplianceRate:w.rate}),await fn("/api/telemetry/skill-execution/complete",{executionId:p,finishedAt:_,status:"completed",summary:{totalToolCalls:d,mcpToolCalls:h,directToolCalls:g,subagentCount:y,totalDuration:v,mcpComplianceRate:w.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 bn=/(?:^|[.!?。!?]\s*)(?:(?:please\s+)?(?:remember|memorize)\b|(?:请记|(?:请)?记住))/iu,Tn=/\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&&bn.test(e)&&!Tn.test(e)?e:null}function Je(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 In(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 Ge(r,e){let t=Je(r);mkdirSync$1(dirname$1(t),{recursive:true}),appendFileSync(t,`${JSON.stringify(e)}
4
+ `,{mode:384});}function We(r){let e=Je(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=In(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(`
5
5
  `)+`
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 He(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 k(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 Gn=600*1e3,Wn=2e3,Hn=250,Bn=2e3,zn=1e3,Be=1024*1024,Kn=2*1024*1024,Vn=6e4,Xn=100,Yn={Agent:"get_project_context",Bash:"get_project_context",Glob:"get_project_context",Grep:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function Qn(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 ne(r){return typeof r=="string"&&r.trim().length>0}function Zn(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(ne(o.projectRoot))return o;let s=ne(t.CLAUDE_PROJECT_DIR)?t.CLAUDE_PROJECT_DIR:n;return ne(s)&&(o.projectRoot=s),o}var oe=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 R,this.runtimeStore=new T(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(),je(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();},Vn),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();},Wn).unref(),d=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")>Be){a();return}if(m.length===0)return;let p=0;for(let f of m){let u=Buffer.byteLength(f,"utf8");if(u>Be){a();return}p+=u+1;}if(s+p>Kn){a();return}s+=p,i=i.then(async()=>{for(let f of m){if(o)return;if(!n){let u;try{u=JSON.parse(f);}catch{await d({error:"handshake_invalid"});return}if(u?.type!=="hello"||typeof u.rootPath!="string"){await d({error:"handshake_invalid"});return}if(u.rootPath!==this.projectRoot){await d({error:"handshake_root_mismatch",expected:this.projectRoot});return}n=true,clearTimeout(c),this.resetIdleTimer();continue}if(f.trim())try{let u=JSON.parse(f),h=await this.handleRequest(u);h!==null&&!o&&await this.writeFrame(e,h);}catch(u){o||await this.writeFrame(e,{error:u.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),d=()=>{clearTimeout(c),e.off("close",m),e.off("error",p),e.off("drain",f);},l=h=>{a||!h&&(!s||!i)||(a=true,d(),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)}
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 Gn=600*1e3,Wn=2e3,Hn=250,Bn=2e3,zn=1e3,ze=1024*1024,Kn=2*1024*1024,Vn=6e4,Xn=100,Yn={Agent:"get_project_context",Bash:"get_project_context",Glob:"get_project_context",Grep:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function Qn(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 Zn(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();},Vn),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();},Wn).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")>ze){a();return}if(m.length===0)return;let p=0;for(let f of m){let d=Buffer.byteLength(f,"utf8");if(d>ze){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
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}`));},Hn),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();},Gn).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=Zn(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,v=g?U(g):null;v&&this.startSkillExecution(n,v);}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 I(this.projectRoot,n,o?.executionId),c=a.getPhase(),d=a.evaluate(t.tool_name,s),l;d.permissionDecision==="deny"?l={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:d.reason??"MCP context required"}}:d.additionalContext?l={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:d.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,u={eventId:k(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:Qn(t.tool_name),input:m,duration:0,blocked:l&&l.hookSpecificOutput?.permissionDecision==="deny"},h=await this.telemetry.sendEvent({...u,blockReason:u.blocked?"DEVFLOW_CONTEXT_REQUIRED":void 0,error:u.blocked?"DEVFLOW_CONTEXT_REQUIRED":void 0});return h&&!u.blocked&&(this.eventMap.push({toolName:t.tool_name,toolUseId:t.tool_use_id,eventId:h,sessionId:n,timestamp:u.timestamp}),this.noteTelemetryOperation()),h&&u.blocked&&await this.telemetry.completeEvent({eventId:h,sessionId:n,error:"DEVFLOW_CONTEXT_REQUIRED",duration:Date.now()-u.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&&O(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,me(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:Y({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(!Yn[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 w=o?.skill,y=w?U(w):null;y&&(this.startSkillExecution(i,y),O(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0}));}n.startsWith("mcp__")&&new I(this.projectRoot).recordMcpCall();let a=this.popEventFromMap(n,i,t.tool_use_id);if(a){let w=typeof s=="string"?s:JSON.stringify(s),y=w.length>5e3?{_truncated:true,_originalSize:w.length,text:w.slice(0,5e3)}:s,ze=Date.now()-a.timestamp;await this.telemetry.completeEvent({eventId:a.eventId,sessionId:i,output:y,duration:ze,completedAt:Date.now()})&&this.noteTelemetryOperation();}let c=typeof s=="object"&&s!==null?s:null,d=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),u=Buffer.byteLength(f,"utf8"),h=this.countResults(s),g,v,_=true;if(n.startsWith("mcp__"))g="mcp_call",v={mcpTool:n.replace(/^mcp__[^_]+__/,""),query:o.query??null,resultCount:h,resultSizeBytes:u};else if(n==="Read"||n==="Write"||n==="Edit")g=n==="Read"?"file_read":"file_write",v={filePath:o.file_path??null,fileContentSize:u};else if(n==="Bash"){let w=m.test(p),y=d!==void 0&&d!==0;_=!w||y,g=y?"bash_error":"bash_command",v={command:p||n,exitCode:d??null,stderr:l??null};}else n==="Agent"?(g="subagent",v={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",v={query:typeof o.query=="string"?o.query.slice(0,200):null}):(_=false,g="tool_use",v={});if(_){let w={id:t.tool_use_id?`memory:${k(i,t.tool_use_id)}`:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:i,tool:n,kind:g,payload:v,command:p||void 0,exitCode:d,stderr:l??void 0,durationMs:0,createdAt:Date.now()};this.postMemoryEvent(w);}let S=this.enforcePostToolUse(n);return S?(console.error("[devflow-daemon] Enforcement:",S),{hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:JSON.stringify(S)}}):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=k(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:${k(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=n.data??n.structuredContent??n;if(Array.isArray(s))return s.length;if(typeof s=="object"&&s!==null){let i=s,a=0;for(let c of o)Array.isArray(i[c])&&(a+=i[c].length);for(let[,c]of Object.entries(i))c&&typeof c=="object"&&!Array.isArray(c)&&(a+=this.countResults(c));return a>0?a: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=He(n);if(o&&s&&this.startSkillExecution(o,s.skillName),n.trim()){let i=$e(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 N({projectRoot:this.projectRoot,sessionId:t,trigger:"pre_compact",memory:n}),s=Y(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=Ge(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),We(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 c={...await(this.explicitMemoryGate??=new MemoryGate(this.projectRoot)).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:"none"}};if(c.memories.length===0)return {status:"ok",enabled:!0,markdown:`## Project memory
10
- \u672C\u9879\u76EE\u6682\u65E0\u8BB0\u5FC6`};let d=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
- ${d.join(`
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}`));},Hn),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();},Gn).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=Zn(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:Qn(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(!Yn[e])return null;let t=Math.floor(Date.now()/1e3),n=Y(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,w=v?U(v):null;w&&(this.startSkillExecution(i,w),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),w=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:w,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,_=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),w=u!==void 0&&u!==0;_=!v||w,g=w?"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}):(_=false,g="tool_use",y={});if(_){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){_e(`/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&&Ge(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
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 Q(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,Bn);e.close(o);}))}cleanupOwnedResources(){try{Fe(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 oe(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{oe as HookDaemon,Zn as injectProjectRootIfMissing};
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,Bn);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,Zn 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 S=1e4,L=3e4,h=500;function N(i,t,e){return new Promise(r=>{try{let n=new URL(i),s=request({hostname:n.hostname,port:n.port||80,path:n.pathname+n.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);});});s.on("error",()=>r(null)),s.on("timeout",()=>{s.destroy(),r(null);}),s.write(t),s.end();}catch{r(null);}})}var g=class{constructor(t){this.retryScheduled=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,n,s=process.env.CLAUDE_PROJECT_DIR??process.cwd(),o=[]){let a={executionId:t,sessionId:e,skillName:r,startedAt:n,projectRoot:s,requiredMcpTools:o};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(t,e="completed",r=Date.now(),n){let s={executionId:t,status:e,finishedAt:r,metadata:n},o=this.commitOrCache("execution_complete",s);return o&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",s),o}async sendSessionStart(t,e,r){let n={id:t,projectRoot:e,startedAt:r,label:e.split("/").pop()??"unknown"},s=this.commitOrCache("session_start",n);return s&&this.postHttp("/api/telemetry/sessions",n),s}async completeEvent(t){let e=this.commitOrCache("complete_event",t);return e&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp("/api/telemetry/tool-call/output",t)),e}async endSession(t,e=Date.now()){let r={sessionId:t,finishedAt:e},n=this.commitOrCache("session_end",r);return n&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),n}async flushCache(){try{let r=this.getDatabase(),n=r.listTelemetryFailures({unresolvedOnly:!0,limit:h}).reverse();for(let s of n)try{this.applyOperation(s.operation,s.payload),r.resolveTelemetryFailure(s.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(n=>n.endsWith(".json")).map(n=>{let s=join(r,n);try{return {cacheFile:s,envelope:$(JSON.parse(readFileSync(s,"utf8")),n)}}catch{return null}})).filter(r=>r!==null).sort((r,n)=>r.envelope.timestamp-n.envelope.timestamp||T(r.envelope.operation)-T(n.envelope.operation));for(let{cacheFile:r,envelope:n}of e)try{let s=this.getDatabase();s.insertTelemetryFailure({id:n.id,operation:n.operation,payload:n.payload,error:n.failure,createdAt:n.timestamp}),this.applyOperation(n.operation,n.payload),s.resolveTelemetryFailure(n.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"});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 n=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:n,label:n==="unknown"?void 0:n.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(t,e,r){let n=Date.now(),s={id:`failure:${n}:${Math.random().toString(36).slice(2,11)}`,operation:t,payload:e,failure:r instanceof Error?r.message:String(r),timestamp:n};try{let o=this.getDatabase();o.insertTelemetryFailure({id:s.id,operation:t,payload:e,error:s.failure,createdAt:n}),o.trimTelemetryFailures(h),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let o=join(this.cacheDir,`${n}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(o,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-h)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},L).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<=S?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,S)}...`}}};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 n=e.payload??{},s=Number(n.timestamp??n.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:r,payload:n,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:s}}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 n=Date.now(),s={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:n,lastActivityAt:n};return this.sessions.set(e,s),this.persist(s),s}startExecution(t,e,r){let n=this.registerSession(t);return (!n.executionId||n.skillName!==e)&&(n.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),n.skillName=e,n.requiredMcpTools=[...new Set(r)],n.lastActivityAt=Date.now(),this.persist(n),n}get(t){let e=t.trim();if(!e)return null;let r=this.sessions.get(e);if(r)return r;let n=this.snapshotPath(e);if(!existsSync(n))return null;try{let s=JSON.parse(readFileSync(n,"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 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 ne(i,t,e){return new Promise((r,n)=>{let s=JSON.stringify(t),o=new URL(i,te),a=false,c=l=>{a||(a=true,l?n(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(s)},timeout:e},l=>{l.on("error",c),l.on("end",()=>{let m=l.statusCode??0;if(m<200||m>=300){c(new Error(`HTTP ${m}`));return}c();}),l.resume();});u.on("error",c),u.on("timeout",()=>{u.destroy(new Error(`PostToolUseFailure request timed out after ${e}ms`));}),u.write(s),u.end();})}function se(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),n=t.retryDelayMs??v(process.env.DEVFLOW_FAILURE_RETRY_DELAY_MS,100),s=`/api/memory/session-events?rootPath=${encodeURIComponent(_)}`;for(let o=0;o<=r;o+=1)try{await ne(s,i,e);return}catch(a){if(o===r)throw a;await se(n*2**o);}}async function ie(i,t={}){let e;try{e=JSON.parse(i);}catch{return}let r=e.tool_name||"",n=e.tool_input||{},s=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(s||`${r||"tool"} failed`),errorStack:s||void 0}),l=typeof n=="object"&&n.command?n.command:r,m={id:`memory:${c}`,sessionId:o,tool:r,command:l,exitCode:-1,stderr:s,durationMs:0,createdAt:Date.now(),kind:r==="Bash"?"bash_error":"tool_use",payload:{command:l,exitCode:-1,stderr:s,failureSource:"PostToolUseFailure",failureCategory:u.category}},O=!t.telemetryClient,y=t.telemetryClient??new g,p=r.startsWith("mcp__"),x={eventId:c,executionId:a.executionId??o,sessionId:o,projectRoot:_,toolUseId:e.tool_use_id,timestamp:m.createdAt,toolName:r,toolType:p?"mcp":"direct",isMcpTool:p,mcpToolName:p?r:void 0,mcpEnforced:p,mcpFallback:false,kind:"error",input:n,duration:0,error:s,blocked:false,failureCategory:u.category};try{await y.sendEvent(x),await y.completeEvent({eventId:c,sessionId:o,error:s,duration:0,completedAt:Date.now()});}finally{O&&y.close();}t.eventDelivery?await t.eventDelivery(m):await oe(m,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 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;});}
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 {MemoryGate}from'@devflow-tools/memory-engine';import {randomUUID,createHash}from'crypto';import {fileURLToPath}from'url';function W(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 F=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",we=getLocalApiKey(),_e=8,be=6e4,H=new Map;function Se(o){let t=new URL(o,F).pathname,e=H.get(t);return e||(e={failures:0,lastFailure:0,openUntil:0,status:"closed"},H.set(t,e)),e}function j(o,t,e){if(t.status===e)return;let n=t.status;t.status=e,W("http_circuit_transition",{path:new URL(o,F).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()+be,j(o,t,"open"));}function Re(o,t){let e=t.status!=="closed";t.failures=0,t.openUntil=0,e&&j(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 z(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"&&j(o,i,"half_open");let c=l=>new Promise(a=>{let w=JSON.stringify(t),d=new URL(o,F),m=request({hostname:d.hostname,port:d.port,path:d.pathname+d.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":we,"Content-Length":Buffer.byteLength(w)},timeout:n},f=>{if(f.resume(),f.statusCode&&f.statusCode>=400){let h=new Error(`HTTP ${f.statusCode}`);if(J(o,h),l>0){let b=Math.min(1e3*Math.pow(2,r-l),8e3);setTimeout(()=>{c(l-1).then(a);},b);}else A(o,i),a();return}Re(o,i),a();});m.on("error",f=>{if(J(o,f),l>0){let h=Math.min(1e3*Math.pow(2,r-l),8e3);setTimeout(()=>{c(l-1).then(a);},h);}else A(o,i),a();}),m.on("timeout",()=>{m.destroy(),l>0?c(l-1).then(a):(A(o,i),a());}),m.write(w),m.end();});return c(r)}function M(o){return o??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function g(o){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(M(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 B(o){let t=g(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)){B(t);return}let e=join(g(t),"receipt.json");if(!existsSync(e))return;let n=ke(e);o.updateHookReceipt(t,()=>n),B(t);}function Y(o,t,e){let n;try{n=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),Te(n,o);let r={},s=n.updateHookReceipt(o,c=>(r=R(c),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 K={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"&&K[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=K[t];if(!r)return {permissionDecision:"allow"};let s=L(this.projectRoot,l=>({...l,bypassCount:(l.bypassCount??0)+1}));if(!s.applied)return {permissionDecision:"allow"};let i=s.receipt.bypassCount??0,c=Ae(t);return i>=c?{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 c=[];i.on("data",l=>c.push(l)),i.on("end",()=>{let l=Buffer.concat(c).toString();n(i.statusCode!=null&&i.statusCode>=200&&i.statusCode<300?l: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.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 c={executionId:t,sessionId:e,skillName:n,startedAt:r,projectRoot:s,requiredMcpTools:i};this.commitOrCache("execution_start",c)&&this.postHttp("/api/telemetry/skill-execution/start",c);}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.getDatabase().aggregatePendingToolMetrics(),this.postHttp("/api/telemetry/tool-call/output",t)),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"});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());}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 Ke(o){return Buffer.from(o,"utf8").toString("base64url")}var E=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(g(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,`${Ke(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=1;function et(){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=et()){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||!tt(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 tt(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)})}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(g(o),"event-map.json");if(!existsSync(r))return null;let s=JSON.parse(readFileSync(r,"utf-8")),i=s.filter(a=>a.toolName===e&&(!a.sessionId||a.sessionId===t));if(i.length===0)return null;let c=n?i.find(a=>a.toolUseId===n):i.sort((a,w)=>a.timestamp-w.timestamp)[0];if(!c)return null;let l=s.findIndex(a=>a.eventId===c.eventId);return l!==-1&&s.splice(l,1),writeFileSync(r,JSON.stringify(s)),{eventId:c.eventId,timestamp:c.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=e.data??e.structuredContent??e;if(Array.isArray(s))return s.length;if(typeof s=="object"&&s!==null){let i=s,c=0;for(let a of n)Array.isArray(i[a])&&(c+=i[a].length);for(let a of r)Array.isArray(i[a])&&(c+=i[a].length);for(let[,a]of Object.entries(i))a&&typeof a=="object"&&!Array.isArray(a)&&(c+=ue(a));let l=!!(i._devflow||i._devflow_unique||i.taskType);return c===0&&l?1:c>0?c: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=M(),r=t.tool_input||{},s=t.tool_response||{},i=t.session_id?.trim()||null,c=new E(n);if(i&&c.registerSession(i),e==="Skill"){let u=typeof r=="object"&&r.skill?r.skill:"",p=ce(u);i&&p&&(c.startExecution(i,p,ae(p)),U(n,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0}));}e.startsWith("mcp__")&&new D(n).recordMcpCall();let l=i?c.get(i):null,a=i?pt(n,i,e,t.tool_use_id):null,w=i&&t.tool_use_id&&!a?(()=>{try{let u=openGlobalDevFlowDatabase();try{return u.getToolCallEventByToolUseId(i,t.tool_use_id)}finally{u.close();}}catch{return null}})():null,d=a??w,m=typeof s=="object"&&s!==null?s:null,f=JSON.stringify(s),h=Buffer.byteLength(f,"utf8"),b=ue(s);if(l?.executionId!==void 0||e.startsWith("mcp__devflow__")||e==="Skill"){let u=getLogWriter(n),p=l?.executionId??`auto-${i??Date.now()}`;u.toolCall({tool:e,runId:p,stepId:`step-${p}`,input:r,output:s,duration:d?Math.max(0,Date.now()-d.timestamp):0,success:!m?.error,resultCount:b,resultSizeBytes:h,error:m?.error});}if(d&&i){let u=typeof s=="string"?s:JSON.stringify(s),p=u.length>5e3?{_truncated:true,_originalSize:u.length,text:u.slice(0,5e3)}:s;await new x().completeEvent({eventId:d.eventId,sessionId:i,output:p,duration:d?Math.max(0,Date.now()-d.timestamp):0,completedAt:Date.now()});}let k=typeof r=="object"&&r.command?r.command:"",S=m?.exitCode,q=m?.stderr,pe=/^\s*(ls|cat|pwd|cd|echo|head|tail|wc|which|whoami|date|env|printenv|id|hostname|uname)\b/,y,v,I=true;if(e.startsWith("mcp__"))y="mcp_call",v={mcpTool:e.replace(/^mcp__[^_]+__/,""),query:typeof r=="object"&&r.query?r.query:null,resultCount:b,resultSizeBytes:h};else if(e==="Read"||e==="Write"||e==="Edit")y=e==="Read"?"file_read":"file_write",v={filePath:typeof r=="object"&&r.file_path?r.file_path:null,fileContentSize:h};else if(e==="Bash"){let u=pe.test(k),p=S!==void 0&&S!==0;I=!u||p,y=p?"bash_error":"bash_command",v={command:k||e,exitCode:S??null,stderr:q??null};}else e==="Agent"?(y="subagent",v={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"?(y=e==="WebSearch"?"web_search":"web_fetch",v={query:typeof r=="object"&&r.query?String(r.query).slice(0,200):null}):(I=false,y="tool_use",v={});if(I){let u={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:y,payload:v,command:k||void 0,exitCode:S,stderr:q??void 0,durationMs:d?Math.max(0,Date.now()-d.timestamp):0,createdAt:Date.now()};i&&(z(`/api/memory/session-events?rootPath=${encodeURIComponent(n)}`,u),(async()=>{let p=new MemoryGate(n);try{await p.forceWarmUp(),await p.recordEvent(u);}catch{}finally{p.close();}})());}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();}
3
- export{mt as handlePostToolUse};
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 F=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",we=getLocalApiKey(),_e=8,be=6e4,W=new Map;function Se(o){let t=new URL(o,F).pathname,e=W.get(t);return e||(e={failures:0,lastFailure:0,openUntil:0,status:"closed"},W.set(t,e)),e}function M(o,t,e){if(t.status===e)return;let n=t.status;t.status=e,H("http_circuit_transition",{path:new URL(o,F).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()+be,M(o,t,"open"));}function Re(o,t){let e=t.status!=="closed";t.failures=0,t.openUntil=0,e&&M(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 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"&&M(o,i,"half_open");let a=c=>new Promise(l=>{let u=JSON.stringify(t),d=new URL(o,F),f=request({hostname:d.hostname,port:d.port,path:d.pathname+d.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":we,"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 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 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 B={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"&&B[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=B[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 Be(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,`${Be(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=1;function et(){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=et()){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||!tt(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 tt(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)})}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 lt=new Set(["Agent","Bash","Glob","Grep","WebSearch","WebFetch"]);function ut(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 pt(o,t){if(!lt.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 dt(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?ut(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,w,I=true;if(e.startsWith("mcp__"))v="mcp_call",w={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",w={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",w={command:k||e,exitCode:S??null,stderr:q??null};}else e==="Agent"?(v="subagent",w={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",w={query:typeof r=="object"&&r.query?String(r.query).slice(0,200):null}):(I=false,v="tool_use",w={});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:w,command:k||void 0,exitCode:S,stderr:q??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=pt(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 dt(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();}
3
+ export{dt as handlePostToolUse};
@@ -1,3 +1,3 @@
1
- import {getLocalApiKey}from'@devflow-tools/sdk';import {existsSync,readdirSync,readFileSync,unlinkSync,mkdirSync,writeFileSync}from'fs';import {request}from'http';import {join}from'path';import {homedir}from'os';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {MemoryGate}from'@devflow-tools/memory-engine';function p(s){return s??process.env.CLAUDE_PROJECT_DIR??process.cwd()}var y=1e4,S=3e4,u=500;function x(s,o,e){return new Promise(t=>{try{let r=new URL(s),n=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(o)},timeout:5e3},i=>{let a=[];i.on("data",l=>a.push(l)),i.on("end",()=>{let l=Buffer.concat(a).toString();t(i.statusCode!=null&&i.statusCode>=200&&i.statusCode<300?l:null);});});n.on("error",()=>t(null)),n.on("timeout",()=>{n.destroy(),t(null);}),n.write(o),n.end();}catch{t(null);}})}var m=class{constructor(o){this.retryScheduled=false;this.apiUrl=o?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=o?.apiKey??getLocalApiKey(),this.cacheDir=o?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=o?.legacyCacheDir??(o?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=o?.database??null,this.ownsDatabase=!o?.database;}async sendEvent(o){let e={...o,input:this.truncateInput(o.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),o.eventId):null}async sendExecutionStart(o,e,t,r,n=process.env.CLAUDE_PROJECT_DIR??process.cwd(),i=[]){let a={executionId:o,sessionId:e,skillName:t,startedAt:r,projectRoot:n,requiredMcpTools:i};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(o,e="completed",t=Date.now(),r){let n={executionId:o,status:e,finishedAt:t,metadata:r},i=this.commitOrCache("execution_complete",n);return i&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",n),i}async sendSessionStart(o,e,t){let r={id:o,projectRoot:e,startedAt:t,label:e.split("/").pop()??"unknown"},n=this.commitOrCache("session_start",r);return n&&this.postHttp("/api/telemetry/sessions",r),n}async completeEvent(o){let e=this.commitOrCache("complete_event",o);return e&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp("/api/telemetry/tool-call/output",o)),e}async endSession(o,e=Date.now()){let t={sessionId:o,finishedAt:e},r=this.commitOrCache("session_end",t);return r&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(o)}/close`,{finishedAt:e})),r}async flushCache(){try{let t=this.getDatabase(),r=t.listTelemetryFailures({unresolvedOnly:!0,limit:u}).reverse();for(let n of r)try{this.applyOperation(n.operation,n.payload),t.resolveTelemetryFailure(n.id);}catch{}t.trimTelemetryFailures(u);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(t=>!!t))].filter(t=>existsSync(t)).flatMap(t=>readdirSync(t).filter(r=>r.endsWith(".json")).map(r=>{let n=join(t,r);try{return {cacheFile:n,envelope:P(JSON.parse(readFileSync(n,"utf8")),r)}}catch{return null}})).filter(t=>t!==null).sort((t,r)=>t.envelope.timestamp-r.envelope.timestamp||v(t.envelope.operation)-v(r.envelope.operation));for(let{cacheFile:t,envelope:r}of e)try{let n=this.getDatabase();n.insertTelemetryFailure({id:r.id,operation:r.operation,payload:r.payload,error:r.failure,createdAt:r.timestamp}),this.applyOperation(r.operation,r.payload),n.resolveTelemetryFailure(r.id),unlinkSync(t);}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(o,e){try{return this.applyOperation(o,e),!0}catch(t){return this.cacheOperation(o,e,t),false}}applyOperation(o,e){let t=this.getDatabase();switch(o){case "tool_call":this.ensureParentSession(t,e),t.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(t,e),t.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running"});return;case "execution_complete":t.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":t.ensureSession(e),t.insertRun({id:D(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(!t.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":t.closeSession(e.sessionId,e.finishedAt),t.updateRun(D(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(o,e){let t=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!t)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";o.ensureSession({id:t,projectRoot:r,label:r==="unknown"?void 0:r.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(o,e,t){let r=Date.now(),n={id:`failure:${r}:${Math.random().toString(36).slice(2,11)}`,operation:o,payload:e,failure:t instanceof Error?t.message:String(t),timestamp:r};try{let i=this.getDatabase();i.insertTelemetryFailure({id:n.id,operation:o,payload:e,error:n.failure,createdAt:r}),i.trimTelemetryFailures(u),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(n,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let o=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of o.slice(0,Math.max(0,o.length-u)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},S).unref());}postHttp(o,e){x(`${this.apiUrl}${o}`,JSON.stringify(e),this.apiKey);}truncateInput(o){let e=JSON.stringify(o);return e===void 0||e.length<=y?o:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,y)}...`}}};function v(s){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(s)}function D(s){return `hook-run:${s}`}function P(s,o){if(!s||typeof s!="object")throw new Error("Invalid telemetry cache envelope");let e=s;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let t=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!t)throw new Error("Unknown legacy telemetry cache operation");let r=e.payload??{},n=Number(r.timestamp??r.startedAt??Date.now());return {id:`legacy-cache:${o}`,operation:t,payload:r,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:n}}async function b(s){let o=!s.memory,e=s.memory??new MemoryGate(s.projectRoot),t=0,r=0;try{await e.forceWarmUp(),s.trigger==="session_end"&&(t=await e.releasePendingDistillLeases(s.sessionId)),r=e.getPendingEventCount();}finally{o&&e.close();}let n={pendingEvents:r,releasedLeases:t,trigger:s.trigger,sessionId:s.sessionId};try{let i=openGlobalDevFlowDatabase();try{let a=Date.now(),l=`distill:${a}:${Math.random().toString(36).slice(2,10)}`;i.recordMemoryDistillCheckpoint({id:l,projectRoot:s.projectRoot,sessionId:s.sessionId,trigger:s.trigger,pendingEvents:r,releasedLeases:t,createdAt:a}),i.insertEvent({kind:"memory_distill_requested",timestamp:a,success:!0,metadata:{...n,projectRoot:s.projectRoot}});}finally{i.close();}}catch{}return n}function _(s){if(s.pendingEvents===0)return "";let o=s.sessionId?`\uFF0CsessionId=${s.sessionId}`:"";return `Memory distill checkpoint: ${s.pendingEvents} \u4E2A\u4E8B\u4EF6\u5F85\u63D0\u70BC${o}\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`}var w="WARNING: DevFlow is in degraded mode (daemon unreachable): 4-Gate enforcement and memory prefetch cache are unavailable. Run `devflow doctor` for details.";function C(){return process.env.DEVFLOW_HOOK_DEGRADED==="1"}async function M(s="",o=p()){let e;try{let i=JSON.parse(s);typeof i.session_id=="string"&&i.session_id.trim()&&(e=i.session_id.trim());}catch{}let t=new m;try{await t.flushAndAggregate();}finally{t.close();}let r=await b({projectRoot:o,sessionId:e,trigger:"pre_compact"}),n=[C()?w:"",_(r)].filter(Boolean).join(`
1
+ import {getLocalApiKey}from'@devflow-tools/sdk';import {existsSync,readdirSync,readFileSync,unlinkSync,mkdirSync,writeFileSync}from'fs';import {request}from'http';import {join}from'path';import {homedir}from'os';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {MemoryGate}from'@devflow-tools/memory-engine';function d(s){return s??process.env.CLAUDE_PROJECT_DIR??process.cwd()}var y=1e4,S=3e4,u=500;function A(s,r,e){return new Promise(t=>{try{let o=new URL(s),n=request({hostname:o.hostname,port:o.port||80,path:o.pathname+o.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(r)},timeout:5e3},i=>{let a=[];i.on("data",l=>a.push(l)),i.on("end",()=>{let l=Buffer.concat(a).toString();t(i.statusCode!=null&&i.statusCode>=200&&i.statusCode<300?l:null);});});n.on("error",()=>t(null)),n.on("timeout",()=>{n.destroy(),t(null);}),n.write(r),n.end();}catch{t(null);}})}var m=class{constructor(r){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=r?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=r?.apiKey??getLocalApiKey(),this.cacheDir=r?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=r?.legacyCacheDir??(r?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=r?.database??null,this.ownsDatabase=!r?.database;}async sendEvent(r){let e={...r,input:this.truncateInput(r.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),r.eventId):null}async sendExecutionStart(r,e,t,o,n=process.env.CLAUDE_PROJECT_DIR??process.cwd(),i=[]){let a={executionId:r,sessionId:e,skillName:t,startedAt:o,projectRoot:n,requiredMcpTools:i};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(r,e="completed",t=Date.now(),o){let n={executionId:r,status:e,finishedAt:t,metadata:o},i=this.commitOrCache("execution_complete",n);return i&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",n),i}async sendSessionStart(r,e,t){let o={id:r,projectRoot:e,startedAt:t,label:e.split("/").pop()??"unknown"},n=this.commitOrCache("session_start",o);return n&&this.postHttp("/api/telemetry/sessions",o),n}async completeEvent(r){let e=this.commitOrCache("complete_event",r);return e&&(this.postHttp("/api/telemetry/tool-call/output",r),this.scheduleMetricAggregation()),e}async endSession(r,e=Date.now()){let t={sessionId:r,finishedAt:e},o=this.commitOrCache("session_end",t);return o&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(r)}/close`,{finishedAt:e})),o}async flushCache(){try{let t=this.getDatabase(),o=t.listTelemetryFailures({unresolvedOnly:!0,limit:u}).reverse();for(let n of o)try{this.applyOperation(n.operation,n.payload),t.resolveTelemetryFailure(n.id);}catch{}t.trimTelemetryFailures(u);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(t=>!!t))].filter(t=>existsSync(t)).flatMap(t=>readdirSync(t).filter(o=>o.endsWith(".json")).map(o=>{let n=join(t,o);try{return {cacheFile:n,envelope:x(JSON.parse(readFileSync(n,"utf8")),o)}}catch{return null}})).filter(t=>t!==null).sort((t,o)=>t.envelope.timestamp-o.envelope.timestamp||v(t.envelope.operation)-v(o.envelope.operation));for(let{cacheFile:t,envelope:o}of e)try{let n=this.getDatabase();n.insertTelemetryFailure({id:o.id,operation:o.operation,payload:o.payload,error:o.failure,createdAt:o.timestamp}),this.applyOperation(o.operation,o.payload),n.resolveTelemetryFailure(o.id),unlinkSync(t);}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(r,e){try{return this.applyOperation(r,e),!0}catch(t){return this.cacheOperation(r,e,t),false}}applyOperation(r,e){let t=this.getDatabase();switch(r){case "tool_call":this.ensureParentSession(t,e),t.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(t,e),t.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running",requiredMcpTools:e.requiredMcpTools});return;case "execution_complete":t.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":t.ensureSession(e),t.insertRun({id:D(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(!t.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":t.closeSession(e.sessionId,e.finishedAt),t.updateRun(D(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(r,e){let t=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!t)throw new Error("Canonical session ID is required for telemetry");let o=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";r.ensureSession({id:t,projectRoot:o,label:o==="unknown"?void 0:o.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(r,e,t){let o=Date.now(),n={id:`failure:${o}:${Math.random().toString(36).slice(2,11)}`,operation:r,payload:e,failure:t instanceof Error?t.message:String(t),timestamp:o};try{let i=this.getDatabase();i.insertTelemetryFailure({id:n.id,operation:r,payload:e,error:n.failure,createdAt:o}),i.trimTelemetryFailures(u),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(n,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let r=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of r.slice(0,Math.max(0,r.length-u)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},S).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(r,e){A(`${this.apiUrl}${r}`,JSON.stringify(e),this.apiKey);}truncateInput(r){let e=JSON.stringify(r);return e===void 0||e.length<=y?r:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,y)}...`}}};function v(s){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(s)}function D(s){return `hook-run:${s}`}function x(s,r){if(!s||typeof s!="object")throw new Error("Invalid telemetry cache envelope");let e=s;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let t=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!t)throw new Error("Unknown legacy telemetry cache operation");let o=e.payload??{},n=Number(o.timestamp??o.startedAt??Date.now());return {id:`legacy-cache:${r}`,operation:t,payload:o,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:n}}async function _(s){let r=!s.memory,e=s.memory??new MemoryGate(s.projectRoot),t=0,o=0;try{await e.forceWarmUp(),s.trigger==="session_end"&&(t=await e.releasePendingDistillLeases(s.sessionId)),o=e.getPendingEventCount();}finally{r&&e.close();}let n={pendingEvents:o,releasedLeases:t,trigger:s.trigger,sessionId:s.sessionId};try{let i=openGlobalDevFlowDatabase();try{let a=Date.now(),l=`distill:${a}:${Math.random().toString(36).slice(2,10)}`;i.recordMemoryDistillCheckpoint({id:l,projectRoot:s.projectRoot,sessionId:s.sessionId,trigger:s.trigger,pendingEvents:o,releasedLeases:t,createdAt:a}),i.insertEvent({kind:"memory_distill_requested",timestamp:a,success:!0,metadata:{...n,projectRoot:s.projectRoot}});}finally{i.close();}}catch{}return n}function b(s){if(s.pendingEvents===0)return "";let r=s.sessionId?`\uFF0CsessionId=${s.sessionId}`:"";return `Memory distill checkpoint: ${s.pendingEvents} \u4E2A\u4E8B\u4EF6\u5F85\u63D0\u70BC${r}\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`}var w="WARNING: DevFlow is in degraded mode (daemon unreachable): 4-Gate enforcement and memory prefetch cache are unavailable. Run `devflow doctor` for details.";function E(){return process.env.DEVFLOW_HOOK_DEGRADED==="1"}async function j(s="",r=d()){let e;try{let i=JSON.parse(s);typeof i.session_id=="string"&&i.session_id.trim()&&(e=i.session_id.trim());}catch{}let t=new m;try{await t.flushAndAggregate();}finally{t.close();}let o=await _({projectRoot:r,sessionId:e,trigger:"pre_compact"}),n=[E()?w:"",b(o)].filter(Boolean).join(`
2
2
 
3
- `);return {hookSpecificOutput:{hookEventName:"PreCompact",...n?{additionalContext:n}:{}}}}if(process.argv[1]?.endsWith("pre-compact")||process.argv[1]?.endsWith("pre-compact.js")){let s=console.log,o=console.info,e=()=>{console.log=s,console.info=o;};console.log=console.error.bind(console),console.info=console.error.bind(console);let t="";process.stdin.setEncoding("utf8"),process.stdin.on("data",r=>{t+=r;}),process.stdin.on("end",()=>{M(t.trim()||process.argv[2]||"").then(r=>{e(),s(JSON.stringify(r)),process.exit(0);}).catch(()=>{e(),process.exit(0);});}),process.stdin.on("error",()=>{e(),process.exit(0);}),setTimeout(()=>process.exit(0),14e3).unref();}export{M as handlePreCompact};
3
+ `);return {hookSpecificOutput:{hookEventName:"PreCompact",...n?{additionalContext:n}:{}}}}if(process.argv[1]?.endsWith("pre-compact")||process.argv[1]?.endsWith("pre-compact.js")){let s=console.log,r=console.info,e=()=>{console.log=s,console.info=r;};console.log=console.error.bind(console),console.info=console.error.bind(console);let t="";process.stdin.setEncoding("utf8"),process.stdin.on("data",o=>{t+=o;}),process.stdin.on("end",()=>{j(t.trim()||process.argv[2]||"").then(o=>{e(),s(JSON.stringify(o)),process.exit(0);}).catch(()=>{e(),process.exit(0);});}),process.stdin.on("error",()=>{e(),process.exit(0);}),setTimeout(()=>process.exit(0),14e3).unref();}export{j as handlePreCompact};
@@ -1,2 +1,2 @@
1
- import {existsSync,readdirSync,readFileSync,unlinkSync,mkdirSync,writeFileSync,rmSync,renameSync}from'fs';import {join,dirname as dirname$1}from'path';import {request}from'http';import {homedir}from'os';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {fileURLToPath}from'url';import {mkdirSync as mkdirSync$1,existsSync as existsSync$1,statSync,appendFileSync,chmodSync,rmSync as rmSync$1,renameSync as renameSync$1}from'node:fs';import {createHash as createHash$1}from'node:crypto';import {homedir as homedir$1}from'node:os';import {dirname,join as join$1}from'node:path';import {randomUUID,createHash}from'crypto';var U=1e4,he=3e4,b=500;function ye(n,t,e){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":e,"Content-Length":Buffer.byteLength(t)},timeout:5e3},s=>{let c=[];s.on("data",l=>c.push(l)),s.on("end",()=>{let l=Buffer.concat(c).toString();o(s.statusCode!=null&&s.statusCode>=200&&s.statusCode<300?l:null);});});i.on("error",()=>o(null)),i.on("timeout",()=>{i.destroy(),o(null);}),i.write(t),i.end();}catch{o(null);}})}var R=class{constructor(t){this.retryScheduled=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,o,r,i=process.env.CLAUDE_PROJECT_DIR??process.cwd(),s=[]){let c={executionId:t,sessionId:e,skillName:o,startedAt:r,projectRoot:i,requiredMcpTools:s};this.commitOrCache("execution_start",c)&&this.postHttp("/api/telemetry/skill-execution/start",c);}async sendExecutionComplete(t,e="completed",o=Date.now(),r){let i={executionId:t,status:e,finishedAt:o,metadata:r},s=this.commitOrCache("execution_complete",i);return s&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",i),s}async sendSessionStart(t,e,o){let r={id:t,projectRoot:e,startedAt:o,label:e.split("/").pop()??"unknown"},i=this.commitOrCache("session_start",r);return i&&this.postHttp("/api/telemetry/sessions",r),i}async completeEvent(t){let e=this.commitOrCache("complete_event",t);return e&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp("/api/telemetry/tool-call/output",t)),e}async endSession(t,e=Date.now()){let o={sessionId:t,finishedAt:e},r=this.commitOrCache("session_end",o);return r&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),r}async flushCache(){try{let o=this.getDatabase(),r=o.listTelemetryFailures({unresolvedOnly:!0,limit:b}).reverse();for(let i of r)try{this.applyOperation(i.operation,i.payload),o.resolveTelemetryFailure(i.id);}catch{}o.trimTelemetryFailures(b);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(o=>!!o))].filter(o=>existsSync(o)).flatMap(o=>readdirSync(o).filter(r=>r.endsWith(".json")).map(r=>{let i=join(o,r);try{return {cacheFile:i,envelope:_e(JSON.parse(readFileSync(i,"utf8")),r)}}catch{return null}})).filter(o=>o!==null).sort((o,r)=>o.envelope.timestamp-r.envelope.timestamp||L(o.envelope.operation)-L(r.envelope.operation));for(let{cacheFile:o,envelope:r}of e)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(),this.database}commitOrCache(t,e){try{return this.applyOperation(t,e),!0}catch(o){return this.cacheOperation(t,e,o),false}}applyOperation(t,e){let o=this.getDatabase();switch(t){case "tool_call":this.ensureParentSession(o,e),o.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(o,e),o.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running"});return;case "execution_complete":o.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":o.ensureSession(e),o.insertRun({id:H(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(!o.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":o.closeSession(e.sessionId,e.finishedAt),o.updateRun(H(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(t,e){let o=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!o)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:o,projectRoot:r,label:r==="unknown"?void 0:r.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(t,e,o){let r=Date.now(),i={id:`failure:${r}:${Math.random().toString(36).slice(2,11)}`,operation:t,payload:e,failure:o instanceof Error?o.message:String(o),timestamp:r};try{let s=this.getDatabase();s.insertTelemetryFailure({id:i.id,operation:t,payload:e,error:i.failure,createdAt:r}),s.trimTelemetryFailures(b),this.scheduleRetry();return}catch{}try{existsSync(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 t=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of t.slice(0,Math.max(0,t.length-b)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},he).unref());}postHttp(t,e){ye(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=U?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,U)}...`}}};function L(n){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(n)}function H(n){return `hook-run:${n}`}function _e(n,t){if(!n||typeof n!="object")throw new Error("Invalid telemetry cache envelope");let e=n;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let o=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!o)throw new Error("Unknown legacy telemetry cache operation");let r=e.payload??{},i=Number(r.timestamp??r.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:o,payload:r,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:i}}function k(n){return n??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function p(n){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(k(n))}var Se=1;function xe(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join(dirname$1(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function W(n=xe()){let t=[join(n,"dist","command-registry.json"),join(n,"command-registry.json")];for(let e of t)try{if(!existsSync(e))continue;let o=JSON.parse(readFileSync(e,"utf8"));if(o.version!==Se||!ke(o.commands)){console.error(`[devflow] command registry contract mismatch: ${e}`);continue}return o.commands}catch(o){console.error(`[devflow] command registry load failed: ${o.message}`);}return null}function ke(n){return !n||typeof n!="object"||Array.isArray(n)?false:Object.values(n).every(t=>{if(!t||typeof t!="object"||Array.isArray(t))return false;let e=t;return q(e.mcpTools)&&q(e.blockedNative)})}function q(n){return Array.isArray(n)&&n.every(t=>typeof t=="string"&&t.length>0)}function G(n){let t=W();return t?t[n]?.mcpTools??[]:[]}function y(n){if(!n||typeof n!="object"||Array.isArray(n))return {};let t=n,e={};for(let o of ["lastMcpCall","bypassCount"])if(o in t){let r=t[o];if(r==null)continue;e[o]=typeof r=="number"&&Number.isFinite(r)&&r>=0?r:0;}return e}function Te(n,t){return n.lastMcpCall===t.lastMcpCall&&n.bypassCount===t.bypassCount}function Ce(n){try{return existsSync(n)?y(JSON.parse(readFileSync(n,"utf-8"))):{}}catch{return {}}}function K(n,t){let e=t instanceof Error?t.message:String(t);console.error(`[devflow] Receipt ${n} skipped: ${e}`);}function J(n){let t=p(n);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 z(n,t){if(n.getHookReceipt(t)){J(t);return}let e=join(p(t),"receipt.json");if(!existsSync(e))return;let o=Ce(e);n.updateHookReceipt(t,()=>o),J(t);}function V(n,t,e){let o;try{o=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),z(o,n);let r={},i=o.updateHookReceipt(n,c=>(r=y(c),y(t(r)))),s=y(i);return {receipt:s,applied:!0,changed:!Te(r,s)}}catch(r){return K(e,r),{receipt:{},applied:false,changed:false}}finally{try{o?.close();}catch{}}}function X(n){let t;try{return t=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),z(t,n),y(t.getHookReceipt(n))}catch(e){return K("update",e),{}}finally{try{t?.close();}catch{}}}function Ie(n,t){return V(n,()=>t,"write").applied}function Ae(n,t){return V(n,t,"update")}var B={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",Bash:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function je(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 S=class{constructor(t,e,o){this.projectRoot=t;this.sessionId=e;this.executionId=o;}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 o=this.getPhase();if(e||t==="Skill")return {permissionDecision:"allow"};if(o==="context_gathering"&&B[t])return {permissionDecision:"deny",reason:`DevFlow context required. Call mcp__devflow__get_project_context, then retry ${t}.`};if(o==="context_ready")return {permissionDecision:"allow"};let r=B[t];if(!r)return {permissionDecision:"allow"};let i=Ae(this.projectRoot,l=>({...l,bypassCount:(l.bypassCount??0)+1}));if(!i.applied)return {permissionDecision:"allow"};let s=i.receipt.bypassCount??0,c=je(t);return s>=c?{permissionDecision:"allow",additionalContext:`\u5DF2 ${s} \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(){Ie(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}};var qe=10*1024*1024,We=5;function Ge(n){return createHash$1("sha256").update(n).digest("hex").slice(0,16)}function Z(n){return join$1(homedir$1(),".devflow","logs","hook-debug",`${Ge(n)}.jsonl`)}function ee(n,t,e={}){let o=e.maxBytes??qe,r=e.maxFiles??We;mkdirSync$1(dirname(n),{recursive:true,mode:448});let i=Buffer.byteLength(t),s=existsSync$1(n)?statSync(n).size:0;s>0&&s+i>o&&Je(n,r),appendFileSync(n,t,{mode:384}),chmodSync(n,384);}function Je(n,t){if(t<=1){rmSync$1(n,{force:true});return}rmSync$1(`${n}.${t-1}`,{force:true});for(let e=t-2;e>=1;e-=1){let o=`${n}.${e}`;existsSync$1(o)&&renameSync$1(o,`${n}.${e+1}`);}existsSync$1(n)&&renameSync$1(n,`${n}.1`);}var A="WARNING: DevFlow is in degraded mode (daemon unreachable): 4-Gate enforcement and memory prefetch cache are unavailable. Run `devflow doctor` for details.";function te(){return process.env.DEVFLOW_HOOK_DEGRADED==="1"}function Ze(n){return Buffer.from(n,"utf8").toString("base64url")}var x=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(p(t),"hook-sessions");}registerSession(t){let e=t.trim();if(!e)throw new Error("session_id_required");let o=this.get(e);if(o)return o.lastActivityAt=Date.now(),this.persist(o),o;let r=Date.now(),i={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:r,lastActivityAt:r};return this.sessions.set(e,i),this.persist(i),i}startExecution(t,e,o){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(o)],r.lastActivityAt=Date.now(),this.persist(r),r}get(t){let e=t.trim();if(!e)return null;let o=this.sessions.get(e);if(o)return o;let r=this.snapshotPath(e);if(!existsSync(r))return null;try{let i=JSON.parse(readFileSync(r,"utf8"));return i.sessionId!==e||i.projectRoot!==this.projectRoot?null:(i.requiredMcpTools=Array.isArray(i.requiredMcpTools)?i.requiredMcpTools:[],this.sessions.set(e,i),i)}catch{return null}}completeExecution(t){let e=this.get(t);if(!e)return null;let o={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),o}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),o=`${e}.${process.pid}.tmp`;writeFileSync(o,JSON.stringify(t),{mode:384}),renameSync(o,e);}};function oe(n){let t=n.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 re(n,t){return t?`evt_tool_${createHash("sha256").update(`${n}\0${t}`).digest("hex").slice(0,24)}`:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`}function _(){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}}function ie(n){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:n}}}function it(n){let t=p(n);return existsSync(t)||mkdirSync(t,{recursive:true}),t}function st(n,t,e,o,r,i){let s=join(it(n),"event-map.json"),c=[];try{existsSync(s)&&(c=JSON.parse(readFileSync(s,"utf-8")));}catch{}c.push({sessionId:t,toolName:e,toolUseId:o,eventId:r,timestamp:i}),writeFileSync(s,JSON.stringify(c));}function ct(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"}}async function at(n){let t=te();try{if(!n||n.trim()==="")return _();let e=JSON.parse(n),o=Date.now(),r=k(),i=new R;e.tool_name.startsWith("mcp__devflow__")&&(typeof e.tool_input.projectRoot!="string"||!e.tool_input.projectRoot.trim())&&(e.tool_input.projectRoot=r);let s=e.session_id?.trim();if(!s)return ie(A);let c=new x(r),l=c.get(s)!==null;if(c.registerSession(s),l||await i.sendSessionStart(s,r,Date.now()),e.tool_name==="Skill"){let w=e.tool_input?.skill,m=w?oe(w):null;if(m){let le=c.get(s)?.executionId,h=c.startExecution(s,m,G(m));h.executionId&&h.executionId!==le&&await i.sendExecutionStart(h.executionId,s,m,h.lastActivityAt,r,h.requiredMcpTools);}}let d=c.get(s);e.tool_name.startsWith("mcp__devflow__")&&(e.tool_input._devflow_session_id=s,e.tool_input._devflow_execution_id=d?.executionId??s,e.tool_use_id&&(e.tool_input._devflow_tool_use_id=e.tool_use_id));let u=e.tool_name.startsWith("mcp__"),ce=u?e.tool_name.replace(/^mcp__[^_]+__/,""):void 0,j=new S(r,s,d?.executionId),F=j.getPhase(),a,v=j.evaluate(e.tool_name,u);v.permissionDecision==="deny"?a={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:v.reason??"MCP context required"}}:v.additionalContext?a=ie(v.additionalContext):a=_(),e.tool_name.startsWith("mcp__devflow__")&&(a.hookSpecificOutput.updatedInput=e.tool_input),t&&a.hookSpecificOutput.permissionDecision==="allow"&&(a.hookSpecificOutput.additionalContext=A);let ae=d?.executionId??s,g={eventId:re(s,e.tool_use_id),executionId:ae,sessionId:s,projectRoot:r,toolUseId:e.tool_use_id,timestamp:Date.now(),toolName:e.tool_name,toolType:u?"mcp":e.tool_name==="Agent"?"subagent":"direct",isMcpTool:u,mcpToolName:ce,mcpEnforced:u&&F==="context_gathering",mcpFallback:!u&&F==="context_gathering",kind:ct(e.tool_name),input:e.tool_input,duration:0,blocked:a.hookSpecificOutput.permissionDecision==="deny",blockReason:a.hookSpecificOutput.permissionDecision==="deny"?"DEVFLOW_CONTEXT_REQUIRED":void 0,error:a.hookSpecificOutput.permissionDecision==="deny"?"DEVFLOW_CONTEXT_REQUIRED":void 0},D=await i.sendEvent(g);D&&!g.blocked&&st(r,s,e.tool_name,e.tool_use_id,D,g.timestamp),D&&g.blocked&&await i.completeEvent({eventId:D,sessionId:s,error:"DEVFLOW_CONTEXT_REQUIRED",duration:Date.now()-g.timestamp,completedAt:Date.now()});try{let w=X(r),m={ts:o,tool:e.tool_name,sessionId:s,skill:d?.skillName??"(none)",requiredTools:d?.requiredMcpTools??[],isMcp:u,enforce:d?.executionId!==void 0,stateless:d?.executionId===void 0&&!u&&e.tool_name!=="Skill",bypassCount:w.bypassCount??0,decision:a.hookSpecificOutput.permissionDecision,env_CLAUDE_PLUGIN_ROOT:process.env.CLAUDE_PLUGIN_ROOT??"(unset)",cwd:process.cwd()};ee(Z(r),`${JSON.stringify(m)}
1
+ import {existsSync,readdirSync,readFileSync,unlinkSync,mkdirSync,writeFileSync,rmSync,renameSync}from'fs';import {join,dirname as dirname$1}from'path';import {request}from'http';import {homedir}from'os';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {fileURLToPath}from'url';import {mkdirSync as mkdirSync$1,existsSync as existsSync$1,statSync,appendFileSync,chmodSync,rmSync as rmSync$1,renameSync as renameSync$1}from'node:fs';import {createHash as createHash$1}from'node:crypto';import {homedir as homedir$1}from'node:os';import {dirname,join as join$1}from'node:path';import {randomUUID,createHash}from'crypto';var U=1e4,he=3e4,b=500;function ye(n,t,e){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":e,"Content-Length":Buffer.byteLength(t)},timeout:5e3},s=>{let c=[];s.on("data",l=>c.push(l)),s.on("end",()=>{let l=Buffer.concat(c).toString();o(s.statusCode!=null&&s.statusCode>=200&&s.statusCode<300?l:null);});});i.on("error",()=>o(null)),i.on("timeout",()=>{i.destroy(),o(null);}),i.write(t),i.end();}catch{o(null);}})}var R=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,o,r,i=process.env.CLAUDE_PROJECT_DIR??process.cwd(),s=[]){let c={executionId:t,sessionId:e,skillName:o,startedAt:r,projectRoot:i,requiredMcpTools:s};this.commitOrCache("execution_start",c)&&this.postHttp("/api/telemetry/skill-execution/start",c);}async sendExecutionComplete(t,e="completed",o=Date.now(),r){let i={executionId:t,status:e,finishedAt:o,metadata:r},s=this.commitOrCache("execution_complete",i);return s&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",i),s}async sendSessionStart(t,e,o){let r={id:t,projectRoot:e,startedAt:o,label:e.split("/").pop()??"unknown"},i=this.commitOrCache("session_start",r);return i&&this.postHttp("/api/telemetry/sessions",r),i}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 o={sessionId:t,finishedAt:e},r=this.commitOrCache("session_end",o);return r&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),r}async flushCache(){try{let o=this.getDatabase(),r=o.listTelemetryFailures({unresolvedOnly:!0,limit:b}).reverse();for(let i of r)try{this.applyOperation(i.operation,i.payload),o.resolveTelemetryFailure(i.id);}catch{}o.trimTelemetryFailures(b);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(o=>!!o))].filter(o=>existsSync(o)).flatMap(o=>readdirSync(o).filter(r=>r.endsWith(".json")).map(r=>{let i=join(o,r);try{return {cacheFile:i,envelope:_e(JSON.parse(readFileSync(i,"utf8")),r)}}catch{return null}})).filter(o=>o!==null).sort((o,r)=>o.envelope.timestamp-r.envelope.timestamp||L(o.envelope.operation)-L(r.envelope.operation));for(let{cacheFile:o,envelope:r}of e)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(),this.database}commitOrCache(t,e){try{return this.applyOperation(t,e),!0}catch(o){return this.cacheOperation(t,e,o),false}}applyOperation(t,e){let o=this.getDatabase();switch(t){case "tool_call":this.ensureParentSession(o,e),o.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(o,e),o.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running",requiredMcpTools:e.requiredMcpTools});return;case "execution_complete":o.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":o.ensureSession(e),o.insertRun({id:H(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(!o.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":o.closeSession(e.sessionId,e.finishedAt),o.updateRun(H(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(t,e){let o=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!o)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:o,projectRoot:r,label:r==="unknown"?void 0:r.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(t,e,o){let r=Date.now(),i={id:`failure:${r}:${Math.random().toString(36).slice(2,11)}`,operation:t,payload:e,failure:o instanceof Error?o.message:String(o),timestamp:r};try{let s=this.getDatabase();s.insertTelemetryFailure({id:i.id,operation:t,payload:e,error:i.failure,createdAt:r}),s.trimTelemetryFailures(b),this.scheduleRetry();return}catch{}try{existsSync(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 t=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of t.slice(0,Math.max(0,t.length-b)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},he).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){ye(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=U?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,U)}...`}}};function L(n){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(n)}function H(n){return `hook-run:${n}`}function _e(n,t){if(!n||typeof n!="object")throw new Error("Invalid telemetry cache envelope");let e=n;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let o=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!o)throw new Error("Unknown legacy telemetry cache operation");let r=e.payload??{},i=Number(r.timestamp??r.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:o,payload:r,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:i}}function k(n){return n??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function p(n){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(k(n))}var Se=1;function xe(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join(dirname$1(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function W(n=xe()){let t=[join(n,"dist","command-registry.json"),join(n,"command-registry.json")];for(let e of t)try{if(!existsSync(e))continue;let o=JSON.parse(readFileSync(e,"utf8"));if(o.version!==Se||!ke(o.commands)){console.error(`[devflow] command registry contract mismatch: ${e}`);continue}return o.commands}catch(o){console.error(`[devflow] command registry load failed: ${o.message}`);}return null}function ke(n){return !n||typeof n!="object"||Array.isArray(n)?false:Object.values(n).every(t=>{if(!t||typeof t!="object"||Array.isArray(t))return false;let e=t;return q(e.mcpTools)&&q(e.blockedNative)})}function q(n){return Array.isArray(n)&&n.every(t=>typeof t=="string"&&t.length>0)}function G(n){let t=W();return t?t[n]?.mcpTools??[]:[]}function y(n){if(!n||typeof n!="object"||Array.isArray(n))return {};let t=n,e={};for(let o of ["lastMcpCall","bypassCount"])if(o in t){let r=t[o];if(r==null)continue;e[o]=typeof r=="number"&&Number.isFinite(r)&&r>=0?r:0;}return e}function Te(n,t){return n.lastMcpCall===t.lastMcpCall&&n.bypassCount===t.bypassCount}function Ce(n){try{return existsSync(n)?y(JSON.parse(readFileSync(n,"utf-8"))):{}}catch{return {}}}function K(n,t){let e=t instanceof Error?t.message:String(t);console.error(`[devflow] Receipt ${n} skipped: ${e}`);}function J(n){let t=p(n);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 z(n,t){if(n.getHookReceipt(t)){J(t);return}let e=join(p(t),"receipt.json");if(!existsSync(e))return;let o=Ce(e);n.updateHookReceipt(t,()=>o),J(t);}function V(n,t,e){let o;try{o=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),z(o,n);let r={},i=o.updateHookReceipt(n,c=>(r=y(c),y(t(r)))),s=y(i);return {receipt:s,applied:!0,changed:!Te(r,s)}}catch(r){return K(e,r),{receipt:{},applied:false,changed:false}}finally{try{o?.close();}catch{}}}function X(n){let t;try{return t=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),z(t,n),y(t.getHookReceipt(n))}catch(e){return K("update",e),{}}finally{try{t?.close();}catch{}}}function Ie(n,t){return V(n,()=>t,"write").applied}function Ae(n,t){return V(n,t,"update")}var B={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",Bash:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function je(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 S=class{constructor(t,e,o){this.projectRoot=t;this.sessionId=e;this.executionId=o;}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 o=this.getPhase();if(e||t==="Skill")return {permissionDecision:"allow"};if(o==="context_gathering"&&B[t])return {permissionDecision:"deny",reason:`DevFlow context required. Call mcp__devflow__get_project_context, then retry ${t}.`};if(o==="context_ready")return {permissionDecision:"allow"};let r=B[t];if(!r)return {permissionDecision:"allow"};let i=Ae(this.projectRoot,l=>({...l,bypassCount:(l.bypassCount??0)+1}));if(!i.applied)return {permissionDecision:"allow"};let s=i.receipt.bypassCount??0,c=je(t);return s>=c?{permissionDecision:"allow",additionalContext:`\u5DF2 ${s} \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(){Ie(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}};var qe=10*1024*1024,We=5;function Ge(n){return createHash$1("sha256").update(n).digest("hex").slice(0,16)}function Z(n){return join$1(homedir$1(),".devflow","logs","hook-debug",`${Ge(n)}.jsonl`)}function ee(n,t,e={}){let o=e.maxBytes??qe,r=e.maxFiles??We;mkdirSync$1(dirname(n),{recursive:true,mode:448});let i=Buffer.byteLength(t),s=existsSync$1(n)?statSync(n).size:0;s>0&&s+i>o&&Je(n,r),appendFileSync(n,t,{mode:384}),chmodSync(n,384);}function Je(n,t){if(t<=1){rmSync$1(n,{force:true});return}rmSync$1(`${n}.${t-1}`,{force:true});for(let e=t-2;e>=1;e-=1){let o=`${n}.${e}`;existsSync$1(o)&&renameSync$1(o,`${n}.${e+1}`);}existsSync$1(n)&&renameSync$1(n,`${n}.1`);}var A="WARNING: DevFlow is in degraded mode (daemon unreachable): 4-Gate enforcement and memory prefetch cache are unavailable. Run `devflow doctor` for details.";function te(){return process.env.DEVFLOW_HOOK_DEGRADED==="1"}function Ze(n){return Buffer.from(n,"utf8").toString("base64url")}var x=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(p(t),"hook-sessions");}registerSession(t){let e=t.trim();if(!e)throw new Error("session_id_required");let o=this.get(e);if(o)return o.lastActivityAt=Date.now(),this.persist(o),o;let r=Date.now(),i={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:r,lastActivityAt:r};return this.sessions.set(e,i),this.persist(i),i}startExecution(t,e,o){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(o)],r.lastActivityAt=Date.now(),this.persist(r),r}get(t){let e=t.trim();if(!e)return null;let o=this.sessions.get(e);if(o)return o;let r=this.snapshotPath(e);if(!existsSync(r))return null;try{let i=JSON.parse(readFileSync(r,"utf8"));return i.sessionId!==e||i.projectRoot!==this.projectRoot?null:(i.requiredMcpTools=Array.isArray(i.requiredMcpTools)?i.requiredMcpTools:[],this.sessions.set(e,i),i)}catch{return null}}completeExecution(t){let e=this.get(t);if(!e)return null;let o={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),o}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),o=`${e}.${process.pid}.tmp`;writeFileSync(o,JSON.stringify(t),{mode:384}),renameSync(o,e);}};function oe(n){let t=n.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 re(n,t){return t?`evt_tool_${createHash("sha256").update(`${n}\0${t}`).digest("hex").slice(0,24)}`:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`}function _(){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}}function ie(n){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:n}}}function it(n){let t=p(n);return existsSync(t)||mkdirSync(t,{recursive:true}),t}function st(n,t,e,o,r,i){let s=join(it(n),"event-map.json"),c=[];try{existsSync(s)&&(c=JSON.parse(readFileSync(s,"utf-8")));}catch{}c.push({sessionId:t,toolName:e,toolUseId:o,eventId:r,timestamp:i}),writeFileSync(s,JSON.stringify(c));}function ct(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"}}async function at(n){let t=te();try{if(!n||n.trim()==="")return _();let e=JSON.parse(n),o=Date.now(),r=k(),i=new R;e.tool_name.startsWith("mcp__devflow__")&&(typeof e.tool_input.projectRoot!="string"||!e.tool_input.projectRoot.trim())&&(e.tool_input.projectRoot=r);let s=e.session_id?.trim();if(!s)return ie(A);let c=new x(r),l=c.get(s)!==null;if(c.registerSession(s),l||await i.sendSessionStart(s,r,Date.now()),e.tool_name==="Skill"){let w=e.tool_input?.skill,m=w?oe(w):null;if(m){let le=c.get(s)?.executionId,h=c.startExecution(s,m,G(m));h.executionId&&h.executionId!==le&&await i.sendExecutionStart(h.executionId,s,m,h.lastActivityAt,r,h.requiredMcpTools);}}let d=c.get(s);e.tool_name.startsWith("mcp__devflow__")&&(e.tool_input._devflow_session_id=s,e.tool_input._devflow_execution_id=d?.executionId??s,e.tool_use_id&&(e.tool_input._devflow_tool_use_id=e.tool_use_id));let u=e.tool_name.startsWith("mcp__"),ce=u?e.tool_name.replace(/^mcp__[^_]+__/,""):void 0,j=new S(r,s,d?.executionId),F=j.getPhase(),a,v=j.evaluate(e.tool_name,u);v.permissionDecision==="deny"?a={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:v.reason??"MCP context required"}}:v.additionalContext?a=ie(v.additionalContext):a=_(),e.tool_name.startsWith("mcp__devflow__")&&(a.hookSpecificOutput.updatedInput=e.tool_input),t&&a.hookSpecificOutput.permissionDecision==="allow"&&(a.hookSpecificOutput.additionalContext=A);let ae=d?.executionId??s,f={eventId:re(s,e.tool_use_id),executionId:ae,sessionId:s,projectRoot:r,toolUseId:e.tool_use_id,timestamp:Date.now(),toolName:e.tool_name,toolType:u?"mcp":e.tool_name==="Agent"?"subagent":"direct",isMcpTool:u,mcpToolName:ce,mcpEnforced:u&&F==="context_gathering",mcpFallback:!u&&F==="context_gathering",kind:ct(e.tool_name),input:e.tool_input,duration:0,blocked:a.hookSpecificOutput.permissionDecision==="deny",blockReason:a.hookSpecificOutput.permissionDecision==="deny"?"DEVFLOW_CONTEXT_REQUIRED":void 0,error:a.hookSpecificOutput.permissionDecision==="deny"?"DEVFLOW_CONTEXT_REQUIRED":void 0},D=await i.sendEvent(f);D&&!f.blocked&&st(r,s,e.tool_name,e.tool_use_id,D,f.timestamp),D&&f.blocked&&await i.completeEvent({eventId:D,sessionId:s,error:"DEVFLOW_CONTEXT_REQUIRED",duration:Date.now()-f.timestamp,completedAt:Date.now()});try{let w=X(r),m={ts:o,tool:e.tool_name,sessionId:s,skill:d?.skillName??"(none)",requiredTools:d?.requiredMcpTools??[],isMcp:u,enforce:d?.executionId!==void 0,stateless:d?.executionId===void 0&&!u&&e.tool_name!=="Skill",bypassCount:w.bypassCount??0,decision:a.hookSpecificOutput.permissionDecision,env_CLAUDE_PLUGIN_ROOT:process.env.CLAUDE_PLUGIN_ROOT??"(unset)",cwd:process.cwd()};ee(Z(r),`${JSON.stringify(m)}
2
2
  `);}catch{}return a}catch{return _()}}if(process.argv[1]?.endsWith("pre-tool-use")||process.argv[1]?.endsWith("pre-tool-use.js")){let n="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{n+=t;}),process.stdin.on("end",async()=>{let t=await at(n);console.log(JSON.stringify(t));}),process.stdin.on("error",()=>{console.log(JSON.stringify(_())),process.exit(0);}),setTimeout(()=>{console.log(JSON.stringify(_())),process.exit(0);},4e3).unref();}export{at as handlePreToolUse};
@@ -1,2 +1,2 @@
1
- import {request}from'http';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {existsSync,readFileSync,rmSync,mkdirSync,writeFileSync,renameSync,readdirSync,unlinkSync}from'fs';import {join}from'path';import {homedir}from'os';import {MemoryGate}from'@devflow-tools/memory-engine';import {randomUUID}from'crypto';function w(i){return i??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function R(i){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(w(i))}var A=1e4,K=3e4,g=500;function B(i,s,e){return new Promise(t=>{try{let n=new URL(i),r=request({hostname:n.hostname,port:n.port||80,path:n.pathname+n.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(s)},timeout:5e3},o=>{let a=[];o.on("data",c=>a.push(c)),o.on("end",()=>{let c=Buffer.concat(a).toString();t(o.statusCode!=null&&o.statusCode>=200&&o.statusCode<300?c:null);});});r.on("error",()=>t(null)),r.on("timeout",()=>{r.destroy(),t(null);}),r.write(s),r.end();}catch{t(null);}})}var y=class{constructor(s){this.retryScheduled=false;this.apiUrl=s?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=s?.apiKey??getLocalApiKey(),this.cacheDir=s?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=s?.legacyCacheDir??(s?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=s?.database??null,this.ownsDatabase=!s?.database;}async sendEvent(s){let e={...s,input:this.truncateInput(s.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),s.eventId):null}async sendExecutionStart(s,e,t,n,r=process.env.CLAUDE_PROJECT_DIR??process.cwd(),o=[]){let a={executionId:s,sessionId:e,skillName:t,startedAt:n,projectRoot:r,requiredMcpTools:o};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(s,e="completed",t=Date.now(),n){let r={executionId:s,status:e,finishedAt:t,metadata:n},o=this.commitOrCache("execution_complete",r);return o&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",r),o}async sendSessionStart(s,e,t){let n={id:s,projectRoot:e,startedAt:t,label:e.split("/").pop()??"unknown"},r=this.commitOrCache("session_start",n);return r&&this.postHttp("/api/telemetry/sessions",n),r}async completeEvent(s){let e=this.commitOrCache("complete_event",s);return e&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp("/api/telemetry/tool-call/output",s)),e}async endSession(s,e=Date.now()){let t={sessionId:s,finishedAt:e},n=this.commitOrCache("session_end",t);return n&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(s)}/close`,{finishedAt:e})),n}async flushCache(){try{let t=this.getDatabase(),n=t.listTelemetryFailures({unresolvedOnly:!0,limit:g}).reverse();for(let r of n)try{this.applyOperation(r.operation,r.payload),t.resolveTelemetryFailure(r.id);}catch{}t.trimTelemetryFailures(g);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(t=>!!t))].filter(t=>existsSync(t)).flatMap(t=>readdirSync(t).filter(n=>n.endsWith(".json")).map(n=>{let r=join(t,n);try{return {cacheFile:r,envelope:G(JSON.parse(readFileSync(r,"utf8")),n)}}catch{return null}})).filter(t=>t!==null).sort((t,n)=>t.envelope.timestamp-n.envelope.timestamp||P(t.envelope.operation)-P(n.envelope.operation));for(let{cacheFile:t,envelope:n}of e)try{let r=this.getDatabase();r.insertTelemetryFailure({id:n.id,operation:n.operation,payload:n.payload,error:n.failure,createdAt:n.timestamp}),this.applyOperation(n.operation,n.payload),r.resolveTelemetryFailure(n.id),unlinkSync(t);}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(s,e){try{return this.applyOperation(s,e),!0}catch(t){return this.cacheOperation(s,e,t),false}}applyOperation(s,e){let t=this.getDatabase();switch(s){case "tool_call":this.ensureParentSession(t,e),t.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(t,e),t.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running"});return;case "execution_complete":t.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":t.ensureSession(e),t.insertRun({id:M(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(!t.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":t.closeSession(e.sessionId,e.finishedAt),t.updateRun(M(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(s,e){let t=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!t)throw new Error("Canonical session ID is required for telemetry");let n=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";s.ensureSession({id:t,projectRoot:n,label:n==="unknown"?void 0:n.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(s,e,t){let n=Date.now(),r={id:`failure:${n}:${Math.random().toString(36).slice(2,11)}`,operation:s,payload:e,failure:t instanceof Error?t.message:String(t),timestamp:n};try{let o=this.getDatabase();o.insertTelemetryFailure({id:r.id,operation:s,payload:e,error:r.failure,createdAt:n}),o.trimTelemetryFailures(g),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let o=join(this.cacheDir,`${n}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(o,JSON.stringify(r,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let s=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of s.slice(0,Math.max(0,s.length-g)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},K).unref());}postHttp(s,e){B(`${this.apiUrl}${s}`,JSON.stringify(e),this.apiKey);}truncateInput(s){let e=JSON.stringify(s);return e===void 0||e.length<=A?s:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,A)}...`}}};function P(i){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(i)}function M(i){return `hook-run:${i}`}function G(i,s){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 t=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!t)throw new Error("Unknown legacy telemetry cache operation");let n=e.payload??{},r=Number(n.timestamp??n.startedAt??Date.now());return {id:`legacy-cache:${s}`,operation:t,payload:n,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:r}}async function O(i){let s=!i.memory,e=i.memory??new MemoryGate(i.projectRoot),t=0,n=0;try{await e.forceWarmUp(),i.trigger==="session_end"&&(t=await e.releasePendingDistillLeases(i.sessionId)),n=e.getPendingEventCount();}finally{s&&e.close();}let r={pendingEvents:n,releasedLeases:t,trigger:i.trigger,sessionId:i.sessionId};try{let o=openGlobalDevFlowDatabase();try{let a=Date.now(),c=`distill:${a}:${Math.random().toString(36).slice(2,10)}`;o.recordMemoryDistillCheckpoint({id:c,projectRoot:i.projectRoot,sessionId:i.sessionId,trigger:i.trigger,pendingEvents:n,releasedLeases:t,createdAt:a}),o.insertEvent({kind:"memory_distill_requested",timestamp:a,success:!0,metadata:{...r,projectRoot:i.projectRoot}});}finally{o.close();}}catch{}return r}function se(i){return Buffer.from(i,"utf8").toString("base64url")}var f=class{constructor(s){this.projectRoot=s;this.sessions=new Map;this.sessionsDir=join(R(s),"hook-sessions");}registerSession(s){let e=s.trim();if(!e)throw new Error("session_id_required");let t=this.get(e);if(t)return t.lastActivityAt=Date.now(),this.persist(t),t;let n=Date.now(),r={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:n,lastActivityAt:n};return this.sessions.set(e,r),this.persist(r),r}startExecution(s,e,t){let n=this.registerSession(s);return (!n.executionId||n.skillName!==e)&&(n.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),n.skillName=e,n.requiredMcpTools=[...new Set(t)],n.lastActivityAt=Date.now(),this.persist(n),n}get(s){let e=s.trim();if(!e)return null;let t=this.sessions.get(e);if(t)return t;let n=this.snapshotPath(e);if(!existsSync(n))return null;try{let r=JSON.parse(readFileSync(n,"utf8"));return r.sessionId!==e||r.projectRoot!==this.projectRoot?null:(r.requiredMcpTools=Array.isArray(r.requiredMcpTools)?r.requiredMcpTools:[],this.sessions.set(e,r),r)}catch{return null}}completeExecution(s){let e=this.get(s);if(!e)return null;let t={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),t}removeSession(s){let e=s.trim();e&&(this.sessions.delete(e),rmSync(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(s){return join(this.sessionsDir,`${se(s)}.json`)}persist(s){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(s.sessionId),t=`${e}.${process.pid}.tmp`;writeFileSync(t,JSON.stringify(s),{mode:384}),renameSync(t,e);}};var oe=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",ae=getLocalApiKey();function ce(i,s){return new Promise(e=>{let t=JSON.stringify(s),n=new URL(i,oe),r=request({hostname:n.hostname,port:n.port,path:n.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":ae,"Content-Length":Buffer.byteLength(t)},timeout:5e3},()=>e());r.on("error",()=>e()),r.write(t),r.end();})}async function le(i,s=w(),e=true,t){let n;try{n=JSON.parse(i);}catch{return}let r=n.session_id?.trim();if(!r)return;let o=new f(s),a=o.get(r),c=t?.telemetry??new y,F=!t?.telemetry;try{try{await c.flushAndAggregate();}catch{}let l;try{l=openGlobalDevFlowDatabase();let d=l.listToolCallEventsBySession(r),p=a?.executionId;if(p){let h=d.filter(m=>m.executionId===p),v=h.length,D=h.filter(m=>m.isMcpTool).length,_=v-D,S=h.filter(m=>m.toolType==="subagent").length,b=h.map(m=>m.timestamp).filter(Boolean),C=b.length>=2?Math.max(...b)-Math.min(...b):0,T=l.getExecutionObligationCompliance(p);try{l.updateSkillExecution(p,{status:"completed",finishedAt:Date.now(),totalToolCalls:v,mcpToolCalls:D,directToolCalls:_,subagentCount:S,totalDuration:C,mcpComplianceRate:T.rate}),await ce("/api/telemetry/skill-execution/complete",{executionId:p,finishedAt:Date.now(),status:"completed",summary:{totalToolCalls:v,mcpToolCalls:D,directToolCalls:_,subagentCount:S,totalDuration:C,mcpComplianceRate:T.rate}});}catch{}}e&&l.deleteHookReceipt(s),l.deleteContextReceipt(s,r);}catch{}finally{l?.close();}try{if(await O({projectRoot:s,sessionId:r,trigger:"session_end",memory:t?.memory}),t?.memory)await t.memory.closeSession(r);else {let d=new(await import('@devflow-tools/memory-engine')).MemoryGate(s);try{await d.closeSession(r);}finally{d.close();}}}catch{}try{await c.endSession(r),await c.flushAndAggregate();}catch{}}finally{F&&c.close(),o.removeSession(r);}}if(process.argv[1]?.endsWith("session-end")||process.argv[1]?.endsWith("session-end.js")){let i="";process.stdin.setEncoding("utf8"),process.stdin.on("data",s=>{i+=s;}),process.stdin.on("end",async()=>{await le(i.trim()||process.argv[2]||""),process.exit(0);}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),1e4).unref();}
2
- export{le as handleSessionEnd};
1
+ import {request}from'http';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {existsSync,readFileSync,rmSync,mkdirSync,writeFileSync,renameSync,readdirSync,unlinkSync}from'fs';import {join}from'path';import {homedir}from'os';import {MemoryGate}from'@devflow-tools/memory-engine';import {randomUUID}from'crypto';function w(n){return n??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function E(n){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(w(n))}var M=1e4,G=3e4,h=500;function W(n,s,e){return new Promise(t=>{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":e,"Content-Length":Buffer.byteLength(s)},timeout:5e3},o=>{let a=[];o.on("data",c=>a.push(c)),o.on("end",()=>{let c=Buffer.concat(a).toString();t(o.statusCode!=null&&o.statusCode>=200&&o.statusCode<300?c:null);});});i.on("error",()=>t(null)),i.on("timeout",()=>{i.destroy(),t(null);}),i.write(s),i.end();}catch{t(null);}})}var g=class{constructor(s){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=s?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=s?.apiKey??getLocalApiKey(),this.cacheDir=s?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=s?.legacyCacheDir??(s?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=s?.database??null,this.ownsDatabase=!s?.database;}async sendEvent(s){let e={...s,input:this.truncateInput(s.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),s.eventId):null}async sendExecutionStart(s,e,t,r,i=process.env.CLAUDE_PROJECT_DIR??process.cwd(),o=[]){let a={executionId:s,sessionId:e,skillName:t,startedAt:r,projectRoot:i,requiredMcpTools:o};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(s,e="completed",t=Date.now(),r){let i={executionId:s,status:e,finishedAt:t,metadata:r},o=this.commitOrCache("execution_complete",i);return o&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",i),o}async sendSessionStart(s,e,t){let r={id:s,projectRoot:e,startedAt:t,label:e.split("/").pop()??"unknown"},i=this.commitOrCache("session_start",r);return i&&this.postHttp("/api/telemetry/sessions",r),i}async completeEvent(s){let e=this.commitOrCache("complete_event",s);return e&&(this.postHttp("/api/telemetry/tool-call/output",s),this.scheduleMetricAggregation()),e}async endSession(s,e=Date.now()){let t={sessionId:s,finishedAt:e},r=this.commitOrCache("session_end",t);return r&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(s)}/close`,{finishedAt:e})),r}async flushCache(){try{let t=this.getDatabase(),r=t.listTelemetryFailures({unresolvedOnly:!0,limit:h}).reverse();for(let i of r)try{this.applyOperation(i.operation,i.payload),t.resolveTelemetryFailure(i.id);}catch{}t.trimTelemetryFailures(h);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(t=>!!t))].filter(t=>existsSync(t)).flatMap(t=>readdirSync(t).filter(r=>r.endsWith(".json")).map(r=>{let i=join(t,r);try{return {cacheFile:i,envelope:B(JSON.parse(readFileSync(i,"utf8")),r)}}catch{return null}})).filter(t=>t!==null).sort((t,r)=>t.envelope.timestamp-r.envelope.timestamp||P(t.envelope.operation)-P(r.envelope.operation));for(let{cacheFile:t,envelope:r}of e)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(t);}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(s,e){try{return this.applyOperation(s,e),!0}catch(t){return this.cacheOperation(s,e,t),false}}applyOperation(s,e){let t=this.getDatabase();switch(s){case "tool_call":this.ensureParentSession(t,e),t.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(t,e),t.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running",requiredMcpTools:e.requiredMcpTools});return;case "execution_complete":t.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":t.ensureSession(e),t.insertRun({id:O(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(!t.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":t.closeSession(e.sessionId,e.finishedAt),t.updateRun(O(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(s,e){let t=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!t)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";s.ensureSession({id:t,projectRoot:r,label:r==="unknown"?void 0:r.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(s,e,t){let r=Date.now(),i={id:`failure:${r}:${Math.random().toString(36).slice(2,11)}`,operation:s,payload:e,failure:t instanceof Error?t.message:String(t),timestamp:r};try{let o=this.getDatabase();o.insertTelemetryFailure({id:i.id,operation:s,payload:e,error:i.failure,createdAt:r}),o.trimTelemetryFailures(h),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let o=join(this.cacheDir,`${r}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(o,JSON.stringify(i,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let s=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of s.slice(0,Math.max(0,s.length-h)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},G).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(s,e){W(`${this.apiUrl}${s}`,JSON.stringify(e),this.apiKey);}truncateInput(s){let e=JSON.stringify(s);return e===void 0||e.length<=M?s:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,M)}...`}}};function P(n){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(n)}function O(n){return `hook-run:${n}`}function B(n,s){if(!n||typeof n!="object")throw new Error("Invalid telemetry cache envelope");let e=n;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let t=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!t)throw new Error("Unknown legacy telemetry cache operation");let r=e.payload??{},i=Number(r.timestamp??r.startedAt??Date.now());return {id:`legacy-cache:${s}`,operation:t,payload:r,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:i}}async function j(n){let s=!n.memory,e=n.memory??new MemoryGate(n.projectRoot),t=0,r=0;try{await e.forceWarmUp(),n.trigger==="session_end"&&(t=await e.releasePendingDistillLeases(n.sessionId)),r=e.getPendingEventCount();}finally{s&&e.close();}let i={pendingEvents:r,releasedLeases:t,trigger:n.trigger,sessionId:n.sessionId};try{let o=openGlobalDevFlowDatabase();try{let a=Date.now(),c=`distill:${a}:${Math.random().toString(36).slice(2,10)}`;o.recordMemoryDistillCheckpoint({id:c,projectRoot:n.projectRoot,sessionId:n.sessionId,trigger:n.trigger,pendingEvents:r,releasedLeases:t,createdAt:a}),o.insertEvent({kind:"memory_distill_requested",timestamp:a,success:!0,metadata:{...i,projectRoot:n.projectRoot}});}finally{o.close();}}catch{}return i}function re(n){return Buffer.from(n,"utf8").toString("base64url")}var y=class{constructor(s){this.projectRoot=s;this.sessions=new Map;this.sessionsDir=join(E(s),"hook-sessions");}registerSession(s){let e=s.trim();if(!e)throw new Error("session_id_required");let t=this.get(e);if(t)return t.lastActivityAt=Date.now(),this.persist(t),t;let r=Date.now(),i={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:r,lastActivityAt:r};return this.sessions.set(e,i),this.persist(i),i}startExecution(s,e,t){let r=this.registerSession(s);return (!r.executionId||r.skillName!==e)&&(r.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),r.skillName=e,r.requiredMcpTools=[...new Set(t)],r.lastActivityAt=Date.now(),this.persist(r),r}get(s){let e=s.trim();if(!e)return null;let t=this.sessions.get(e);if(t)return t;let r=this.snapshotPath(e);if(!existsSync(r))return null;try{let i=JSON.parse(readFileSync(r,"utf8"));return i.sessionId!==e||i.projectRoot!==this.projectRoot?null:(i.requiredMcpTools=Array.isArray(i.requiredMcpTools)?i.requiredMcpTools:[],this.sessions.set(e,i),i)}catch{return null}}completeExecution(s){let e=this.get(s);if(!e)return null;let t={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),t}removeSession(s){let e=s.trim();e&&(this.sessions.delete(e),rmSync(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(s){return join(this.sessionsDir,`${re(s)}.json`)}persist(s){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(s.sessionId),t=`${e}.${process.pid}.tmp`;writeFileSync(t,JSON.stringify(s),{mode:384}),renameSync(t,e);}};var ae=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",ce=getLocalApiKey();function le(n,s){return new Promise(e=>{let t=JSON.stringify(s),r=new URL(n,ae),i=request({hostname:r.hostname,port:r.port,path:r.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":ce,"Content-Length":Buffer.byteLength(t)},timeout:5e3},()=>e());i.on("error",()=>e()),i.write(t),i.end();})}async function ue(n,s=w(),e=true,t){let r;try{r=JSON.parse(n);}catch{return}let i=r.session_id?.trim();if(!i)return;let o=new y(s),a=o.get(i),c=t?.telemetry??new g,L=!t?.telemetry;try{try{await c.flushAndAggregate();}catch{}let l;try{l=openGlobalDevFlowDatabase();let d=l.listToolCallEventsBySession(i),u=a?.executionId;if(u){let f=d.filter(p=>p.executionId===u),v=f.length,D=f.filter(p=>p.isMcpTool).length,S=v-D,_=f.filter(p=>p.toolType==="subagent").length,b=Date.now(),T=l.getSkillExecution(u),C=T?.startedAt!=null?Math.max(0,b-T.startedAt):0,R=l.getExecutionObligationCompliance(u);try{l.updateSkillExecution(u,{status:"completed",finishedAt:b,totalToolCalls:v,mcpToolCalls:D,directToolCalls:S,subagentCount:_,totalDuration:C,mcpComplianceRate:R.rate}),await le("/api/telemetry/skill-execution/complete",{executionId:u,finishedAt:b,status:"completed",summary:{totalToolCalls:v,mcpToolCalls:D,directToolCalls:S,subagentCount:_,totalDuration:C,mcpComplianceRate:R.rate}});}catch{}}e&&l.deleteHookReceipt(s),l.deleteContextReceipt(s,i);}catch{}finally{l?.close();}try{if(await j({projectRoot:s,sessionId:i,trigger:"session_end",memory:t?.memory}),t?.memory)await t.memory.closeSession(i);else {let d=new(await import('@devflow-tools/memory-engine')).MemoryGate(s);try{await d.closeSession(i);}finally{d.close();}}}catch{}try{await c.endSession(i),await c.flushAndAggregate();}catch{}}finally{L&&c.close(),o.removeSession(i);}}if(process.argv[1]?.endsWith("session-end")||process.argv[1]?.endsWith("session-end.js")){let n="";process.stdin.setEncoding("utf8"),process.stdin.on("data",s=>{n+=s;}),process.stdin.on("end",async()=>{await ue(n.trim()||process.argv[2]||""),process.exit(0);}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),1e4).unref();}
2
+ export{ue as handleSessionEnd};
@@ -1,3 +1,3 @@
1
1
  import {createHash}from'node:crypto';import {mkdirSync,appendFileSync}from'node:fs';import {homedir as homedir$1}from'node:os';import {dirname,join as join$1}from'node:path';import {MemoryGate}from'@devflow-tools/memory-engine';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {existsSync,readFileSync,rmSync,mkdirSync as mkdirSync$1,writeFileSync,renameSync,readdirSync,unlinkSync}from'fs';import {join,dirname as dirname$1}from'path';import {randomUUID}from'crypto';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {fileURLToPath}from'url';import {request}from'http';import {homedir}from'os';var L=/(?:^|[.!?。!?]\s*)(?:(?:please\s+)?(?:remember|memorize)\b|(?:请记|(?:请)?记住))/iu,J=/\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 S(s){let t=s.trim();return t&&L.test(t)&&!J.test(t)?t:null}function H(s){let t=createHash("sha256").update(s).digest("hex").slice(0,16),e=process.env.DEVFLOW_STATE_DIR||join$1(homedir$1(),".devflow","state",t);return join$1(e,"memory-intents.jsonl")}function D(s,t){let e=H(s);mkdirSync(dirname(e),{recursive:true}),appendFileSync(e,`${JSON.stringify(t)}
2
- `,{mode:384});}var w="WARNING: DevFlow is in degraded mode (daemon unreachable): 4-Gate enforcement and memory prefetch cache are unavailable. Run `devflow doctor` for details.";function b(){return process.env.DEVFLOW_HOOK_DEGRADED==="1"}function W(s){return s??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function f(s){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(W(s))}function Q(s){return Buffer.from(s,"utf8").toString("base64url")}var p=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(f(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 n=Date.now(),o={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:n,lastActivityAt:n};return this.sessions.set(e,o),this.persist(o),o}startExecution(t,e,r){let n=this.registerSession(t);return (!n.executionId||n.skillName!==e)&&(n.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),n.skillName=e,n.requiredMcpTools=[...new Set(r)],n.lastActivityAt=Date.now(),this.persist(n),n}get(t){let e=t.trim();if(!e)return null;let r=this.sessions.get(e);if(r)return r;let n=this.snapshotPath(e);if(!existsSync(n))return null;try{let o=JSON.parse(readFileSync(n,"utf8"));return o.sessionId!==e||o.projectRoot!==this.projectRoot?null:(o.requiredMcpTools=Array.isArray(o.requiredMcpTools)?o.requiredMcpTools:[],this.sessions.set(e,o),o)}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,`${Q(t)}.json`)}persist(t){mkdirSync$1(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 ee(s){let t=s.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 I(s){let t=s.trimStart().match(/^\/(devflow:[a-z0-9][a-z0-9-]*)\b/i);if(!t)return null;let e=ee(t[1]);return e?{rawName:t[1],skillName:e}:null}var se=1;function ie(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join(dirname$1(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function x(s=ie()){let t=[join(s,"dist","command-registry.json"),join(s,"command-registry.json")];for(let e of t)try{if(!existsSync(e))continue;let r=JSON.parse(readFileSync(e,"utf8"));if(r.version!==se||!ae(r.commands)){console.error(`[devflow] command registry contract mismatch: ${e}`);continue}return r.commands}catch(r){console.error(`[devflow] command registry load failed: ${r.message}`);}return null}function ae(s){return !s||typeof s!="object"||Array.isArray(s)?false:Object.values(s).every(t=>{if(!t||typeof t!="object"||Array.isArray(t))return false;let e=t;return R(e.mcpTools)&&R(e.blockedNative)})}function R(s){return Array.isArray(s)&&s.every(t=>typeof t=="string"&&t.length>0)}function E(s){let t=x();return t?t[s]?.mcpTools??[]:[]}var A=1e4,he=3e4,d=500;function ge(s,t,e){return new Promise(r=>{try{let n=new URL(s),o=request({hostname:n.hostname,port:n.port||80,path:n.pathname+n.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);});});o.on("error",()=>r(null)),o.on("timeout",()=>{o.destroy(),r(null);}),o.write(t),o.end();}catch{r(null);}})}var h=class{constructor(t){this.retryScheduled=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,r,n,o=process.env.CLAUDE_PROJECT_DIR??process.cwd(),i=[]){let a={executionId:t,sessionId:e,skillName:r,startedAt:n,projectRoot:o,requiredMcpTools:i};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(t,e="completed",r=Date.now(),n){let o={executionId:t,status:e,finishedAt:r,metadata:n},i=this.commitOrCache("execution_complete",o);return i&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",o),i}async sendSessionStart(t,e,r){let n={id:t,projectRoot:e,startedAt:r,label:e.split("/").pop()??"unknown"},o=this.commitOrCache("session_start",n);return o&&this.postHttp("/api/telemetry/sessions",n),o}async completeEvent(t){let e=this.commitOrCache("complete_event",t);return e&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp("/api/telemetry/tool-call/output",t)),e}async endSession(t,e=Date.now()){let r={sessionId:t,finishedAt:e},n=this.commitOrCache("session_end",r);return n&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),n}async flushCache(){try{let r=this.getDatabase(),n=r.listTelemetryFailures({unresolvedOnly:!0,limit:d}).reverse();for(let o of n)try{this.applyOperation(o.operation,o.payload),r.resolveTelemetryFailure(o.id);}catch{}r.trimTelemetryFailures(d);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(r=>!!r))].filter(r=>existsSync(r)).flatMap(r=>readdirSync(r).filter(n=>n.endsWith(".json")).map(n=>{let o=join(r,n);try{return {cacheFile:o,envelope:fe(JSON.parse(readFileSync(o,"utf8")),n)}}catch{return null}})).filter(r=>r!==null).sort((r,n)=>r.envelope.timestamp-n.envelope.timestamp||N(r.envelope.operation)-N(n.envelope.operation));for(let{cacheFile:r,envelope:n}of e)try{let o=this.getDatabase();o.insertTelemetryFailure({id:n.id,operation:n.operation,payload:n.payload,error:n.failure,createdAt:n.timestamp}),this.applyOperation(n.operation,n.payload),o.resolveTelemetryFailure(n.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"});return;case "execution_complete":r.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":r.ensureSession(e),r.insertRun({id:M(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(M(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 n=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:n,label:n==="unknown"?void 0:n.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(t,e,r){let n=Date.now(),o={id:`failure:${n}:${Math.random().toString(36).slice(2,11)}`,operation:t,payload:e,failure:r instanceof Error?r.message:String(r),timestamp:n};try{let i=this.getDatabase();i.insertTelemetryFailure({id:o.id,operation:t,payload:e,error:o.failure,createdAt:n}),i.trimTelemetryFailures(d),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync$1(this.cacheDir,{recursive:!0});let i=join(this.cacheDir,`${n}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(i,JSON.stringify(o,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-d)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},he).unref());}postHttp(t,e){ge(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=A?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,A)}...`}}};function N(s){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(s)}function M(s){return `hook-run:${s}`}function fe(s,t){if(!s||typeof s!="object")throw new Error("Invalid telemetry cache envelope");let e=s;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 n=e.payload??{},o=Number(n.timestamp??n.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:r,payload:n,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:o}}var m=process.env.CLAUDE_PROJECT_DIR||process.cwd();async function ve(s){let t=b()?{additionalContext:w}:{},e;try{e=JSON.parse(s);}catch{return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t}}}let r=e.prompt||"";if(!r.trim())return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t}};let n=S(r);if(n)try{D(m,{content:n,sessionId:e.session_id,createdAt:Date.now()});}catch{}let o=e.session_id?.trim(),i=I(r);if(o&&i){let c=new p(m),P=c.get(o)?.executionId,l=c.startExecution(o,i.skillName,E(i.skillName));if(l.executionId&&l.executionId!==P){let g=new h;try{await g.sendSessionStart(o,m,l.startedAt),await g.sendExecutionStart(l.executionId,o,i.skillName,l.lastActivityAt,m,l.requiredMcpTools);}finally{g.close();}}}let a=new MemoryGate(m);try{await a.forceWarmUp(),o&&await a.recordUserMessage(r.slice(0,2e3),o,r.slice(0,200));}catch{}finally{a.close();}return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t}}}if(process.argv[1]?.endsWith("user-prompt-submit")||process.argv[1]?.endsWith("user-prompt-submit.js")){let s="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{s+=t;}),process.stdin.on("end",async()=>{let t=await ve(s.trim()||process.argv[2]||"");console.log(JSON.stringify(t)),process.exit(0);}),process.stdin.on("error",()=>{console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}})),process.exit(0);}),setTimeout(()=>{console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}})),process.exit(0);},3e4).unref();}
2
+ `,{mode:384});}var w="WARNING: DevFlow is in degraded mode (daemon unreachable): 4-Gate enforcement and memory prefetch cache are unavailable. Run `devflow doctor` for details.";function b(){return process.env.DEVFLOW_HOOK_DEGRADED==="1"}function W(s){return s??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function f(s){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(W(s))}function Q(s){return Buffer.from(s,"utf8").toString("base64url")}var p=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(f(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 n=Date.now(),o={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:n,lastActivityAt:n};return this.sessions.set(e,o),this.persist(o),o}startExecution(t,e,r){let n=this.registerSession(t);return (!n.executionId||n.skillName!==e)&&(n.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),n.skillName=e,n.requiredMcpTools=[...new Set(r)],n.lastActivityAt=Date.now(),this.persist(n),n}get(t){let e=t.trim();if(!e)return null;let r=this.sessions.get(e);if(r)return r;let n=this.snapshotPath(e);if(!existsSync(n))return null;try{let o=JSON.parse(readFileSync(n,"utf8"));return o.sessionId!==e||o.projectRoot!==this.projectRoot?null:(o.requiredMcpTools=Array.isArray(o.requiredMcpTools)?o.requiredMcpTools:[],this.sessions.set(e,o),o)}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,`${Q(t)}.json`)}persist(t){mkdirSync$1(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 ee(s){let t=s.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 R(s){let t=s.trimStart().match(/^\/(devflow:[a-z0-9][a-z0-9-]*)\b/i);if(!t)return null;let e=ee(t[1]);return e?{rawName:t[1],skillName:e}:null}var se=1;function ie(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join(dirname$1(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function x(s=ie()){let t=[join(s,"dist","command-registry.json"),join(s,"command-registry.json")];for(let e of t)try{if(!existsSync(e))continue;let r=JSON.parse(readFileSync(e,"utf8"));if(r.version!==se||!ae(r.commands)){console.error(`[devflow] command registry contract mismatch: ${e}`);continue}return r.commands}catch(r){console.error(`[devflow] command registry load failed: ${r.message}`);}return null}function ae(s){return !s||typeof s!="object"||Array.isArray(s)?false:Object.values(s).every(t=>{if(!t||typeof t!="object"||Array.isArray(t))return false;let e=t;return I(e.mcpTools)&&I(e.blockedNative)})}function I(s){return Array.isArray(s)&&s.every(t=>typeof t=="string"&&t.length>0)}function E(s){let t=x();return t?t[s]?.mcpTools??[]:[]}var C=1e4,he=3e4,d=500;function ge(s,t,e){return new Promise(r=>{try{let n=new URL(s),o=request({hostname:n.hostname,port:n.port||80,path:n.pathname+n.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);});});o.on("error",()=>r(null)),o.on("timeout",()=>{o.destroy(),r(null);}),o.write(t),o.end();}catch{r(null);}})}var h=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,r,n,o=process.env.CLAUDE_PROJECT_DIR??process.cwd(),i=[]){let a={executionId:t,sessionId:e,skillName:r,startedAt:n,projectRoot:o,requiredMcpTools:i};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(t,e="completed",r=Date.now(),n){let o={executionId:t,status:e,finishedAt:r,metadata:n},i=this.commitOrCache("execution_complete",o);return i&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",o),i}async sendSessionStart(t,e,r){let n={id:t,projectRoot:e,startedAt:r,label:e.split("/").pop()??"unknown"},o=this.commitOrCache("session_start",n);return o&&this.postHttp("/api/telemetry/sessions",n),o}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},n=this.commitOrCache("session_end",r);return n&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),n}async flushCache(){try{let r=this.getDatabase(),n=r.listTelemetryFailures({unresolvedOnly:!0,limit:d}).reverse();for(let o of n)try{this.applyOperation(o.operation,o.payload),r.resolveTelemetryFailure(o.id);}catch{}r.trimTelemetryFailures(d);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(r=>!!r))].filter(r=>existsSync(r)).flatMap(r=>readdirSync(r).filter(n=>n.endsWith(".json")).map(n=>{let o=join(r,n);try{return {cacheFile:o,envelope:fe(JSON.parse(readFileSync(o,"utf8")),n)}}catch{return null}})).filter(r=>r!==null).sort((r,n)=>r.envelope.timestamp-n.envelope.timestamp||M(r.envelope.operation)-M(n.envelope.operation));for(let{cacheFile:r,envelope:n}of e)try{let o=this.getDatabase();o.insertTelemetryFailure({id:n.id,operation:n.operation,payload:n.payload,error:n.failure,createdAt:n.timestamp}),this.applyOperation(n.operation,n.payload),o.resolveTelemetryFailure(n.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:N(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(N(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 n=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:n,label:n==="unknown"?void 0:n.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(t,e,r){let n=Date.now(),o={id:`failure:${n}:${Math.random().toString(36).slice(2,11)}`,operation:t,payload:e,failure:r instanceof Error?r.message:String(r),timestamp:n};try{let i=this.getDatabase();i.insertTelemetryFailure({id:o.id,operation:t,payload:e,error:o.failure,createdAt:n}),i.trimTelemetryFailures(d),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync$1(this.cacheDir,{recursive:!0});let i=join(this.cacheDir,`${n}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(i,JSON.stringify(o,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-d)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},he).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){ge(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=C?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,C)}...`}}};function M(s){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(s)}function N(s){return `hook-run:${s}`}function fe(s,t){if(!s||typeof s!="object")throw new Error("Invalid telemetry cache envelope");let e=s;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 n=e.payload??{},o=Number(n.timestamp??n.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:r,payload:n,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:o}}var m=process.env.CLAUDE_PROJECT_DIR||process.cwd();async function ve(s){let t=b()?{additionalContext:w}:{},e;try{e=JSON.parse(s);}catch{return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t}}}let r=e.prompt||"";if(!r.trim())return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t}};let n=S(r);if(n)try{D(m,{content:n,sessionId:e.session_id,createdAt:Date.now()});}catch{}let o=e.session_id?.trim(),i=R(r);if(o&&i){let c=new p(m),P=c.get(o)?.executionId,l=c.startExecution(o,i.skillName,E(i.skillName));if(l.executionId&&l.executionId!==P){let g=new h;try{await g.sendSessionStart(o,m,l.startedAt),await g.sendExecutionStart(l.executionId,o,i.skillName,l.lastActivityAt,m,l.requiredMcpTools);}finally{g.close();}}}let a=new MemoryGate(m);try{await a.forceWarmUp(),o&&await a.recordUserMessage(r.slice(0,2e3),o,r.slice(0,200));}catch{}finally{a.close();}return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t}}}if(process.argv[1]?.endsWith("user-prompt-submit")||process.argv[1]?.endsWith("user-prompt-submit.js")){let s="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{s+=t;}),process.stdin.on("end",async()=>{let t=await ve(s.trim()||process.argv[2]||"");console.log(JSON.stringify(t)),process.exit(0);}),process.stdin.on("error",()=>{console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}})),process.exit(0);}),setTimeout(()=>{console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}})),process.exit(0);},3e4).unref();}
3
3
  export{ve as handleUserPromptSubmit};
@@ -46,7 +46,7 @@ rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
46
46
 
47
47
  **新功能开发:** 需要加组件或页面 → 先确认现有实现模式,再做影响范围评估,然后生成符合项目约定的组件/Hook/类型代码,最后通过类型检查和测试。
48
48
 
49
- **性能卡顿:** 列表页滚动掉帧、输入响应慢 → 先用静态工具生成候选,再用 Profiler、浏览器 Performance trace 或可复现 benchmark 测量渲染次数、耗时和 CPU;只有测量后才能确认影响和排序优化。
49
+ **性能卡顿:** 列表页滚动掉帧、输入响应慢 → Profiler 分析渲染次数和耗时,检查 memo/useMemo/useCallback 使用情况,检测不必要重渲染,生成优化方案并对比前后基准。
50
50
 
51
51
  **Hooks 审查:** lint 警告、useEffect 循环触发 → 全量扫描所有 Hooks 调用位置,检查顶层调用规则、依赖数组完整性、清理函数缺失、反模式(渲染中创建组件、过大 useEffect),自动修复安全项。
52
52
 
@@ -59,10 +59,12 @@ rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
59
59
  - 使用函数组件 + Hooks,不使用 Class 组件
60
60
  - 每个文件只导出一个组件
61
61
  - Props 使用 TypeScript interface 定义
62
- - 仅在父组件重复渲染、props 稳定且组件渲染成本可观时评估 React.memo;React.memo 不会阻止组件自身 state 或所消费 Context 变化引发的更新
62
+ - 仅在父组件重复渲染、props 稳定且渲染成本可观时评估 React.memo;它不会阻止组件自身 state 或 Context 更新
63
63
  - 自定义 Hooks 命名以 use 开头
64
- - 内联 DOM style 默认是低影响静态发现;只有测量证明位于热点路径时才升级影响等级
65
- - requestAnimationFrame 只对齐浏览器绘制时机;没有明确采样频率、合并和丢弃策略时,不得把它描述为通用节流
64
+ - 用运行时测量决定性能优化;内联 DOM style 默认低影响,requestAnimationFrame 没有采样策略时不是通用节流
65
+ - runtimeProfilingOccurred=false 时所有性能发现只能称为静态候选,不得标记 P0、声称卡顿/掉帧/CPU 热点或给出具体收益比例
66
+ - 性能报告必须分为工具发现、模型假设、需要运行时验证;工具返回 0 条发现不得表述为静态分析通过
67
+ - scannedFiles=0 表示目标定位失败,必须修正 filePath/componentName 后重试,不能基于空结果继续推断
66
68
  - 遇到不确定的 API 时先查 React 官方文档再回答
67
69
  - 修复 Bug 前先查 git log 找最近变更
68
70
  - 生成代码后必须通过 TypeScript 类型检查
@@ -82,15 +84,3 @@ rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
82
84
  | Hooks 审查: | 调用 MCP 工具 `react_review_hooks` |
83
85
  | 组件拆分: | 调用 MCP 工具 `react_refactor_component` |
84
86
  | 新建组件: | 调用 MCP 工具 `react_new_component` |
85
-
86
- ## 性能证据契约
87
-
88
- `react_audit_performance` 返回静态候选,并通过 `runtimeProfilingOccurred` 明确说明是否执行过运行时 profiling。每条发现必须保留 `evidenceMode`、`detectionMethod`、`confidence`、`impact` 和 `limitations`,不得省略限制条件。
89
-
90
- 最终答复必须按以下三个标题分开陈述:
91
-
92
- 1. **工具发现**:只复述工具实际返回的证据、位置和限制,不扩写为已测事实。
93
- 2. **模型假设**:明确标注基于代码上下文的推断,不与工具发现混写。
94
- 3. **需要运行时验证**:列出复现场景、Profiler/trace/benchmark 方法和待采集指标。
95
-
96
- 没有运行时测量时,不得把任何静态发现标为 P0,不得声称已观察到 jank/卡顿或 CPU 影响,也不得称其为性能瓶颈。只有包含场景、指标、数值和单位的 Profiler、Performance trace 或 benchmark 结果,才能支持这些结论。
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devflow-tools/claude-code-plugin",
3
- "version": "0.16.5",
3
+ "version": "0.16.6",
4
4
  "description": "DevFlow — Developer Intelligence Platform for Claude Code",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -8,18 +8,18 @@
8
8
  "deploy": "npx tsx scripts/inject-gates.ts --deploy"
9
9
  },
10
10
  "dependencies": {
11
- "@devflow-tools/database": "0.16.5",
12
- "@devflow-tools/memory-engine": "0.16.5",
13
- "@devflow-tools/sdk": "0.16.5",
14
- "@devflow-tools/telemetry": "0.16.5",
15
- "@devflow-tools/workflow-engine": "0.16.5"
11
+ "@devflow-tools/database": "0.16.6",
12
+ "@devflow-tools/memory-engine": "0.16.6",
13
+ "@devflow-tools/sdk": "0.16.6",
14
+ "@devflow-tools/telemetry": "0.16.6",
15
+ "@devflow-tools/workflow-engine": "0.16.6"
16
16
  },
17
17
  "allowScripts": {
18
18
  "better-sqlite3@12.11.1": true,
19
19
  "sharp@0.32.6": true
20
20
  },
21
21
  "devDependencies": {
22
- "@devflow-tools/mcp-server": "0.16.5"
22
+ "@devflow-tools/mcp-server": "0.16.6"
23
23
  },
24
24
  "keywords": [
25
25
  "pi-package",
@@ -13,7 +13,7 @@ description: React 18 专家能力 — Bug 诊断、组件重构、性能优化
13
13
 
14
14
  **新功能开发:** 需要加组件或页面 → 先确认现有实现模式,再做影响范围评估,然后生成符合项目约定的组件/Hook/类型代码,最后通过类型检查和测试。
15
15
 
16
- **性能卡顿:** 列表页滚动掉帧、输入响应慢 → 先用静态工具生成候选,再用 Profiler、浏览器 Performance trace 或可复现 benchmark 测量渲染次数、耗时和 CPU;只有测量后才能确认影响和排序优化。
16
+ **性能卡顿:** 列表页滚动掉帧、输入响应慢 → Profiler 分析渲染次数和耗时,检查 memo/useMemo/useCallback 使用情况,检测不必要重渲染,生成优化方案并对比前后基准。
17
17
 
18
18
  **Hooks 审查:** lint 警告、useEffect 循环触发 → 全量扫描所有 Hooks 调用位置,检查顶层调用规则、依赖数组完整性、清理函数缺失、反模式(渲染中创建组件、过大 useEffect),自动修复安全项。
19
19
 
@@ -26,10 +26,12 @@ description: React 18 专家能力 — Bug 诊断、组件重构、性能优化
26
26
  - 使用函数组件 + Hooks,不使用 Class 组件
27
27
  - 每个文件只导出一个组件
28
28
  - Props 使用 TypeScript interface 定义
29
- - 仅在父组件重复渲染、props 稳定且组件渲染成本可观时评估 React.memo;React.memo 不会阻止组件自身 state 或所消费 Context 变化引发的更新
29
+ - 仅在父组件重复渲染、props 稳定且渲染成本可观时评估 React.memo;它不会阻止组件自身 state 或 Context 更新
30
30
  - 自定义 Hooks 命名以 use 开头
31
- - 内联 DOM style 默认是低影响静态发现;只有测量证明位于热点路径时才升级影响等级
32
- - requestAnimationFrame 只对齐浏览器绘制时机;没有明确采样频率、合并和丢弃策略时,不得把它描述为通用节流
31
+ - 用运行时测量决定性能优化;内联 DOM style 默认低影响,requestAnimationFrame 没有采样策略时不是通用节流
32
+ - runtimeProfilingOccurred=false 时所有性能发现只能称为静态候选,不得标记 P0、声称卡顿/掉帧/CPU 热点或给出具体收益比例
33
+ - 性能报告必须分为工具发现、模型假设、需要运行时验证;工具返回 0 条发现不得表述为静态分析通过
34
+ - scannedFiles=0 表示目标定位失败,必须修正 filePath/componentName 后重试,不能基于空结果继续推断
33
35
  - 遇到不确定的 API 时先查 React 官方文档再回答
34
36
  - 修复 Bug 前先查 git log 找最近变更
35
37
  - 生成代码后必须通过 TypeScript 类型检查
@@ -49,15 +51,3 @@ description: React 18 专家能力 — Bug 诊断、组件重构、性能优化
49
51
  | Hooks 审查: | 调用 MCP 工具 `react_review_hooks` |
50
52
  | 组件拆分: | 调用 MCP 工具 `react_refactor_component` |
51
53
  | 新建组件: | 调用 MCP 工具 `react_new_component` |
52
-
53
- ## 性能证据契约
54
-
55
- `react_audit_performance` 返回静态候选,并通过 `runtimeProfilingOccurred` 明确说明是否执行过运行时 profiling。每条发现必须保留 `evidenceMode`、`detectionMethod`、`confidence`、`impact` 和 `limitations`,不得省略限制条件。
56
-
57
- 最终答复必须按以下三个标题分开陈述:
58
-
59
- 1. **工具发现**:只复述工具实际返回的证据、位置和限制,不扩写为已测事实。
60
- 2. **模型假设**:明确标注基于代码上下文的推断,不与工具发现混写。
61
- 3. **需要运行时验证**:列出复现场景、Profiler/trace/benchmark 方法和待采集指标。
62
-
63
- 没有运行时测量时,不得把任何静态发现标为 P0,不得声称已观察到 jank/卡顿或 CPU 影响,也不得称其为性能瓶颈。只有包含场景、指标、数值和单位的 Profiler、Performance trace 或 benchmark 结果,才能支持这些结论。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devflow-tools/cli",
3
- "version": "0.16.5",
3
+ "version": "0.16.6",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -12,31 +12,31 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "@colbymchenry/codegraph": "^1.1.1",
15
- "@devflow-tools/adapters": "0.16.5",
16
- "@devflow-tools/benchmark": "0.16.5",
17
- "@devflow-tools/context-engine": "0.16.5",
18
- "@devflow-tools/database": "0.16.5",
19
- "@devflow-tools/knowledge-engine": "0.16.5",
20
- "@devflow-tools/mcp-server": "0.16.5",
21
- "@devflow-tools/memory-engine": "0.16.5",
22
- "@devflow-tools/plugin-animation": "0.16.5",
23
- "@devflow-tools/plugin-css": "0.16.5",
24
- "@devflow-tools/plugin-docker": "0.16.5",
25
- "@devflow-tools/plugin-electron": "0.16.5",
26
- "@devflow-tools/plugin-git": "0.16.5",
27
- "@devflow-tools/plugin-graphql": "0.16.5",
28
- "@devflow-tools/plugin-nest": "0.16.5",
29
- "@devflow-tools/plugin-nextjs": "0.16.5",
30
- "@devflow-tools/plugin-performance": "0.16.5",
31
- "@devflow-tools/plugin-react": "0.16.5",
32
- "@devflow-tools/plugin-tailwind": "0.16.5",
33
- "@devflow-tools/plugin-taro": "0.16.5",
34
- "@devflow-tools/plugin-ui-layout": "0.16.5",
35
- "@devflow-tools/plugin-vue": "0.16.5",
36
- "@devflow-tools/sdk": "0.16.5",
37
- "@devflow-tools/server": "0.16.5",
38
- "@devflow-tools/telemetry": "0.16.5",
39
- "@devflow-tools/workflow-engine": "0.16.5",
15
+ "@devflow-tools/adapters": "0.16.6",
16
+ "@devflow-tools/benchmark": "0.16.6",
17
+ "@devflow-tools/context-engine": "0.16.6",
18
+ "@devflow-tools/database": "0.16.6",
19
+ "@devflow-tools/knowledge-engine": "0.16.6",
20
+ "@devflow-tools/mcp-server": "0.16.6",
21
+ "@devflow-tools/memory-engine": "0.16.6",
22
+ "@devflow-tools/plugin-animation": "0.16.6",
23
+ "@devflow-tools/plugin-css": "0.16.6",
24
+ "@devflow-tools/plugin-docker": "0.16.6",
25
+ "@devflow-tools/plugin-electron": "0.16.6",
26
+ "@devflow-tools/plugin-git": "0.16.6",
27
+ "@devflow-tools/plugin-graphql": "0.16.6",
28
+ "@devflow-tools/plugin-nest": "0.16.6",
29
+ "@devflow-tools/plugin-nextjs": "0.16.6",
30
+ "@devflow-tools/plugin-performance": "0.16.6",
31
+ "@devflow-tools/plugin-react": "0.16.6",
32
+ "@devflow-tools/plugin-tailwind": "0.16.6",
33
+ "@devflow-tools/plugin-taro": "0.16.6",
34
+ "@devflow-tools/plugin-ui-layout": "0.16.6",
35
+ "@devflow-tools/plugin-vue": "0.16.6",
36
+ "@devflow-tools/sdk": "0.16.6",
37
+ "@devflow-tools/server": "0.16.6",
38
+ "@devflow-tools/telemetry": "0.16.6",
39
+ "@devflow-tools/workflow-engine": "0.16.6",
40
40
  "@inquirer/prompts": "^7.10.1",
41
41
  "chalk": "^5.3.0",
42
42
  "commander": "^12.0.0"
@@ -49,5 +49,5 @@
49
49
  "dist",
50
50
  "bin"
51
51
  ],
52
- "gitHead": "7adad5462e435a6ee4fba92c350e85930db0ee80"
52
+ "gitHead": "b5628c42b4f6795103838c11e65601f6a59cb559"
53
53
  }