@devflow-tools/cli 0.16.10 → 0.16.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/commands/doctor.d.ts.map +1 -1
  2. package/dist/commands/doctor.js +354 -13
  3. package/dist/commands/doctor.js.map +1 -1
  4. package/dist/commands/setup.d.ts +25 -2
  5. package/dist/commands/setup.d.ts.map +1 -1
  6. package/dist/commands/setup.js +71 -63
  7. package/dist/commands/setup.js.map +1 -1
  8. package/dist/index.js +2 -2
  9. package/dist/index.js.map +1 -1
  10. package/dist/plugin-files/.claude-plugin/plugin.json +1 -1
  11. package/dist/plugin-files/dist/command-registry.json +39 -13
  12. package/dist/plugin-files/dist/hooks/hook-client.js +5 -5
  13. package/dist/plugin-files/dist/hooks/hook-daemon.js +13 -13
  14. package/dist/plugin-files/dist/hooks/post-tool-use-failure.js +1 -1
  15. package/dist/plugin-files/dist/hooks/post-tool-use.js +2 -2
  16. package/dist/plugin-files/dist/hooks/pre-compact.js +1 -1
  17. package/dist/plugin-files/dist/hooks/pre-tool-use.js +2 -2
  18. package/dist/plugin-files/dist/hooks/session-end.js +2 -2
  19. package/dist/plugin-files/dist/hooks/stop.js +2 -2
  20. package/dist/plugin-files/dist/hooks/user-prompt-submit.js +3 -4
  21. package/dist/plugin-files/dist/skills/devflow:animation/SKILL.md +3 -1
  22. package/dist/plugin-files/dist/skills/devflow:css/SKILL.md +3 -1
  23. package/dist/plugin-files/dist/skills/devflow:docker/SKILL.md +3 -1
  24. package/dist/plugin-files/dist/skills/devflow:electron/SKILL.md +3 -1
  25. package/dist/plugin-files/dist/skills/devflow:git/SKILL.md +3 -1
  26. package/dist/plugin-files/dist/skills/devflow:graphql/SKILL.md +3 -1
  27. package/dist/plugin-files/dist/skills/devflow:nest/SKILL.md +3 -1
  28. package/dist/plugin-files/dist/skills/devflow:nextjs/SKILL.md +3 -1
  29. package/dist/plugin-files/dist/skills/devflow:performance/SKILL.md +3 -1
  30. package/dist/plugin-files/dist/skills/devflow:react/SKILL.md +6 -4
  31. package/dist/plugin-files/dist/skills/devflow:tailwind/SKILL.md +3 -1
  32. package/dist/plugin-files/dist/skills/devflow:taro/SKILL.md +3 -1
  33. package/dist/plugin-files/dist/skills/devflow:ui-layout/SKILL.md +3 -1
  34. package/dist/plugin-files/dist/skills/devflow:vue/SKILL.md +3 -1
  35. package/dist/plugin-files/hooks/hooks.json +3 -2
  36. package/dist/plugin-files/hooks/session-start +54 -67
  37. package/dist/plugin-files/package.json +7 -7
  38. package/dist/plugin-files/skills/animation/SKILL.md +3 -1
  39. package/dist/plugin-files/skills/css/SKILL.md +3 -1
  40. package/dist/plugin-files/skills/docker/SKILL.md +3 -1
  41. package/dist/plugin-files/skills/doctor/SKILL.md +1 -1
  42. package/dist/plugin-files/skills/electron/SKILL.md +3 -1
  43. package/dist/plugin-files/skills/git/SKILL.md +3 -1
  44. package/dist/plugin-files/skills/graphql/SKILL.md +3 -1
  45. package/dist/plugin-files/skills/nest/SKILL.md +3 -1
  46. package/dist/plugin-files/skills/nextjs/SKILL.md +3 -1
  47. package/dist/plugin-files/skills/performance/SKILL.md +3 -1
  48. package/dist/plugin-files/skills/react/SKILL.md +6 -4
  49. package/dist/plugin-files/skills/tailwind/SKILL.md +3 -1
  50. package/dist/plugin-files/skills/taro/SKILL.md +3 -1
  51. package/dist/plugin-files/skills/ui-layout/SKILL.md +3 -1
  52. package/dist/plugin-files/skills/vue/SKILL.md +3 -1
  53. package/package.json +27 -27
