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