@devflow-tools/devflow 0.16.0 → 0.16.2
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/cli.js +78 -49
- package/dist/dashboard/404.html +1 -1
- package/dist/dashboard/_next/static/chunks/{736-db1a009357c222d2.js → 384-484184f146e1b764.js} +1 -1
- package/dist/dashboard/_next/static/chunks/{429-eac85fdc49353ed8.js → 429-3a8e078af4ea04bd.js} +1 -1
- package/dist/dashboard/_next/static/chunks/app/accuracy/{page-ca6a419d8b29eb4f.js → page-3b8b7448c9c7aeee.js} +1 -1
- package/dist/dashboard/_next/static/chunks/app/benchmark/page-d8f3f089f1d15cc7.js +1 -0
- package/dist/dashboard/_next/static/chunks/app/context/{page-1874ae1050524cb6.js → page-480f7324dee9cf74.js} +1 -1
- package/dist/dashboard/_next/static/chunks/app/doctor/{page-d542f2ab8d6a1c59.js → page-c32c81c69968099f.js} +1 -1
- package/dist/dashboard/_next/static/chunks/app/executions/{page-b98ec834dcda8d93.js → page-bee250062acc03a8.js} +1 -1
- package/dist/dashboard/_next/static/chunks/app/graph/{page-f0e7e15288791e9c.js → page-75947209e5b7741c.js} +1 -1
- package/dist/dashboard/_next/static/chunks/app/knowledge/{page-86808abd0d47c026.js → page-61a2d663940fb04b.js} +1 -1
- package/dist/dashboard/_next/static/chunks/app/{layout-4b78776412bd4fee.js → layout-572650e435947896.js} +1 -1
- package/dist/dashboard/_next/static/chunks/app/memory/{page-e169762426ac7a23.js → page-71d7c8926179b065.js} +1 -1
- package/dist/dashboard/_next/static/chunks/app/{page-c3e980fb2621470c.js → page-10dcf844701d7903.js} +1 -1
- package/dist/dashboard/_next/static/chunks/app/replay/{page-a8a465ed885df0cf.js → page-f354eb8ed0bfbb11.js} +1 -1
- package/dist/dashboard/_next/static/chunks/app/runs/[id]/{page-cc1d5182707fca5b.js → page-ffc0fda72263722c.js} +1 -1
- package/dist/dashboard/_next/static/chunks/app/runs/{page-bbf0a58abd70430c.js → page-4da6997bddcfeb45.js} +1 -1
- package/dist/dashboard/_next/static/chunks/app/settings/{page-9602ff45892e7910.js → page-0fa1d3b02152a97c.js} +1 -1
- package/dist/dashboard/_next/static/chunks/app/timeline/{page-a8eb3caf23be8a86.js → page-b676878a19561da3.js} +1 -1
- package/dist/dashboard/_next/static/chunks/app/tool-metrics/{page-1f86f62b187f2b5e.js → page-6452423bc1f543ec.js} +1 -1
- package/dist/dashboard/_next/static/chunks/app/workflows/{page-551939c9e86b26c6.js → page-0ff0044616ac56aa.js} +1 -1
- package/dist/dashboard/_next/static/chunks/main-app-cf693a9b955f073e.js +1 -0
- package/dist/dashboard/accuracy.html +1 -1
- package/dist/dashboard/accuracy.txt +3 -3
- package/dist/dashboard/benchmark.html +1 -1
- package/dist/dashboard/benchmark.txt +3 -3
- package/dist/dashboard/context.html +1 -1
- package/dist/dashboard/context.txt +3 -3
- package/dist/dashboard/doctor.html +1 -1
- package/dist/dashboard/doctor.txt +3 -3
- package/dist/dashboard/executions.html +1 -1
- package/dist/dashboard/executions.txt +3 -3
- package/dist/dashboard/graph.html +1 -1
- package/dist/dashboard/graph.txt +3 -3
- package/dist/dashboard/index.html +1 -1
- package/dist/dashboard/index.txt +3 -3
- package/dist/dashboard/knowledge.html +1 -1
- package/dist/dashboard/knowledge.txt +3 -3
- package/dist/dashboard/memory.html +1 -1
- package/dist/dashboard/memory.txt +3 -3
- package/dist/dashboard/replay.html +1 -1
- package/dist/dashboard/replay.txt +3 -3
- package/dist/dashboard/runs/_.html +1 -1
- package/dist/dashboard/runs/_.txt +3 -3
- package/dist/dashboard/runs.html +1 -1
- package/dist/dashboard/runs.txt +3 -3
- package/dist/dashboard/settings.html +1 -1
- package/dist/dashboard/settings.txt +3 -3
- package/dist/dashboard/timeline.html +1 -1
- package/dist/dashboard/timeline.txt +3 -3
- package/dist/dashboard/tool-metrics.html +1 -1
- package/dist/dashboard/tool-metrics.txt +3 -3
- package/dist/dashboard/workflows.html +1 -1
- package/dist/dashboard/workflows.txt +3 -3
- package/dist/hooks/hook-client.js +5 -5
- package/dist/hooks/hook-daemon.js +11 -11
- package/dist/hooks/post-tool-use-failure.js +2 -2
- package/dist/hooks/post-tool-use.js +3 -3
- package/dist/hooks/pre-compact.js +2 -2
- package/dist/hooks/pre-tool-use.js +2 -2
- package/dist/hooks/session-end.js +2 -2
- package/dist/hooks/stop.js +2 -2
- package/dist/hooks/user-prompt-submit.js +3 -2
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/CLAUDE.md +34 -0
- package/dist/plugin/dist/command-registry.json +297 -0
- package/dist/plugin/dist/hooks/hook-client.js +5 -5
- package/dist/plugin/dist/hooks/hook-daemon.js +11 -11
- package/dist/plugin/dist/hooks/post-tool-use-failure.js +2 -2
- package/dist/plugin/dist/hooks/post-tool-use.js +3 -3
- package/dist/plugin/dist/hooks/pre-compact.js +2 -2
- package/dist/plugin/dist/hooks/pre-tool-use.js +2 -2
- package/dist/plugin/dist/hooks/session-end.js +2 -2
- package/dist/plugin/dist/hooks/stop.js +2 -2
- package/dist/plugin/dist/hooks/user-prompt-submit.js +3 -2
- package/dist/plugin/dist/skills/devflow:animation/SKILL.md +63 -0
- package/dist/plugin/dist/skills/devflow:context/SKILL.md +90 -0
- package/dist/plugin/dist/skills/devflow:css/SKILL.md +63 -0
- package/dist/plugin/dist/skills/devflow:docker/SKILL.md +67 -0
- package/dist/plugin/dist/skills/devflow:electron/SKILL.md +67 -0
- package/dist/plugin/dist/skills/devflow:git/SKILL.md +67 -0
- package/dist/plugin/dist/skills/devflow:graph/SKILL.md +80 -0
- package/dist/plugin/dist/skills/devflow:graphql/SKILL.md +66 -0
- package/dist/plugin/dist/skills/devflow:knowledge/SKILL.md +80 -0
- package/dist/plugin/dist/skills/devflow:memory/SKILL.md +79 -0
- package/dist/plugin/dist/skills/devflow:nest/SKILL.md +68 -0
- package/dist/plugin/dist/skills/devflow:nextjs/SKILL.md +65 -0
- package/dist/plugin/dist/skills/devflow:performance/SKILL.md +67 -0
- package/dist/plugin/dist/skills/devflow:react/SKILL.md +96 -0
- package/dist/plugin/dist/skills/devflow:tailwind/SKILL.md +65 -0
- package/dist/plugin/dist/skills/devflow:taro/SKILL.md +66 -0
- package/dist/plugin/dist/skills/devflow:ui-layout/SKILL.md +63 -0
- package/dist/plugin/dist/skills/devflow:vue/SKILL.md +72 -0
- package/dist/plugin/dist/skills/devflow:workflow/SKILL.md +80 -0
- package/dist/plugin/hooks/constitution.md +20 -0
- package/dist/plugin/hooks/post-tool-use +20 -0
- package/dist/plugin/hooks/post-tool-use-failure +19 -0
- package/dist/plugin/hooks/pre-compact +19 -0
- package/dist/plugin/hooks/pre-tool-use +23 -0
- package/dist/plugin/hooks/session-end +19 -0
- package/dist/plugin/hooks/session-start +249 -0
- package/dist/plugin/hooks/stop +27 -0
- package/dist/plugin/hooks/user-prompt-submit +19 -0
- package/dist/plugin/package.json +43 -0
- package/dist/plugin/skills/{devflow:react → react}/SKILL.md +16 -3
- package/package.json +13 -13
- package/plugin-registry.json +29 -29
- package/dist/dashboard/_next/static/chunks/app/benchmark/page-6c6d0acbcde4eb85.js +0 -1
- package/dist/dashboard/_next/static/chunks/main-app-202a5f91ead17fa7.js +0 -1
- /package/dist/dashboard/_next/static/{n_2t1_63toasXsidAssD9 → ZaapgyqOj6AXHY5zdw6_X}/_buildManifest.js +0 -0
- /package/dist/dashboard/_next/static/{n_2t1_63toasXsidAssD9 → ZaapgyqOj6AXHY5zdw6_X}/_ssgManifest.js +0 -0
- /package/dist/plugin/{hooks.json → hooks/hooks.json} +0 -0
- /package/dist/plugin/skills/{devflow:animation → animation}/SKILL.md +0 -0
- /package/dist/plugin/skills/{devflow:context → context}/SKILL.md +0 -0
- /package/dist/plugin/skills/{devflow:css → css}/SKILL.md +0 -0
- /package/dist/plugin/skills/{devflow:docker → docker}/SKILL.md +0 -0
- /package/dist/plugin/skills/{devflow:doctor → doctor}/SKILL.md +0 -0
- /package/dist/plugin/skills/{devflow:electron → electron}/SKILL.md +0 -0
- /package/dist/plugin/skills/{devflow:git → git}/SKILL.md +0 -0
- /package/dist/plugin/skills/{devflow:graph → graph}/SKILL.md +0 -0
- /package/dist/plugin/skills/{devflow:graphql → graphql}/SKILL.md +0 -0
- /package/dist/plugin/skills/{devflow:knowledge → knowledge}/SKILL.md +0 -0
- /package/dist/plugin/skills/{devflow:memory → memory}/SKILL.md +0 -0
- /package/dist/plugin/skills/{devflow:nest → nest}/SKILL.md +0 -0
- /package/dist/plugin/skills/{devflow:nextjs → nextjs}/SKILL.md +0 -0
- /package/dist/plugin/skills/{devflow:performance → performance}/SKILL.md +0 -0
- /package/dist/plugin/skills/{devflow:start → start}/SKILL.md +0 -0
- /package/dist/plugin/skills/{devflow:tailwind → tailwind}/SKILL.md +0 -0
- /package/dist/plugin/skills/{devflow:taro → taro}/SKILL.md +0 -0
- /package/dist/plugin/skills/{devflow:ui-layout → ui-layout}/SKILL.md +0 -0
- /package/dist/plugin/skills/{devflow:vue → vue}/SKILL.md +0 -0
- /package/dist/plugin/skills/{devflow:workflow → workflow}/SKILL.md +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {existsSync,readdirSync,readFileSync,unlinkSync,mkdirSync,writeFileSync}from'fs';import {join,dirname as dirname$1}from'path';import {createHash}from'crypto';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,renameSync}from'node:fs';import {createHash as createHash$1}from'node:crypto';import {homedir as homedir$1}from'node:os';import {dirname,join as join$1}from'node:path';var $=1e4,be=3e4,b=500;function De(n,e,t){return new Promise(r=>{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":t,"Content-Length":Buffer.byteLength(e)},timeout:5e3},s=>{let c=[];s.on("data",u=>c.push(u)),s.on("end",()=>{let u=Buffer.concat(c).toString();r(s.statusCode!=null&&s.statusCode>=200&&s.statusCode<300?u:null);});});i.on("error",()=>r(null)),i.on("timeout",()=>{i.destroy(),r(null);}),i.write(e),i.end();}catch{r(null);}})}var D=class{constructor(e){this.retryScheduled=false;this.apiUrl=e?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=e?.apiKey??getLocalApiKey(),this.cacheDir=e?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=e?.legacyCacheDir??(e?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=e?.database??null,this.ownsDatabase=!e?.database;}async sendEvent(e){let t={...e,input:this.truncateInput(e.input)};return this.commitOrCache("tool_call",t)?(this.postHttp("/api/telemetry/tool-call",t),e.eventId):null}async sendExecutionStart(e,t,r,o){let i={executionId:e,sessionId:t,skillName:r,startedAt:o};this.commitOrCache("execution_start",i)&&this.postHttp("/api/telemetry/skill-execution/start",i);}async sendSessionStart(e,t,r){let o={id:e,projectRoot:t,startedAt:r,label:t.split("/").pop()??"unknown"},i=this.commitOrCache("session_start",o);return i&&this.postHttp("/api/telemetry/sessions",o),i}async completeEvent(e){let t=this.commitOrCache("complete_event",e);return t&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp("/api/telemetry/tool-call/output",e)),t}async endSession(e,t=Date.now()){let r={sessionId:e,finishedAt:t},o=this.commitOrCache("session_end",r);return o&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(e)}/close`,{finishedAt:t})),o}async flushCache(){try{let r=this.getDatabase(),o=r.listTelemetryFailures({unresolvedOnly:!0,limit:b}).reverse();for(let i of o)try{this.applyOperation(i.operation,i.payload),r.resolveTelemetryFailure(i.id);}catch{}r.trimTelemetryFailures(b);}catch{}let t=[...new Set([this.cacheDir,this.legacyCacheDir].filter(r=>!!r))].filter(r=>existsSync(r)).flatMap(r=>readdirSync(r).filter(o=>o.endsWith(".json")).map(o=>{let i=join(r,o);try{return {cacheFile:i,envelope:ve(JSON.parse(readFileSync(i,"utf8")),o)}}catch{return null}})).filter(r=>r!==null).sort((r,o)=>r.envelope.timestamp-o.envelope.timestamp||L(r.envelope.operation)-L(o.envelope.operation));for(let{cacheFile:r,envelope:o}of t)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(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(e,t){try{return this.applyOperation(e,t),!0}catch(r){return this.cacheOperation(e,t,r),false}}applyOperation(e,t){let r=this.getDatabase();switch(e){case "tool_call":r.insertToolCallEvent(t);return;case "execution_start":r.insertSkillExecution({executionId:t.executionId,sessionId:t.sessionId,skillName:t.skillName,startedAt:t.startedAt,status:"running"});return;case "session_start":r.insertSession(t),r.insertRun({id:U(t.id),source:"hook",tool:"session",input:{projectRoot:t.projectRoot},status:"active",startedAt:t.startedAt,tokenUsed:0,metadata:{sessionId:t.id,projectRoot:t.projectRoot}});return;case "complete_event":if(!r.updateToolCallEvent(t.eventId,{output:t.output===void 0?void 0:JSON.stringify(t.output),error:t.error,duration:t.duration}))throw new Error(`Tool call event ${t.eventId} is not available for completion`);return;case "session_end":r.closeSession(t.sessionId,t.finishedAt),r.updateRun(U(t.sessionId),{status:"completed",finishedAt:t.finishedAt});return}}cacheOperation(e,t,r){let o=Date.now(),i={id:`failure:${o}:${Math.random().toString(36).slice(2,11)}`,operation:e,payload:t,failure:r instanceof Error?r.message:String(r),timestamp:o};try{let s=this.getDatabase();s.insertTelemetryFailure({id:i.id,operation:e,payload:t,error:i.failure,createdAt:o}),s.trimTelemetryFailures(b),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let s=join(this.cacheDir,`${o}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(s,JSON.stringify(i,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let e=readdirSync(this.cacheDir).filter(t=>t.endsWith(".json")).sort();for(let t of e.slice(0,Math.max(0,e.length-b)))unlinkSync(join(this.cacheDir,t));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},be).unref());}postHttp(e,t){De(`${this.apiUrl}${e}`,JSON.stringify(t),this.apiKey);}truncateInput(e){let t=JSON.stringify(e);return t===void 0||t.length<=$?e:{_truncated:true,_original_size:t.length,_preview:`${t.substring(0,$)}...`}}};function L(n){return ["session_start","execution_start","tool_call","complete_event","session_end"].indexOf(n)}function U(n){return `hook-run:${n}`}function ve(n,e){if(!n||typeof n!="object")throw new Error("Invalid telemetry cache envelope");let t=n;if(typeof t.operation=="string"&&t.payload!==void 0)return t;let r=t.type==="tool_call"?"tool_call":t.type==="execution_start"?"execution_start":null;if(!r)throw new Error("Unknown legacy telemetry cache operation");let o=t.payload??{},i=Number(o.timestamp??o.startedAt??Date.now());return {id:`legacy-cache:${e}`,operation:r,payload:o,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:i}}function x(n){return n??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function l(n){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(x(n))}var Ee=1;function Ce(){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 q(n=Ce()){let e=[join(n,"dist","command-registry.json"),join(n,"command-registry.json")];for(let t of e)try{if(!existsSync(t))continue;let r=JSON.parse(readFileSync(t,"utf8"));if(r.version!==Ee||!Oe(r.commands)){console.error(`[devflow] command registry contract mismatch: ${t}`);continue}return r.commands}catch(r){console.error(`[devflow] command registry load failed: ${r.message}`);}return null}function Oe(n){return !n||typeof n!="object"||Array.isArray(n)?false:Object.values(n).every(e=>{if(!e||typeof e!="object"||Array.isArray(e))return false;let t=e;return G(t.mcpTools)&&G(t.blockedNative)})}function G(n){return Array.isArray(n)&&n.every(e=>typeof e=="string"&&e.length>0)}var Ae=1800*1e3,Fe=300*1e3;function B(n){let e=q();return e?e[n]?.mcpTools??[]:[]}var v=class{constructor(e){this.stateDir=l(e);}detect(){let e=join(this.stateDir,"current-skill.json");if(!existsSync(e))return null;try{let t=readFileSync(e,"utf8"),r=JSON.parse(t);return !r.registeredAt||Date.now()-r.registeredAt>Ae?(this.endExecution(),null):(Date.now()-r.registeredAt>Fe&&(r.registeredAt=Date.now(),writeFileSync(e,JSON.stringify(r))),r)}catch{return null}}startExecution(e,t){existsSync(this.stateDir)||mkdirSync(this.stateDir,{recursive:true});let r=this.getCurrentExecutionId();if(r)return r;let o=`exec_${Date.now()}_${Math.random().toString(36).slice(2,11)}`;return writeFileSync(join(this.stateDir,"current-execution-id"),o),writeFileSync(join(this.stateDir,"current-skill.json"),JSON.stringify({name:e,required_mcp_tools:t?.required_mcp_tools??[],registeredAt:Date.now()})),o}getCurrentExecutionId(){let e=join(this.stateDir,"current-execution-id");if(!existsSync(e))return null;try{return readFileSync(e,"utf8").trim()}catch{return null}}endExecution(){let e=join(this.stateDir,"current-skill.json"),t=join(this.stateDir,"current-execution-id");try{existsSync(e)&&unlinkSync(e);}catch{}try{existsSync(t)&&unlinkSync(t);}catch{}}};function h(n){if(!n||typeof n!="object"||Array.isArray(n))return {};let e=n,t={};for(let r of ["lastMcpCall","bypassCount"])if(r in e){let o=e[r];if(o==null)continue;t[r]=typeof o=="number"&&Number.isFinite(o)&&o>=0?o:0;}return t}function je(n,e){return n.lastMcpCall===e.lastMcpCall&&n.bypassCount===e.bypassCount}function Me(n){try{return existsSync(n)?h(JSON.parse(readFileSync(n,"utf-8"))):{}}catch{return {}}}function X(n,e){let t=e instanceof Error?e.message:String(e);console.error(`[devflow] Receipt ${n} skipped: ${t}`);}function K(n){let e=l(n);for(let t of ["receipt.json","receipt-lock.sqlite","receipt-lock.sqlite-shm","receipt-lock.sqlite-wal"])try{existsSync(join(e,t))&&unlinkSync(join(e,t));}catch{}}function Y(n,e){if(n.getHookReceipt(e)){K(e);return}let t=join(l(e),"receipt.json");if(!existsSync(t))return;let r=Me(t);n.updateHookReceipt(e,()=>r),K(e);}function Z(n,e,t){let r;try{r=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),Y(r,n);let o={},i=r.updateHookReceipt(n,c=>(o=h(c),h(e(o)))),s=h(i);return {receipt:s,applied:!0,changed:!je(o,s)}}catch(o){return X(t,o),{receipt:{},applied:false,changed:false}}finally{try{r?.close();}catch{}}}function O(n){let e;try{return e=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),Y(e,n),h(e.getHookReceipt(n))}catch(t){return X("update",t),{}}finally{try{e?.close();}catch{}}}function Ne(n,e){return Z(n,()=>e,"write").applied}function Pe(n,e){return Z(n,e,"update")}var He={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",Bash:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function $e(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 w=class{constructor(e){this.projectRoot=e;}getPhase(){let t=O(this.projectRoot).lastMcpCall??0;if(Math.floor(Date.now()/1e3)-t<30)return "context_ready";let o=join(l(this.projectRoot),"current-skill.json");if(existsSync(o))try{let c=JSON.parse(readFileSync(o,"utf-8")).registeredAt??0;if(Date.now()-c<300*1e3)return "context_gathering"}catch{}return "idle"}evaluate(e,t){let r=this.getPhase();if(t||e==="Skill")return {permissionDecision:"allow"};if(r==="context_gathering")return {permissionDecision:"deny",reason:"DevFlow skill requires MCP context first. The context is being gathered automatically \u2014 retry this tool after the MCP call completes."};if(r==="context_ready")return {permissionDecision:"allow"};let o=He[e];if(!o)return {permissionDecision:"allow"};let i=Pe(this.projectRoot,u=>({...u,bypassCount:(u.bypassCount??0)+1}));if(!i.applied)return {permissionDecision:"allow"};let s=i.receipt.bypassCount??0,c=$e(e);return s>=c?{permissionDecision:"allow",additionalContext:`\u5DF2 ${s} \u6B21\u76F4\u63A5\u4F7F\u7528 ${e}\uFF0C\u5EFA\u8BAE\u7528 mcp__devflow__${o} \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`}:{permissionDecision:"allow"}}recordMcpCall(){Ne(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}};var Ve=10*1024*1024,ze=5;function Xe(n){return createHash$1("sha256").update(n).digest("hex").slice(0,16)}function te(n){return join$1(homedir$1(),".devflow","logs","hook-debug",`${Xe(n)}.jsonl`)}function ne(n,e,t={}){let r=t.maxBytes??Ve,o=t.maxFiles??ze;mkdirSync$1(dirname(n),{recursive:true,mode:448});let i=Buffer.byteLength(e),s=existsSync$1(n)?statSync(n).size:0;s>0&&s+i>r&&Ye(n,o),appendFileSync(n,e,{mode:384}),chmodSync(n,384);}function Ye(n,e){if(e<=1){rmSync(n,{force:true});return}rmSync(`${n}.${e-1}`,{force:true});for(let t=e-2;t>=1;t-=1){let r=`${n}.${t}`;existsSync$1(r)&&renameSync(r,`${n}.${t+1}`);}existsSync$1(n)&&renameSync(n,`${n}.1`);}var re="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 y(){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}}function ie(n){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:n}}}function le(n){let e=l(n);return existsSync(e)||mkdirSync(e,{recursive:true}),e}function ue(n){let e=createHash("sha256").update(n).digest("hex").slice(0,12);return join(l(n),`session-id-${e}`)}function et(n){try{let e=ue(n);if(existsSync(e))return readFileSync(e,"utf-8").trim()}catch{}return null}function tt(n,e){le(e),writeFileSync(ue(e),n);}function nt(n,e,t,r){let o=join(le(n),"event-map.json"),i=[];try{existsSync(o)&&(i=JSON.parse(readFileSync(o,"utf-8")));}catch{}i.push({toolName:e,eventId:t,timestamp:r}),writeFileSync(o,JSON.stringify(i));}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 ot(n){if(oe())return ie(re);try{if(!n||n.trim()==="")return y();let e=JSON.parse(n),t=Date.now(),r=x(),o=r.split("/").pop()||"unknown",i=new D,s=et(r);s||(s=`session:${o}:${Date.now().toString(36)}`,tt(s,r),i.sendSessionStart(s,r,Date.now()).catch(()=>{}));let c=new v(r);if(e.tool_name==="Skill"){let a=e.tool_input?.skill;if(a&&a.startsWith("devflow:")){let R=c.startExecution(a,{required_mcp_tools:B(a)});i.sendExecutionStart(R,s,a,Date.now()).catch(()=>{});}}let u=c.detect(),p=e.tool_name.startsWith("mcp__"),pe=p?e.tool_name.replace(/^mcp__[^_]+__/,""):void 0,F=new w(r),I=F.getPhase(),d,_=F.evaluate(e.tool_name,p);_.permissionDecision==="deny"?d={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:_.reason??"MCP context required"}}:_.additionalContext?d=ie(_.additionalContext):d=y();let de=c.getCurrentExecutionId()??s,j={eventId:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`,executionId:de,sessionId:s,timestamp:Date.now(),toolName:e.tool_name,toolType:p?"mcp":e.tool_name==="Agent"?"subagent":"direct",isMcpTool:p,mcpToolName:pe,mcpEnforced:p&&I==="context_gathering",mcpFallback:!p&&I==="context_gathering",kind:rt(e.tool_name),input:e.tool_input,duration:0,blocked:d.hookSpecificOutput.permissionDecision==="deny"};i.sendEvent(j).then(a=>{a&&nt(r,e.tool_name,a,j.timestamp);}).catch(()=>{});try{let a=O(r),R={ts:t,tool:e.tool_name,skill:u?.name??"(none)",requiredTools:requiredMcpTools,isMcp:p,enforce:shouldSkillEnforce,stateless:!shouldSkillEnforce&&!p&&e.tool_name!=="Skill",bypassCount:a.bypassCount??0,decision:d.hookSpecificOutput.permissionDecision,env_CLAUDE_PLUGIN_ROOT:process.env.CLAUDE_PLUGIN_ROOT??"(unset)",cwd:process.cwd()};ne(te(r),`${JSON.stringify(R)}
|
|
2
|
-
`);}catch{}return
|
|
1
|
+
import {existsSync,readdirSync,readFileSync,unlinkSync,mkdirSync,writeFileSync,rmSync,renameSync}from'fs';import {join,dirname as dirname$1}from'path';import {request}from'http';import {homedir}from'os';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {fileURLToPath}from'url';import {mkdirSync as mkdirSync$1,existsSync as existsSync$1,statSync,appendFileSync,chmodSync,rmSync as rmSync$1,renameSync as renameSync$1}from'node:fs';import {createHash as createHash$1}from'node:crypto';import {homedir as homedir$1}from'node:os';import {dirname,join as join$1}from'node:path';import {randomUUID,createHash}from'crypto';var U=1e4,he=3e4,D=500;function ye(n,t,e){return new Promise(o=>{try{let r=new URL(n),i=request({hostname:r.hostname,port:r.port||80,path:r.pathname+r.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(t)},timeout:5e3},s=>{let c=[];s.on("data",l=>c.push(l)),s.on("end",()=>{let l=Buffer.concat(c).toString();o(s.statusCode!=null&&s.statusCode>=200&&s.statusCode<300?l:null);});});i.on("error",()=>o(null)),i.on("timeout",()=>{i.destroy(),o(null);}),i.write(t),i.end();}catch{o(null);}})}var w=class{constructor(t){this.retryScheduled=false;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??getLocalApiKey(),this.cacheDir=t?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=t?.legacyCacheDir??(t?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=t?.database??null,this.ownsDatabase=!t?.database;}async sendEvent(t){let e={...t,input:this.truncateInput(t.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),t.eventId):null}async sendExecutionStart(t,e,o,r,i=process.env.CLAUDE_PROJECT_DIR??process.cwd()){let s={executionId:t,sessionId:e,skillName:o,startedAt:r,projectRoot:i};this.commitOrCache("execution_start",s)&&this.postHttp("/api/telemetry/skill-execution/start",s);}async sendExecutionComplete(t,e="completed",o=Date.now(),r){let i={executionId:t,status:e,finishedAt:o,metadata:r},s=this.commitOrCache("execution_complete",i);return s&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",i),s}async sendSessionStart(t,e,o){let r={id:t,projectRoot:e,startedAt:o,label:e.split("/").pop()??"unknown"},i=this.commitOrCache("session_start",r);return i&&this.postHttp("/api/telemetry/sessions",r),i}async completeEvent(t){let e=this.commitOrCache("complete_event",t);return e&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp("/api/telemetry/tool-call/output",t)),e}async endSession(t,e=Date.now()){let o={sessionId:t,finishedAt:e},r=this.commitOrCache("session_end",o);return r&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),r}async flushCache(){try{let o=this.getDatabase(),r=o.listTelemetryFailures({unresolvedOnly:!0,limit:D}).reverse();for(let i of r)try{this.applyOperation(i.operation,i.payload),o.resolveTelemetryFailure(i.id);}catch{}o.trimTelemetryFailures(D);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(o=>!!o))].filter(o=>existsSync(o)).flatMap(o=>readdirSync(o).filter(r=>r.endsWith(".json")).map(r=>{let i=join(o,r);try{return {cacheFile:i,envelope:_e(JSON.parse(readFileSync(i,"utf8")),r)}}catch{return null}})).filter(o=>o!==null).sort((o,r)=>o.envelope.timestamp-r.envelope.timestamp||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"});return;case "execution_complete":o.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":o.ensureSession(e),o.insertRun({id:L(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(L(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(D),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-D)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},he).unref());}postHttp(t,e){ye(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=U?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,U)}...`}}};function H(n){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(n)}function L(n){return `hook-run:${n}`}function _e(n,t){if(!n||typeof n!="object")throw new Error("Invalid telemetry cache envelope");let e=n;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let o=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!o)throw new Error("Unknown legacy telemetry cache operation");let r=e.payload??{},i=Number(r.timestamp??r.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:o,payload:r,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:i}}function S(n){return n??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function p(n){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(S(n))}var Se=1;function xe(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join(dirname$1(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function G(n=xe()){let t=[join(n,"dist","command-registry.json"),join(n,"command-registry.json")];for(let e of t)try{if(!existsSync(e))continue;let o=JSON.parse(readFileSync(e,"utf8"));if(o.version!==Se||!ke(o.commands)){console.error(`[devflow] command registry contract mismatch: ${e}`);continue}return o.commands}catch(o){console.error(`[devflow] command registry load failed: ${o.message}`);}return null}function ke(n){return !n||typeof n!="object"||Array.isArray(n)?false:Object.values(n).every(t=>{if(!t||typeof t!="object"||Array.isArray(t))return false;let e=t;return q(e.mcpTools)&&q(e.blockedNative)})}function q(n){return Array.isArray(n)&&n.every(t=>typeof t=="string"&&t.length>0)}function W(n){let t=G();return t?t[n]?.mcpTools??[]:[]}function f(n){if(!n||typeof n!="object"||Array.isArray(n))return {};let t=n,e={};for(let o of ["lastMcpCall","bypassCount"])if(o in t){let r=t[o];if(r==null)continue;e[o]=typeof r=="number"&&Number.isFinite(r)&&r>=0?r:0;}return e}function Te(n,t){return n.lastMcpCall===t.lastMcpCall&&n.bypassCount===t.bypassCount}function Oe(n){try{return existsSync(n)?f(JSON.parse(readFileSync(n,"utf-8"))):{}}catch{return {}}}function K(n,t){let e=t instanceof Error?t.message:String(t);console.error(`[devflow] Receipt ${n} skipped: ${e}`);}function J(n){let t=p(n);for(let e of ["receipt.json","receipt-lock.sqlite","receipt-lock.sqlite-shm","receipt-lock.sqlite-wal"])try{existsSync(join(t,e))&&unlinkSync(join(t,e));}catch{}}function z(n,t){if(n.getHookReceipt(t)){J(t);return}let e=join(p(t),"receipt.json");if(!existsSync(e))return;let o=Oe(e);n.updateHookReceipt(t,()=>o),J(t);}function V(n,t,e){let o;try{o=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),z(o,n);let r={},i=o.updateHookReceipt(n,c=>(r=f(c),f(t(r)))),s=f(i);return {receipt:s,applied:!0,changed:!Te(r,s)}}catch(r){return K(e,r),{receipt:{},applied:false,changed:false}}finally{try{o?.close();}catch{}}}function X(n){let t;try{return t=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),z(t,n),f(t.getHookReceipt(n))}catch(e){return K("update",e),{}}finally{try{t?.close();}catch{}}}function Ie(n,t){return V(n,()=>t,"write").applied}function Ae(n,t){return V(n,t,"update")}var B={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",Bash:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function je(n){switch(n){case "WebSearch":case "WebFetch":return 1;case "Agent":return 3;case "Grep":case "Glob":return 3;case "Bash":return 5;default:return 2}}var b=class{constructor(t,e,o){this.projectRoot=t;this.sessionId=e;this.executionId=o;}getPhase(){if(!this.sessionId||!this.executionId)return "idle";let t;try{t=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250});let e=t.getContextReceipt(this.projectRoot,this.sessionId,this.executionId);if(e&&e.expiresAt>Date.now())return "context_ready";e&&t.deleteContextReceipt(this.projectRoot,this.sessionId,this.executionId);}catch{}finally{try{t?.close();}catch{}}return "context_gathering"}evaluate(t,e){let o=this.getPhase();if(e||t==="Skill")return {permissionDecision:"allow"};if(o==="context_gathering"&&B[t])return {permissionDecision:"deny",reason:`DevFlow context required. Call mcp__devflow__get_project_context, then retry ${t}.`};if(o==="context_ready")return {permissionDecision:"allow"};let r=B[t];if(!r)return {permissionDecision:"allow"};let i=Ae(this.projectRoot,l=>({...l,bypassCount:(l.bypassCount??0)+1}));if(!i.applied)return {permissionDecision:"allow"};let s=i.receipt.bypassCount??0,c=je(t);return s>=c?{permissionDecision:"allow",additionalContext:`\u5DF2 ${s} \u6B21\u76F4\u63A5\u4F7F\u7528 ${t}\uFF0C\u5EFA\u8BAE\u7528 mcp__devflow__${r} \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`}:{permissionDecision:"allow"}}recordMcpCall(){Ie(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}};var qe=10*1024*1024,Ge=5;function We(n){return createHash$1("sha256").update(n).digest("hex").slice(0,16)}function Q(n){return join$1(homedir$1(),".devflow","logs","hook-debug",`${We(n)}.jsonl`)}function ee(n,t,e={}){let o=e.maxBytes??qe,r=e.maxFiles??Ge;mkdirSync$1(dirname(n),{recursive:true,mode:448});let i=Buffer.byteLength(t),s=existsSync$1(n)?statSync(n).size:0;s>0&&s+i>o&&Je(n,r),appendFileSync(n,t,{mode:384}),chmodSync(n,384);}function Je(n,t){if(t<=1){rmSync$1(n,{force:true});return}rmSync$1(`${n}.${t-1}`,{force:true});for(let e=t-2;e>=1;e-=1){let o=`${n}.${e}`;existsSync$1(o)&&renameSync$1(o,`${n}.${e+1}`);}existsSync$1(n)&&renameSync$1(n,`${n}.1`);}var O="WARNING: DevFlow is in degraded mode (daemon unreachable): 4-Gate enforcement and memory prefetch cache are unavailable. Run `devflow doctor` for details.";function te(){return process.env.DEVFLOW_HOOK_DEGRADED==="1"}function Qe(n){return Buffer.from(n,"utf8").toString("base64url")}var R=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(p(t),"hook-sessions");}registerSession(t){let e=t.trim();if(!e)throw new Error("session_id_required");let o=this.get(e);if(o)return o.lastActivityAt=Date.now(),this.persist(o),o;let r=Date.now(),i={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:r,lastActivityAt:r};return this.sessions.set(e,i),this.persist(i),i}startExecution(t,e,o){let r=this.registerSession(t);return (!r.executionId||r.skillName!==e)&&(r.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),r.skillName=e,r.requiredMcpTools=[...new Set(o)],r.lastActivityAt=Date.now(),this.persist(r),r}get(t){let e=t.trim();if(!e)return null;let o=this.sessions.get(e);if(o)return o;let r=this.snapshotPath(e);if(!existsSync(r))return null;try{let i=JSON.parse(readFileSync(r,"utf8"));return i.sessionId!==e||i.projectRoot!==this.projectRoot?null:(i.requiredMcpTools=Array.isArray(i.requiredMcpTools)?i.requiredMcpTools:[],this.sessions.set(e,i),i)}catch{return null}}completeExecution(t){let e=this.get(t);if(!e)return null;let o={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),o}removeSession(t){let e=t.trim();e&&(this.sessions.delete(e),rmSync(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(t){return join(this.sessionsDir,`${Qe(t)}.json`)}persist(t){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(t.sessionId),o=`${e}.${process.pid}.tmp`;writeFileSync(o,JSON.stringify(t),{mode:384}),renameSync(o,e);}};function oe(n){let t=n.trim().replace(/^\//,"");if(!t.startsWith("devflow:"))return null;let e=t.slice(8).replace(/^devflow-/,"");return /^[a-z0-9][a-z0-9-]*$/i.test(e)?`devflow:${e.toLowerCase()}`:null}function re(n,t){return t?`evt_tool_${createHash("sha256").update(`${n}\0${t}`).digest("hex").slice(0,24)}`:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`}function h(){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}}function ie(n){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:n}}}function it(n){let t=p(n);return existsSync(t)||mkdirSync(t,{recursive:true}),t}function st(n,t,e,o,r,i){let s=join(it(n),"event-map.json"),c=[];try{existsSync(s)&&(c=JSON.parse(readFileSync(s,"utf-8")));}catch{}c.push({sessionId:t,toolName:e,toolUseId:o,eventId:r,timestamp:i}),writeFileSync(s,JSON.stringify(c));}function ct(n){switch(n){case "Read":case "Glob":return "file_read";case "Write":case "Edit":case "NotebookEdit":return "file_write";case "Bash":return "bash_command";case "Agent":return "subagent";case "Skill":return "skill_invoke";default:return "tool_use"}}async function at(n){let t=te();try{if(!n||n.trim()==="")return h();let e=JSON.parse(n),o=Date.now(),r=S(),i=new w;e.tool_name.startsWith("mcp__devflow__")&&(typeof e.tool_input.projectRoot!="string"||!e.tool_input.projectRoot.trim())&&(e.tool_input.projectRoot=r);let s=e.session_id?.trim();if(!s)return ie(O);let c=new R(r),l=c.get(s)!==null;if(c.registerSession(s),l||await i.sendSessionStart(s,r,Date.now()),e.tool_name==="Skill"){let _=e.tool_input?.skill,m=_?oe(_):null;if(m){let le=c.get(s)?.executionId,v=c.startExecution(s,m,W(m));v.executionId&&v.executionId!==le&&await i.sendExecutionStart(v.executionId,s,m,v.lastActivityAt,r);}}let d=c.get(s);e.tool_name.startsWith("mcp__devflow__")&&(e.tool_input._devflow_session_id=s,e.tool_input._devflow_execution_id=d?.executionId??s,e.tool_use_id&&(e.tool_input._devflow_tool_use_id=e.tool_use_id));let u=e.tool_name.startsWith("mcp__"),ce=u?e.tool_name.replace(/^mcp__[^_]+__/,""):void 0,I=new b(r,s,d?.executionId),A=I.getPhase(),a,y=I.evaluate(e.tool_name,u);y.permissionDecision==="deny"?a={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:y.reason??"MCP context required"}}:y.additionalContext?a=ie(y.additionalContext):a=h(),e.tool_name.startsWith("mcp__devflow__")&&(a.hookSpecificOutput.updatedInput=e.tool_input),t&&a.hookSpecificOutput.permissionDecision==="allow"&&(a.hookSpecificOutput.additionalContext=O);let ae=d?.executionId??s,j={eventId:re(s,e.tool_use_id),executionId:ae,sessionId:s,projectRoot:r,toolUseId:e.tool_use_id,timestamp:Date.now(),toolName:e.tool_name,toolType:u?"mcp":e.tool_name==="Agent"?"subagent":"direct",isMcpTool:u,mcpToolName:ce,mcpEnforced:u&&A==="context_gathering",mcpFallback:!u&&A==="context_gathering",kind:ct(e.tool_name),input:e.tool_input,duration:0,blocked:a.hookSpecificOutput.permissionDecision==="deny"},F=await i.sendEvent(j);F&&st(r,s,e.tool_name,e.tool_use_id,F,j.timestamp);try{let _=X(r),m={ts:o,tool:e.tool_name,sessionId:s,skill:d?.skillName??"(none)",requiredTools:d?.requiredMcpTools??[],isMcp:u,enforce:d?.executionId!==void 0,stateless:d?.executionId===void 0&&!u&&e.tool_name!=="Skill",bypassCount:_.bypassCount??0,decision:a.hookSpecificOutput.permissionDecision,env_CLAUDE_PLUGIN_ROOT:process.env.CLAUDE_PLUGIN_ROOT??"(unset)",cwd:process.cwd()};ee(Q(r),`${JSON.stringify(m)}
|
|
2
|
+
`);}catch{}return a}catch{return h()}}if(process.argv[1]?.endsWith("pre-tool-use")||process.argv[1]?.endsWith("pre-tool-use.js")){let n="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{n+=t;}),process.stdin.on("end",async()=>{let t=await at(n);console.log(JSON.stringify(t));}),process.stdin.on("error",()=>{console.log(JSON.stringify(h())),process.exit(0);}),setTimeout(()=>{console.log(JSON.stringify(h())),process.exit(0);},4e3).unref();}export{at as handlePreToolUse};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {request}from'http';import {existsSync,readFileSync,readdirSync,unlinkSync,mkdirSync,writeFileSync}from'fs';import {join}from'path';import {createHash}from'crypto';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {homedir}from'os';import {MemoryGate}from'@devflow-tools/memory-engine';function b(i){return i??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function I(i){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(b(i))}var O=1e4,B=3e4,g=500;function G(i,t,e){return new Promise(n=>{try{let r=new URL(i),o=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 l=[];s.on("data",a=>l.push(a)),s.on("end",()=>{let a=Buffer.concat(l).toString();n(s.statusCode!=null&&s.statusCode>=200&&s.statusCode<300?a:null);});});o.on("error",()=>n(null)),o.on("timeout",()=>{o.destroy(),n(null);}),o.write(t),o.end();}catch{n(null);}})}var y=class{constructor(t){this.retryScheduled=false;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??getLocalApiKey(),this.cacheDir=t?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=t?.legacyCacheDir??(t?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=t?.database??null,this.ownsDatabase=!t?.database;}async sendEvent(t){let e={...t,input:this.truncateInput(t.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),t.eventId):null}async sendExecutionStart(t,e,n,r){let o={executionId:t,sessionId:e,skillName:n,startedAt:r};this.commitOrCache("execution_start",o)&&this.postHttp("/api/telemetry/skill-execution/start",o);}async sendSessionStart(t,e,n){let r={id:t,projectRoot:e,startedAt:n,label:e.split("/").pop()??"unknown"},o=this.commitOrCache("session_start",r);return o&&this.postHttp("/api/telemetry/sessions",r),o}async completeEvent(t){let e=this.commitOrCache("complete_event",t);return e&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp("/api/telemetry/tool-call/output",t)),e}async endSession(t,e=Date.now()){let n={sessionId:t,finishedAt:e},r=this.commitOrCache("session_end",n);return r&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),r}async flushCache(){try{let n=this.getDatabase(),r=n.listTelemetryFailures({unresolvedOnly:!0,limit:g}).reverse();for(let o of r)try{this.applyOperation(o.operation,o.payload),n.resolveTelemetryFailure(o.id);}catch{}n.trimTelemetryFailures(g);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(n=>!!n))].filter(n=>existsSync(n)).flatMap(n=>readdirSync(n).filter(r=>r.endsWith(".json")).map(r=>{let o=join(n,r);try{return {cacheFile:o,envelope:J(JSON.parse(readFileSync(o,"utf8")),r)}}catch{return null}})).filter(n=>n!==null).sort((n,r)=>n.envelope.timestamp-r.envelope.timestamp||P(n.envelope.operation)-P(r.envelope.operation));for(let{cacheFile:n,envelope:r}of e)try{let o=this.getDatabase();o.insertTelemetryFailure({id:r.id,operation:r.operation,payload:r.payload,error:r.failure,createdAt:r.timestamp}),this.applyOperation(r.operation,r.payload),o.resolveTelemetryFailure(r.id),unlinkSync(n);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(t,e){try{return this.applyOperation(t,e),!0}catch(n){return this.cacheOperation(t,e,n),false}}applyOperation(t,e){let n=this.getDatabase();switch(t){case "tool_call":n.insertToolCallEvent(e);return;case "execution_start":n.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running"});return;case "session_start":n.insertSession(e),n.insertRun({id:M(e.id),source:"hook",tool:"session",input:{projectRoot:e.projectRoot},status:"active",startedAt:e.startedAt,tokenUsed:0,metadata:{sessionId:e.id,projectRoot:e.projectRoot}});return;case "complete_event":if(!n.updateToolCallEvent(e.eventId,{output:e.output===void 0?void 0:JSON.stringify(e.output),error:e.error,duration:e.duration}))throw new Error(`Tool call event ${e.eventId} is not available for completion`);return;case "session_end":n.closeSession(e.sessionId,e.finishedAt),n.updateRun(M(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}cacheOperation(t,e,n){let r=Date.now(),o={id:`failure:${r}:${Math.random().toString(36).slice(2,11)}`,operation:t,payload:e,failure:n instanceof Error?n.message:String(n),timestamp:r};try{let s=this.getDatabase();s.insertTelemetryFailure({id:o.id,operation:t,payload:e,error:o.failure,createdAt:r}),s.trimTelemetryFailures(g),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(o,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let t=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of t.slice(0,Math.max(0,t.length-g)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},B).unref());}postHttp(t,e){G(`${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 P(i){return ["session_start","execution_start","tool_call","complete_event","session_end"].indexOf(i)}function M(i){return `hook-run:${i}`}function J(i,t){if(!i||typeof i!="object")throw new Error("Invalid telemetry cache envelope");let e=i;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let n=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!n)throw new Error("Unknown legacy telemetry cache operation");let r=e.payload??{},o=Number(r.timestamp??r.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:n,payload:r,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:o}}async function F(i){let t=!i.memory,e=i.memory??new MemoryGate(i.projectRoot),n=0,r=0;try{await e.forceWarmUp(),i.trigger==="session_end"&&(n=await e.releasePendingDistillLeases(i.sessionId)),r=e.getPendingEventCount();}finally{t&&e.close();}let o={pendingEvents:r,releasedLeases:n,trigger:i.trigger,sessionId:i.sessionId};try{let s=openGlobalDevFlowDatabase();try{let l=Date.now(),a=`distill:${l}:${Math.random().toString(36).slice(2,10)}`;s.recordMemoryDistillCheckpoint({id:a,projectRoot:i.projectRoot,sessionId:i.sessionId,trigger:i.trigger,pendingEvents:r,releasedLeases:n,createdAt:l}),s.insertEvent({kind:"memory_distill_requested",timestamp:l,success:!0,metadata:{...o,projectRoot:i.projectRoot}});}finally{s.close();}}catch{}return o}var ee=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",te=getLocalApiKey();function ne(i,t){return new Promise(e=>{let n=JSON.stringify(t),r=new URL(i,ee),o=request({hostname:r.hostname,port:r.port,path:r.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":te,"Content-Length":Buffer.byteLength(n)},timeout:5e3},()=>e());o.on("error",()=>e()),o.write(n),o.end();})}function _(i){let t=I(i),e=createHash("sha256").update(i).digest("hex").slice(0,12);return [join(t,`session-id-${e}`),join(t,"current-execution-id"),join(t,"current-skill.json"),join(t,"event-map.json")]}function re(i){try{let t=_(i)[0];if(existsSync(t))return readFileSync(t,"utf-8").trim()}catch{}return null}function ie(i){for(let t of _(i))try{existsSync(t)&&unlinkSync(t);}catch{}}async function oe(i,t=b(),e=true,n){let r;try{r=JSON.parse(i);}catch{return}let o=r.session_id||re(t);if(!o)return;let s=n?.telemetry??new y,l=!n?.telemetry;try{try{await s.flushAndAggregate();}catch{}let a;try{a=openGlobalDevFlowDatabase();let L=a.listToolCallEventsBySession(o),C=_(t)[1];if(e&&existsSync(C)){let p="";try{p=readFileSync(C,"utf-8").trim();}catch{}if(p){let h=L.filter(m=>m.executionId===p),c=h.length,d=h.filter(m=>m.isMcpTool).length,T=c-d,w=h.filter(m=>m.toolType==="subagent").length,v=h.map(m=>m.timestamp).filter(Boolean),E=v.length>=2?Math.max(...v)-Math.min(...v):0;try{a.updateSkillExecution(p,{status:"completed",finishedAt:Date.now(),totalToolCalls:c,mcpToolCalls:d,directToolCalls:T,subagentCount:w,totalDuration:E,mcpComplianceRate:c>0?Math.round(d/c*1e4)/100:0}),await ne("/api/telemetry/skill-execution/complete",{executionId:p,finishedAt:Date.now(),status:"completed",summary:{totalToolCalls:c,mcpToolCalls:d,directToolCalls:T,subagentCount:w,totalDuration:E,mcpComplianceRate:c>0?Math.round(d/c*1e4)/100:0}});}catch{}}}e&&a.deleteHookReceipt(t);}catch{}finally{a?.close();}try{await F({projectRoot:t,sessionId:o,trigger:"session_end",memory:n?.memory});}catch{}try{await s.endSession(o),await s.flushAndAggregate();}catch{}}finally{l&&s.close(),e&&ie(t);}}if(process.argv[1]?.endsWith("session-end")||process.argv[1]?.endsWith("session-end.js")){let i="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{i+=t;}),process.stdin.on("end",async()=>{await oe(i.trim()||process.argv[2]||""),process.exit(0);}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),1e4).unref();}
|
|
2
|
-
export{
|
|
1
|
+
import {request}from'http';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {existsSync,readFileSync,rmSync,mkdirSync,writeFileSync,renameSync,readdirSync,unlinkSync}from'fs';import {join}from'path';import {homedir}from'os';import {MemoryGate}from'@devflow-tools/memory-engine';import {randomUUID}from'crypto';function w(o){return o??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function C(o){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(w(o))}var k=1e4,H=3e4,f=500;function K(o,s,e){return new Promise(t=>{try{let n=new URL(o),r=request({hostname:n.hostname,port:n.port||80,path:n.pathname+n.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(s)},timeout:5e3},i=>{let c=[];i.on("data",a=>c.push(a)),i.on("end",()=>{let a=Buffer.concat(c).toString();t(i.statusCode!=null&&i.statusCode>=200&&i.statusCode<300?a:null);});});r.on("error",()=>t(null)),r.on("timeout",()=>{r.destroy(),t(null);}),r.write(s),r.end();}catch{t(null);}})}var v=class{constructor(s){this.retryScheduled=false;this.apiUrl=s?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=s?.apiKey??getLocalApiKey(),this.cacheDir=s?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=s?.legacyCacheDir??(s?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=s?.database??null,this.ownsDatabase=!s?.database;}async sendEvent(s){let e={...s,input:this.truncateInput(s.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),s.eventId):null}async sendExecutionStart(s,e,t,n,r=process.env.CLAUDE_PROJECT_DIR??process.cwd()){let i={executionId:s,sessionId:e,skillName:t,startedAt:n,projectRoot:r};this.commitOrCache("execution_start",i)&&this.postHttp("/api/telemetry/skill-execution/start",i);}async sendExecutionComplete(s,e="completed",t=Date.now(),n){let r={executionId:s,status:e,finishedAt:t,metadata:n},i=this.commitOrCache("execution_complete",r);return i&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",r),i}async sendSessionStart(s,e,t){let n={id:s,projectRoot:e,startedAt:t,label:e.split("/").pop()??"unknown"},r=this.commitOrCache("session_start",n);return r&&this.postHttp("/api/telemetry/sessions",n),r}async completeEvent(s){let e=this.commitOrCache("complete_event",s);return e&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp("/api/telemetry/tool-call/output",s)),e}async endSession(s,e=Date.now()){let t={sessionId:s,finishedAt:e},n=this.commitOrCache("session_end",t);return n&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(s)}/close`,{finishedAt:e})),n}async flushCache(){try{let t=this.getDatabase(),n=t.listTelemetryFailures({unresolvedOnly:!0,limit:f}).reverse();for(let r of n)try{this.applyOperation(r.operation,r.payload),t.resolveTelemetryFailure(r.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(n=>n.endsWith(".json")).map(n=>{let r=join(t,n);try{return {cacheFile:r,envelope:B(JSON.parse(readFileSync(r,"utf8")),n)}}catch{return null}})).filter(t=>t!==null).sort((t,n)=>t.envelope.timestamp-n.envelope.timestamp||A(t.envelope.operation)-A(n.envelope.operation));for(let{cacheFile:t,envelope:n}of e)try{let r=this.getDatabase();r.insertTelemetryFailure({id:n.id,operation:n.operation,payload:n.payload,error:n.failure,createdAt:n.timestamp}),this.applyOperation(n.operation,n.payload),r.resolveTelemetryFailure(n.id),unlinkSync(t);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(s,e){try{return this.applyOperation(s,e),!0}catch(t){return this.cacheOperation(s,e,t),false}}applyOperation(s,e){let t=this.getDatabase();switch(s){case "tool_call":this.ensureParentSession(t,e),t.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(t,e),t.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running"});return;case "execution_complete":t.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":t.ensureSession(e),t.insertRun({id: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(!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(P(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(s,e){let t=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!t)throw new Error("Canonical session ID is required for telemetry");let n=typeof e.projectRoot=="string"&&e.projectRoot.trim()?e.projectRoot:typeof e.input?.projectRoot=="string"&&e.input.projectRoot.trim()?e.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";s.ensureSession({id:t,projectRoot:n,label:n==="unknown"?void 0:n.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(s,e,t){let n=Date.now(),r={id:`failure:${n}:${Math.random().toString(36).slice(2,11)}`,operation:s,payload:e,failure:t instanceof Error?t.message:String(t),timestamp:n};try{let i=this.getDatabase();i.insertTelemetryFailure({id:r.id,operation:s,payload:e,error:r.failure,createdAt:n}),i.trimTelemetryFailures(f),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let i=join(this.cacheDir,`${n}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(i,JSON.stringify(r,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let s=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of s.slice(0,Math.max(0,s.length-f)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},H).unref());}postHttp(s,e){K(`${this.apiUrl}${s}`,JSON.stringify(e),this.apiKey);}truncateInput(s){let e=JSON.stringify(s);return e===void 0||e.length<=k?s:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,k)}...`}}};function A(o){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(o)}function P(o){return `hook-run:${o}`}function B(o,s){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 t=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!t)throw new Error("Unknown legacy telemetry cache operation");let n=e.payload??{},r=Number(n.timestamp??n.startedAt??Date.now());return {id:`legacy-cache:${s}`,operation:t,payload:n,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:r}}async function M(o){let s=!o.memory,e=o.memory??new MemoryGate(o.projectRoot),t=0,n=0;try{await e.forceWarmUp(),o.trigger==="session_end"&&(t=await e.releasePendingDistillLeases(o.sessionId)),n=e.getPendingEventCount();}finally{s&&e.close();}let r={pendingEvents:n,releasedLeases:t,trigger:o.trigger,sessionId:o.sessionId};try{let i=openGlobalDevFlowDatabase();try{let c=Date.now(),a=`distill:${c}:${Math.random().toString(36).slice(2,10)}`;i.recordMemoryDistillCheckpoint({id:a,projectRoot:o.projectRoot,sessionId:o.sessionId,trigger:o.trigger,pendingEvents:n,releasedLeases:t,createdAt:c}),i.insertEvent({kind:"memory_distill_requested",timestamp:c,success:!0,metadata:{...r,projectRoot:o.projectRoot}});}finally{i.close();}}catch{}return r}function te(o){return Buffer.from(o,"utf8").toString("base64url")}var D=class{constructor(s){this.projectRoot=s;this.sessions=new Map;this.sessionsDir=join(C(s),"hook-sessions");}registerSession(s){let e=s.trim();if(!e)throw new Error("session_id_required");let t=this.get(e);if(t)return t.lastActivityAt=Date.now(),this.persist(t),t;let n=Date.now(),r={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:n,lastActivityAt:n};return this.sessions.set(e,r),this.persist(r),r}startExecution(s,e,t){let n=this.registerSession(s);return (!n.executionId||n.skillName!==e)&&(n.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),n.skillName=e,n.requiredMcpTools=[...new Set(t)],n.lastActivityAt=Date.now(),this.persist(n),n}get(s){let e=s.trim();if(!e)return null;let t=this.sessions.get(e);if(t)return t;let n=this.snapshotPath(e);if(!existsSync(n))return null;try{let r=JSON.parse(readFileSync(n,"utf8"));return r.sessionId!==e||r.projectRoot!==this.projectRoot?null:(r.requiredMcpTools=Array.isArray(r.requiredMcpTools)?r.requiredMcpTools:[],this.sessions.set(e,r),r)}catch{return null}}completeExecution(s){let e=this.get(s);if(!e)return null;let t={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),t}removeSession(s){let e=s.trim();e&&(this.sessions.delete(e),rmSync(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(s){return join(this.sessionsDir,`${te(s)}.json`)}persist(s){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(s.sessionId),t=`${e}.${process.pid}.tmp`;writeFileSync(t,JSON.stringify(s),{mode:384}),renameSync(t,e);}};var oe=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",ie=getLocalApiKey();function ae(o,s){return new Promise(e=>{let t=JSON.stringify(s),n=new URL(o,oe),r=request({hostname:n.hostname,port:n.port,path:n.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":ie,"Content-Length":Buffer.byteLength(t)},timeout:5e3},()=>e());r.on("error",()=>e()),r.write(t),r.end();})}async function ce(o,s=w(),e=true,t){let n;try{n=JSON.parse(o);}catch{return}let r=n.session_id?.trim();if(!r)return;let i=new D(s),c=i.get(r),a=t?.telemetry??new v,j=!t?.telemetry;try{try{await a.flushAndAggregate();}catch{}let u;try{u=openGlobalDevFlowDatabase();let h=u.listToolCallEventsBySession(r),g=c?.executionId;if(g){let y=h.filter(m=>m.executionId===g),l=y.length,d=y.filter(m=>m.isMcpTool).length,_=l-d,S=y.filter(m=>m.toolType==="subagent").length,b=y.map(m=>m.timestamp).filter(Boolean),T=b.length>=2?Math.max(...b)-Math.min(...b):0;try{u.updateSkillExecution(g,{status:"completed",finishedAt:Date.now(),totalToolCalls:l,mcpToolCalls:d,directToolCalls:_,subagentCount:S,totalDuration:T,mcpComplianceRate:l>0?Math.round(d/l*1e4)/100:0}),await ae("/api/telemetry/skill-execution/complete",{executionId:g,finishedAt:Date.now(),status:"completed",summary:{totalToolCalls:l,mcpToolCalls:d,directToolCalls:_,subagentCount:S,totalDuration:T,mcpComplianceRate:l>0?Math.round(d/l*1e4)/100:0}});}catch{}}e&&u.deleteHookReceipt(s),u.deleteContextReceipt(s,r);}catch{}finally{u?.close();}try{if(await M({projectRoot:s,sessionId:r,trigger:"session_end",memory:t?.memory}),t?.memory)await t.memory.closeSession(r);else {let h=new(await import('@devflow-tools/memory-engine')).MemoryGate(s);try{await h.closeSession(r);}finally{h.close();}}}catch{}try{await a.endSession(r),await a.flushAndAggregate();}catch{}}finally{j&&a.close(),i.removeSession(r);}}if(process.argv[1]?.endsWith("session-end")||process.argv[1]?.endsWith("session-end.js")){let o="";process.stdin.setEncoding("utf8"),process.stdin.on("data",s=>{o+=s;}),process.stdin.on("end",async()=>{await ce(o.trim()||process.argv[2]||""),process.exit(0);}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),1e4).unref();}
|
|
2
|
+
export{ce as handleSessionEnd};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {request}from'http';import {
|
|
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 c(i){return i??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function a(i){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(c(i))}function y(i){return Buffer.from(i,"utf8").toString("base64url")}var r=class{constructor(e){this.projectRoot=e;this.sessions=new Map;this.sessionsDir=join(a(e),"hook-sessions");}registerSession(e){let t=e.trim();if(!t)throw new Error("session_id_required");let s=this.get(t);if(s)return s.lastActivityAt=Date.now(),this.persist(s),s;let o=Date.now(),n={sessionId:t,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:o,lastActivityAt:o};return this.sessions.set(t,n),this.persist(n),n}startExecution(e,t,s){let o=this.registerSession(e);return (!o.executionId||o.skillName!==t)&&(o.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),o.skillName=t,o.requiredMcpTools=[...new Set(s)],o.lastActivityAt=Date.now(),this.persist(o),o}get(e){let t=e.trim();if(!t)return null;let s=this.sessions.get(t);if(s)return s;let o=this.snapshotPath(t);if(!existsSync(o))return null;try{let n=JSON.parse(readFileSync(o,"utf8"));return n.sessionId!==t||n.projectRoot!==this.projectRoot?null:(n.requiredMcpTools=Array.isArray(n.requiredMcpTools)?n.requiredMcpTools:[],this.sessions.set(t,n),n)}catch{return null}}completeExecution(e){let t=this.get(e);if(!t)return null;let s={...t,requiredMcpTools:[...t.requiredMcpTools]};return delete t.executionId,delete t.skillName,t.requiredMcpTools=[],t.lastActivityAt=Date.now(),this.persist(t),s}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,`${y(e)}.json`)}persist(e){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let t=this.snapshotPath(e.sessionId),s=`${t}.${process.pid}.tmp`;writeFileSync(s,JSON.stringify(e),{mode:384}),renameSync(s,t);}};var D=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",I=getLocalApiKey();function w(i,e){return new Promise(t=>{let s=JSON.stringify(e),o=new URL(i,D),n=request({hostname:o.hostname,port:o.port,path:o.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":I,"Content-Length":Buffer.byteLength(s)},timeout:5e3},()=>t());n.on("error",()=>t()),n.write(s),n.end();})}async function _(i,e=c()){let t;try{t=JSON.parse(i);}catch{return null}if(t.stop_hook_active)return null;let s=t.session_id?.trim();if(!s)return null;let o=new r(e).completeExecution(s);if(o?.executionId){let n=openGlobalDevFlowDatabase();try{n.reconcileSkillExecution(o.executionId,"completed"),n.deleteContextReceipt(e,s,o.executionId);}finally{n.close();}}return w(`/api/telemetry/sessions/${encodeURIComponent(s)}/tick`,{timestamp:Date.now()}).catch(()=>{}),null}if(process.argv[1]?.endsWith("stop")||process.argv[1]?.endsWith("stop.js")){let i="";process.stdin.setEncoding("utf8"),process.stdin.on("data",e=>{i+=e;}),process.stdin.on("end",async()=>{await _(i.trim()||process.argv[2]||""),process.exit(0);}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),4e3).unref();}
|
|
2
|
+
export{_ as handleStop};
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import {createHash}from'node:crypto';import {mkdirSync,appendFileSync}from'node:fs';import {homedir}from'node:os';import {dirname,join}from'node:path';import {MemoryGate}from'@devflow-tools/memory-engine';import'@devflow-tools/database';var
|
|
2
|
-
`,{mode:384});}var m="WARNING: DevFlow is in degraded mode (daemon unreachable): 4-Gate enforcement and memory prefetch cache are unavailable. Run `devflow doctor` for details.";function u(){return process.env.DEVFLOW_HOOK_DEGRADED==="1"}var p=process.env.CLAUDE_PROJECT_DIR||process.cwd();async function I(e){let t=u()?{additionalContext:m}:{},n;try{n=JSON.parse(e);}catch{return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t}}}let r=n.prompt||"";if(!r.trim())return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t}};let s=c(r);if(s)try{a(p,{content:s,sessionId:n.session_id,createdAt:Date.now()});}catch{}let o=new MemoryGate(p);try{await o.forceWarmUp(),await o.recordUserMessage(r.slice(0,2e3));}catch{}finally{o.close();}return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t}}}if(process.argv[1]?.endsWith("user-prompt-submit")||process.argv[1]?.endsWith("user-prompt-submit.js")){let e="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{e+=t;}),process.stdin.on("end",async()=>{let t=await I(e.trim()||process.argv[2]||"");console.log(JSON.stringify(t)),process.exit(0);}),process.stdin.on("error",()=>{console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}})),process.exit(0);}),setTimeout(()=>{console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}})),process.exit(0);},3e4).unref();}export{I as handleUserPromptSubmit};
|
|
1
|
+
import {createHash}from'node:crypto';import {mkdirSync,appendFileSync}from'node:fs';import {homedir as homedir$1}from'node:os';import {dirname,join as join$1}from'node:path';import {MemoryGate}from'@devflow-tools/memory-engine';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {existsSync,readFileSync,rmSync,mkdirSync as mkdirSync$1,writeFileSync,renameSync,readdirSync,unlinkSync}from'fs';import {join,dirname as dirname$1}from'path';import {randomUUID}from'crypto';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {fileURLToPath}from'url';import {request}from'http';import {homedir}from'os';var L=/(?:^|[.!?。!?]\s*)(?:(?:please\s+)?(?:remember|memorize)\b|(?:请记|(?:请)?记住))/iu,J=/\b(?:(?:do\s+not|don't|dont|never|not)(?:\s+need\s+to)?|no\s+need\s+to)\s+(?:please\s+)?(?:remember|memorize)\b|(?:不要|别|不用|无需|不必|不需要)(?:再)?(?:记住|记|记忆)/iu;function S(o){let t=o.trim();return t&&L.test(t)&&!J.test(t)?t:null}function H(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 D(o,t){let e=H(o);mkdirSync(dirname(e),{recursive:true}),appendFileSync(e,`${JSON.stringify(t)}
|
|
2
|
+
`,{mode:384});}var w="WARNING: DevFlow is in degraded mode (daemon unreachable): 4-Gate enforcement and memory prefetch cache are unavailable. Run `devflow doctor` for details.";function b(){return process.env.DEVFLOW_HOOK_DEGRADED==="1"}function W(o){return o??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function f(o){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(W(o))}function Q(o){return Buffer.from(o,"utf8").toString("base64url")}var p=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(f(t),"hook-sessions");}registerSession(t){let e=t.trim();if(!e)throw new Error("session_id_required");let r=this.get(e);if(r)return r.lastActivityAt=Date.now(),this.persist(r),r;let n=Date.now(),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,`${Q(t)}.json`)}persist(t){mkdirSync$1(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(t.sessionId),r=`${e}.${process.pid}.tmp`;writeFileSync(r,JSON.stringify(t),{mode:384}),renameSync(r,e);}};function ee(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 I(o){let t=o.trimStart().match(/^\/(devflow:[a-z0-9][a-z0-9-]*)\b/i);if(!t)return null;let e=ee(t[1]);return e?{rawName:t[1],skillName:e}:null}var oe=1;function ie(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join(dirname$1(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function x(o=ie()){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!==oe||!ae(r.commands)){console.error(`[devflow] command registry contract mismatch: ${e}`);continue}return r.commands}catch(r){console.error(`[devflow] command registry load failed: ${r.message}`);}return null}function ae(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 R(e.mcpTools)&&R(e.blockedNative)})}function R(o){return Array.isArray(o)&&o.every(t=>typeof t=="string"&&t.length>0)}function E(o){let t=x();return t?t[o]?.mcpTools??[]:[]}var A=1e4,he=3e4,d=500;function ge(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 a=[];i.on("data",c=>a.push(c)),i.on("end",()=>{let c=Buffer.concat(a).toString();r(i.statusCode!=null&&i.statusCode>=200&&i.statusCode<300?c:null);});});s.on("error",()=>r(null)),s.on("timeout",()=>{s.destroy(),r(null);}),s.write(t),s.end();}catch{r(null);}})}var h=class{constructor(t){this.retryScheduled=false;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??getLocalApiKey(),this.cacheDir=t?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=t?.legacyCacheDir??(t?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=t?.database??null,this.ownsDatabase=!t?.database;}async sendEvent(t){let e={...t,input:this.truncateInput(t.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),t.eventId):null}async sendExecutionStart(t,e,r,n,s=process.env.CLAUDE_PROJECT_DIR??process.cwd()){let i={executionId:t,sessionId:e,skillName:r,startedAt:n,projectRoot:s};this.commitOrCache("execution_start",i)&&this.postHttp("/api/telemetry/skill-execution/start",i);}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.getDatabase().aggregatePendingToolMetrics(),this.postHttp("/api/telemetry/tool-call/output",t)),e}async endSession(t,e=Date.now()){let r={sessionId:t,finishedAt:e},n=this.commitOrCache("session_end",r);return n&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),n}async flushCache(){try{let r=this.getDatabase(),n=r.listTelemetryFailures({unresolvedOnly:!0,limit:d}).reverse();for(let s of n)try{this.applyOperation(s.operation,s.payload),r.resolveTelemetryFailure(s.id);}catch{}r.trimTelemetryFailures(d);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(r=>!!r))].filter(r=>existsSync(r)).flatMap(r=>readdirSync(r).filter(n=>n.endsWith(".json")).map(n=>{let s=join(r,n);try{return {cacheFile:s,envelope:fe(JSON.parse(readFileSync(s,"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 s=this.getDatabase();s.insertTelemetryFailure({id:n.id,operation:n.operation,payload:n.payload,error:n.failure,createdAt:n.timestamp}),this.applyOperation(n.operation,n.payload),s.resolveTelemetryFailure(n.id),unlinkSync(r);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(t,e){try{return this.applyOperation(t,e),!0}catch(r){return this.cacheOperation(t,e,r),false}}applyOperation(t,e){let r=this.getDatabase();switch(t){case "tool_call":this.ensureParentSession(r,e),r.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(r,e),r.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running"});return;case "execution_complete":r.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":r.ensureSession(e),r.insertRun({id:M(e.id),source:"hook",tool:"session",input:{projectRoot:e.projectRoot},status:"active",startedAt:e.startedAt,tokenUsed:0,metadata:{sessionId:e.id,projectRoot:e.projectRoot}});return;case "complete_event":if(!r.updateToolCallEvent(e.eventId,{output:e.output===void 0?void 0:JSON.stringify(e.output),error:e.error,duration:e.duration}))throw new Error(`Tool call event ${e.eventId} is not available for completion`);return;case "session_end":r.closeSession(e.sessionId,e.finishedAt),r.updateRun(M(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(t,e){let r=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!r)throw new Error("Canonical session ID is required for telemetry");let n=typeof e.projectRoot=="string"&&e.projectRoot.trim()?e.projectRoot:typeof e.input?.projectRoot=="string"&&e.input.projectRoot.trim()?e.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";t.ensureSession({id:r,projectRoot:n,label:n==="unknown"?void 0:n.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(t,e,r){let n=Date.now(),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(d),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync$1(this.cacheDir,{recursive:!0});let i=join(this.cacheDir,`${n}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(i,JSON.stringify(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-d)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},he).unref());}postHttp(t,e){ge(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=A?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,A)}...`}}};function N(o){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(o)}function M(o){return `hook-run:${o}`}function fe(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 m=process.env.CLAUDE_PROJECT_DIR||process.cwd();async function ve(o){let t=b()?{additionalContext:w}:{},e;try{e=JSON.parse(o);}catch{return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t}}}let r=e.prompt||"";if(!r.trim())return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t}};let n=S(r);if(n)try{D(m,{content:n,sessionId:e.session_id,createdAt:Date.now()});}catch{}let s=e.session_id?.trim(),i=I(r);if(s&&i){let c=new p(m),P=c.get(s)?.executionId,u=c.startExecution(s,i.skillName,E(i.skillName));if(u.executionId&&u.executionId!==P){let g=new h;try{await g.sendSessionStart(s,m,u.startedAt),await g.sendExecutionStart(u.executionId,s,i.skillName,u.lastActivityAt,m);}finally{g.close();}}}let a=new MemoryGate(m);try{await a.forceWarmUp(),s&&await a.recordUserMessage(r.slice(0,2e3),s,r.slice(0,200));}catch{}finally{a.close();}return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t}}}if(process.argv[1]?.endsWith("user-prompt-submit")||process.argv[1]?.endsWith("user-prompt-submit.js")){let o="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{o+=t;}),process.stdin.on("end",async()=>{let t=await ve(o.trim()||process.argv[2]||"");console.log(JSON.stringify(t)),process.exit(0);}),process.stdin.on("error",()=>{console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}})),process.exit(0);}),setTimeout(()=>{console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}})),process.exit(0);},3e4).unref();}
|
|
3
|
+
export{ve as handleUserPromptSubmit};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: devflow:animation
|
|
3
|
+
description: Animation 动画专家 — CSS 动画、Framer Motion、GSAP、Lottie、性能优化
|
|
4
|
+
required_mcp_tools:
|
|
5
|
+
- animation_suggest
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
<HARD-GATE>
|
|
9
|
+
收到 devflow:animation 时,必须先调用以下 MCP 工具之一:
|
|
10
|
+
animation_suggest
|
|
11
|
+
</HARD-GATE>
|
|
12
|
+
|
|
13
|
+
## MANDATORY FIRST STEP - Skill Registration
|
|
14
|
+
|
|
15
|
+
> **IMPORTANT: Before ANY other action, execute the registration command below. This is NON-NEGOTIABLE.**
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
mkdir -p ~/.devflow && echo '{"name":"devflow:animation","required_mcp_tools":["animation_suggest"],"description":"devflow:animation skill","registeredAt":'"$(date +%s)000"'}' > ~/.devflow/current-skill.json
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
After registration, you **MUST** use ONLY these MCP tools for all work in this skill:
|
|
22
|
+
- `mcp__devflow__animation_suggest`
|
|
23
|
+
|
|
24
|
+
**Never use direct tools** (Read, Write, Edit, Bash, Grep, etc.) for tasks that can be handled by the MCP tools above.
|
|
25
|
+
|
|
26
|
+
When the skill execution is complete, clean up:
|
|
27
|
+
```bash
|
|
28
|
+
rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
# ✨ ANIMATION
|
|
32
|
+
|
|
33
|
+
你是一个动画专家。精通 CSS Animation、Framer Motion、GSAP。动画必须 60fps 且支持 prefers-reduced-motion。
|
|
34
|
+
|
|
35
|
+
## 适用场景
|
|
36
|
+
|
|
37
|
+
**动画审计:性能检查 → 可访问性 → 实现质量**
|
|
38
|
+
|
|
39
|
+
**新建动画:需求分析 → 选型建议 → 实现 → 性能验证**
|
|
40
|
+
|
|
41
|
+
**动画 GPU 迁移:扫描 → 替换为 GPU 属性 → 验证**
|
|
42
|
+
|
|
43
|
+
## 核心规则
|
|
44
|
+
|
|
45
|
+
- 动画属性只使用 transform 和 opacity(GPU 加速)
|
|
46
|
+
- 过渡动画 200-300ms,入场 300-500ms
|
|
47
|
+
- 使用 ease-out 入场、ease-in 退场
|
|
48
|
+
- 尊重 prefers-reduced-motion,提供减弱方案
|
|
49
|
+
- 避免同时动画过多元素(批量编排使用 staggerChildren)
|
|
50
|
+
- 使用 AnimatePresence 处理 React 组件退出动画
|
|
51
|
+
- Lottie 动画控制文件大小 < 50KB
|
|
52
|
+
|
|
53
|
+
## 当前版本
|
|
54
|
+
|
|
55
|
+
- animation-docs: 2024 — https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animations
|
|
56
|
+
|
|
57
|
+
## 工作流
|
|
58
|
+
|
|
59
|
+
| 场景 | 触发方式 |
|
|
60
|
+
|------|----------|
|
|
61
|
+
| 动画审计:性能检查 → 可访问性 → 实现质量 | 调用 MCP 工具 `animation_audit:animation` |
|
|
62
|
+
| 新建动画:需求分析 → 选型建议 → 实现 → 性能验证 | 调用 MCP 工具 `animation_new:animation` |
|
|
63
|
+
| 动画 GPU 迁移:扫描 → 替换为 GPU 属性 → 验证 | 调用 MCP 工具 `animation_migrate:gpu-animation` |
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: devflow:context
|
|
3
|
+
description: 检索项目上下文 — 自然语言查询相关代码、符号、依赖关系
|
|
4
|
+
required_mcp_tools:
|
|
5
|
+
- get_project_context
|
|
6
|
+
- search_symbol
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<HARD-GATE>
|
|
10
|
+
收到 devflow:context 时,必须先调用以下 MCP 工具之一:
|
|
11
|
+
get_project_context / search_symbol
|
|
12
|
+
</HARD-GATE>
|
|
13
|
+
|
|
14
|
+
## MANDATORY FIRST STEP - Skill Registration
|
|
15
|
+
|
|
16
|
+
> **IMPORTANT: Before ANY other action, execute the registration command below. This is NON-NEGOTIABLE.**
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
mkdir -p ~/.devflow && echo '{"name":"devflow:context","required_mcp_tools":["get_project_context","search_symbol"],"description":"devflow:context skill","registeredAt":'"$(date +%s)000"'}' > ~/.devflow/current-skill.json
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
After registration, you **MUST** use ONLY these MCP tools for all work in this skill:
|
|
23
|
+
- `mcp__devflow__get_project_context`
|
|
24
|
+
- `mcp__devflow__search_symbol`
|
|
25
|
+
|
|
26
|
+
**Never use direct tools** (Read, Write, Edit, Bash, Grep, etc.) for tasks that can be handled by the MCP tools above.
|
|
27
|
+
|
|
28
|
+
When the skill execution is complete, clean up:
|
|
29
|
+
```bash
|
|
30
|
+
rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 工作流(必须按顺序执行)
|
|
34
|
+
|
|
35
|
+
### Step 1: 获取精确上下文
|
|
36
|
+
调用 `mcp__devflow__get_project_context` 获取与用户问题相关的代码、记忆和知识库信息。
|
|
37
|
+
这一步确保你拿到的是项目真实状态,而非凭空猜测。
|
|
38
|
+
|
|
39
|
+
### Step 2: 分析
|
|
40
|
+
基于 Step 1 返回的上下文,分析问题根因或设计方案。
|
|
41
|
+
此时可以自由使用 Read 查看具体文件。
|
|
42
|
+
|
|
43
|
+
### Step 3: 执行
|
|
44
|
+
根据分析结果修改代码,逐文件编辑。
|
|
45
|
+
|
|
46
|
+
### Step 4: 验证
|
|
47
|
+
运行类型检查和测试,确保改动正确。
|
|
48
|
+
|
|
49
|
+
## 可用工具
|
|
50
|
+
|
|
51
|
+
| 工具 | 何时调用 |
|
|
52
|
+
|------|----------|
|
|
53
|
+
| `mcp__devflow__get_project_context` | 每次任务第一步(必须) |
|
|
54
|
+
| `mcp__devflow__get_project_context` | 按需调用 |
|
|
55
|
+
| `mcp__devflow__search_symbol` | 按需调用 |
|
|
56
|
+
|
|
57
|
+
## 规则
|
|
58
|
+
- 永远从 `mcp__devflow__get_project_context` 开始——你不知道项目里有什么
|
|
59
|
+
- 上下文返回后,用 Read 确认关键文件
|
|
60
|
+
- 改完代码后跑类型检查
|
|
61
|
+
|
|
62
|
+
## 规则
|
|
63
|
+
- 永远从 `get_project_context` 开始——你不知道项目里有什么
|
|
64
|
+
- 上下文返回后,用 Read 确认关键文件
|
|
65
|
+
- 改完代码后跑类型检查
|
|
66
|
+
|
|
67
|
+
## 规则
|
|
68
|
+
- 永远从 `get_project_context` 开始——你不知道项目里有什么
|
|
69
|
+
- 上下文返回后,用 Read 确认关键文件
|
|
70
|
+
- 改完代码后跑类型检查
|
|
71
|
+
|
|
72
|
+
# /devflow:context
|
|
73
|
+
|
|
74
|
+
## 执行
|
|
75
|
+
|
|
76
|
+
1. 如果用户的查询包含中文,先将其中的中文关键词翻译为英文代码术语(空格分隔)
|
|
77
|
+
2. 调用 `mcp__devflow__get_project_context` MCP 工具:
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
query = 用户的原始查询内容(包含中文)
|
|
81
|
+
expandedTerms = 你翻译的英文关键词(如 "code review audit module")
|
|
82
|
+
mode = "fast" (首次调用;如需更深入结果,再改用 "deep")
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
也可以使用 `mcp__devflow__search_symbol` 按符号名搜索代码。
|
|
86
|
+
|
|
87
|
+
示例:
|
|
88
|
+
- 用户查询 "代码审核模块的流程" → expandedTerms = "code review audit module workflow pipeline process"
|
|
89
|
+
- 用户查询 "用户登录认证逻辑" → expandedTerms = "user login authentication auth logic service handler"
|
|
90
|
+
- 用户查询 "数据库缓存策略" → expandedTerms = "database db cache memo redis storage strategy"
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: devflow:css
|
|
3
|
+
description: CSS 专家 — 布局调试、响应式审查、BEM 规范、性能优化
|
|
4
|
+
required_mcp_tools:
|
|
5
|
+
- css_audit_selectors
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
<HARD-GATE>
|
|
9
|
+
收到 devflow:css 时,必须先调用以下 MCP 工具之一:
|
|
10
|
+
css_audit_selectors
|
|
11
|
+
</HARD-GATE>
|
|
12
|
+
|
|
13
|
+
## MANDATORY FIRST STEP - Skill Registration
|
|
14
|
+
|
|
15
|
+
> **IMPORTANT: Before ANY other action, execute the registration command below. This is NON-NEGOTIABLE.**
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
mkdir -p ~/.devflow && echo '{"name":"devflow:css","required_mcp_tools":["css_audit_selectors"],"description":"devflow:css skill","registeredAt":'"$(date +%s)000"'}' > ~/.devflow/current-skill.json
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
After registration, you **MUST** use ONLY these MCP tools for all work in this skill:
|
|
22
|
+
- `mcp__devflow__css_audit_selectors`
|
|
23
|
+
|
|
24
|
+
**Never use direct tools** (Read, Write, Edit, Bash, Grep, etc.) for tasks that can be handled by the MCP tools above.
|
|
25
|
+
|
|
26
|
+
When the skill execution is complete, clean up:
|
|
27
|
+
```bash
|
|
28
|
+
rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
# 🎨 CSS
|
|
32
|
+
|
|
33
|
+
你是一个 CSS 专家。使用现代布局方案(Flexbox/Grid),遵循 BEM 命名,移动优先响应式设计。遇到问题先查 MDN 文档。
|
|
34
|
+
|
|
35
|
+
## 适用场景
|
|
36
|
+
|
|
37
|
+
**布局调试:检查元素定位 → 弹性/网格属性 → 修复**
|
|
38
|
+
|
|
39
|
+
**响应式审查:断点检查 → 媒体查询 → 流体方案**
|
|
40
|
+
|
|
41
|
+
**BEM 命名审查:扫描类名 → 规范检查 → 修复建议**
|
|
42
|
+
|
|
43
|
+
## 核心规则
|
|
44
|
+
|
|
45
|
+
- 使用 BEM 命名规范
|
|
46
|
+
- 优先使用 Flexbox 和 Grid 布局
|
|
47
|
+
- 移动优先响应式设计
|
|
48
|
+
- 使用 CSS 变量管理主题
|
|
49
|
+
- 避免 !important 和深层嵌套
|
|
50
|
+
- 使用 rem/em 做相对单位
|
|
51
|
+
- 不确定属性行为时先查 MDN 文档确认
|
|
52
|
+
|
|
53
|
+
## 当前版本
|
|
54
|
+
|
|
55
|
+
- mdn-css: 2024 — https://developer.mozilla.org/en-US/docs/Web/CSS
|
|
56
|
+
|
|
57
|
+
## 工作流
|
|
58
|
+
|
|
59
|
+
| 场景 | 触发方式 |
|
|
60
|
+
|------|----------|
|
|
61
|
+
| 布局调试:检查元素定位 → 弹性/网格属性 → 修复 | 调用 MCP 工具 `css_debug:layout` |
|
|
62
|
+
| 响应式审查:断点检查 → 媒体查询 → 流体方案 | 调用 MCP 工具 `css_audit:responsive` |
|
|
63
|
+
| BEM 命名审查:扫描类名 → 规范检查 → 修复建议 | 调用 MCP 工具 `css_review:bem` |
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: devflow:docker
|
|
3
|
+
description: Docker 专家 — 镜像优化、Compose 编排、安全扫描、多阶段构建
|
|
4
|
+
required_mcp_tools:
|
|
5
|
+
- docker_dockerize_project
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
<HARD-GATE>
|
|
9
|
+
收到 devflow:docker 时,必须先调用以下 MCP 工具之一:
|
|
10
|
+
docker_dockerize_project
|
|
11
|
+
</HARD-GATE>
|
|
12
|
+
|
|
13
|
+
## MANDATORY FIRST STEP - Skill Registration
|
|
14
|
+
|
|
15
|
+
> **IMPORTANT: Before ANY other action, execute the registration command below. This is NON-NEGOTIABLE.**
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
mkdir -p ~/.devflow && echo '{"name":"devflow:docker","required_mcp_tools":["docker_dockerize_project"],"description":"devflow:docker skill","registeredAt":'"$(date +%s)000"'}' > ~/.devflow/current-skill.json
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
After registration, you **MUST** use ONLY these MCP tools for all work in this skill:
|
|
22
|
+
- `mcp__devflow__docker_dockerize_project`
|
|
23
|
+
|
|
24
|
+
**Never use direct tools** (Read, Write, Edit, Bash, Grep, etc.) for tasks that can be handled by the MCP tools above.
|
|
25
|
+
|
|
26
|
+
When the skill execution is complete, clean up:
|
|
27
|
+
```bash
|
|
28
|
+
rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
# 🐳 DOCKER
|
|
32
|
+
|
|
33
|
+
你是一个 Docker 专家。使用多阶段构建优化镜像,遵循安全和性能最佳实践。遇到问题先查官方文档。
|
|
34
|
+
|
|
35
|
+
## 适用场景
|
|
36
|
+
|
|
37
|
+
**项目 Docker 化:分析项目 → 多阶段 Dockerfile → Compose → 构建验证**
|
|
38
|
+
|
|
39
|
+
**镜像优化:体积分析 → 层优化 → 多阶段重构**
|
|
40
|
+
|
|
41
|
+
**安全扫描:漏洞检测 → 配置审查 → 修复建议**
|
|
42
|
+
|
|
43
|
+
**多服务 Compose 编排:依赖分析 → 网络设计 → 环境管理**
|
|
44
|
+
|
|
45
|
+
## 核心规则
|
|
46
|
+
|
|
47
|
+
- 使用多阶段构建减小镜像体积
|
|
48
|
+
- 基础镜像固定版本标签(不使用 latest)
|
|
49
|
+
- 利用层缓存优化构建顺序
|
|
50
|
+
- 使用非 root 用户运行应用
|
|
51
|
+
- 使用 .dockerignore 排除无用文件
|
|
52
|
+
- 添加 HEALTHCHECK 监控容器健康状态
|
|
53
|
+
- 不在镜像层中写入敏感信息
|
|
54
|
+
- 修复安全问题时先查 Docker 官方文档确认方案
|
|
55
|
+
|
|
56
|
+
## 当前版本
|
|
57
|
+
|
|
58
|
+
- docker-docs: 26.0.0 — https://docs.docker.com/reference/
|
|
59
|
+
|
|
60
|
+
## 工作流
|
|
61
|
+
|
|
62
|
+
| 场景 | 触发方式 |
|
|
63
|
+
|------|----------|
|
|
64
|
+
| 项目 Docker 化:分析项目 → 多阶段 Dockerfile → Compose → 构建验证 | 调用 MCP 工具 `docker_dockerize_project` |
|
|
65
|
+
| 镜像优化:体积分析 → 层优化 → 多阶段重构 | 调用 MCP 工具 `docker_optimize:image` |
|
|
66
|
+
| 安全扫描:漏洞检测 → 配置审查 → 修复建议 | 调用 MCP 工具 `docker_scan:security` |
|
|
67
|
+
| 多服务 Compose 编排:依赖分析 → 网络设计 → 环境管理 | 调用 MCP 工具 `docker_setup:compose` |
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: devflow:electron
|
|
3
|
+
description: Electron 专家 — IPC 通信、窗口管理、原生模块、自动更新、安全配置
|
|
4
|
+
required_mcp_tools:
|
|
5
|
+
- electron_diagnose_bug
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
<HARD-GATE>
|
|
9
|
+
收到 devflow:electron 时,必须先调用以下 MCP 工具之一:
|
|
10
|
+
electron_diagnose_bug
|
|
11
|
+
</HARD-GATE>
|
|
12
|
+
|
|
13
|
+
## MANDATORY FIRST STEP - Skill Registration
|
|
14
|
+
|
|
15
|
+
> **IMPORTANT: Before ANY other action, execute the registration command below. This is NON-NEGOTIABLE.**
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
mkdir -p ~/.devflow && echo '{"name":"devflow:electron","required_mcp_tools":["electron_diagnose_bug"],"description":"devflow:electron skill","registeredAt":'"$(date +%s)000"'}' > ~/.devflow/current-skill.json
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
After registration, you **MUST** use ONLY these MCP tools for all work in this skill:
|
|
22
|
+
- `mcp__devflow__electron_diagnose_bug`
|
|
23
|
+
|
|
24
|
+
**Never use direct tools** (Read, Write, Edit, Bash, Grep, etc.) for tasks that can be handled by the MCP tools above.
|
|
25
|
+
|
|
26
|
+
When the skill execution is complete, clean up:
|
|
27
|
+
```bash
|
|
28
|
+
rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
# ⚛️ ELECTRON
|
|
32
|
+
|
|
33
|
+
你是一个 Electron 专家。使用 contextBridge + IPC 安全通信模式。严格遵循 Electron 安全最佳实践。
|
|
34
|
+
|
|
35
|
+
## 适用场景
|
|
36
|
+
|
|
37
|
+
**新建 IPC 通道:定义 API → Preload 暴露 → 主进程处理**
|
|
38
|
+
|
|
39
|
+
**安全审计:配置检查 → IPC 暴露面 → CSP → 第三方依赖**
|
|
40
|
+
|
|
41
|
+
**自动更新配置:electron-updater → 发布渠道 → 更新 UI**
|
|
42
|
+
|
|
43
|
+
**Electron Bug 诊断:进程分类 → IPC 追踪 → 原生调试**
|
|
44
|
+
|
|
45
|
+
## 核心规则
|
|
46
|
+
|
|
47
|
+
- 使用 contextBridge + ipcRenderer/ipcMain 通信
|
|
48
|
+
- 必须启用 contextIsolation,禁用 nodeIntegration
|
|
49
|
+
- 主进程负责系统操作,渲染进程只做 UI
|
|
50
|
+
- Preload 脚本只暴露最小必要的安全 API
|
|
51
|
+
- 使用 electron-updater 实现自动更新
|
|
52
|
+
- 启用 sandbox 和 CSP 安全策略
|
|
53
|
+
- 避免使用 remote 模块(已废弃)
|
|
54
|
+
- 构建使用 electron-builder 并配置代码签名
|
|
55
|
+
|
|
56
|
+
## 当前版本
|
|
57
|
+
|
|
58
|
+
- electron-docs: 30.0.0 — https://www.electronjs.org/docs/latest/
|
|
59
|
+
|
|
60
|
+
## 工作流
|
|
61
|
+
|
|
62
|
+
| 场景 | 触发方式 |
|
|
63
|
+
|------|----------|
|
|
64
|
+
| 新建 IPC 通道:定义 API → Preload 暴露 → 主进程处理 | 调用 MCP 工具 `electron_new:ipc` |
|
|
65
|
+
| 安全审计:配置检查 → IPC 暴露面 → CSP → 第三方依赖 | 调用 MCP 工具 `electron_audit:security` |
|
|
66
|
+
| 自动更新配置:electron-updater → 发布渠道 → 更新 UI | 调用 MCP 工具 `electron_setup:auto-update` |
|
|
67
|
+
| Electron Bug 诊断:进程分类 → IPC 追踪 → 原生调试 | 调用 MCP 工具 `electron_diagnose_bug` |
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: devflow:git
|
|
3
|
+
description: Git 专家 — 提交规范、分支审查、Changelog 生成、冲突解决
|
|
4
|
+
required_mcp_tools:
|
|
5
|
+
- git_analyze_repo
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
<HARD-GATE>
|
|
9
|
+
收到 devflow:git 时,必须先调用以下 MCP 工具之一:
|
|
10
|
+
git_analyze_repo
|
|
11
|
+
</HARD-GATE>
|
|
12
|
+
|
|
13
|
+
## MANDATORY FIRST STEP - Skill Registration
|
|
14
|
+
|
|
15
|
+
> **IMPORTANT: Before ANY other action, execute the registration command below. This is NON-NEGOTIABLE.**
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
mkdir -p ~/.devflow && echo '{"name":"devflow:git","required_mcp_tools":["git_analyze_repo"],"description":"devflow:git skill","registeredAt":'"$(date +%s)000"'}' > ~/.devflow/current-skill.json
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
After registration, you **MUST** use ONLY these MCP tools for all work in this skill:
|
|
22
|
+
- `mcp__devflow__git_analyze_repo`
|
|
23
|
+
|
|
24
|
+
**Never use direct tools** (Read, Write, Edit, Bash, Grep, etc.) for tasks that can be handled by the MCP tools above.
|
|
25
|
+
|
|
26
|
+
When the skill execution is complete, clean up:
|
|
27
|
+
```bash
|
|
28
|
+
rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
# 🔀 GIT
|
|
32
|
+
|
|
33
|
+
你是一个 Git 工作流专家。遵循 Conventional Commits 规范和 GitFlow 分支策略。
|
|
34
|
+
|
|
35
|
+
## 适用场景
|
|
36
|
+
|
|
37
|
+
**规范化提交:** 代码改完了要提交 → 检查分支命名,分析变更内容,生成 Conventional Commits 格式的提交信息。
|
|
38
|
+
|
|
39
|
+
**发布流程:** 从提交历史确定版本号,生成 Changelog,创建 Tag 并推送。
|
|
40
|
+
|
|
41
|
+
**分支清理:** 分支太多太乱 → 列出所有分支,检查命名规范、合并状态、过期分支,生成清理建议。
|
|
42
|
+
|
|
43
|
+
**冲突解决:** merge/rebase 冲突 → 检测所有冲突文件和类型,分析冲突来源(两边最后修改者),建议解决策略。
|
|
44
|
+
|
|
45
|
+
## 核心规则
|
|
46
|
+
|
|
47
|
+
- 提交信息使用 Conventional Commits 格式:type(scope): description
|
|
48
|
+
- 功能分支从 develop 拉出,完成后合并回 develop
|
|
49
|
+
- 提交信息主题行不超过 50 字符
|
|
50
|
+
- 每个提交是逻辑上独立的变更
|
|
51
|
+
- 公共分支禁止 force push 和 rebase
|
|
52
|
+
- PR 必须关联 Issue
|
|
53
|
+
- 解决冲突前先理解两边变更的意图
|
|
54
|
+
|
|
55
|
+
## 当前版本
|
|
56
|
+
|
|
57
|
+
- conventional-commits: 1.0.0 — https://www.conventionalcommits.org/en/v1.0.0/
|
|
58
|
+
- git-scm-docs: 2.45.0 — https://git-scm.com/docs
|
|
59
|
+
|
|
60
|
+
## 工作流
|
|
61
|
+
|
|
62
|
+
| 场景 | 触发方式 |
|
|
63
|
+
|------|----------|
|
|
64
|
+
| 规范化提交: | 调用 MCP 工具 `git_commit` |
|
|
65
|
+
| 发布流程: | 调用 MCP 工具 `git_release` |
|
|
66
|
+
| 分支清理: | 调用 MCP 工具 `git_audit:branches` |
|
|
67
|
+
| 冲突解决: | 调用 MCP 工具 `git_resolve:conflicts` |
|