@@ -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 m(s){return s??process.env.CLAUDE_PROJECT_DIR??process.cwd()}var y=1e4,S=3e4,u=500;function x(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 d=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:A(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){x(`${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 A(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 I(){return process.env.DEVFLOW_HOOK_DEGRADED==="1"}async function j(s="",r=m()){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 d;try{await t.flushAndAggregate();}finally{t.close();}let o=await _({projectRoot:r,sessionId:e,trigger:"pre_compact"}),n=[I()?w:"",b(o)].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 m(s){return s??process.env.CLAUDE_PROJECT_DIR??process.cwd()}var y=1e4,O=3e4,u=500;function M(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 d=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;let e=r?.busyTimeoutMs??Number(process.env.DEVFLOW_TELEMETRY_DB_BUSY_TIMEOUT_MS);this.busyTimeoutMs=Number.isSafeInteger(e)&&e>=0?e:100;}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(void 0,{busyTimeoutMs:this.busyTimeoutMs}),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:b(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(b(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();},O).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){M(`${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 b(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 D(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 _(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 T(){return process.env.DEVFLOW_HOOK_DEGRADED==="1"}async function j(s="",r=m()){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 d;try{await t.flushAndAggregate();}finally{t.close();}let o=await D({projectRoot:r,sessionId:e,trigger:"pre_compact"}),n=[T()?w:"",_(o)].filter(Boolean).join(`
2
2
 
3
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 {resolve,basename,relative,sep,dirname,join as join$1,isAbsolute}from'node:path';import {randomUUID,createHash}from'crypto';var q=1e4,Se=3e4,b=500;function ke(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 S=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:xe(JSON.parse(readFileSync(i,"utf8")),r)}}catch{return null}})).filter(o=>o!==null).sort((o,r)=>o.envelope.timestamp-r.envelope.timestamp||H(o.envelope.operation)-H(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:W(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(W(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();},Se).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){ke(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=q?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,q)}...`}}};function H(n){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(n)}function W(n){return `hook-run:${n}`}function xe(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 O(n){return n??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function d(n){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(O(n))}var Ae=2,Me=1;function je(){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 B(n=je()){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!==Ae&&o.version!==Me||!Ne(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 Ne(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 G(e.mcpTools)&&G(e.blockedNative)&&(e.source===void 0||e.source==="core"||e.source==="plugin")&&(e.requiresContext===void 0||typeof e.requiresContext=="boolean")&&(e.evidenceMode===void 0||["static","runtime","mixed"].includes(e.evidenceMode))})}function G(n){return Array.isArray(n)&&n.every(t=>typeof t=="string"&&t.length>0)}function J(n){let t=B();return t?t[n]?.mcpTools??[]:[]}function _(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 Ue(n,t){return n.lastMcpCall===t.lastMcpCall&&n.bypassCount===t.bypassCount}function Le(n){try{return existsSync(n)?_(JSON.parse(readFileSync(n,"utf-8"))):{}}catch{return {}}}function z(n,t){let e=t instanceof Error?t.message:String(t);console.error(`[devflow] Receipt ${n} skipped: ${e}`);}function K(n){let t=d(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 Y(n,t){if(n.getHookReceipt(t)){K(t);return}let e=join(d(t),"receipt.json");if(!existsSync(e))return;let o=Le(e);n.updateHookReceipt(t,()=>o),K(t);}function X(n,t,e){let o;try{o=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),Y(o,n);let r={},i=o.updateHookReceipt(n,c=>(r=_(c),_(t(r)))),s=_(i);return {receipt:s,applied:!0,changed:!Ue(r,s)}}catch(r){return z(e,r),{receipt:{},applied:false,changed:false}}finally{try{o?.close();}catch{}}}function Q(n){let t;try{return t=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),Y(t,n),_(t.getHookReceipt(n))}catch(e){return z("update",e),{}}finally{try{t?.close();}catch{}}}function $e(n,t){return X(n,()=>t,"write").applied}function qe(n,t){return X(n,t,"update")}var V={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",Bash:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function He(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 k=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"&&V[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=V[t];if(!r)return {permissionDecision:"allow"};let i=qe(this.projectRoot,l=>({...l,bypassCount:(l.bypassCount??0)+1}));if(!i.applied)return {permissionDecision:"allow"};let s=i.receipt.bypassCount??0,c=He(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(){$e(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}};var Xe=10*1024*1024,Qe=5;function Ze(n){return createHash$1("sha256").update(n).digest("hex").slice(0,16)}function te(n){return join$1(homedir$1(),".devflow","logs","hook-debug",`${Ze(n)}.jsonl`)}function ne(n,t,e={}){let o=e.maxBytes??Xe,r=e.maxFiles??Qe;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&&et(n,r),appendFileSync(n,t,{mode:384}),chmodSync(n,384);}function et(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 j="WARNING: DevFlow is in degraded mode (daemon unreachable): 4-Gate enforcement and memory prefetch cache are unavailable. Run `devflow doctor` for details.";function oe(){return process.env.DEVFLOW_HOOK_DEGRADED==="1"}function at(n){return Buffer.from(n,"utf8").toString("base64url")}var x=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(d(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,`${at(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 ie(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 se(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)}`}var pt=new Set(["Write","Edit","NotebookEdit"]),ce="Claude native memory writes are disabled for this project. Use DevFlow explicit memory or mcp__devflow__memory_commit_turn so a canonical receipt is created.";function pe(n){if(!pt.has(n.toolName))return null;let t=mt(n.toolInput);if(!t)return null;let e=gt(t,n.projectRoot),o=resolve(n.projectRoot,".claude");if(ae(e,o)&&basename(e).toLocaleUpperCase("en-US")==="MEMORY.MD")return ce;let r=resolve(n.home??homedir$1(),".claude","projects");return ae(e,r)&&relative(r,e).split(sep).some(s=>s.toLocaleLowerCase("en-US")==="memory")?ce:null}function mt(n){for(let t of ["file_path","notebook_path","path"]){let e=n[t];if(typeof e=="string"&&e.trim())return e.trim()}return null}function gt(n,t){return resolve(isAbsolute(n)?n:resolve(t,n))}function ae(n,t){let e=relative(t,n);return e===""||!e.startsWith(`..${sep}`)&&e!==".."&&!isAbsolute(e)}function v(){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}}function me(n){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:n}}}function vt(n){let t=d(n);return existsSync(t)||mkdirSync(t,{recursive:true}),t}function Dt(n,t,e,o,r,i){let s=join(vt(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 Rt(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 wt(n){let t=oe();try{if(!n||n.trim()==="")return v();let e=JSON.parse(n),o=Date.now(),r=O(),i=new S;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 me(j);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?ie(w):null;if(m){let ye=c.get(s)?.executionId,y=c.startExecution(s,m,J(m));y.executionId&&y.executionId!==ye&&await i.sendExecutionStart(y.executionId,s,m,y.lastActivityAt,r,y.requiredMcpTools);}}let p=c.get(s);e.tool_name.startsWith("mcp__devflow__")&&(e.tool_input._devflow_session_id=s,e.tool_input._devflow_execution_id=p?.executionId??s,e.tool_use_id&&(e.tool_input._devflow_tool_use_id=e.tool_use_id));let u=e.tool_name.startsWith("mcp__"),fe=u?e.tool_name.replace(/^mcp__[^_]+__/,""):void 0,N=new k(r,s,p?.executionId),F=N.getPhase(),a,f=pe({toolName:e.tool_name,toolInput:e.tool_input,projectRoot:r}),D=f?{permissionDecision:"deny",reason:f}:N.evaluate(e.tool_name,u);D.permissionDecision==="deny"?a={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:D.reason??"MCP context required"}}:D.additionalContext?a=me(D.additionalContext):a=v(),e.tool_name.startsWith("mcp__devflow__")&&(a.hookSpecificOutput.updatedInput=e.tool_input),t&&a.hookSpecificOutput.permissionDecision==="allow"&&(a.hookSpecificOutput.additionalContext=j);let he=p?.executionId??s,h={eventId:se(s,e.tool_use_id),executionId:he,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:fe,mcpEnforced:u&&F==="context_gathering",mcpFallback:!u&&F==="context_gathering",kind:Rt(e.tool_name),input:e.tool_input,duration:0,blocked:a.hookSpecificOutput.permissionDecision==="deny",blockReason:a.hookSpecificOutput.permissionDecision==="deny"?f?"DEVFLOW_CANONICAL_MEMORY_REQUIRED":"DEVFLOW_CONTEXT_REQUIRED":void 0,error:a.hookSpecificOutput.permissionDecision==="deny"?f?"DEVFLOW_CANONICAL_MEMORY_REQUIRED":"DEVFLOW_CONTEXT_REQUIRED":void 0},R=await i.sendEvent(h);R&&!h.blocked&&Dt(r,s,e.tool_name,e.tool_use_id,R,h.timestamp),R&&h.blocked&&await i.completeEvent({eventId:R,sessionId:s,error:f?"DEVFLOW_CANONICAL_MEMORY_REQUIRED":"DEVFLOW_CONTEXT_REQUIRED",duration:Date.now()-h.timestamp,completedAt:Date.now()});try{let w=Q(r),m={ts:o,tool:e.tool_name,sessionId:s,skill:p?.skillName??"(none)",requiredTools:p?.requiredMcpTools??[],isMcp:u,enforce:p?.executionId!==void 0,stateless:p?.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()};ne(te(r),`${JSON.stringify(m)}
2
- `);}catch{}return a}catch{return v()}}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 wt(n);console.log(JSON.stringify(t));}),process.stdin.on("error",()=>{console.log(JSON.stringify(v())),process.exit(0);}),setTimeout(()=>{console.log(JSON.stringify(v())),process.exit(0);},4e3).unref();}export{wt as handlePreToolUse};
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 {resolve,basename,relative,sep,dirname,join as join$1,isAbsolute}from'node:path';import {randomUUID,createHash}from'crypto';var H=1e4,Se=3e4,w=500;function ke(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 S=class{constructor(t){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??getLocalApiKey(),this.cacheDir=t?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=t?.legacyCacheDir??(t?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=t?.database??null,this.ownsDatabase=!t?.database;let e=t?.busyTimeoutMs??Number(process.env.DEVFLOW_TELEMETRY_DB_BUSY_TIMEOUT_MS);this.busyTimeoutMs=Number.isSafeInteger(e)&&e>=0?e:100;}async sendEvent(t){let e={...t,input:this.truncateInput(t.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),t.eventId):null}async sendExecutionStart(t,e,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:w}).reverse();for(let i of r)try{this.applyOperation(i.operation,i.payload),o.resolveTelemetryFailure(i.id);}catch{}o.trimTelemetryFailures(w);}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:Ee(JSON.parse(readFileSync(i,"utf8")),r)}}catch{return null}})).filter(o=>o!==null).sort((o,r)=>o.envelope.timestamp-r.envelope.timestamp||W(o.envelope.operation)-W(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(void 0,{busyTimeoutMs:this.busyTimeoutMs}),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:G(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(G(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(w),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-w)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},Se).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){ke(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=H?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,H)}...`}}};function W(n){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(n)}function G(n){return `hook-run:${n}`}function Ee(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 T(n){return n??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function d(n){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(T(n))}var Ae=2,Me=1;function je(){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 B(n=je()){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!==Ae&&o.version!==Me||!Ne(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 Ne(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 O(e.mcpTools)&&O(e.blockedNative)&&(e.source===void 0||e.source==="core"||e.source==="plugin")&&(e.requiresContext===void 0||typeof e.requiresContext=="boolean")&&(e.runtimePackages===void 0||O(e.runtimePackages))&&(e.evidenceMode===void 0||["static","runtime","mixed"].includes(e.evidenceMode))})}function O(n){return Array.isArray(n)&&n.every(t=>typeof t=="string"&&t.length>0)}function J(n){let t=B();return t?t[n]?.mcpTools??[]:[]}function _(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 Ue(n,t){return n.lastMcpCall===t.lastMcpCall&&n.bypassCount===t.bypassCount}function Le(n){try{return existsSync(n)?_(JSON.parse(readFileSync(n,"utf-8"))):{}}catch{return {}}}function z(n,t){let e=t instanceof Error?t.message:String(t);console.error(`[devflow] Receipt ${n} skipped: ${e}`);}function K(n){let t=d(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 Y(n,t){if(n.getHookReceipt(t)){K(t);return}let e=join(d(t),"receipt.json");if(!existsSync(e))return;let o=Le(e);n.updateHookReceipt(t,()=>o),K(t);}function X(n,t,e){let o;try{o=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),Y(o,n);let r={},i=o.updateHookReceipt(n,c=>(r=_(c),_(t(r)))),s=_(i);return {receipt:s,applied:!0,changed:!Ue(r,s)}}catch(r){return z(e,r),{receipt:{},applied:false,changed:false}}finally{try{o?.close();}catch{}}}function Q(n){let t;try{return t=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),Y(t,n),_(t.getHookReceipt(n))}catch(e){return z("update",e),{}}finally{try{t?.close();}catch{}}}function $e(n,t){return X(n,()=>t,"write").applied}function qe(n,t){return X(n,t,"update")}var V={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",Bash:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function He(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 k=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"&&V[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=V[t];if(!r)return {permissionDecision:"allow"};let i=qe(this.projectRoot,l=>({...l,bypassCount:(l.bypassCount??0)+1}));if(!i.applied)return {permissionDecision:"allow"};let s=i.receipt.bypassCount??0,c=He(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(){$e(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}};var Xe=10*1024*1024,Qe=5;function Ze(n){return createHash$1("sha256").update(n).digest("hex").slice(0,16)}function te(n){return join$1(homedir$1(),".devflow","logs","hook-debug",`${Ze(n)}.jsonl`)}function ne(n,t,e={}){let o=e.maxBytes??Xe,r=e.maxFiles??Qe;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&&et(n,r),appendFileSync(n,t,{mode:384}),chmodSync(n,384);}function et(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 N="WARNING: DevFlow is in degraded mode (daemon unreachable): 4-Gate enforcement and memory prefetch cache are unavailable. Run `devflow doctor` for details.";function oe(){return process.env.DEVFLOW_HOOK_DEGRADED==="1"}function at(n){return Buffer.from(n,"utf8").toString("base64url")}var E=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(d(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,`${at(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 ie(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 se(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)}`}var pt=new Set(["Write","Edit","NotebookEdit"]),ce="Claude native memory writes are disabled for this project. Use DevFlow explicit memory or mcp__devflow__memory_commit_turn so a canonical receipt is created.";function pe(n){if(!pt.has(n.toolName))return null;let t=mt(n.toolInput);if(!t)return null;let e=gt(t,n.projectRoot),o=resolve(n.projectRoot,".claude");if(ae(e,o)&&basename(e).toLocaleUpperCase("en-US")==="MEMORY.MD")return ce;let r=resolve(n.home??homedir$1(),".claude","projects");return ae(e,r)&&relative(r,e).split(sep).some(s=>s.toLocaleLowerCase("en-US")==="memory")?ce:null}function mt(n){for(let t of ["file_path","notebook_path","path"]){let e=n[t];if(typeof e=="string"&&e.trim())return e.trim()}return null}function gt(n,t){return resolve(isAbsolute(n)?n:resolve(t,n))}function ae(n,t){let e=relative(t,n);return e===""||!e.startsWith(`..${sep}`)&&e!==".."&&!isAbsolute(e)}function v(){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}}function me(n){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:n}}}function vt(n){let t=d(n);return existsSync(t)||mkdirSync(t,{recursive:true}),t}function Dt(n,t,e,o,r,i){let s=join(vt(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 Rt(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 bt(n){let t=oe();try{if(!n||n.trim()==="")return v();let e=JSON.parse(n),o=Date.now(),r=T(),i=new S;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 me(N);let c=new E(r),l=c.get(s)!==null;if(c.registerSession(s),l||await i.sendSessionStart(s,r,Date.now()),e.tool_name==="Skill"){let b=e.tool_input?.skill,m=b?ie(b):null;if(m){let ye=c.get(s)?.executionId,y=c.startExecution(s,m,J(m));y.executionId&&y.executionId!==ye&&await i.sendExecutionStart(y.executionId,s,m,y.lastActivityAt,r,y.requiredMcpTools);}}let p=c.get(s);e.tool_name.startsWith("mcp__devflow__")&&(e.tool_input._devflow_session_id=s,e.tool_input._devflow_execution_id=p?.executionId??s,e.tool_use_id&&(e.tool_input._devflow_tool_use_id=e.tool_use_id));let u=e.tool_name.startsWith("mcp__"),fe=u?e.tool_name.replace(/^mcp__[^_]+__/,""):void 0,F=new k(r,s,p?.executionId),P=F.getPhase(),a,f=pe({toolName:e.tool_name,toolInput:e.tool_input,projectRoot:r}),D=f?{permissionDecision:"deny",reason:f}:F.evaluate(e.tool_name,u);D.permissionDecision==="deny"?a={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:D.reason??"MCP context required"}}:D.additionalContext?a=me(D.additionalContext):a=v(),e.tool_name.startsWith("mcp__devflow__")&&(a.hookSpecificOutput.updatedInput=e.tool_input),t&&a.hookSpecificOutput.permissionDecision==="allow"&&(a.hookSpecificOutput.additionalContext=N);let he=p?.executionId??s,h={eventId:se(s,e.tool_use_id),executionId:he,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:fe,mcpEnforced:u&&P==="context_gathering",mcpFallback:!u&&P==="context_gathering",kind:Rt(e.tool_name),input:e.tool_input,duration:0,blocked:a.hookSpecificOutput.permissionDecision==="deny",blockReason:a.hookSpecificOutput.permissionDecision==="deny"?f?"DEVFLOW_CANONICAL_MEMORY_REQUIRED":"DEVFLOW_CONTEXT_REQUIRED":void 0,error:a.hookSpecificOutput.permissionDecision==="deny"?f?"DEVFLOW_CANONICAL_MEMORY_REQUIRED":"DEVFLOW_CONTEXT_REQUIRED":void 0},R=await i.sendEvent(h);R&&!h.blocked&&Dt(r,s,e.tool_name,e.tool_use_id,R,h.timestamp),R&&h.blocked&&await i.completeEvent({eventId:R,sessionId:s,error:f?"DEVFLOW_CANONICAL_MEMORY_REQUIRED":"DEVFLOW_CONTEXT_REQUIRED",duration:Date.now()-h.timestamp,completedAt:Date.now()});try{let b=Q(r),m={ts:o,tool:e.tool_name,sessionId:s,skill:p?.skillName??"(none)",requiredTools:p?.requiredMcpTools??[],isMcp:u,enforce:p?.executionId!==void 0,stateless:p?.executionId===void 0&&!u&&e.tool_name!=="Skill",bypassCount:b.bypassCount??0,decision:a.hookSpecificOutput.permissionDecision,env_CLAUDE_PLUGIN_ROOT:process.env.CLAUDE_PLUGIN_ROOT??"(unset)",cwd:process.cwd()};ne(te(r),`${JSON.stringify(m)}
2
+ `);}catch{}return a}catch{return v()}}if(process.argv[1]?.endsWith("pre-tool-use")||process.argv[1]?.endsWith("pre-tool-use.js")){let n=console.log.bind(console);console.log=console.error.bind(console),console.info=console.error.bind(console);let t="";process.stdin.setEncoding("utf8"),process.stdin.on("data",e=>{t+=e;}),process.stdin.on("end",async()=>{let e=await bt(t);n(JSON.stringify(e));}),process.stdin.on("error",()=>{n(JSON.stringify(v())),process.exit(0);}),setTimeout(()=>{n(JSON.stringify(v())),process.exit(0);},4e3).unref();}export{bt 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 as rmSync$1,mkdirSync,writeFileSync,renameSync,readdirSync,unlinkSync}from'fs';import {join as join$1}from'path';import {homedir}from'os';import {MemoryGate}from'@devflow-tools/memory-engine';import {randomUUID}from'crypto';import {createHash}from'node:crypto';import {rmSync}from'node:fs';import {join}from'node:path';function b(n){return n??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function y(n){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(b(n))}var T=1e4,U=3e4,f=500;function $(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 c=[];o.on("data",l=>c.push(l)),o.on("end",()=>{let l=Buffer.concat(c).toString();t(o.statusCode!=null&&o.statusCode>=200&&o.statusCode<300?l:null);});});i.on("error",()=>t(null)),i.on("timeout",()=>{i.destroy(),t(null);}),i.write(s),i.end();}catch{t(null);}})}var v=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$1(process.env.DEVFLOW_STATE_DIR??join$1(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=s?.legacyCacheDir??(s?.cacheDir?null:join$1(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 c={executionId:s,sessionId:e,skillName:t,startedAt:r,projectRoot:i,requiredMcpTools:o};this.commitOrCache("execution_start",c)&&this.postHttp("/api/telemetry/skill-execution/start",c);}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:f}).reverse();for(let i of r)try{this.applyOperation(i.operation,i.payload),t.resolveTelemetryFailure(i.id);}catch{}t.trimTelemetryFailures(f);}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$1(t,r);try{return {cacheFile:i,envelope:N(JSON.parse(readFileSync(i,"utf8")),r)}}catch{return null}})).filter(t=>t!==null).sort((t,r)=>t.envelope.timestamp-r.envelope.timestamp||R(t.envelope.operation)-R(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:I(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(I(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(f),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let o=join$1(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-f)))unlinkSync(join$1(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},U).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){$(`${this.apiUrl}${s}`,JSON.stringify(e),this.apiKey);}truncateInput(s){let e=JSON.stringify(s);return e===void 0||e.length<=T?s:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,T)}...`}}};function R(n){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(n)}function I(n){return `hook-run:${n}`}function N(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 E(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 c=Date.now(),l=`distill:${c}:${Math.random().toString(36).slice(2,10)}`;o.recordMemoryDistillCheckpoint({id:l,projectRoot:n.projectRoot,sessionId:n.sessionId,trigger:n.trigger,pendingEvents:r,releasedLeases:t,createdAt:c}),o.insertEvent({kind:"memory_distill_requested",timestamp:c,success:!0,metadata:{...i,projectRoot:n.projectRoot}});}finally{o.close();}}catch{}return i}function X(n){return Buffer.from(n,"utf8").toString("base64url")}var D=class{constructor(s){this.projectRoot=s;this.sessions=new Map;this.sessionsDir=join$1(y(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$1(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(s){return join$1(this.sessionsDir,`${X(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 re=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",ie=getLocalApiKey();function ne(n,s){return new Promise(e=>{let t=JSON.stringify(s),r=new URL(n,re),i=request({hostname:r.hostname,port:r.port,path:r.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":ie,"Content-Length":Buffer.byteLength(t)},timeout:5e3},()=>e());i.on("error",()=>e()),i.write(t),i.end();})}async function oe(n,s=b(),e=true,t){let r;try{r=JSON.parse(n);}catch{return}let i=r.session_id?.trim();if(!i)return;let o=new D(s),c=o.get(i),l=t?.telemetry??new v,x=!t?.telemetry;try{try{await l.flushAndAggregate();}catch{}let m;try{m=openGlobalDevFlowDatabase();let u=c?.executionId;if(u){let d=Date.now(),h=m.listMemoryTurns(s,i,100);try{m.reconcileSkillExecution(u,"completed",d,{memoryReceiptIds:h.flatMap(g=>g.receiptId?[g.receiptId]:[]),missingMemoryDecisionCount:h.filter(g=>g.status==="pending").length});let a=m.getSkillExecution(u);await ne("/api/telemetry/skill-execution/complete",{executionId:u,finishedAt:d,status:"completed",summary:{totalToolCalls:a?.totalToolCalls??0,mcpToolCalls:a?.mcpToolCalls??0,directToolCalls:a?.directToolCalls??0,subagentCount:a?.subagentCount??0,totalDuration:a?.totalDuration??0,mcpComplianceRate:a?.mcpComplianceRate??0,missedTools:a?.missedMcpTools??[],actualFailureCount:a?.failedToolCalls??0,fallbackCount:a?.fallbackCount??0,fallbackReasons:a?.fallbackReasons??[]}});}catch{}}if(e){m.deleteHookReceipt(s);let d=y(s),h=createHash("sha256").update(s).digest("hex").slice(0,12);for(let a of [`session-id-${h}`,"current-execution-id","current-skill.json","event-map.json"])rmSync(join(d,a),{force:!0});}m.deleteContextReceipt(s,i);}catch{}finally{m?.close();}try{if(await E({projectRoot:s,sessionId:i,trigger:"session_end",memory:t?.memory}),t?.memory)await t.memory.closeSession(i);else {let u=new(await import('@devflow-tools/memory-engine')).MemoryGate(s);try{await u.closeSession(i);}finally{u.close();}}}catch{}try{await l.endSession(i),await l.flushAndAggregate();}catch{}}finally{x&&l.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 oe(n.trim()||process.argv[2]||""),process.exit(0);}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),1e4).unref();}
2
- export{oe as handleSessionEnd};
1
+ import {request}from'http';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {existsSync as existsSync$1,readFileSync,rmSync as rmSync$1,mkdirSync as mkdirSync$1,writeFileSync as writeFileSync$1,renameSync as renameSync$1,readdirSync,unlinkSync as unlinkSync$1}from'fs';import {join as join$1}from'path';import {homedir}from'os';import {MemoryGate}from'@devflow-tools/memory-engine';import {randomUUID}from'crypto';import {createHash}from'node:crypto';import {mkdirSync,existsSync,writeFileSync,renameSync,unlinkSync,rmSync}from'node:fs';import {join}from'node:path';function S(n){return n??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function g(n){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(S(n))}var R=1e4,K=3e4,v=500;function B(n,t,e){return new Promise(s=>{try{let o=new URL(n),i=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(t)},timeout:5e3},r=>{let c=[];r.on("data",u=>c.push(u)),r.on("end",()=>{let u=Buffer.concat(c).toString();s(r.statusCode!=null&&r.statusCode>=200&&r.statusCode<300?u:null);});});i.on("error",()=>s(null)),i.on("timeout",()=>{i.destroy(),s(null);}),i.write(t),i.end();}catch{s(null);}})}var b=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$1(process.env.DEVFLOW_STATE_DIR??join$1(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=t?.legacyCacheDir??(t?.cacheDir?null:join$1(homedir(),".devflow","telemetry-cache")),this.database=t?.database??null,this.ownsDatabase=!t?.database;let e=t?.busyTimeoutMs??Number(process.env.DEVFLOW_TELEMETRY_DB_BUSY_TIMEOUT_MS);this.busyTimeoutMs=Number.isSafeInteger(e)&&e>=0?e:100;}async sendEvent(t){let e={...t,input:this.truncateInput(t.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),t.eventId):null}async sendExecutionStart(t,e,s,o,i=process.env.CLAUDE_PROJECT_DIR??process.cwd(),r=[]){let c={executionId:t,sessionId:e,skillName:s,startedAt:o,projectRoot:i,requiredMcpTools:r};this.commitOrCache("execution_start",c)&&this.postHttp("/api/telemetry/skill-execution/start",c);}async sendExecutionComplete(t,e="completed",s=Date.now(),o){let i={executionId:t,status:e,finishedAt:s,metadata:o},r=this.commitOrCache("execution_complete",i);return r&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",i),r}async sendSessionStart(t,e,s){let o={id:t,projectRoot:e,startedAt:s,label:e.split("/").pop()??"unknown"},i=this.commitOrCache("session_start",o);return i&&this.postHttp("/api/telemetry/sessions",o),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 s={sessionId:t,finishedAt:e},o=this.commitOrCache("session_end",s);return o&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),o}async flushCache(){try{let s=this.getDatabase(),o=s.listTelemetryFailures({unresolvedOnly:!0,limit:v}).reverse();for(let i of o)try{this.applyOperation(i.operation,i.payload),s.resolveTelemetryFailure(i.id);}catch{}s.trimTelemetryFailures(v);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(s=>!!s))].filter(s=>existsSync$1(s)).flatMap(s=>readdirSync(s).filter(o=>o.endsWith(".json")).map(o=>{let i=join$1(s,o);try{return {cacheFile:i,envelope:G(JSON.parse(readFileSync(i,"utf8")),o)}}catch{return null}})).filter(s=>s!==null).sort((s,o)=>s.envelope.timestamp-o.envelope.timestamp||k(s.envelope.operation)-k(o.envelope.operation));for(let{cacheFile:s,envelope:o}of e)try{let i=this.getDatabase();i.insertTelemetryFailure({id:o.id,operation:o.operation,payload:o.payload,error:o.failure,createdAt:o.timestamp}),this.applyOperation(o.operation,o.payload),i.resolveTelemetryFailure(o.id),unlinkSync$1(s);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:this.busyTimeoutMs}),this.database}commitOrCache(t,e){try{return this.applyOperation(t,e),!0}catch(s){return this.cacheOperation(t,e,s),false}}applyOperation(t,e){let s=this.getDatabase();switch(t){case "tool_call":this.ensureParentSession(s,e),s.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(s,e),s.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running",requiredMcpTools:e.requiredMcpTools});return;case "execution_complete":s.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":s.ensureSession(e),s.insertRun({id:x(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(!s.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":s.closeSession(e.sessionId,e.finishedAt),s.updateRun(x(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(t,e){let s=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!s)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";t.ensureSession({id:s,projectRoot:o,label:o==="unknown"?void 0:o.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(t,e,s){let o=Date.now(),i={id:`failure:${o}:${Math.random().toString(36).slice(2,11)}`,operation:t,payload:e,failure:s instanceof Error?s.message:String(s),timestamp:o};try{let r=this.getDatabase();r.insertTelemetryFailure({id:i.id,operation:t,payload:e,error:i.failure,createdAt:o}),r.trimTelemetryFailures(v),this.scheduleRetry();return}catch{}try{existsSync$1(this.cacheDir)||mkdirSync$1(this.cacheDir,{recursive:!0});let r=join$1(this.cacheDir,`${o}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync$1(r,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-v)))unlinkSync$1(join$1(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},K).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){B(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=R?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,R)}...`}}};function k(n){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(n)}function x(n){return `hook-run:${n}`}function G(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 s=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!s)throw new Error("Unknown legacy telemetry cache operation");let o=e.payload??{},i=Number(o.timestamp??o.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:s,payload:o,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:i}}async function E(n){let t=!n.memory,e=n.memory??new MemoryGate(n.projectRoot),s=0,o=0;try{await e.forceWarmUp(),n.trigger==="session_end"&&(s=await e.releasePendingDistillLeases(n.sessionId)),o=e.getPendingEventCount();}finally{t&&e.close();}let i={pendingEvents:o,releasedLeases:s,trigger:n.trigger,sessionId:n.sessionId};try{let r=openGlobalDevFlowDatabase();try{let c=Date.now(),u=`distill:${c}:${Math.random().toString(36).slice(2,10)}`;r.recordMemoryDistillCheckpoint({id:u,projectRoot:n.projectRoot,sessionId:n.sessionId,trigger:n.trigger,pendingEvents:o,releasedLeases:s,createdAt:c}),r.insertEvent({kind:"memory_distill_requested",timestamp:c,success:!0,metadata:{...i,projectRoot:n.projectRoot}});}finally{r.close();}}catch{}return i}function ie(n){return Buffer.from(n,"utf8").toString("base64url")}var D=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join$1(g(t),"hook-sessions");}registerSession(t){let e=t.trim();if(!e)throw new Error("session_id_required");let s=this.get(e);if(s)return s.lastActivityAt=Date.now(),this.persist(s),s;let o=Date.now(),i={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:o,lastActivityAt:o};return this.sessions.set(e,i),this.persist(i),i}startExecution(t,e,s){let o=this.registerSession(t);return (!o.executionId||o.skillName!==e)&&(o.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),o.skillName=e,o.requiredMcpTools=[...new Set(s)],o.lastActivityAt=Date.now(),this.persist(o),o}get(t){let e=t.trim();if(!e)return null;let s=this.sessions.get(e);if(s)return s;let o=this.snapshotPath(e);if(!existsSync$1(o))return null;try{let i=JSON.parse(readFileSync(o,"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 s={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),s}removeSession(t){let e=t.trim();e&&(this.sessions.delete(e),rmSync$1(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(t){return join$1(this.sessionsDir,`${ie(t)}.json`)}persist(t){mkdirSync$1(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(t.sessionId),s=`${e}.${process.pid}.tmp`;writeFileSync$1(s,JSON.stringify(t),{mode:384}),renameSync$1(s,e);}};function F(n,t){let e=le(n);mkdirSync(e,{recursive:true,mode:448});let s=join(e,`${t.receiptId}.json`);if(existsSync(s))return t.receiptId;let o=`${s}.${process.pid}.tmp`;writeFileSync(o,JSON.stringify(t),{mode:384});try{renameSync(o,s);}catch(i){try{unlinkSync(o);}catch{}if(!existsSync(s))throw i}return t.receiptId}function le(n){return join(g(n),"session-finalize-pending")}var ge=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",he=getLocalApiKey();function ye(n,t=S(),e=true,s,o=true){let i;try{i=JSON.parse(n);}catch{return null}let r=i.session_id?.trim();if(!r)return null;let c=`session-close:${createHash("sha256").update(`${t}\0${r}`).digest("hex").slice(0,24)}`,u={inputJson:n,sessionId:r,cleanupSharedState:e,executionId:s},m;try{m=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:50});let l=m.requestSessionClosure({receiptId:c,projectRoot:t,sessionId:r,payload:u}),d=m.getWorkByIdempotencyKey(c);return {status:l.state==="closed"||l.state==="closed_with_pending_work"?"completed":"accepted",receiptId:c,workState:d?.state??"pending",closureState:l.state,pendingWorkCount:l.pendingWorkCount}}catch(l){if(!o)throw l;return F(t,{receiptId:c,...u}),{status:"accepted",receiptId:c,workState:"spooled",closureState:"closing",pendingWorkCount:1}}finally{m?.close();}}function O(n,t){return new Promise(e=>{let s=JSON.stringify(t),o=new URL(n,ge),i=request({hostname:o.hostname,port:o.port,path:o.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":he,"Content-Length":Buffer.byteLength(s)},timeout:5e3},()=>e());i.on("error",()=>e()),i.write(s),i.end();})}async function et(n,t=S(),e=true,s){let o;try{o=JSON.parse(n);}catch{return}let i=o.session_id?.trim();if(!i)return;let r=new D(t),c=r.get(i),u=s?.telemetry??new b,m=!s?.telemetry;try{try{await u.flushAndAggregate();}catch{}let l;try{l=openGlobalDevFlowDatabase();let d=c?.executionId;if(d){let a=Date.now(),y=l.listMemoryTurns(t,i,100);try{l.reconcileSkillExecution(d,"completed",a,{memoryReceiptIds:y.flatMap(f=>f.receiptId?[f.receiptId]:[]),missingMemoryDecisionCount:y.filter(f=>f.status==="pending").length});let p=l.getSkillExecution(d);await O("/api/telemetry/skill-execution/complete",{executionId:d,finishedAt:a,status:"completed",summary:{totalToolCalls:p?.totalToolCalls??0,mcpToolCalls:p?.mcpToolCalls??0,directToolCalls:p?.directToolCalls??0,subagentCount:p?.subagentCount??0,totalDuration:p?.totalDuration??0,mcpComplianceRate:p?.mcpComplianceRate??0,missedTools:p?.missedMcpTools??[],actualFailureCount:p?.failedToolCalls??0,fallbackCount:p?.fallbackCount??0,fallbackReasons:p?.fallbackReasons??[]}});}catch{}}let w=l.listMemoryTurns(t,i,100),N=l.reconcileRunningSkillExecutionsForSession(i,Date.now(),{memoryReceiptIds:w.flatMap(a=>a.receiptId?[a.receiptId]:[]),missingMemoryDecisionCount:w.filter(a=>a.status==="pending").length,closureReason:"session_finalize"});for(let a of N)await O("/api/telemetry/skill-execution/complete",{executionId:a.executionId,finishedAt:a.finishedAt,status:a.status,summary:{totalToolCalls:a.totalToolCalls??0,mcpToolCalls:a.mcpToolCalls??0,directToolCalls:a.directToolCalls??0,subagentCount:a.subagentCount??0,totalDuration:a.totalDuration??0,mcpComplianceRate:a.mcpComplianceRate??0,missedTools:a.missedMcpTools??[],actualFailureCount:a.failedToolCalls??0,fallbackCount:a.fallbackCount??0,fallbackReasons:a.fallbackReasons??[]}});if(e){l.deleteHookReceipt(t);let a=g(t),y=createHash("sha256").update(t).digest("hex").slice(0,12);for(let p of [`session-id-${y}`,"current-execution-id","current-skill.json","event-map.json"])rmSync(join(a,p),{force:!0});}l.deleteContextReceipt(t,i);}catch{}finally{l?.close();}try{if(await E({projectRoot:t,sessionId:i,trigger:"session_end",memory:s?.memory}),s?.memory)await s.memory.closeSession(i);else {let d=new(await import('@devflow-tools/memory-engine')).MemoryGate(t);try{await d.closeSession(i);}finally{d.close();}}}catch{}try{await u.endSession(i),await u.flushAndAggregate();}catch{}}finally{m&&u.close(),r.removeSession(i);}}if(process.argv[1]?.endsWith("session-end")||process.argv[1]?.endsWith("session-end.js")){let n=console.log.bind(console);console.log=console.error.bind(console),console.info=console.error.bind(console);let t="";process.stdin.setEncoding("utf8"),process.stdin.on("data",e=>{t+=e;}),process.stdin.on("end",async()=>{let e=ye(t.trim()||process.argv[2]||"");e&&n(JSON.stringify(e)),process.exit(0);}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),1e4).unref();}
2
+ export{ye as enqueueSessionEndFinalization,et as handleSessionEnd};
@@ -1,2 +1,2 @@
1
- import {request}from'http';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {existsSync,readFileSync,rmSync,mkdirSync,writeFileSync,renameSync}from'fs';import {join}from'path';import {randomUUID}from'crypto';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';function a(s){return s??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function m(s){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(a(s))}function x(s){return Buffer.from(s,"utf8").toString("base64url")}var i=class{constructor(e){this.projectRoot=e;this.sessions=new Map;this.sessionsDir=join(m(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 r=Date.now(),o={sessionId:t,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:r,lastActivityAt:r};return this.sessions.set(t,o),this.persist(o),o}startExecution(e,t,n){let r=this.registerSession(e);return (!r.executionId||r.skillName!==t)&&(r.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),r.skillName=t,r.requiredMcpTools=[...new Set(n)],r.lastActivityAt=Date.now(),this.persist(r),r}get(e){let t=e.trim();if(!t)return null;let n=this.sessions.get(t);if(n)return n;let r=this.snapshotPath(t);if(!existsSync(r))return null;try{let o=JSON.parse(readFileSync(r,"utf8"));return o.sessionId!==t||o.projectRoot!==this.projectRoot?null:(o.requiredMcpTools=Array.isArray(o.requiredMcpTools)?o.requiredMcpTools:[],this.sessions.set(t,o),o)}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,`${x(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);}};function M(s){return `DevFlow memory decision required for ${s.turnId}. Before the final response, semantically decide whether this turn contains durable project knowledge. Call mcp__devflow__memory_commit_turn with evidence-bound candidates, or mcp__devflow__memory_skip_turn with a concrete reason. Do not claim memory success without a canonical receipt.`}function p(s,e,t){if(t)return null;let n=openGlobalDevFlowDatabase();try{let r=n.getPendingMemoryTurn(s,e);return !r||!n.markMemoryTurnStopPrompted(r.turnId)?null:{decision:"block",reason:M(r)}}finally{n.close();}}var T=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",k=getLocalApiKey();function D(s,e){return new Promise(t=>{let n=JSON.stringify(e),r=new URL(s,T),o=request({hostname:r.hostname,port:r.port,path:r.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":k,"Content-Length":Buffer.byteLength(n)},timeout:5e3},()=>t());o.on("error",()=>t()),o.write(n),o.end();})}async function b(s,e=a()){let t;try{t=JSON.parse(s);}catch{return null}let n=t.session_id?.trim();if(!n)return null;let r=p(e,n,t.stop_hook_active===true);if(r)return r;if(t.stop_hook_active)return null;let o=new i(e).completeExecution(n);if(o?.executionId){let c=openGlobalDevFlowDatabase();try{c.reconcileSkillExecution(o.executionId,"completed"),c.deleteContextReceipt(e,n,o.executionId);}finally{c.close();}}return D(`/api/telemetry/sessions/${encodeURIComponent(n)}/tick`,{timestamp:Date.now()}).catch(()=>{}),null}if(process.argv[1]?.endsWith("stop")||process.argv[1]?.endsWith("stop.js")){let s="";process.stdin.setEncoding("utf8"),process.stdin.on("data",e=>{s+=e;}),process.stdin.on("end",async()=>{let e=await b(s.trim()||process.argv[2]||"");e&&console.log(JSON.stringify(e)),process.exit(0);}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),4e3).unref();}
2
- export{b as handleStop};
1
+ import {request}from'http';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {existsSync,readFileSync,rmSync,mkdirSync,writeFileSync,renameSync}from'fs';import {join}from'path';import {randomUUID}from'crypto';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';function a(s){return s??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function u(s){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(a(s))}function S(s){return Buffer.from(s,"utf8").toString("base64url")}var i=class{constructor(e){this.projectRoot=e;this.sessions=new Map;this.sessionsDir=join(u(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 r=Date.now(),o={sessionId:t,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:r,lastActivityAt:r};return this.sessions.set(t,o),this.persist(o),o}startExecution(e,t,n){let r=this.registerSession(e);return (!r.executionId||r.skillName!==t)&&(r.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),r.skillName=t,r.requiredMcpTools=[...new Set(n)],r.lastActivityAt=Date.now(),this.persist(r),r}get(e){let t=e.trim();if(!t)return null;let n=this.sessions.get(t);if(n)return n;let r=this.snapshotPath(t);if(!existsSync(r))return null;try{let o=JSON.parse(readFileSync(r,"utf8"));return o.sessionId!==t||o.projectRoot!==this.projectRoot?null:(o.requiredMcpTools=Array.isArray(o.requiredMcpTools)?o.requiredMcpTools:[],this.sessions.set(t,o),o)}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,`${S(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);}};function v(s){return `DevFlow memory decision required for ${s.turnId}. Before the final response, semantically decide whether this turn contains durable project knowledge. Call mcp__devflow__memory_commit_turn with evidence-bound candidates, or mcp__devflow__memory_skip_turn with a concrete reason. Do not claim memory success without a canonical receipt.`}function d(s,e,t){if(t)return null;let n=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:50});try{let r=n.getPendingMemoryTurn(s,e);return !r||!n.markMemoryTurnStopPrompted(r.turnId)?null:{decision:"block",reason:v(r)}}finally{n.close();}}var T=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",w=getLocalApiKey();function b(s,e){return new Promise(t=>{let n=JSON.stringify(e),r=new URL(s,T),o=request({hostname:r.hostname,port:r.port,path:r.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":w,"Content-Length":Buffer.byteLength(n)},timeout:5e3},()=>t());o.on("error",()=>t()),o.write(n),o.end();})}async function D(s,e=a()){let t;try{t=JSON.parse(s);}catch{return null}let n=t.session_id?.trim();if(!n)return null;let r=d(e,n,t.stop_hook_active===true);if(r)return r;if(t.stop_hook_active)return null;let o=new i(e).completeExecution(n);if(o?.executionId){let c=openGlobalDevFlowDatabase();try{c.reconcileSkillExecution(o.executionId,"completed"),c.deleteContextReceipt(e,n,o.executionId);}finally{c.close();}}return b(`/api/telemetry/sessions/${encodeURIComponent(n)}/tick`,{timestamp:Date.now()}).catch(()=>{}),null}if(process.argv[1]?.endsWith("stop")||process.argv[1]?.endsWith("stop.js")){let s=console.log.bind(console);console.log=console.error.bind(console),console.info=console.error.bind(console);let e="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{e+=t;}),process.stdin.on("end",async()=>{let t=await D(e.trim()||process.argv[2]||"");t&&s(JSON.stringify(t)),process.exit(0);}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),4e3).unref();}
2
+ export{D as handleStop};
@@ -1,5 +1,4 @@
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 G=/^\/(?:devflow:)?[\w-]+\b\s*/iu,W=/\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,z=/(?:^|[.!?]\s*)(?:(?:please|kindly)\s+)?(?:remember|memorize|save\s+this|store\s+this|note\s+that)(?:\s+(?:that|to))?[\s:,-]*(.+)$/iu,K=/(?:^|[。!?;,,]\s*)(?:(?:请|麻烦|务必|一定要|帮我|帮忙|需要)\s*)?(?:记住|记一下|记下来|记|记录一下|记录下来|保存一下|保存下来)(?:这(?:件事|一点|个信息|些内容))?[\s::,,]*(.+)$/iu;function p(o){let t=o.trim().replace(G,"").trim();return !t||W.test(t)?null:(K.exec(t)??z.exec(t))?.[1]?.trim().replace(/^[::,,\s]+/u,"")||null}function V(o){let t=createHash("sha256").update(o).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 w(o,t){let e=V(o);mkdirSync(dirname(e),{recursive:true}),appendFileSync(e,`${JSON.stringify(t)}
2
- `,{mode:384});}var _="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 Y(o){return o??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function I(o){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(Y(o))}function oe(o){return Buffer.from(o,"utf8").toString("base64url")}var h=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,`${oe(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 se(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 R(o){let t=o.trimStart().match(/^\/(devflow:[a-z0-9][a-z0-9-]*)\b/i);if(!t)return null;let e=se(t[1]);return e?{rawName:t[1],skillName:e}:null}var ue=2,me=1;function de(){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 T(o=de()){let t=[join(o,"dist","command-registry.json"),join(o,"command-registry.json")];for(let e of t)try{if(!existsSync(e))continue;let r=JSON.parse(readFileSync(e,"utf8"));if(r.version!==ue&&r.version!==me||!pe(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 pe(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 k(e.mcpTools)&&k(e.blockedNative)&&(e.source===void 0||e.source==="core"||e.source==="plugin")&&(e.requiresContext===void 0||typeof e.requiresContext=="boolean")&&(e.evidenceMode===void 0||["static","runtime","mixed"].includes(e.evidenceMode))})}function k(o){return Array.isArray(o)&&o.every(t=>typeof t=="string"&&t.length>0)}function M(o){let t=T();return t?t[o]?.mcpTools??[]:[]}var N=1e4,Se=3e4,g=500;function De(o,t,e){return new Promise(r=>{try{let n=new URL(o),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},i=>{let c=[];i.on("data",a=>c.push(a)),i.on("end",()=>{let a=Buffer.concat(c).toString();r(i.statusCode!=null&&i.statusCode>=200&&i.statusCode<300?a:null);});});s.on("error",()=>r(null)),s.on("timeout",()=>{s.destroy(),r(null);}),s.write(t),s.end();}catch{r(null);}})}var f=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,s=process.env.CLAUDE_PROJECT_DIR??process.cwd(),i=[]){let c={executionId:t,sessionId:e,skillName:r,startedAt:n,projectRoot:s,requiredMcpTools:i};this.commitOrCache("execution_start",c)&&this.postHttp("/api/telemetry/skill-execution/start",c);}async sendExecutionComplete(t,e="completed",r=Date.now(),n){let s={executionId:t,status:e,finishedAt:r,metadata:n},i=this.commitOrCache("execution_complete",s);return i&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",s),i}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.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:g}).reverse();for(let s of n)try{this.applyOperation(s.operation,s.payload),r.resolveTelemetryFailure(s.id);}catch{}r.trimTelemetryFailures(g);}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:we(JSON.parse(readFileSync(s,"utf8")),n)}}catch{return null}})).filter(r=>r!==null).sort((r,n)=>r.envelope.timestamp-n.envelope.timestamp||P(r.envelope.operation)-P(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",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:j(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(j(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 i=this.getDatabase();i.insertTelemetryFailure({id:s.id,operation:t,payload:e,error:s.failure,createdAt:n}),i.trimTelemetryFailures(g),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(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-g)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},Se).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){De(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=N?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,N)}...`}}};function P(o){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(o)}function j(o){return `hook-run:${o}`}function we(o,t){if(!o||typeof o!="object")throw new Error("Invalid telemetry cache envelope");let e=o;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let r=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!r)throw new Error("Unknown legacy telemetry cache operation");let 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}}var be=/^(?:ok(?:ay)?|yes|no|thanks?|thank you|continue|好的?|可以|行|是|否|谢谢|继续|收到|明白了?)[.!。!\s]*$/iu;function xe(o){let t=o.trim();return t.length>0&&!be.test(t)}function Re(o,t,e=Date.now()){let r=createHash("sha256").update(t.trim()).digest("hex"),n=createHash("sha256").update(`${o}\0${e}\0${r}`).digest("hex").slice(0,24);return {turnId:`turn:${n}`,eventId:`memory-turn:${n}`,promptHash:r,createdAt:e}}function ke(o,t,e=[]){return `memory-receipt:${createHash("sha256").update(`${o}\0${t}\0${[...e].sort().join("\0")}`).digest("hex").slice(0,24)}`}function Te(o){return `DevFlow memory decision required for ${o.turnId}. Before the final response, semantically decide whether this turn contains durable project knowledge. Call mcp__devflow__memory_commit_turn with evidence-bound candidates, or mcp__devflow__memory_skip_turn with a concrete reason. Do not claim memory success without a canonical receipt.`}function F(o){let t=o.memoryIds.length>0?o.memoryIds.join(","):"none";return `DevFlow canonical memory receipt: status=${o.status}; receiptId=${o.receiptId??"missing"}; turnId=${o.turnId}; memoryIds=${t}; source=${o.source??"unknown"}.`}async function $(o){if(!xe(o.prompt))return null;let t=openGlobalDevFlowDatabase();try{let e=createHash("sha256").update(o.prompt.trim()).digest("hex"),r=t.listMemoryTurns(o.projectRoot,o.sessionId,5).find(u=>u.promptHash===e&&Date.now()-u.createdAt<1e4),n=r?{turnId:r.turnId,eventId:r.eventId,promptHash:r.promptHash,createdAt:r.createdAt}:Re(o.sessionId,o.prompt),s=r??t.beginMemoryTurn({...n,projectRoot:o.projectRoot,sessionId:o.sessionId});if(s.status!=="pending")return {turn:s,additionalContext:F(s),skipSkillRouting:s.source==="explicit_intent"};await o.memory.recordUserMessage(o.prompt.slice(0,4e3),o.sessionId,o.prompt.slice(0,200)),await o.memory.recordEvent({id:n.eventId,sessionId:o.sessionId,tool:"UserPromptSubmit",kind:"user_message",payload:{prompt:o.prompt.slice(0,4e3),turnId:n.turnId},createdAt:n.createdAt});let i=p(o.prompt);if(!i)return {turn:s,additionalContext:Te(s),skipSkillRouting:!1};let c=await o.memory.saveExplicitMemoryIntent(i,o.sessionId,n.eventId),a=ke(s.turnId,"committed",[c.id]);return s=t.commitMemoryTurn({turnId:s.turnId,receiptId:a,memoryIds:[c.id],source:"explicit_intent",reason:"explicit_user_request"}),{turn:s,explicitContent:i,additionalContext:F(s),skipSkillRouting:!0}}finally{t.close();}}var m=process.env.CLAUDE_PROJECT_DIR||process.cwd();async function Ce(o){let t=b()?[_]:[],e;try{e=JSON.parse(o);}catch{return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t.length?{additionalContext:t.join(`
1
+ import {MemoryGate}from'@devflow-tools/memory-engine';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {existsSync,readFileSync,rmSync,mkdirSync as mkdirSync$1,writeFileSync as writeFileSync$1,renameSync as renameSync$1,readdirSync,unlinkSync}from'fs';import {join as join$1,dirname}from'path';import {randomUUID as randomUUID$1}from'crypto';import {formatSkillRuntimeOverlay,createSkillRuntimeOverlay,getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {fileURLToPath}from'url';import {request}from'http';import {homedir}from'os';import {createHash,randomUUID}from'node:crypto';import {mkdirSync,writeFileSync,renameSync}from'node:fs';import {join}from'node:path';var _="WARNING: DevFlow is in degraded mode (daemon unreachable): 4-Gate enforcement and memory prefetch cache are unavailable. Run `devflow doctor` for details.";function D(){return process.env.DEVFLOW_HOOK_DEGRADED==="1"}function J(o){return o??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function d(o){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(J(o))}function X(o){return Buffer.from(o,"utf8").toString("base64url")}var y=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join$1(d(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(),i={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:n,lastActivityAt:n};return this.sessions.set(e,i),this.persist(i),i}startExecution(t,e,r){let n=this.registerSession(t);return (!n.executionId||n.skillName!==e)&&(n.executionId=`exec_${Date.now()}_${randomUUID$1().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 i=JSON.parse(readFileSync(n,"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 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$1(this.sessionsDir,`${X(t)}.json`)}persist(t){mkdirSync$1(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(t.sessionId),r=`${e}.${process.pid}.tmp`;writeFileSync$1(r,JSON.stringify(t),{mode:384}),renameSync$1(r,e);}};function Q(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 T(o){let t=o.trimStart().match(/^\/(devflow:[a-z0-9][a-z0-9-]*)\b/i);if(!t)return null;let e=Q(t[1]);return e?{rawName:t[1],skillName:e}:null}var ne=2,oe=1;function ie(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join$1(dirname(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function k(o=ie()){let t=[join$1(o,"dist","command-registry.json"),join$1(o,"command-registry.json")];for(let e of t)try{if(!existsSync(e))continue;let r=JSON.parse(readFileSync(e,"utf8"));if(r.version!==ne&&r.version!==oe||!se(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 se(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 S(e.mcpTools)&&S(e.blockedNative)&&(e.source===void 0||e.source==="core"||e.source==="plugin")&&(e.requiresContext===void 0||typeof e.requiresContext=="boolean")&&(e.runtimePackages===void 0||S(e.runtimePackages))&&(e.evidenceMode===void 0||["static","runtime","mixed"].includes(e.evidenceMode))})}function S(o){return Array.isArray(o)&&o.every(t=>typeof t=="string"&&t.length>0)}function R(o){let t=k();return t?t[o]?.mcpTools??[]:[]}var O=1e4,pe=3e4,g=500;function ye(o,t,e){return new Promise(r=>{try{let n=new URL(o),i=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},s=>{let a=[];s.on("data",c=>a.push(c)),s.on("end",()=>{let c=Buffer.concat(a).toString();r(s.statusCode!=null&&s.statusCode>=200&&s.statusCode<300?c:null);});});i.on("error",()=>r(null)),i.on("timeout",()=>{i.destroy(),r(null);}),i.write(t),i.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$1(process.env.DEVFLOW_STATE_DIR??join$1(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=t?.legacyCacheDir??(t?.cacheDir?null:join$1(homedir(),".devflow","telemetry-cache")),this.database=t?.database??null,this.ownsDatabase=!t?.database;let e=t?.busyTimeoutMs??Number(process.env.DEVFLOW_TELEMETRY_DB_BUSY_TIMEOUT_MS);this.busyTimeoutMs=Number.isSafeInteger(e)&&e>=0?e:100;}async sendEvent(t){let e={...t,input:this.truncateInput(t.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),t.eventId):null}async sendExecutionStart(t,e,r,n,i=process.env.CLAUDE_PROJECT_DIR??process.cwd(),s=[]){let a={executionId:t,sessionId:e,skillName:r,startedAt:n,projectRoot:i,requiredMcpTools:s};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(t,e="completed",r=Date.now(),n){let i={executionId:t,status:e,finishedAt:r,metadata:n},s=this.commitOrCache("execution_complete",i);return s&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",i),s}async sendSessionStart(t,e,r){let n={id:t,projectRoot:e,startedAt:r,label:e.split("/").pop()??"unknown"},i=this.commitOrCache("session_start",n);return i&&this.postHttp("/api/telemetry/sessions",n),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 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:g}).reverse();for(let i of n)try{this.applyOperation(i.operation,i.payload),r.resolveTelemetryFailure(i.id);}catch{}r.trimTelemetryFailures(g);}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 i=join$1(r,n);try{return {cacheFile:i,envelope:ge(JSON.parse(readFileSync(i,"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 i=this.getDatabase();i.insertTelemetryFailure({id:n.id,operation:n.operation,payload:n.payload,error:n.failure,createdAt:n.timestamp}),this.applyOperation(n.operation,n.payload),i.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(void 0,{busyTimeoutMs:this.busyTimeoutMs}),this.database}commitOrCache(t,e){try{return this.applyOperation(t,e),!0}catch(r){return this.cacheOperation(t,e,r),false}}applyOperation(t,e){let r=this.getDatabase();switch(t){case "tool_call":this.ensureParentSession(r,e),r.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(r,e),r.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running",requiredMcpTools:e.requiredMcpTools});return;case "execution_complete":r.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":r.ensureSession(e),r.insertRun({id:P(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(P(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(),i={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 s=this.getDatabase();s.insertTelemetryFailure({id:i.id,operation:t,payload:e,error:i.failure,createdAt:n}),s.trimTelemetryFailures(g),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync$1(this.cacheDir,{recursive:!0});let s=join$1(this.cacheDir,`${n}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync$1(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-g)))unlinkSync(join$1(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},pe).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<=O?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,O)}...`}}};function N(o){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(o)}function P(o){return `hook-run:${o}`}function ge(o,t){if(!o||typeof o!="object")throw new Error("Invalid telemetry cache envelope");let e=o;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let r=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!r)throw new Error("Unknown legacy telemetry cache operation");let n=e.payload??{},i=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:i}}var he=/^\/(?:devflow:)?[\w-]+\b\s*/iu,fe=/\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,Ie=/(?:^|[.!?]\s*)(?:(?:please|kindly)\s+)?(?:remember|memorize|save\s+this|store\s+this|note\s+that)(?:\s+(?:that|to))?[\s:,-]*(.+)$/iu,ve=/(?:^|[。!?;,,]\s*)(?:(?:请|麻烦|务必|一定要|帮我|帮忙|需要)\s*)?(?:记住|记一下|记下来|记|记录一下|记录下来|保存一下|保存下来)(?:这(?:件事|一点|个信息|些内容))?[\s::,,]*(.+)$/iu;function f(o){let t=o.trim().replace(he,"").trim();return !t||fe.test(t)?null:(ve.exec(t)??Ie.exec(t))?.[1]?.trim().replace(/^[::,,\s]+/u,"")||null}var Se=/^(?:ok(?:ay)?|yes|no|thanks?|thank you|continue|好的?|可以|行|是|否|谢谢|继续|收到|明白了?)[.!。!\s]*$/iu;function xe(o){let t=o.trim();return t.length>0&&!Se.test(t)}function be(o,t,e=Date.now()){let r=createHash("sha256").update(t.trim()).digest("hex"),n=createHash("sha256").update(`${o}\0${e}\0${r}`).digest("hex").slice(0,24);return {turnId:`turn:${n}`,eventId:`memory-turn:${n}`,promptHash:r,createdAt:e}}function _e(o,t,e=[]){return `memory-receipt:${createHash("sha256").update(`${o}\0${t}\0${[...e].sort().join("\0")}`).digest("hex").slice(0,24)}`}function j(o){return `DevFlow memory decision required for ${o.turnId}. Before the final response, semantically decide whether this turn contains durable project knowledge. Call mcp__devflow__memory_commit_turn with evidence-bound candidates, or mcp__devflow__memory_skip_turn with a concrete reason. Do not claim memory success without a canonical receipt.`}function b(o){let t=o.memoryIds.length>0?o.memoryIds.join(","):"none",e=o.status==="committed"?"accepted":o.status,r=o.status==="committed"?"; indexStatus=pending":"";return `DevFlow canonical memory receipt: status=${e}; receiptId=${o.receiptId??"missing"}; turnId=${o.turnId}; memoryIds=${t}; source=${o.source??"unknown"}${r}.`}function F(o){if(!xe(o.prompt))return null;let t=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:50});try{let e=o.prompt.trim().slice(0,4e3),r=createHash("sha256").update(e).digest("hex"),n=t.listMemoryTurns(o.projectRoot,o.sessionId,5).find(u=>u.promptHash===r&&Date.now()-u.createdAt<1e4),i=n?{turnId:n.turnId,eventId:n.eventId,promptHash:n.promptHash,createdAt:n.createdAt}:be(o.sessionId,e),s=n??t.beginMemoryTurn({...i,projectRoot:o.projectRoot,sessionId:o.sessionId});if(s.status!=="pending")return {turn:s,additionalContext:b(s),skipSkillRouting:s.source==="explicit_intent"};let a=f(e)??void 0,c=a?"commit_explicit":"capture_prompt";return t.enqueueWork({idempotencyKey:`memory-turn:${c}:${s.turnId}`,kind:a?"memory.explicit_commit":"memory.turn_capture",projectRoot:o.projectRoot,sessionId:o.sessionId,turnId:s.turnId,payload:{operation:c,turnId:s.turnId,eventId:s.eventId,sessionId:o.sessionId,prompt:e,explicitContent:a,createdAt:s.createdAt}}),{turn:s,explicitContent:a,additionalContext:a?`DevFlow explicit memory is durably queued for ${s.turnId}, but no canonical receipt exists yet. Do not claim that it was remembered.`:j(s),skipSkillRouting:!!a}}finally{t.close();}}async function q(o){let{payload:t,memory:e,projectRoot:r}=o,n=openGlobalDevFlowDatabase();try{let i=n.getMemoryTurn(t.turnId);if(!i)throw new Error(`Memory turn ${t.turnId} does not exist`);if(i.status!=="pending")return {turn:i,additionalContext:b(i),skipSkillRouting:i.source==="explicit_intent"};if(await e.recordUserMessage(t.prompt,t.sessionId,t.prompt.slice(0,200)),await e.recordEvent({id:t.eventId,sessionId:t.sessionId,tool:"UserPromptSubmit",kind:"user_message",payload:{prompt:t.prompt,turnId:t.turnId},createdAt:t.createdAt}),t.operation==="capture_prompt"||!t.explicitContent)return {turn:i,additionalContext:j(i),skipSkillRouting:!1};let s=await e.saveExplicitMemoryIntent(t.explicitContent,t.sessionId,t.eventId),a=_e(i.turnId,"committed",[s.id]),c=n.commitMemoryTurn({turnId:i.turnId,receiptId:a,memoryIds:[s.id],source:"explicit_intent",reason:"explicit_user_request"});return n.enqueueWork({idempotencyKey:`vector:${r}:${s.id}`,kind:"memory.vector_backfill",projectRoot:r,sessionId:t.sessionId,turnId:c.turnId,payload:{observationId:s.id},maxAttempts:20}),{turn:c,explicitContent:t.explicitContent,additionalContext:b(c),skipSkillRouting:!0}}finally{n.close();}}function H(o,t){let e=Re(o);mkdirSync(e,{recursive:true,mode:448});let r=`memory-turn-spool-${Date.now()}-${process.pid}-${randomUUID()}`,n=join(e,`${r}.json`),i=`${n}.tmp`;return writeFileSync(i,JSON.stringify({...t,prompt:t.prompt.trim().slice(0,4e3)}),{mode:384}),renameSync(i,n),r}function Re(o){return join(d(o),"memory-turn-pending")}var l=process.env.CLAUDE_PROJECT_DIR||process.cwd();async function Ae(o){let t=D()?[_]:[],e;try{e=JSON.parse(o);}catch{return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t.length?{additionalContext:t.join(`
3
2
  `)}:{}}}}let r=e.prompt||"";if(!r.trim())return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t.length?{additionalContext:t.join(`
4
- `)}:{}}};let n=e.session_id?.trim(),s=R(r),i=new MemoryGate(m),c=false;try{if(await i.forceWarmUp(),n){let a=await $({projectRoot:m,sessionId:n,prompt:r,memory:i});a&&(t.push(a.additionalContext),c=a.skipSkillRouting);}}catch{let a=p(r);if(a){try{let u=openGlobalDevFlowDatabase();try{let l=n?u.getPendingMemoryTurn(m,n):null;w(m,{content:a,sessionId:n,turnId:l?.turnId,eventId:l?.eventId,createdAt:Date.now()});}finally{u.close();}}catch{}t.push("DevFlow explicit memory save failed; no canonical receipt was created. Do not claim that the content was remembered."),c=true;}}finally{i.close();}if(n&&s&&!c){let a=new h(m),u=a.get(n)?.executionId,l=a.startExecution(n,s.skillName,M(s.skillName));if(l.executionId&&l.executionId!==u){let v=new f;try{await v.sendSessionStart(n,m,l.startedAt),await v.sendExecutionStart(l.executionId,n,s.skillName,l.lastActivityAt,m,l.requiredMcpTools);}finally{v.close();}}}return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t.length?{additionalContext:t.join(`
5
- `)}:{}}}}if(process.argv[1]?.endsWith("user-prompt-submit")||process.argv[1]?.endsWith("user-prompt-submit.js")){let o="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{o+=t;}),process.stdin.on("end",async()=>{let t=await Ce(o.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();}export{Ce as handleUserPromptSubmit};
3
+ `)}:{}}};let n=e.session_id?.trim(),i=T(r),s=false;if(n)try{let a=F({projectRoot:l,sessionId:n,prompt:r});if(a){let c=a;if(a.explicitContent&&a.turn.status==="pending"){let u=new MemoryGate(l);try{c=await q({projectRoot:l,payload:{operation:"commit_explicit",turnId:a.turn.turnId,eventId:a.turn.eventId,sessionId:n,prompt:r.trim().slice(0,4e3),explicitContent:a.explicitContent,createdAt:a.turn.createdAt},memory:u});}catch(p){console.error("[devflow] Explicit memory commit deferred:",p.message);}finally{u.close();}}t.push(c.additionalContext),s=c.skipSkillRouting;}}catch(a){s=!!f(r);try{let c=H(l,{sessionId:n,prompt:r,createdAt:Date.now()});t.push(`DevFlow memory turn status=accepted; durable=true; receiptId=${c}; canonicalMemory=false; SQLite queue replay pending. Do not claim memory success without a canonical receipt.`),console.error("[devflow] Memory turn spooled after database contention:",a.message);}catch(c){t.push("DevFlow memory turn status=degraded; durable=false; no canonical receipt exists. Do not claim memory success."),console.error("[devflow] Memory turn persistence unavailable:",c.message);}}if(n&&i&&!s){let a=new y(l),c=a.get(n)?.executionId,u=a.startExecution(n,i.skillName,R(i.skillName));if(u.executionId&&u.executionId!==c){let v=new h;try{await v.sendSessionStart(n,l,u.startedAt),await v.sendExecutionStart(u.executionId,n,i.skillName,u.lastActivityAt,l,u.requiredMcpTools);}finally{v.close();}}let p=formatSkillRuntimeOverlay(createSkillRuntimeOverlay(i.skillName,l));p&&t.push(p);}return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t.length?{additionalContext:t.join(`
4
+ `)}:{}}}}if(process.argv[1]?.endsWith("user-prompt-submit")||process.argv[1]?.endsWith("user-prompt-submit.js")){let o=console.log.bind(console);console.log=console.error.bind(console),console.info=console.error.bind(console);let t="";process.stdin.setEncoding("utf8"),process.stdin.on("data",e=>{t+=e;}),process.stdin.on("end",async()=>{let e=await Ae(t.trim()||process.argv[2]||"");o(JSON.stringify(e)),process.exit(0);}),process.stdin.on("error",()=>{o(JSON.stringify({hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}})),process.exit(0);}),setTimeout(()=>{o(JSON.stringify({hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}})),process.exit(0);},3e4).unref();}export{Ae as handleUserPromptSubmit};
@@ -50,7 +50,9 @@ rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
50
50
  - 使用 AnimatePresence 处理 React 组件退出动画
