@devflow-tools/devflow 0.16.9 → 0.16.10
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.
- package/dist/dashboard/404.html +1 -1
- package/dist/dashboard/accuracy.html +1 -1
- package/dist/dashboard/accuracy.txt +1 -1
- package/dist/dashboard/benchmark.html +1 -1
- package/dist/dashboard/benchmark.txt +1 -1
- package/dist/dashboard/context.html +1 -1
- package/dist/dashboard/context.txt +1 -1
- package/dist/dashboard/doctor.html +1 -1
- package/dist/dashboard/doctor.txt +1 -1
- package/dist/dashboard/executions.html +1 -1
- package/dist/dashboard/executions.txt +1 -1
- package/dist/dashboard/graph.html +1 -1
- package/dist/dashboard/graph.txt +1 -1
- package/dist/dashboard/index.html +1 -1
- package/dist/dashboard/index.txt +1 -1
- package/dist/dashboard/knowledge.html +1 -1
- package/dist/dashboard/knowledge.txt +1 -1
- package/dist/dashboard/memory.html +1 -1
- package/dist/dashboard/memory.txt +1 -1
- package/dist/dashboard/replay.html +1 -1
- package/dist/dashboard/replay.txt +1 -1
- package/dist/dashboard/runs/_.html +1 -1
- package/dist/dashboard/runs/_.txt +1 -1
- package/dist/dashboard/runs.html +1 -1
- package/dist/dashboard/runs.txt +1 -1
- package/dist/dashboard/settings.html +1 -1
- package/dist/dashboard/settings.txt +1 -1
- package/dist/dashboard/timeline.html +1 -1
- package/dist/dashboard/timeline.txt +1 -1
- package/dist/dashboard/tool-metrics.html +1 -1
- package/dist/dashboard/tool-metrics.txt +1 -1
- package/dist/dashboard/workflows.html +1 -1
- package/dist/dashboard/workflows.txt +1 -1
- package/dist/hooks/hook-client.js +6 -6
- package/dist/hooks/hook-daemon.js +14 -13
- package/dist/hooks/pre-compact.js +1 -1
- package/dist/hooks/pre-tool-use.js +2 -2
- package/dist/hooks/session-end.js +2 -2
- package/dist/hooks/stop.js +2 -2
- package/dist/hooks/user-prompt-submit.js +5 -3
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/dist/command-registry.json +5 -5
- package/dist/plugin/dist/hooks/hook-client.js +6 -6
- package/dist/plugin/dist/hooks/hook-daemon.js +14 -13
- package/dist/plugin/dist/hooks/pre-compact.js +1 -1
- package/dist/plugin/dist/hooks/pre-tool-use.js +2 -2
- package/dist/plugin/dist/hooks/session-end.js +2 -2
- package/dist/plugin/dist/hooks/stop.js +2 -2
- package/dist/plugin/dist/hooks/user-prompt-submit.js +5 -3
- package/dist/plugin/hooks/stop +6 -4
- package/dist/plugin/package.json +7 -7
- package/dist/scripts/audit-session-data.js +13 -5
- package/package.json +12 -12
- package/plugin-registry.json +29 -29
- /package/dist/dashboard/_next/static/{JAGOZdxW2bLxLynmBzFBM → E8qR8p8oP4qQpCa0xXyqj}/_buildManifest.js +0 -0
- /package/dist/dashboard/_next/static/{JAGOZdxW2bLxLynmBzFBM → E8qR8p8oP4qQpCa0xXyqj}/_ssgManifest.js +0 -0
|
@@ -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
|
|
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(`
|
|
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 {dirname,join as join$1}from'node:path';import {randomUUID,createHash}from'crypto';var L=1e4,he=3e4,R=500;function ye(n,t,e){return new Promise(o=>{try{let r=new URL(n),i=request({hostname:r.hostname,port:r.port||80,path:r.pathname+r.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(t)},timeout:5e3},s=>{let c=[];s.on("data",l=>c.push(l)),s.on("end",()=>{let l=Buffer.concat(c).toString();o(s.statusCode!=null&&s.statusCode>=200&&s.statusCode<300?l:null);});});i.on("error",()=>o(null)),i.on("timeout",()=>{i.destroy(),o(null);}),i.write(t),i.end();}catch{o(null);}})}var 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(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:R}).reverse();for(let i of r)try{this.applyOperation(i.operation,i.payload),o.resolveTelemetryFailure(i.id);}catch{}o.trimTelemetryFailures(R);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(o=>!!o))].filter(o=>existsSync(o)).flatMap(o=>readdirSync(o).filter(r=>r.endsWith(".json")).map(r=>{let i=join(o,r);try{return {cacheFile:i,envelope:_e(JSON.parse(readFileSync(i,"utf8")),r)}}catch{return null}})).filter(o=>o!==null).sort((o,r)=>o.envelope.timestamp-r.envelope.timestamp||U(o.envelope.operation)-U(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:q(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(q(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(R),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-R)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},he).unref());}scheduleMetricAggregation(){if(this.metricAggregationScheduled||process.env.DEVFLOW_HOOK_DEGRADED==="1")return;this.metricAggregationScheduled=true,setTimeout(()=>{this.metricAggregationScheduled=false;try{this.getDatabase().aggregatePendingToolMetrics(50)===50&&this.scheduleMetricAggregation();}catch{}},25).unref();}postHttp(t,e){ye(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=L?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,L)}...`}}};function U(n){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(n)}function q(n){return `hook-run:${n}`}function _e(n,t){if(!n||typeof n!="object")throw new Error("Invalid telemetry cache envelope");let e=n;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let o=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!o)throw new Error("Unknown legacy telemetry cache operation");let r=e.payload??{},i=Number(r.timestamp??r.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:o,payload:r,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:i}}function k(n){return n??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function p(n){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(k(n))}var Se=2,xe=1;function ke(){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 G(n=ke()){let t=[join(n,"dist","command-registry.json"),join(n,"command-registry.json")];for(let e of t)try{if(!existsSync(e))continue;let o=JSON.parse(readFileSync(e,"utf8"));if(o.version!==Se&&o.version!==xe||!Ee(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 Ee(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 H(e.mcpTools)&&H(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 H(n){return Array.isArray(n)&&n.every(t=>typeof t=="string"&&t.length>0)}function W(n){let t=G();return t?t[n]?.mcpTools??[]:[]}function y(n){if(!n||typeof n!="object"||Array.isArray(n))return {};let t=n,e={};for(let o of ["lastMcpCall","bypassCount"])if(o in t){let r=t[o];if(r==null)continue;e[o]=typeof r=="number"&&Number.isFinite(r)&&r>=0?r:0;}return e}function Ce(n,t){return n.lastMcpCall===t.lastMcpCall&&n.bypassCount===t.bypassCount}function Ie(n){try{return existsSync(n)?y(JSON.parse(readFileSync(n,"utf-8"))):{}}catch{return {}}}function K(n,t){let e=t instanceof Error?t.message:String(t);console.error(`[devflow] Receipt ${n} skipped: ${e}`);}function J(n){let t=p(n);for(let e of ["receipt.json","receipt-lock.sqlite","receipt-lock.sqlite-shm","receipt-lock.sqlite-wal"])try{existsSync(join(t,e))&&unlinkSync(join(t,e));}catch{}}function z(n,t){if(n.getHookReceipt(t)){J(t);return}let e=join(p(t),"receipt.json");if(!existsSync(e))return;let o=Ie(e);n.updateHookReceipt(t,()=>o),J(t);}function V(n,t,e){let o;try{o=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),z(o,n);let r={},i=o.updateHookReceipt(n,c=>(r=y(c),y(t(r)))),s=y(i);return {receipt:s,applied:!0,changed:!Ce(r,s)}}catch(r){return K(e,r),{receipt:{},applied:false,changed:false}}finally{try{o?.close();}catch{}}}function X(n){let t;try{return t=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),z(t,n),y(t.getHookReceipt(n))}catch(e){return K("update",e),{}}finally{try{t?.close();}catch{}}}function Ae(n,t){return V(n,()=>t,"write").applied}function je(n,t){return V(n,t,"update")}var B={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",Bash:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function Fe(n){switch(n){case "WebSearch":case "WebFetch":return 1;case "Agent":return 3;case "Grep":case "Glob":return 3;case "Bash":return 5;default:return 2}}var S=class{constructor(t,e,o){this.projectRoot=t;this.sessionId=e;this.executionId=o;}getPhase(){if(!this.sessionId||!this.executionId)return "idle";let t;try{t=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250});let e=t.getContextReceipt(this.projectRoot,this.sessionId,this.executionId);if(e&&e.expiresAt>Date.now())return "context_ready";e&&t.deleteContextReceipt(this.projectRoot,this.sessionId,this.executionId);}catch{}finally{try{t?.close();}catch{}}return "context_gathering"}evaluate(t,e){let o=this.getPhase();if(e||t==="Skill")return {permissionDecision:"allow"};if(o==="context_gathering"&&B[t])return {permissionDecision:"deny",reason:`DevFlow context required. Call mcp__devflow__get_project_context, then retry ${t}.`};if(o==="context_ready")return {permissionDecision:"allow"};let r=B[t];if(!r)return {permissionDecision:"allow"};let i=je(this.projectRoot,l=>({...l,bypassCount:(l.bypassCount??0)+1}));if(!i.applied)return {permissionDecision:"allow"};let s=i.receipt.bypassCount??0,c=Fe(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(){Ae(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}};var Ge=10*1024*1024,We=5;function Je(n){return createHash$1("sha256").update(n).digest("hex").slice(0,16)}function Z(n){return join$1(homedir$1(),".devflow","logs","hook-debug",`${Je(n)}.jsonl`)}function ee(n,t,e={}){let o=e.maxBytes??Ge,r=e.maxFiles??We;mkdirSync$1(dirname(n),{recursive:true,mode:448});let i=Buffer.byteLength(t),s=existsSync$1(n)?statSync(n).size:0;s>0&&s+i>o&&Be(n,r),appendFileSync(n,t,{mode:384}),chmodSync(n,384);}function Be(n,t){if(t<=1){rmSync$1(n,{force:true});return}rmSync$1(`${n}.${t-1}`,{force:true});for(let e=t-2;e>=1;e-=1){let o=`${n}.${e}`;existsSync$1(o)&&renameSync$1(o,`${n}.${e+1}`);}existsSync$1(n)&&renameSync$1(n,`${n}.1`);}var A="WARNING: DevFlow is in degraded mode (daemon unreachable): 4-Gate enforcement and memory prefetch cache are unavailable. Run `devflow doctor` for details.";function te(){return process.env.DEVFLOW_HOOK_DEGRADED==="1"}function et(n){return Buffer.from(n,"utf8").toString("base64url")}var x=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(p(t),"hook-sessions");}registerSession(t){let e=t.trim();if(!e)throw new Error("session_id_required");let o=this.get(e);if(o)return o.lastActivityAt=Date.now(),this.persist(o),o;let r=Date.now(),i={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:r,lastActivityAt:r};return this.sessions.set(e,i),this.persist(i),i}startExecution(t,e,o){let r=this.registerSession(t);return (!r.executionId||r.skillName!==e)&&(r.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),r.skillName=e,r.requiredMcpTools=[...new Set(o)],r.lastActivityAt=Date.now(),this.persist(r),r}get(t){let e=t.trim();if(!e)return null;let o=this.sessions.get(e);if(o)return o;let r=this.snapshotPath(e);if(!existsSync(r))return null;try{let i=JSON.parse(readFileSync(r,"utf8"));return i.sessionId!==e||i.projectRoot!==this.projectRoot?null:(i.requiredMcpTools=Array.isArray(i.requiredMcpTools)?i.requiredMcpTools:[],this.sessions.set(e,i),i)}catch{return null}}completeExecution(t){let e=this.get(t);if(!e)return null;let o={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),o}removeSession(t){let e=t.trim();e&&(this.sessions.delete(e),rmSync(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(t){return join(this.sessionsDir,`${et(t)}.json`)}persist(t){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(t.sessionId),o=`${e}.${process.pid}.tmp`;writeFileSync(o,JSON.stringify(t),{mode:384}),renameSync(o,e);}};function oe(n){let t=n.trim().replace(/^\//,"");if(!t.startsWith("devflow:"))return null;let e=t.slice(8).replace(/^devflow-/,"");return /^[a-z0-9][a-z0-9-]*$/i.test(e)?`devflow:${e.toLowerCase()}`:null}function re(n,t){return t?`evt_tool_${createHash("sha256").update(`${n}\0${t}`).digest("hex").slice(0,24)}`:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`}function _(){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}}function ie(n){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:n}}}function st(n){let t=p(n);return existsSync(t)||mkdirSync(t,{recursive:true}),t}function ct(n,t,e,o,r,i){let s=join(st(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 at(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 lt(n){let t=te();try{if(!n||n.trim()==="")return _();let e=JSON.parse(n),o=Date.now(),r=k(),i=new b;e.tool_name.startsWith("mcp__devflow__")&&(typeof e.tool_input.projectRoot!="string"||!e.tool_input.projectRoot.trim())&&(e.tool_input.projectRoot=r);let s=e.session_id?.trim();if(!s)return ie(A);let c=new x(r),l=c.get(s)!==null;if(c.registerSession(s),l||await i.sendSessionStart(s,r,Date.now()),e.tool_name==="Skill"){let w=e.tool_input?.skill,m=w?oe(w):null;if(m){let le=c.get(s)?.executionId,h=c.startExecution(s,m,W(m));h.executionId&&h.executionId!==le&&await i.sendExecutionStart(h.executionId,s,m,h.lastActivityAt,r,h.requiredMcpTools);}}let d=c.get(s);e.tool_name.startsWith("mcp__devflow__")&&(e.tool_input._devflow_session_id=s,e.tool_input._devflow_execution_id=d?.executionId??s,e.tool_use_id&&(e.tool_input._devflow_tool_use_id=e.tool_use_id));let u=e.tool_name.startsWith("mcp__"),ce=u?e.tool_name.replace(/^mcp__[^_]+__/,""):void 0,j=new S(r,s,d?.executionId),F=j.getPhase(),a,v=j.evaluate(e.tool_name,u);v.permissionDecision==="deny"?a={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:v.reason??"MCP context required"}}:v.additionalContext?a=ie(v.additionalContext):a=_(),e.tool_name.startsWith("mcp__devflow__")&&(a.hookSpecificOutput.updatedInput=e.tool_input),t&&a.hookSpecificOutput.permissionDecision==="allow"&&(a.hookSpecificOutput.additionalContext=A);let ae=d?.executionId??s,f={eventId:re(s,e.tool_use_id),executionId:ae,sessionId:s,projectRoot:r,toolUseId:e.tool_use_id,timestamp:Date.now(),toolName:e.tool_name,toolType:u?"mcp":e.tool_name==="Agent"?"subagent":"direct",isMcpTool:u,mcpToolName:ce,mcpEnforced:u&&F==="context_gathering",mcpFallback:!u&&F==="context_gathering",kind:at(e.tool_name),input:e.tool_input,duration:0,blocked:a.hookSpecificOutput.permissionDecision==="deny",blockReason:a.hookSpecificOutput.permissionDecision==="deny"?"DEVFLOW_CONTEXT_REQUIRED":void 0,error:a.hookSpecificOutput.permissionDecision==="deny"?"DEVFLOW_CONTEXT_REQUIRED":void 0},D=await i.sendEvent(f);D&&!f.blocked&&ct(r,s,e.tool_name,e.tool_use_id,D,f.timestamp),D&&f.blocked&&await i.completeEvent({eventId:D,sessionId:s,error:"DEVFLOW_CONTEXT_REQUIRED",duration:Date.now()-f.timestamp,completedAt:Date.now()});try{let w=X(r),m={ts:o,tool:e.tool_name,sessionId:s,skill:d?.skillName??"(none)",requiredTools:d?.requiredMcpTools??[],isMcp:u,enforce:d?.executionId!==void 0,stateless:d?.executionId===void 0&&!u&&e.tool_name!=="Skill",bypassCount:w.bypassCount??0,decision:a.hookSpecificOutput.permissionDecision,env_CLAUDE_PLUGIN_ROOT:process.env.CLAUDE_PLUGIN_ROOT??"(unset)",cwd:process.cwd()};ee(Z(r),`${JSON.stringify(m)}
|
|
2
|
-
`);}catch{}return a}catch{return
|
|
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,2 +1,2 @@
|
|
|
1
|
-
import {request}from'http';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {existsSync,readFileSync,rmSync,mkdirSync,writeFileSync,renameSync,readdirSync,unlinkSync}from'fs';import {join}from'path';import {homedir}from'os';import {MemoryGate}from'@devflow-tools/memory-engine';import {randomUUID}from'crypto';function w(n){return n??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function E(n){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(w(n))}var M=1e4,G=3e4,h=500;function W(n,s,e){return new Promise(t=>{try{let r=new URL(n),i=request({hostname:r.hostname,port:r.port||80,path:r.pathname+r.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(s)},timeout:5e3},o=>{let a=[];o.on("data",c=>a.push(c)),o.on("end",()=>{let c=Buffer.concat(a).toString();t(o.statusCode!=null&&o.statusCode>=200&&o.statusCode<300?c:null);});});i.on("error",()=>t(null)),i.on("timeout",()=>{i.destroy(),t(null);}),i.write(s),i.end();}catch{t(null);}})}var g=class{constructor(s){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=s?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=s?.apiKey??getLocalApiKey(),this.cacheDir=s?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=s?.legacyCacheDir??(s?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=s?.database??null,this.ownsDatabase=!s?.database;}async sendEvent(s){let e={...s,input:this.truncateInput(s.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),s.eventId):null}async sendExecutionStart(s,e,t,r,i=process.env.CLAUDE_PROJECT_DIR??process.cwd(),o=[]){let a={executionId:s,sessionId:e,skillName:t,startedAt:r,projectRoot:i,requiredMcpTools:o};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(s,e="completed",t=Date.now(),r){let i={executionId:s,status:e,finishedAt:t,metadata:r},o=this.commitOrCache("execution_complete",i);return o&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",i),o}async sendSessionStart(s,e,t){let r={id:s,projectRoot:e,startedAt:t,label:e.split("/").pop()??"unknown"},i=this.commitOrCache("session_start",r);return i&&this.postHttp("/api/telemetry/sessions",r),i}async completeEvent(s){let e=this.commitOrCache("complete_event",s);return e&&(this.postHttp("/api/telemetry/tool-call/output",s),this.scheduleMetricAggregation()),e}async endSession(s,e=Date.now()){let t={sessionId:s,finishedAt:e},r=this.commitOrCache("session_end",t);return r&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(s)}/close`,{finishedAt:e})),r}async flushCache(){try{let t=this.getDatabase(),r=t.listTelemetryFailures({unresolvedOnly:!0,limit:h}).reverse();for(let i of r)try{this.applyOperation(i.operation,i.payload),t.resolveTelemetryFailure(i.id);}catch{}t.trimTelemetryFailures(h);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(t=>!!t))].filter(t=>existsSync(t)).flatMap(t=>readdirSync(t).filter(r=>r.endsWith(".json")).map(r=>{let i=join(t,r);try{return {cacheFile:i,envelope:B(JSON.parse(readFileSync(i,"utf8")),r)}}catch{return null}})).filter(t=>t!==null).sort((t,r)=>t.envelope.timestamp-r.envelope.timestamp||P(t.envelope.operation)-P(r.envelope.operation));for(let{cacheFile:t,envelope:r}of e)try{let i=this.getDatabase();i.insertTelemetryFailure({id:r.id,operation:r.operation,payload:r.payload,error:r.failure,createdAt:r.timestamp}),this.applyOperation(r.operation,r.payload),i.resolveTelemetryFailure(r.id),unlinkSync(t);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(s,e){try{return this.applyOperation(s,e),!0}catch(t){return this.cacheOperation(s,e,t),false}}applyOperation(s,e){let t=this.getDatabase();switch(s){case "tool_call":this.ensureParentSession(t,e),t.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(t,e),t.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running",requiredMcpTools:e.requiredMcpTools});return;case "execution_complete":t.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":t.ensureSession(e),t.insertRun({id:O(e.id),source:"hook",tool:"session",input:{projectRoot:e.projectRoot},status:"active",startedAt:e.startedAt,tokenUsed:0,metadata:{sessionId:e.id,projectRoot:e.projectRoot}});return;case "complete_event":if(!t.updateToolCallEvent(e.eventId,{output:e.output===void 0?void 0:JSON.stringify(e.output),error:e.error,duration:e.duration}))throw new Error(`Tool call event ${e.eventId} is not available for completion`);return;case "session_end":t.closeSession(e.sessionId,e.finishedAt),t.updateRun(O(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(s,e){let t=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!t)throw new Error("Canonical session ID is required for telemetry");let r=typeof e.projectRoot=="string"&&e.projectRoot.trim()?e.projectRoot:typeof e.input?.projectRoot=="string"&&e.input.projectRoot.trim()?e.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";s.ensureSession({id:t,projectRoot:r,label:r==="unknown"?void 0:r.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(s,e,t){let r=Date.now(),i={id:`failure:${r}:${Math.random().toString(36).slice(2,11)}`,operation:s,payload:e,failure:t instanceof Error?t.message:String(t),timestamp:r};try{let o=this.getDatabase();o.insertTelemetryFailure({id:i.id,operation:s,payload:e,error:i.failure,createdAt:r}),o.trimTelemetryFailures(h),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let o=join(this.cacheDir,`${r}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(o,JSON.stringify(i,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let s=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of s.slice(0,Math.max(0,s.length-h)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},G).unref());}scheduleMetricAggregation(){if(this.metricAggregationScheduled||process.env.DEVFLOW_HOOK_DEGRADED==="1")return;this.metricAggregationScheduled=true,setTimeout(()=>{this.metricAggregationScheduled=false;try{this.getDatabase().aggregatePendingToolMetrics(50)===50&&this.scheduleMetricAggregation();}catch{}},25).unref();}postHttp(s,e){W(`${this.apiUrl}${s}`,JSON.stringify(e),this.apiKey);}truncateInput(s){let e=JSON.stringify(s);return e===void 0||e.length<=M?s:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,M)}...`}}};function P(n){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(n)}function O(n){return `hook-run:${n}`}function B(n,s){if(!n||typeof n!="object")throw new Error("Invalid telemetry cache envelope");let e=n;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let t=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!t)throw new Error("Unknown legacy telemetry cache operation");let r=e.payload??{},i=Number(r.timestamp??r.startedAt??Date.now());return {id:`legacy-cache:${s}`,operation:t,payload:r,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:i}}async function j(n){let s=!n.memory,e=n.memory??new MemoryGate(n.projectRoot),t=0,r=0;try{await e.forceWarmUp(),n.trigger==="session_end"&&(t=await e.releasePendingDistillLeases(n.sessionId)),r=e.getPendingEventCount();}finally{s&&e.close();}let i={pendingEvents:r,releasedLeases:t,trigger:n.trigger,sessionId:n.sessionId};try{let o=openGlobalDevFlowDatabase();try{let a=Date.now(),c=`distill:${a}:${Math.random().toString(36).slice(2,10)}`;o.recordMemoryDistillCheckpoint({id:c,projectRoot:n.projectRoot,sessionId:n.sessionId,trigger:n.trigger,pendingEvents:r,releasedLeases:t,createdAt:a}),o.insertEvent({kind:"memory_distill_requested",timestamp:a,success:!0,metadata:{...i,projectRoot:n.projectRoot}});}finally{o.close();}}catch{}return i}function re(n){return Buffer.from(n,"utf8").toString("base64url")}var y=class{constructor(s){this.projectRoot=s;this.sessions=new Map;this.sessionsDir=join(E(s),"hook-sessions");}registerSession(s){let e=s.trim();if(!e)throw new Error("session_id_required");let t=this.get(e);if(t)return t.lastActivityAt=Date.now(),this.persist(t),t;let r=Date.now(),i={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:r,lastActivityAt:r};return this.sessions.set(e,i),this.persist(i),i}startExecution(s,e,t){let r=this.registerSession(s);return (!r.executionId||r.skillName!==e)&&(r.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),r.skillName=e,r.requiredMcpTools=[...new Set(t)],r.lastActivityAt=Date.now(),this.persist(r),r}get(s){let e=s.trim();if(!e)return null;let t=this.sessions.get(e);if(t)return t;let r=this.snapshotPath(e);if(!existsSync(r))return null;try{let i=JSON.parse(readFileSync(r,"utf8"));return i.sessionId!==e||i.projectRoot!==this.projectRoot?null:(i.requiredMcpTools=Array.isArray(i.requiredMcpTools)?i.requiredMcpTools:[],this.sessions.set(e,i),i)}catch{return null}}completeExecution(s){let e=this.get(s);if(!e)return null;let t={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),t}removeSession(s){let e=s.trim();e&&(this.sessions.delete(e),rmSync(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(s){return join(this.sessionsDir,`${re(s)}.json`)}persist(s){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(s.sessionId),t=`${e}.${process.pid}.tmp`;writeFileSync(t,JSON.stringify(s),{mode:384}),renameSync(t,e);}};var ae=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",ce=getLocalApiKey();function le(n,s){return new Promise(e=>{let t=JSON.stringify(s),r=new URL(n,ae),i=request({hostname:r.hostname,port:r.port,path:r.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":ce,"Content-Length":Buffer.byteLength(t)},timeout:5e3},()=>e());i.on("error",()=>e()),i.write(t),i.end();})}async function ue(n,s=w(),e=true,t){let r;try{r=JSON.parse(n);}catch{return}let i=r.session_id?.trim();if(!i)return;let o=new y(s),a=o.get(i),c=t?.telemetry??new g,L=!t?.telemetry;try{try{await c.flushAndAggregate();}catch{}let l;try{l=openGlobalDevFlowDatabase();let d=l.listToolCallEventsBySession(i),u=a?.executionId;if(u){let f=d.filter(p=>p.executionId===u),v=f.length,D=f.filter(p=>p.isMcpTool).length,S=v-D,_=f.filter(p=>p.toolType==="subagent").length,b=Date.now(),T=l.getSkillExecution(u),C=T?.startedAt!=null?Math.max(0,b-T.startedAt):0,R=l.getExecutionObligationCompliance(u);try{l.updateSkillExecution(u,{status:"completed",finishedAt:b,totalToolCalls:v,mcpToolCalls:D,directToolCalls:S,subagentCount:_,totalDuration:C,mcpComplianceRate:R.rate}),await le("/api/telemetry/skill-execution/complete",{executionId:u,finishedAt:b,status:"completed",summary:{totalToolCalls:v,mcpToolCalls:D,directToolCalls:S,subagentCount:_,totalDuration:C,mcpComplianceRate:R.rate}});}catch{}}e&&l.deleteHookReceipt(s),l.deleteContextReceipt(s,i);}catch{}finally{l?.close();}try{if(await j({projectRoot:s,sessionId:i,trigger:"session_end",memory:t?.memory}),t?.memory)await t.memory.closeSession(i);else {let d=new(await import('@devflow-tools/memory-engine')).MemoryGate(s);try{await d.closeSession(i);}finally{d.close();}}}catch{}try{await c.endSession(i),await c.flushAndAggregate();}catch{}}finally{L&&c.close(),o.removeSession(i);}}if(process.argv[1]?.endsWith("session-end")||process.argv[1]?.endsWith("session-end.js")){let n="";process.stdin.setEncoding("utf8"),process.stdin.on("data",s=>{n+=s;}),process.stdin.on("end",async()=>{await ue(n.trim()||process.argv[2]||""),process.exit(0);}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),1e4).unref();}
|
|
2
|
-
export{
|
|
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,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
|
|
2
|
-
export{
|
|
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,3 +1,5 @@
|
|
|
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
|
|
2
|
-
`,{mode:384});}var w="WARNING: DevFlow is in degraded mode (daemon unreachable): 4-Gate enforcement and memory prefetch cache are unavailable. Run `devflow doctor` for details.";function b(){return process.env.DEVFLOW_HOOK_DEGRADED==="1"}function W(s){return s??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function f(s){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(W(s))}function Q(s){return Buffer.from(s,"utf8").toString("base64url")}var d=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(f(t),"hook-sessions");}registerSession(t){let e=t.trim();if(!e)throw new Error("session_id_required");let r=this.get(e);if(r)return r.lastActivityAt=Date.now(),this.persist(r),r;let n=Date.now(),o={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:n,lastActivityAt:n};return this.sessions.set(e,o),this.persist(o),o}startExecution(t,e,r){let n=this.registerSession(t);return (!n.executionId||n.skillName!==e)&&(n.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),n.skillName=e,n.requiredMcpTools=[...new Set(r)],n.lastActivityAt=Date.now(),this.persist(n),n}get(t){let e=t.trim();if(!e)return null;let r=this.sessions.get(e);if(r)return r;let n=this.snapshotPath(e);if(!existsSync(n))return null;try{let o=JSON.parse(readFileSync(n,"utf8"));return o.sessionId!==e||o.projectRoot!==this.projectRoot?null:(o.requiredMcpTools=Array.isArray(o.requiredMcpTools)?o.requiredMcpTools:[],this.sessions.set(e,o),o)}catch{return null}}completeExecution(t){let e=this.get(t);if(!e)return null;let r={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),r}removeSession(t){let e=t.trim();e&&(this.sessions.delete(e),rmSync(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(t){return join(this.sessionsDir,`${Q(t)}.json`)}persist(t){mkdirSync$1(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(t.sessionId),r=`${e}.${process.pid}.tmp`;writeFileSync(r,JSON.stringify(t),{mode:384}),renameSync(r,e);}};function ee(s){let t=s.trim().replace(/^\//,"");if(!t.startsWith("devflow:"))return null;let e=t.slice(8).replace(/^devflow-/,"");return /^[a-z0-9][a-z0-9-]*$/i.test(e)?`devflow:${e.toLowerCase()}`:null}function x(s){let t=s.trimStart().match(/^\/(devflow:[a-z0-9][a-z0-9-]*)\b/i);if(!t)return null;let e=ee(t[1]);return e?{rawName:t[1],skillName:e}:null}var se=2,ie=1;function ae(){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 R(s=ae()){let t=[join(s,"dist","command-registry.json"),join(s,"command-registry.json")];for(let e of t)try{if(!existsSync(e))continue;let r=JSON.parse(readFileSync(e,"utf8"));if(r.version!==se&&r.version!==ie||!ce(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 ce(s){return !s||typeof s!="object"||Array.isArray(s)?false:Object.values(s).every(t=>{if(!t||typeof t!="object"||Array.isArray(t))return false;let e=t;return I(e.mcpTools)&&I(e.blockedNative)&&(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 I(s){return Array.isArray(s)&&s.every(t=>typeof t=="string"&&t.length>0)}function E(s){let t=R();return t?t[s]?.mcpTools??[]:[]}var C=1e4,ge=3e4,p=500;function fe(s,t,e){return new Promise(r=>{try{let n=new URL(s),o=request({hostname:n.hostname,port:n.port||80,path:n.pathname+n.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(t)},timeout:5e3},i=>{let a=[];i.on("data",c=>a.push(c)),i.on("end",()=>{let c=Buffer.concat(a).toString();r(i.statusCode!=null&&i.statusCode>=200&&i.statusCode<300?c:null);});});o.on("error",()=>r(null)),o.on("timeout",()=>{o.destroy(),r(null);}),o.write(t),o.end();}catch{r(null);}})}var h=class{constructor(t){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??getLocalApiKey(),this.cacheDir=t?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=t?.legacyCacheDir??(t?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=t?.database??null,this.ownsDatabase=!t?.database;}async sendEvent(t){let e={...t,input:this.truncateInput(t.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),t.eventId):null}async sendExecutionStart(t,e,r,n,o=process.env.CLAUDE_PROJECT_DIR??process.cwd(),i=[]){let a={executionId:t,sessionId:e,skillName:r,startedAt:n,projectRoot:o,requiredMcpTools:i};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(t,e="completed",r=Date.now(),n){let o={executionId:t,status:e,finishedAt:r,metadata:n},i=this.commitOrCache("execution_complete",o);return i&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",o),i}async sendSessionStart(t,e,r){let n={id:t,projectRoot:e,startedAt:r,label:e.split("/").pop()??"unknown"},o=this.commitOrCache("session_start",n);return o&&this.postHttp("/api/telemetry/sessions",n),o}async completeEvent(t){let e=this.commitOrCache("complete_event",t);return e&&(this.postHttp("/api/telemetry/tool-call/output",t),this.scheduleMetricAggregation()),e}async endSession(t,e=Date.now()){let r={sessionId:t,finishedAt:e},n=this.commitOrCache("session_end",r);return n&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),n}async flushCache(){try{let r=this.getDatabase(),n=r.listTelemetryFailures({unresolvedOnly:!0,limit:p}).reverse();for(let o of n)try{this.applyOperation(o.operation,o.payload),r.resolveTelemetryFailure(o.id);}catch{}r.trimTelemetryFailures(p);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(r=>!!r))].filter(r=>existsSync(r)).flatMap(r=>readdirSync(r).filter(n=>n.endsWith(".json")).map(n=>{let o=join(r,n);try{return {cacheFile:o,envelope:ye(JSON.parse(readFileSync(o,"utf8")),n)}}catch{return null}})).filter(r=>r!==null).sort((r,n)=>r.envelope.timestamp-n.envelope.timestamp||M(r.envelope.operation)-M(n.envelope.operation));for(let{cacheFile:r,envelope:n}of e)try{let o=this.getDatabase();o.insertTelemetryFailure({id:n.id,operation:n.operation,payload:n.payload,error:n.failure,createdAt:n.timestamp}),this.applyOperation(n.operation,n.payload),o.resolveTelemetryFailure(n.id),unlinkSync(r);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(t,e){try{return this.applyOperation(t,e),!0}catch(r){return this.cacheOperation(t,e,r),false}}applyOperation(t,e){let r=this.getDatabase();switch(t){case "tool_call":this.ensureParentSession(r,e),r.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(r,e),r.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running",requiredMcpTools:e.requiredMcpTools});return;case "execution_complete":r.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":r.ensureSession(e),r.insertRun({id:N(e.id),source:"hook",tool:"session",input:{projectRoot:e.projectRoot},status:"active",startedAt:e.startedAt,tokenUsed:0,metadata:{sessionId:e.id,projectRoot:e.projectRoot}});return;case "complete_event":if(!r.updateToolCallEvent(e.eventId,{output:e.output===void 0?void 0:JSON.stringify(e.output),error:e.error,duration:e.duration}))throw new Error(`Tool call event ${e.eventId} is not available for completion`);return;case "session_end":r.closeSession(e.sessionId,e.finishedAt),r.updateRun(N(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(t,e){let r=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!r)throw new Error("Canonical session ID is required for telemetry");let n=typeof e.projectRoot=="string"&&e.projectRoot.trim()?e.projectRoot:typeof e.input?.projectRoot=="string"&&e.input.projectRoot.trim()?e.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";t.ensureSession({id:r,projectRoot:n,label:n==="unknown"?void 0:n.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(t,e,r){let n=Date.now(),o={id:`failure:${n}:${Math.random().toString(36).slice(2,11)}`,operation:t,payload:e,failure:r instanceof Error?r.message:String(r),timestamp:n};try{let i=this.getDatabase();i.insertTelemetryFailure({id:o.id,operation:t,payload:e,error:o.failure,createdAt:n}),i.trimTelemetryFailures(p),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync$1(this.cacheDir,{recursive:!0});let i=join(this.cacheDir,`${n}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(i,JSON.stringify(o,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let t=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of t.slice(0,Math.max(0,t.length-p)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},ge).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){fe(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=C?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,C)}...`}}};function M(s){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(s)}function N(s){return `hook-run:${s}`}function ye(s,t){if(!s||typeof s!="object")throw new Error("Invalid telemetry cache envelope");let e=s;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let r=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!r)throw new Error("Unknown legacy telemetry cache operation");let n=e.payload??{},o=Number(n.timestamp??n.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:r,payload:n,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:o}}var m=process.env.CLAUDE_PROJECT_DIR||process.cwd();async function Se(s){let t=b()?{additionalContext:w}:{},e;try{e=JSON.parse(s);}catch{return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t}}}let r=e.prompt||"";if(!r.trim())return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t}};let n=S(r);if(n)try{D(m,{content:n,sessionId:e.session_id,createdAt:Date.now()});}catch{}let o=e.session_id?.trim(),i=x(r);if(o&&i){let c=new d(m),P=c.get(o)?.executionId,l=c.startExecution(o,i.skillName,E(i.skillName));if(l.executionId&&l.executionId!==P){let g=new h;try{await g.sendSessionStart(o,m,l.startedAt),await g.sendExecutionStart(l.executionId,o,i.skillName,l.lastActivityAt,m,l.requiredMcpTools);}finally{g.close();}}}let a=new MemoryGate(m);try{await a.forceWarmUp(),o&&await a.recordUserMessage(r.slice(0,2e3),o,r.slice(0,200));}catch{}finally{a.close();}return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t}}}if(process.argv[1]?.endsWith("user-prompt-submit")||process.argv[1]?.endsWith("user-prompt-submit.js")){let s="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{s+=t;}),process.stdin.on("end",async()=>{let t=await Se(s.trim()||process.argv[2]||"");console.log(JSON.stringify(t)),process.exit(0);}),process.stdin.on("error",()=>{console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}})),process.exit(0);}),setTimeout(()=>{console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}})),process.exit(0);},3e4).unref();}
|
|
3
|
-
|
|
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(`
|
|
3
|
+
`)}:{}}}}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};
|
package/dist/plugin/hooks/stop
CHANGED
|
@@ -5,17 +5,19 @@ set -euo pipefail
|
|
|
5
5
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
6
|
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$SCRIPT_DIR/..}"
|
|
7
7
|
|
|
8
|
-
# 1.
|
|
8
|
+
# 1. Use the daemon as the authoritative Stop handler.
|
|
9
9
|
CLIENT_JS="$PLUGIN_ROOT/dist/hooks/hook-client.js"
|
|
10
10
|
INPUT="$(cat)"
|
|
11
11
|
if [ -f "$CLIENT_JS" ]; then
|
|
12
|
-
node "$CLIENT_JS" stop "$INPUT" </dev/null 2>/dev/null
|
|
12
|
+
if node "$CLIENT_JS" stop "$INPUT" </dev/null 2>/dev/null; then
|
|
13
|
+
exit 0
|
|
14
|
+
fi
|
|
13
15
|
fi
|
|
14
16
|
|
|
15
|
-
# 2. Run
|
|
17
|
+
# 2. Run standalone fallback only when daemon delivery failed.
|
|
16
18
|
STOP_JS="$PLUGIN_ROOT/dist/hooks/stop.js"
|
|
17
19
|
if [ -f "$STOP_JS" ]; then
|
|
18
|
-
printf '%s' "$INPUT" | node "$STOP_JS" 2>/dev/null || true
|
|
20
|
+
printf '%s' "$INPUT" | DEVFLOW_HOOK_DEGRADED=1 node "$STOP_JS" 2>/dev/null || true
|
|
19
21
|
fi
|
|
20
22
|
|
|
21
23
|
# 3. Clean up stale receipt files for this project
|
package/dist/plugin/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devflow-tools/claude-code-plugin",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.10",
|
|
4
4
|
"description": "DevFlow — Developer Intelligence Platform for Claude Code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -8,18 +8,18 @@
|
|
|
8
8
|
"deploy": "npx tsx scripts/inject-gates.ts --deploy"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@devflow-tools/database": "0.16.
|
|
12
|
-
"@devflow-tools/memory-engine": "0.16.
|
|
13
|
-
"@devflow-tools/sdk": "0.16.
|
|
14
|
-
"@devflow-tools/telemetry": "0.16.
|
|
15
|
-
"@devflow-tools/workflow-engine": "0.16.
|
|
11
|
+
"@devflow-tools/database": "0.16.10",
|
|
12
|
+
"@devflow-tools/memory-engine": "0.16.10",
|
|
13
|
+
"@devflow-tools/sdk": "0.16.10",
|
|
14
|
+
"@devflow-tools/telemetry": "0.16.10",
|
|
15
|
+
"@devflow-tools/workflow-engine": "0.16.10"
|
|
16
16
|
},
|
|
17
17
|
"allowScripts": {
|
|
18
18
|
"better-sqlite3@12.11.1": true,
|
|
19
19
|
"sharp@0.32.6": true
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
|
-
"@devflow-tools/mcp-server": "0.16.
|
|
22
|
+
"@devflow-tools/mcp-server": "0.16.10"
|
|
23
23
|
},
|
|
24
24
|
"keywords": [
|
|
25
25
|
"pi-package",
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
import {execFileSync}from'node:child_process';import {createRequire}from'node:module';import {existsSync,readdirSync,readFileSync}from'node:fs';import {homedir}from'node:os';import {join}from'node:path';var
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {execFileSync}from'node:child_process';import {createRequire}from'node:module';import {existsSync,readdirSync,readFileSync}from'node:fs';import {homedir}from'node:os';import {join}from'node:path';var I=6e4,E;function N(){if(E)return E;let e=createRequire(import.meta.url)("node:sqlite");if(typeof e.DatabaseSync!="function")throw new Error("audit-session-data requires Node.js with node:sqlite (Node.js >=22.13.0)");return E=e.DatabaseSync,E}function V(e){let t,n,o=false;for(let i=0;i<e.length;i+=1){let l=e[i];if(l==="--json")o=true;else if(l==="--project")t=e[++i];else if(l==="--session")n=e[++i];else throw new Error(`Unknown argument: ${l}`)}if(!t)throw new Error("--project is required");if(!n)throw new Error("--session is required");return {project:t,session:n,json:o}}function a(e){return typeof e=="string"&&e.length>0?e:null}function j(e){return typeof e=="number"&&Number.isFinite(e)?e:null}function S(e){return e===true||e===1}function c(e,t){return !!e.prepare("SELECT 1 FROM sqlite_master WHERE type IN ('table', 'view') AND name = ?").get(t)}function h(e,t,n){return c(e,t)?e.prepare(`PRAGMA table_info(${t})`).all().some(o=>o.name===n):false}function R(e,t,n=[]){let o=e.prepare(t).get(...n);return Number(o?.count??0)}function T(e){if(typeof e!="string"||e.length===0)return e??null;try{return JSON.parse(e)}catch{return e}}function L(e){let t=T(e);return Array.isArray(t)?t.filter(n=>typeof n=="string"):[]}function w(e){return e===null||typeof e!="object"?JSON.stringify(e)??String(e):Array.isArray(e)?`[${e.map(w).join(",")}]`:`{${Object.entries(e).sort(([t],[n])=>t.localeCompare(n)).map(([t,n])=>`${JSON.stringify(t)}:${w(n)}`).join(",")}}`}function q(e){return w({tool:e.tool,command:e.command,kind:e.kind,input:e.input})}function $(e){let t=new Map;for(let n of e){if(n.toolUseId){let l=`tool_use_id:${n.toolUseId}`,u=t.get(l)??{reason:"canonical_tool_use_id",events:[]};u.events.push(n),t.set(l,u);continue}let o=`payload:${q(n)}`,i=t.get(o)??{reason:"normalized_payload",events:[]};i.events.push(n),t.set(o,i);}return [...t.entries()].map(([n,o])=>{let i=o.events.map(u=>u.timestamp).filter(u=>u!==null),l=i.length>1?Math.max(...i)-Math.min(...i):null;return {key:n,reason:o.reason,count:o.events.length,eventIds:o.events.map(u=>u.id),timeSpanMs:l,tool:o.events[0]?.tool??null}}).filter(n=>n.count>1&&(n.reason==="canonical_tool_use_id"||n.timeSpanMs===null||n.timeSpanMs<=I)).sort((n,o)=>o.count-n.count||n.key.localeCompare(o.key))}function x(e){return existsSync(e)?new(N())(e,{readOnly:true,timeout:1e3}):null}function U(e,t){if(!existsSync(e))return {path:e,exists:false,sessions:[],executions:[],events:[],duplicateCandidates:[],hookFallbacks:[],memoryTurns:[]};let n=null;try{if(n=x(e),!n)return {path:e,exists:!1,sessions:[],executions:[],events:[],duplicateCandidates:[],hookFallbacks:[],memoryTurns:[]};let o=c(n,"sessions")?n.prepare("SELECT * FROM sessions WHERE id = ?").all(t):[],i=h(n,"skill_executions","session_id"),l=c(n,"skill_executions")?n.prepare(i?"SELECT * FROM skill_executions WHERE session_id = ? OR execution_id = ? ORDER BY started_at":"SELECT * FROM skill_executions WHERE execution_id = ? ORDER BY started_at").all(...i?[t,t]:[t]):[],u=l.map(s=>a(s.execution_id)).filter(s=>s!==null),p=h(n,"tool_call_events","tool_use_id"),m=h(n,"tool_call_events","session_id"),y=h(n,"tool_call_events","kind"),g=u.length>0?`execution_id IN (${u.map(()=>"?").join(",")})`:"execution_id = ?",C=u.length>0?u:[t],k=(c(n,"tool_call_events")?n.prepare(`SELECT * FROM tool_call_events WHERE ${m?"session_id = ? OR ":""}${g} ORDER BY timestamp`).all(...m?[t,...C]:C):[]).map(s=>({id:a(s.event_id)??"unknown",sessionId:a(s.session_id),executionId:a(s.execution_id),timestamp:j(s.timestamp),tool:a(s.tool_name),toolType:a(s.tool_type),toolUseId:p?a(s.tool_use_id):null,input:T(s.input),command:null,kind:y?a(s.kind):null,fallback:S(s.mcp_fallback),failed:a(s.error)!==null&&!S(s.blocked),blocked:S(s.blocked),processed:!1})),r=c(n,"devflow_hook_fallbacks")?n.prepare("SELECT * FROM devflow_hook_fallbacks WHERE session_id = ? ORDER BY created_at").all(t):[],f=c(n,"devflow_memory_turns")?n.prepare("SELECT * FROM devflow_memory_turns WHERE session_id = ? ORDER BY created_at").all(t):[];return {path:e,exists:!0,sessions:o,executions:l,events:k,duplicateCandidates:$(k),hookFallbacks:r,memoryTurns:f}}catch(o){return {path:e,exists:true,sessions:[],executions:[],events:[],duplicateCandidates:[],hookFallbacks:[],memoryTurns:[],error:o instanceof Error?o.message:String(o)}}finally{n?.close();}}function W(e,t){let n=join(e,".devflow","memory","memories.db"),o={databasePath:n,session:null,eventCount:0,processedEventCount:0,pendingEventCount:0,eventsByTool:[],duplicateCandidates:[],projectObservationCount:0,quarantinedObservationCount:0,supersededObservationCount:0,sessionObservationCount:0,sessionObservationVectorCount:0,projectObservationVectorCount:0,pendingLeaseCount:0,distillReceiptIds:[],activeSessionCount:0};if(!existsSync(n))return o;let i=null;try{if(i=x(n),!i)return o;let u=(c(i,"session_events")?i.prepare("SELECT * FROM session_events WHERE session_id = ? ORDER BY created_at").all(t):[]).map(r=>({id:a(r.id)??"unknown",sessionId:a(r.session_id),executionId:null,timestamp:j(r.created_at),tool:a(r.tool),toolType:null,toolUseId:null,input:T(r.payload),command:a(r.command),kind:a(r.kind),fallback:!1,failed:!1,blocked:!1,processed:S(r.processed)})),p=c(i,"observations")?i.prepare(`
|
|
2
|
+
SELECT session_id, embedding, quality_status, resolution_state
|
|
3
|
+
FROM observations
|
|
4
|
+
`).all():[],m=c(i,"sessions")?i.prepare("SELECT * FROM sessions WHERE id = ?").get(t)??null:null,y=new Map;for(let r of u){let f=`${r.tool??""}\0${r.kind??""}`,s=y.get(f)??{tool:r.tool,kind:r.kind,count:0};s.count+=1,y.set(f,s);}let g=p.filter(r=>a(r.quality_status)!=="quarantined"&&a(r.resolution_state)!=="superseded"),C=g.filter(r=>r.session_id===t),_=c(i,"distill_receipts")&&c(i,"distill_leases")?i.prepare(`
|
|
5
|
+
SELECT r.receipt_id, l.session_ids
|
|
6
|
+
FROM distill_receipts r
|
|
7
|
+
JOIN distill_leases l ON l.batch_id = r.batch_id
|
|
8
|
+
ORDER BY r.created_at
|
|
9
|
+
`).all().filter(r=>L(r.session_ids).includes(t)).map(r=>a(r.receipt_id)).filter(r=>r!==null):[],k=r=>r.embedding!==null&&r.embedding!==void 0;return {databasePath:n,session:m,eventCount:u.length,processedEventCount:u.filter(r=>r.processed).length,pendingEventCount:u.filter(r=>!r.processed).length,eventsByTool:[...y.values()].sort((r,f)=>f.count-r.count),duplicateCandidates:$(u),projectObservationCount:g.length,quarantinedObservationCount:p.filter(r=>a(r.quality_status)==="quarantined").length,supersededObservationCount:p.filter(r=>a(r.resolution_state)==="superseded").length,sessionObservationCount:C.length,sessionObservationVectorCount:C.filter(k).length,projectObservationVectorCount:g.filter(k).length,pendingLeaseCount:c(i,"distill_leases")?R(i,"SELECT COUNT(*) AS count FROM distill_leases WHERE status = 'pending'"):0,distillReceiptIds:_,activeSessionCount:c(i,"sessions")?R(i,"SELECT COUNT(*) AS count FROM sessions WHERE status = 'active'"):0}}catch(l){return {...o,error:l instanceof Error?l.message:String(l)}}finally{i?.close();}}function B(e){let t=join(e,".devflow","knowledge","knowledge.db"),n={databasePath:t,documentCount:0,chunkCount:0,emptyTitleCount:0,vectorRowCount:0,sourceCounts:[]};if(!existsSync(t))return n;let o=null;try{if(o=x(t),!o)return n;let i=c(o,"knowledge_docs")?R(o,"SELECT COUNT(*) AS count FROM knowledge_docs"):0,l=c(o,"knowledge_chunks")?R(o,"SELECT COUNT(*) AS count FROM knowledge_chunks"):0,u=c(o,"knowledge_docs")?R(o,"SELECT COUNT(*) AS count FROM knowledge_docs WHERE TRIM(COALESCE(title, '')) = ''"):0,p=c(o,"knowledge_docs")?o.prepare("SELECT source, COUNT(*) AS count FROM knowledge_docs GROUP BY source ORDER BY count DESC").all().map(m=>({source:a(m.source),count:Number(m.count??0)})):[];return {databasePath:t,documentCount:i,chunkCount:l,emptyTitleCount:u,vectorRowCount:c(o,"knowledge_vec_rowids")?R(o,"SELECT COUNT(*) AS count FROM knowledge_vec_rowids"):0,sourceCounts:p}}catch(i){return {...n,error:i instanceof Error?i.message:String(i)}}finally{o?.close();}}function H(){let e=join(homedir(),".claude","plugins","cache","devflow-local","devflow");if(!existsSync(e))return null;let t=readdirSync(e,{withFileTypes:true}).filter(n=>n.isDirectory()).map(n=>n.name).sort();return t.length>0?join(e,t[t.length-1]):null}function D(e){if(!e)return null;try{let t=JSON.parse(readFileSync(join(e,".claude-plugin","plugin.json"),"utf8"));return a(t.version)}catch{return null}}function Y(e,t){try{return execFileSync(e,t,{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()||null}catch{return null}}function J(e,t,n){let o=join(e,".devflow","logs","traces");if(!existsSync(o))return null;let i=[];for(let l of readdirSync(o,{withFileTypes:true})){if(!l.isDirectory())continue;for(let p of t){let m=join(o,l.name,`${p}.jsonl`);existsSync(m)&&i.push(m);}let u=join(o,l.name,`${n}.jsonl`);existsSync(u)&&i.push(u);}return i[0]??null}function K(e){if(!e)return null;try{return readFileSync(e,"utf8").split(`
|
|
10
|
+
`).filter(t=>t.trim().length>0).length}catch{return null}}function z(e){try{return execFileSync("git",["-C",e,"status","--short","--branch"],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{return "unavailable"}}function G(e,t){let n=process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),o=[join(n,"devflow.db"),join(e,".devflow","devflow.db")],i=o.filter((s,v)=>o.indexOf(s)===v).map(s=>U(s,t)),l=i.flatMap(s=>s.events),u=i.flatMap(s=>s.hookFallbacks),p=i.flatMap(s=>s.memoryTurns),m=[...new Set(i.flatMap(s=>s.executions).map(s=>a(s.execution_id)).filter(s=>s!==null))],y=new Map;for(let s of l)y.set(s.tool,(y.get(s.tool)??0)+1);let g=H(),C=J(e,m,t),_=W(e,t),k=B(e),r=join(homedir(),".claude","skills","devflow:react","SKILL.md"),f=i.find(s=>s.executions.length>0)??i.find(s=>s.sessions.length>0);return {schemaVersion:1,readOnly:true,generatedAt:new Date().toISOString(),project:{root:e,gitStatus:z(e)},query:{sessionId:t,executionIds:m},runtime:{nodeVersion:process.version,claudeVersion:Y("claude",["--version"]),devflowVersion:D(g),activePluginPath:g,activePluginVersion:D(g),activeReactSkillPath:g?join(g,"skills","react","SKILL.md"):null,activeReactSkillExists:g?existsSync(join(g,"skills","react","SKILL.md")):false,legacyReactSkillPath:r,legacyReactSkillExists:existsSync(r),tracePath:C,traceEventCount:K(C)},telemetry:{databases:i,eventCount:l.length,mcpEventCount:l.filter(s=>s.toolType==="mcp").length,fallbackCount:l.filter(s=>s.fallback).length+u.length,fallbackReasons:[...new Set([...l.filter(s=>s.fallback).map(()=>"direct_tool_during_context"),...u.map(s=>`daemon_${a(s.reason)??"unknown"}`)])],actualFailureCount:l.filter(s=>s.failed).length,missedTools:L(f?.executions[0]?.missed_mcp_tools),memoryReceiptIds:[...new Set(p.map(s=>a(s.receipt_id)).filter(s=>s!==null))],missingMemoryDecisionCount:p.filter(s=>a(s.status)==="pending").length,blockedCount:l.filter(s=>s.blocked).length,duplicateCandidates:$(l),byTool:[...y.entries()].map(([s,v])=>({tool:s,count:v})).sort((s,v)=>v.count-s.count||String(s.tool).localeCompare(String(v.tool)))},memory:_,knowledge:k,lifecycle:{telemetrySession:f?.sessions[0]??null,telemetryExecution:f?.executions[0]??null,memorySession:_.session}}}function Q(e){let t=e.telemetry,n=e.memory,o=e.lifecycle;return [`Session audit: ${e.query.sessionId}`,`Project: ${e.project.root}`,`Git: ${e.project.gitStatus||"(clean)"}`,`Telemetry: ${t.eventCount} events (${t.mcpEventCount} MCP, ${t.fallbackCount} fallback, ${t.actualFailureCount} failed, ${t.blockedCount} blocked)`,`Compliance: missed tools ${t.missedTools.join(", ")||"none"}; fallback reasons ${t.fallbackReasons.join(", ")||"none"}`,`Memory decisions: ${t.memoryReceiptIds.length} receipts, ${t.missingMemoryDecisionCount} pending; distill receipts ${e.memory.distillReceiptIds.length}`,`Memory events: ${n.eventCount} (${n.processedEventCount} processed, ${n.pendingEventCount} pending)`,`Observations: ${n.sessionObservationCount} eligible for session / ${n.projectObservationCount} eligible for project; embedding coverage ${n.sessionObservationVectorCount}/${n.sessionObservationCount} for session, ${n.projectObservationVectorCount}/${n.projectObservationCount} for project`,`Observation exclusions: ${n.quarantinedObservationCount} quarantined, ${n.supersededObservationCount} superseded`,`Duplicates: ${t.duplicateCandidates.length} telemetry candidate groups, ${n.duplicateCandidates.length} Memory candidate groups`,`Lifecycle: telemetry ${a(o.telemetrySession?.status)??"missing"}, execution ${a(o.telemetryExecution?.status)??"missing"}, memory ${a(o.memorySession?.status)??"missing"}`,`Knowledge: ${e.knowledge.documentCount} docs, ${e.knowledge.chunkCount} chunks, ${e.knowledge.vectorRowCount} vectors, ${e.knowledge.emptyTitleCount} empty titles`,`Runtime: Claude ${e.runtime.claudeVersion??"unknown"}, DevFlow ${e.runtime.devflowVersion??"unknown"}, trace events ${e.runtime.traceEventCount??"unknown"}`].join(`
|
|
11
|
+
`)}function X(){try{let e=V(process.argv.slice(2)),t=G(e.project,e.session);process.stdout.write(e.json?`${JSON.stringify(t,null,2)}
|
|
12
|
+
`:`${Q(t)}
|
|
5
13
|
`);}catch(e){process.stderr.write(`${e instanceof Error?e.message:String(e)}
|
|
6
|
-
`),process.exitCode=1;}}
|
|
14
|
+
`),process.exitCode=1;}}X();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devflow-tools/devflow",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "DevFlow - Developer Intelligence Platform (Distribution Package)",
|
|
6
6
|
"bin": {
|
|
@@ -16,17 +16,17 @@
|
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@colbymchenry/codegraph": "^1.1.1",
|
|
19
|
-
"@devflow-tools/adapters": "0.16.
|
|
20
|
-
"@devflow-tools/benchmark": "0.16.
|
|
21
|
-
"@devflow-tools/context-engine": "0.16.
|
|
22
|
-
"@devflow-tools/database": "0.16.
|
|
23
|
-
"@devflow-tools/knowledge-engine": "0.16.
|
|
24
|
-
"@devflow-tools/mcp-server": "0.16.
|
|
25
|
-
"@devflow-tools/memory-engine": "0.16.
|
|
26
|
-
"@devflow-tools/sdk": "0.16.
|
|
27
|
-
"@devflow-tools/server": "0.16.
|
|
28
|
-
"@devflow-tools/telemetry": "0.16.
|
|
29
|
-
"@devflow-tools/workflow-engine": "0.16.
|
|
19
|
+
"@devflow-tools/adapters": "0.16.10",
|
|
20
|
+
"@devflow-tools/benchmark": "0.16.10",
|
|
21
|
+
"@devflow-tools/context-engine": "0.16.10",
|
|
22
|
+
"@devflow-tools/database": "0.16.10",
|
|
23
|
+
"@devflow-tools/knowledge-engine": "0.16.10",
|
|
24
|
+
"@devflow-tools/mcp-server": "0.16.10",
|
|
25
|
+
"@devflow-tools/memory-engine": "0.16.10",
|
|
26
|
+
"@devflow-tools/sdk": "0.16.10",
|
|
27
|
+
"@devflow-tools/server": "0.16.10",
|
|
28
|
+
"@devflow-tools/telemetry": "0.16.10",
|
|
29
|
+
"@devflow-tools/workflow-engine": "0.16.10",
|
|
30
30
|
"@inquirer/prompts": "^7.10.1",
|
|
31
31
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
32
32
|
"@nestjs/common": "^10.4.0",
|