51
51
  - Lottie 动画控制文件大小 < 50KB
52
52
 
53
- ## 当前版本
53
+ ## 知识来源
54
+
55
+ 以下版本描述知识源,不代表目标项目版本;目标项目的 package.json 与 lockfile 证据始终优先。
54
56
 
55
57
  - animation-docs: 2024 — https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animations
56
58
 
@@ -50,7 +50,9 @@ rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
50
50
  - 使用 rem/em 做相对单位
51
51
  - 不确定属性行为时先查 MDN 文档确认
52
52
 
53
- ## 当前版本
53
+ ## 知识来源
54
+
55
+ 以下版本描述知识源,不代表目标项目版本;目标项目的 package.json 与 lockfile 证据始终优先。
54
56
 
55
57
  - mdn-css: 2024 — https://developer.mozilla.org/en-US/docs/Web/CSS
56
58
 
@@ -53,7 +53,9 @@ rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
53
53
  - 不在镜像层中写入敏感信息
54
54
  - 修复安全问题时先查 Docker 官方文档确认方案
55
55
 
56
- ## 当前版本
56
+ ## 知识来源
57
+
58
+ 以下版本描述知识源,不代表目标项目版本;目标项目的 package.json 与 lockfile 证据始终优先。
57
59
 
58
60
  - docker-docs: 26.0.0 — https://docs.docker.com/reference/
59
61
 
@@ -53,7 +53,9 @@ rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
53
53
  - 避免使用 remote 模块(已废弃)
54
54
  - 构建使用 electron-builder 并配置代码签名
55
55
 
56
- ## 当前版本
56
+ ## 知识来源
57
+
58
+ 以下版本描述知识源,不代表目标项目版本;目标项目的 package.json 与 lockfile 证据始终优先。
57
59
 
58
60
  - electron-docs: 30.0.0 — https://www.electronjs.org/docs/latest/
59
61
 
@@ -52,7 +52,9 @@ rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
52
52
  - PR 必须关联 Issue
53
53
  - 解决冲突前先理解两边变更的意图
54
54
 
55
- ## 当前版本
55
+ ## 知识来源
56
+
57
+ 以下版本描述知识源,不代表目标项目版本;目标项目的 package.json 与 lockfile 证据始终优先。
56
58
 
57
59
  - conventional-commits: 1.0.0 — https://www.conventionalcommits.org/en/v1.0.0/
58
60
  - git-scm-docs: 2.45.0 — https://git-scm.com/docs
@@ -52,7 +52,9 @@ rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
52
52
  - 限制查询深度和复杂度防止滥用
53
53
  - 遇到不确定的 Schema 设计时先查 GraphQL 官方规范
54
54
 
55
- ## 当前版本
55
+ ## 知识来源
56
+
57
+ 以下版本描述知识源,不代表目标项目版本;目标项目的 package.json 与 lockfile 证据始终优先。
56
58
 
57
59
  - graphql-docs: 2024 — https://graphql.org/learn/
58
60
 
@@ -54,7 +54,9 @@ rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
54
54
  - 修复 Bug 前先追踪 DI 链和 Module 注册
55
55
  - 使用 Prisma 作为 ORM,事务使用 $transaction
56
56
 
57
- ## 当前版本
57
+ ## 知识来源
58
+
59
+ 以下版本描述知识源,不代表目标项目版本;目标项目的 package.json 与 lockfile 证据始终优先。
58
60
 
59
61
  - nestjs-docs: 10.3.0 — https://docs.nestjs.com/
60
62
 
@@ -52,7 +52,9 @@ rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
52
52
  - 中间件在 Edge Runtime 执行,不能直接访问数据库
53
53
  - 使用 generateStaticParams + ISR 优化静态页面
54
54
 
55
- ## 当前版本
55
+ ## 知识来源
56
+
57
+ 以下版本描述知识源,不代表目标项目版本;目标项目的 package.json 与 lockfile 证据始终优先。
56
58
 
57
59
  - nextjs-docs: 14.2.0 — https://nextjs.org/docs
58
60
 
@@ -53,7 +53,9 @@ rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
53
53
  - 所有优化建议必须有 Lighthouse/Performance 数据支持
54
54
  - 不使用 setTimeout 做动画,使用 requestAnimationFrame
55
55
 
56
- ## 当前版本
56
+ ## 知识来源
57
+
58
+ 以下版本描述知识源,不代表目标项目版本;目标项目的 package.json 与 lockfile 证据始终优先。
57
59
 
58
60
  - web-vitals-docs: 2024 — https://web.dev/vitals/
59
61
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: devflow:react
3
- description: React 18 专家能力 — Bug 诊断、组件重构、性能优化、Hooks 审查
3
+ description: React 专家能力 — Bug 诊断、组件重构、性能优化、Hooks 审查
4
4
  required_mcp_tools:
5
5
  - react_review_hooks
6
6
  - react_audit_performance
@@ -38,7 +38,7 @@ rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
38
38
 
39
39
  # ⚛️ REACT
40
40
 
41
- 你是一个 React 18 专家。使用函数组件、Hooks 和 TypeScript。遇到问题先查文档再给方案,不凭空猜测 API 行为。
41
+ 你是一个 React 专家。项目 package.json 与 lockfile 检测到的版本是唯一权威。使用函数组件、Hooks 和 TypeScript;遇到问题先查对应版本文档再给方案。
42
42
 
43
43
  ## 适用场景
44
44
 
@@ -70,9 +70,11 @@ rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
70
70
  - 生成代码后必须通过 TypeScript 类型检查
71
71
  - 不能凭记忆猜测 API 行为,必须基于当前项目版本
72
72
 
73
- ## 当前版本
73
+ ## 知识来源
74
74
 
75
- - react-docs: 18.3.1 — https://react.dev/reference/react
75
+ 以下版本描述知识源,不代表目标项目版本;目标项目的 package.json 与 lockfile 证据始终优先。
76
+
77
+ - react-docs: current — https://react.dev/reference/react
76
78
 
77
79
  ## 工作流
78
80
 
@@ -52,7 +52,9 @@ rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
52
52
  - 避免内联 style 和自定义 CSS
53
53
  - 不确定 class 名称时先查 Tailwind 官方文档
54
54
 
55
- ## 当前版本
55
+ ## 知识来源
56
+
57
+ 以下版本描述知识源,不代表目标项目版本;目标项目的 package.json 与 lockfile 证据始终优先。
56
58
 
57
59
  - tailwind-docs: 3.4.0 — https://tailwindcss.com/docs
58
60
 
@@ -52,7 +52,9 @@ rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
52
52
  - 合理使用分包加载减少首屏体积
53
53
  - 不确定跨端兼容性时先查 Taro 文档确认
54
54
 
55
- ## 当前版本
55
+ ## 知识来源
56
+
57
+ 以下版本描述知识源,不代表目标项目版本;目标项目的 package.json 与 lockfile 证据始终优先。
56
58
 
57
59
  - taro-docs: 3.6.0 — https://docs.taro.zone/docs/
58
60
 
@@ -50,7 +50,9 @@ rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
50
50
  - 使用 CSS 变量管理 Design Token
51
51
  - 确保触控目标 ≥ 44×44px,对比度 ≥ 4.5:1
52
52
 
53
- ## 当前版本
53
+ ## 知识来源
54
+
55
+ 以下版本描述知识源,不代表目标项目版本;目标项目的 package.json 与 lockfile 证据始终优先。
54
56
 
55
57
  - web-dev-layout: 2024 — https://web.dev/learn/design/
56
58
 
@@ -57,7 +57,9 @@ rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
57
57
  - 生成代码后必须通过 vue-tsc 类型检查
58
58
  - 不能凭记忆猜测 API 行为,必须基于当前项目版本
59
59
 
60
- ## 当前版本
60
+ ## 知识来源
61
+
62
+ 以下版本描述知识源,不代表目标项目版本;目标项目的 package.json 与 lockfile 证据始终优先。
61
63
 
62
64
  - vue-docs: 3.4.0 — https://vuejs.org/guide/introduction.html
63
65
 
@@ -51,7 +51,7 @@
51
51
  {
52
52
  "type": "command",
53
53
  "command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/user-prompt-submit\"",
54
- "timeout": 30
54
+ "timeout": 5
55
55
  }
56
56
  ]
57
57
  }
@@ -85,7 +85,8 @@
85
85
  "hooks": [
86
86
  {
87
87
  "type": "command",
88
- "command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/session-end\""
88
+ "command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/session-end\"",
89
+ "timeout": 5
89
90
  }
90
91
  ]
91
92
  }