@devflow-tools/devflow 0.12.5 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/hooks/hook-client.js +6 -6
  2. package/dist/hooks/hook-daemon.js +8 -4
  3. package/dist/hooks/post-tool-use.js +2 -2
  4. package/dist/hooks/pre-tool-use.js +2 -2
  5. package/dist/plugin/.claude-plugin/plugin.json +19 -19
  6. package/dist/plugin/skills/devflow:animation/SKILL.md +38 -7
  7. package/dist/plugin/skills/devflow:context/SKILL.md +36 -17
  8. package/dist/plugin/skills/devflow:css/SKILL.md +38 -7
  9. package/dist/plugin/skills/devflow:docker/SKILL.md +38 -8
  10. package/dist/plugin/skills/devflow:electron/SKILL.md +38 -8
  11. package/dist/plugin/skills/devflow:git/SKILL.md +38 -8
  12. package/dist/plugin/skills/devflow:graph/SKILL.md +33 -15
  13. package/dist/plugin/skills/devflow:graphql/SKILL.md +39 -8
  14. package/dist/plugin/skills/devflow:knowledge/SKILL.md +33 -15
  15. package/dist/plugin/skills/devflow:memory/SKILL.md +34 -16
  16. package/dist/plugin/skills/devflow:nest/SKILL.md +39 -8
  17. package/dist/plugin/skills/devflow:nextjs/SKILL.md +39 -7
  18. package/dist/plugin/skills/devflow:performance/SKILL.md +38 -8
  19. package/dist/plugin/skills/devflow:react/SKILL.md +16 -12
  20. package/dist/plugin/skills/devflow:tailwind/SKILL.md +38 -7
  21. package/dist/plugin/skills/devflow:taro/SKILL.md +38 -8
  22. package/dist/plugin/skills/devflow:ui-layout/SKILL.md +38 -7
  23. package/dist/plugin/skills/devflow:vue/SKILL.md +40 -9
  24. package/dist/plugin/skills/devflow:workflow/SKILL.md +33 -15
  25. package/package.json +13 -13
  26. package/dist/dashboard/404.html +0 -1
  27. package/dist/dashboard/_next/static/EDDf6rtQ-c_W_Mi0ANVIA/_buildManifest.js +0 -1
  28. package/dist/dashboard/_next/static/EDDf6rtQ-c_W_Mi0ANVIA/_ssgManifest.js +0 -1
  29. package/dist/dashboard/_next/static/chunks/1dd3208c-f3e84932bdc53383.js +0 -1
  30. package/dist/dashboard/_next/static/chunks/254-814190ccf12d2df8.js +0 -1
  31. package/dist/dashboard/_next/static/chunks/528-7d4fc14903647f1e.js +0 -2
  32. package/dist/dashboard/_next/static/chunks/6b3bb038-34d5cf36bd0210d6.js +0 -1
  33. package/dist/dashboard/_next/static/chunks/781-97eaf0e93843fa67.js +0 -1
  34. package/dist/dashboard/_next/static/chunks/app/_not-found/page-5252c260804ee1ce.js +0 -1
  35. package/dist/dashboard/_next/static/chunks/app/layout-0e71002867d54cb5.js +0 -1
  36. package/dist/dashboard/_next/static/chunks/app/page-c3269fe44e0465e7.js +0 -1
  37. package/dist/dashboard/_next/static/chunks/framework-58c3457e28c4602f.js +0 -1
  38. package/dist/dashboard/_next/static/chunks/main-595171e579e3e067.js +0 -1
  39. package/dist/dashboard/_next/static/chunks/main-app-202a5f91ead17fa7.js +0 -1
  40. package/dist/dashboard/_next/static/chunks/pages/_app-58c3f1a8a070bd35.js +0 -1
  41. package/dist/dashboard/_next/static/chunks/pages/_error-312a564ec7e2663f.js +0 -1
  42. package/dist/dashboard/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
  43. package/dist/dashboard/_next/static/chunks/webpack-d6388c5dcd85b0c5.js +0 -1
  44. package/dist/dashboard/_next/static/css/322e14d06135256b.css +0 -31
  45. package/dist/dashboard/_next/static/css/9bb745101fd21603.css +0 -3
  46. package/dist/dashboard/index.html +0 -1
  47. package/dist/dashboard/index.txt +0 -9
  48. package/dist/dashboard/locales/en.json +0 -328
  49. package/dist/dashboard/locales/zh.json +0 -328
  50. package/plugin-registry.json +0 -190
@@ -1,7 +1,7 @@
1
- import {createConnection}from'net';import {join}from'path';import {homedir}from'os';var f=join(homedir(),".devflow","hook-daemon.sock"),s=process.argv[2],u=process.argv[3];function t(){s==="pre-tool-use"&&console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}})),process.exit(0);}var o=createConnection(f,()=>{if(s==="stop")o.write(JSON.stringify({type:"stop"})+`
2
- `);else if(s==="post-tool-use")o.write(JSON.stringify({type:"post-tool-use",input:u||""})+`
3
- `);else if(s==="pre-tool-use"){let e="";process.stdin.setEncoding("utf8"),process.stdin.on("data",n=>{e+=n;}),process.stdin.on("end",()=>{o.write(JSON.stringify({type:"pre-tool-use",input:e})+`
4
- `);}),process.stdin.on("error",()=>t());}else t();}),i="";o.on("data",e=>{if(i+=e.toString(),i.includes(`
5
- `)){let n=i.split(`
6
- `).filter(Boolean);for(let r of n)process.stdout.write(r+`
1
+ import {createConnection}from'net';import {join}from'path';import {homedir}from'os';var f=join(homedir(),".devflow","hook-daemon.sock"),n=process.argv[2],d=process.argv[3];function t(){process.exit(1);}var o=createConnection(f,()=>{if(n==="stop")o.write(JSON.stringify({type:"stop"})+`
2
+ `);else if(n==="post-tool-use")o.write(JSON.stringify({type:"post-tool-use",input:d||""})+`
3
+ `);else if(n==="pre-tool-use"){let e="";process.stdin.setEncoding("utf8"),process.stdin.on("data",s=>{e+=s;}),process.stdin.on("end",()=>{o.write(JSON.stringify({type:"pre-tool-use",input:e})+`
4
+ `);}),process.stdin.on("error",()=>t());}else t();}),r="";o.on("data",e=>{if(r+=e.toString(),r.includes(`
5
+ `)){let s=r.split(`
6
+ `).filter(Boolean);for(let i of s)process.stdout.write(i+`
7
7
  `);o.end();}});o.on("error",()=>t());o.on("close",()=>process.exit(0));setTimeout(()=>t(),4e3).unref();
@@ -1,4 +1,8 @@
1
- import {createServer}from'net';import {join}from'path';import {homedir}from'os';import {existsSync,unlinkSync,readdirSync,readFileSync,mkdirSync,writeFileSync}from'fs';import {createHash}from'crypto';import {request}from'http';var E=1e4,J=3e4,L=300*1e3;function y(a,t,e){return new Promise(n=>{try{let o=new URL(a),s=request({hostname:o.hostname,port:o.port||80,path:o.pathname+o.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(t)},timeout:5e3},r=>{let i=[];r.on("data",c=>i.push(c)),r.on("end",()=>{let c=Buffer.concat(i).toString();n(r.statusCode!=null&&r.statusCode>=200&&r.statusCode<300?c:null);});});s.on("error",()=>n(null)),s.on("timeout",()=>{s.destroy(),n(null);}),s.write(t),s.end();}catch{n(null);}})}var v=class{constructor(t){this.retryScheduled=false;this.retryAttempts=0;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??process.env.DEVFLOW_API_KEY??"dev-key-000000",this.cacheDir=t?.cacheDir??join(homedir(),".devflow","telemetry-cache");}async sendEvent(t){let e=this.truncateInput(t.input),n={...t,input:e},o=`${this.apiUrl}/api/telemetry/tool-call`,s=await y(o,JSON.stringify(n),this.apiKey);if(s)try{let r=JSON.parse(s),i=r.data?.eventId??r.eventId??null;if(i)return i}catch{}return this.cacheEvent({type:"tool_call",payload:n}),null}async sendExecutionStart(t,e,n,o){let s={executionId:t,sessionId:e,skillName:n,startedAt:o},r=`${this.apiUrl}/api/telemetry/skill-execution/start`;await y(r,JSON.stringify(s),this.apiKey)||this.cacheEvent({type:"execution_start",payload:s});}async sendSessionStart(t,e,n){let o={id:t,projectRoot:e,startedAt:n,label:e.split("/").pop()??"unknown"},s=`${this.apiUrl}/api/telemetry/sessions`;return await y(s,JSON.stringify(o),this.apiKey)!==null}async flushCache(){if(!existsSync(this.cacheDir))return;let t=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json"));for(let e of t)try{let n=join(this.cacheDir,e),o=readFileSync(n,"utf8"),s=JSON.parse(o),r=s.type==="execution_start"?"/api/telemetry/skill-execution/start":"/api/telemetry/tool-call",i=`${this.apiUrl}${r}`;await y(i,JSON.stringify(s.payload),this.apiKey)&&unlinkSync(n);}catch{}}cacheEvent(t){try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let e=join(this.cacheDir,`${Date.now()}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(e,JSON.stringify(t,null,2)),this.scheduleRetry();}catch{}}scheduleRetry(){if(this.retryScheduled)return;this.retryScheduled=true;let t=Math.min(J*Math.pow(2,this.retryAttempts),L);setTimeout(()=>{this.retryScheduled=false,this.flushCache().then(()=>{this.retryAttempts=0;}).catch(()=>{this.retryAttempts++;});},t).unref();}truncateInput(t){let e=JSON.stringify(t);return e.length<=E?t:{_truncated:true,_original_size:e.length,_preview:e.substring(0,E)+"..."}}};var W=1800*1e3;function G(){return process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow")}var S=class{constructor(t){this.stateDir=G();}detect(){let t=join(this.stateDir,"current-skill.json");if(!existsSync(t))return null;try{let e=readFileSync(t,"utf8"),n=JSON.parse(e);return !n.registeredAt||Date.now()-n.registeredAt>W?(this.endExecution(),null):n}catch{return null}}startExecution(t,e){existsSync(this.stateDir)||mkdirSync(this.stateDir,{recursive:true});let n=this.getCurrentExecutionId();if(n)return n;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:t,required_mcp_tools:e?.required_mcp_tools??[],registeredAt:Date.now()})),o}getCurrentExecutionId(){let t=join(this.stateDir,"current-execution-id");if(!existsSync(t))return null;try{return readFileSync(t,"utf8").trim()}catch{return null}}endExecution(){let t=join(this.stateDir,"current-skill.json"),e=join(this.stateDir,"current-execution-id");try{existsSync(t)&&unlinkSync(t);}catch{}try{existsSync(e)&&unlinkSync(e);}catch{}}};var m=join(homedir(),".devflow","hook-daemon.sock");join(homedir(),".devflow");var Q=600*1e3,tt=new Set(["Agent","Bash","Glob","Grep","WebSearch","WebFetch"]);function et(a){switch(a){case "Read":case "Glob":return "file_read";case "Write":case "Edit":case "NotebookEdit":return "file_write";case "Bash":return "bash_command";case "Agent":return "subagent";case "Skill":return "skill_invoke";default:return "tool_use"}}function C(a){let t=createHash("sha256").update(a).digest("hex").slice(0,16);return join(process.env.TMPDIR||"/tmp",".devflow-receipts",`${t}.json`)}function nt(a){try{let t=C(a);return existsSync(t)?JSON.parse(readFileSync(t,"utf-8")):{}}catch{return {}}}function R(a,t){try{let e=C(a),n=join(process.env.TMPDIR||"/tmp",".devflow-receipts");existsSync(n)||mkdirSync(n,{recursive:!0}),writeFileSync(e,JSON.stringify(t));}catch{}}var k=class{constructor(t){this.sessionId=null;this.eventMap=[];this.idleTimer=null;this.projectRoot=t,this.projectName=t.split("/").pop()||"unknown",this.telemetry=new v,this.skillManager=new S;}async start(){try{existsSync(m)&&unlinkSync(m);}catch{}let t=createServer(e=>{this.resetIdleTimer();let n="";e.on("data",async o=>{n+=o.toString();let s=n.split(`
2
- `);n=s.pop()||"";for(let r of s)if(r.trim())try{let i=JSON.parse(r),c=await this.handleRequest(i);c!==null&&e.write(JSON.stringify(c)+`
3
- `);}catch(i){e.write(JSON.stringify({error:i.message})+`
4
- `);}}),e.on("error",()=>{});});t.on("error",e=>{console.error("[devflow-daemon] Server error:",e.message);}),t.listen(m,()=>{console.error("[devflow-daemon] Listening on",m);}),this.resetIdleTimer();}resetIdleTimer(){this.idleTimer&&clearTimeout(this.idleTimer),this.idleTimer=setTimeout(()=>{console.error("[devflow-daemon] Idle timeout, exiting"),this.shutdown();},Q).unref();}async handleRequest(t){switch(t.type){case "pre-tool-use":return this.handlePreToolUse(t.input);case "post-tool-use":return this.handlePostToolUse(t.input);case "stop":return await this.handleStop(),{status:"ok"};default:return {error:"unknown request type"}}}async handlePreToolUse(t){try{if(!t||t.trim()==="")return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}};let e=JSON.parse(t);if(this.sessionId||(this.sessionId=`session:${this.projectName}:${Date.now().toString(36)}`,this.telemetry.sendSessionStart(this.sessionId,this.projectRoot,Date.now()).catch(()=>{})),e.tool_name==="Skill"){let l=e.tool_input?.skill;if(l&&l.startsWith("devflow:")){let N=this.skillManager.startExecution(l);this.telemetry.sendExecutionStart(N,this.sessionId,l,Date.now()).catch(()=>{});}}let n=this.skillManager.detect(),o=e.tool_name.startsWith("mcp__devflow__"),s=o?e.tool_name.replace("mcp__devflow__",""):void 0,r=n?.required_mcp_tools??[],i=r.length>0,c=i&&!o,p;c?p={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:`Skill "${n?.name||"unknown"}" requires MCP tools. Use one of: ${r.join(", ")}`}}:p={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}};let d=this.skillManager.getCurrentExecutionId()??this.sessionId,g={eventId:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`,executionId:d,sessionId:this.sessionId,timestamp:Date.now(),toolName:e.tool_name,toolType:o?"mcp":e.tool_name==="Agent"?"subagent":"direct",isMcpTool:o,mcpToolName:s,mcpEnforced:o&&i,mcpFallback:!o&&i,kind:et(e.tool_name),input:e.tool_input,duration:0,blocked:c};return this.telemetry.sendEvent(g).then(l=>{l&&this.eventMap.push({toolName:e.tool_name,eventId:l,timestamp:g.timestamp});}).catch(()=>{}),p}catch{return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}}}popEventFromMap(t){let e=this.eventMap.filter(o=>o.toolName===t);if(e.length===0)return null;e.sort((o,s)=>Math.abs(o.timestamp-Date.now())-Math.abs(s.timestamp-Date.now()));let n=e[0];return this.eventMap=this.eventMap.filter(o=>o.eventId!==n.eventId),n}enforcePostToolUse(t){if(!tt.has(t))return null;let e=nt(this.projectRoot),n=e.lastMcpCall??0;if(Math.floor(Date.now()/1e3)-n<30)return null;let s=e.bypassCount??0;if(s===0)return R(this.projectRoot,{...e,bypassCount:1}),null;let r=s+1;R(this.projectRoot,{...e,bypassCount:r});let i;switch(t){case "Agent":i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u4F7F\u7528\u4E86 Agent \u5B50\u4EE3\u7406\u6267\u884C\u641C\u7D22\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Bash":i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Bash \u547D\u4EE4\u641C\u7D22\u4EE3\u7801\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Glob":i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Glob \u5339\u914D\u6587\u4EF6\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Grep":i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Grep \u641C\u7D22\u4EE3\u7801\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "WebSearch":case "WebFetch":i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86\u7F51\u7EDC\u641C\u7D22\u3002\u4E0B\u6B21\u8BF7\u7528 get_knowledge MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;default:i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u8BF7\u4F7F\u7528 DevFlow MCP \u5DE5\u5177\u66FF\u4EE3\u76F4\u63A5\u8C03\u7528\u3002`;}return r>=3?i=`${i} \u5DF2\u8FBE\u5230\u6700\u5927\u8FDD\u89C4\u6B21\u6570\uFF0C\u540E\u7EED\u7ED5\u8FC7\u5C06\u88AB\u786C\u963B\u6B62\u3002`:r>=2?i=`${i} \u4E0B\u6B21\u8FDD\u89C4\u5C06\u88AB\u963B\u6B62\u3002`:i=`${i} \u8BF7\u7ACB\u5373\u7EA0\u6B63\u3002`,i}async handlePostToolUse(t){if(!t)return null;let e;try{e=JSON.parse(t);}catch{return null}let n=e.tool_name||"",o=e.tool_input||{},s=e.tool_response,r=this.popEventFromMap(n);if(r&&this.sessionId){let f=typeof s=="string"?s:JSON.stringify(s),g=f.length>5e3?{_truncated:true,_originalSize:f.length,text:f.slice(0,5e3)}:s,l=Date.now()-r.timestamp;this.sendHttp("/api/telemetry/tool-call/output",{eventId:r.eventId,sessionId:this.sessionId,output:g,duration:l,completedAt:Date.now()});}let i=typeof s=="object"&&s!==null?s:null,c=i?.exitCode,p=i?.stderr;n==="Bash"&&c!==void 0&&c!==0&&this.sendHttp(`/api/memory/events?rootPath=${encodeURIComponent(this.projectRoot)}`,{id:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:this.sessionId??`session:${this.projectName}`,tool:n,command:o.command||n,exitCode:c,stderr:p??null,durationMs:0,createdAt:Date.now()});let d=this.enforcePostToolUse(n);return d?(console.error("[devflow-daemon] Enforcement:",d),{hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:JSON.stringify(d)}}):null}sendHttp(t,e){let n=JSON.stringify(e),o=new URL(t,"http://127.0.0.1:13337"),s=request({hostname:o.hostname,port:o.port,path:o.pathname+o.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":"dev-key-000000","Content-Length":Buffer.byteLength(n)},timeout:5e3},()=>{});s.on("error",()=>{}),s.write(n),s.end();}async handleStop(){this.sessionId&&this.sendHttp(`/api/telemetry/sessions/${this.sessionId}/close`,{finishedAt:Date.now()}),this.sendHttp(`/api/memory/process?rootPath=${encodeURIComponent(this.projectRoot)}`,{}),await this.telemetry.flushCache().catch(()=>{}),this.shutdown();}shutdown(){this.idleTimer&&clearTimeout(this.idleTimer);try{unlinkSync(m);}catch{}process.exit(0);}},ot=process.argv[2]||process.env.CLAUDE_PROJECT_DIR||process.cwd(),st=new k(ot);st.start().catch(a=>{console.error("[devflow-daemon] Failed to start:",a),process.exit(1);});
1
+ import {createServer}from'net';import {join,dirname}from'path';import {homedir}from'os';import {existsSync,unlinkSync,readdirSync,readFileSync,mkdirSync,writeFileSync}from'fs';import {createHash}from'crypto';import {request}from'http';import {fileURLToPath}from'url';import {spawn}from'child_process';var $=1e4,lt=3e4,ut=300*1e3;function S(a,e,t){return new Promise(s=>{try{let o=new URL(a),n=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},r=>{let i=[];r.on("data",c=>i.push(c)),r.on("end",()=>{let c=Buffer.concat(i).toString();s(r.statusCode!=null&&r.statusCode>=200&&r.statusCode<300?c:null);});});n.on("error",()=>s(null)),n.on("timeout",()=>{n.destroy(),s(null);}),n.write(e),n.end();}catch{s(null);}})}var w=class{constructor(e){this.retryScheduled=false;this.retryAttempts=0;this.apiUrl=e?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=e?.apiKey??process.env.DEVFLOW_API_KEY??"dev-key-000000",this.cacheDir=e?.cacheDir??join(homedir(),".devflow","telemetry-cache");}async sendEvent(e){let t=this.truncateInput(e.input),s={...e,input:t},o=`${this.apiUrl}/api/telemetry/tool-call`,n=await S(o,JSON.stringify(s),this.apiKey);if(n)try{let r=JSON.parse(n),i=r.data?.eventId??r.eventId??null;if(i)return i}catch{}return this.cacheEvent({type:"tool_call",payload:s}),null}async sendExecutionStart(e,t,s,o){let n={executionId:e,sessionId:t,skillName:s,startedAt:o},r=`${this.apiUrl}/api/telemetry/skill-execution/start`;await S(r,JSON.stringify(n),this.apiKey)||this.cacheEvent({type:"execution_start",payload:n});}async sendSessionStart(e,t,s){let o={id:e,projectRoot:t,startedAt:s,label:t.split("/").pop()??"unknown"},n=`${this.apiUrl}/api/telemetry/sessions`;return await S(n,JSON.stringify(o),this.apiKey)!==null}async flushCache(){if(!existsSync(this.cacheDir))return;let e=readdirSync(this.cacheDir).filter(t=>t.endsWith(".json"));for(let t of e)try{let s=join(this.cacheDir,t),o=readFileSync(s,"utf8"),n=JSON.parse(o),r=n.type==="execution_start"?"/api/telemetry/skill-execution/start":"/api/telemetry/tool-call",i=`${this.apiUrl}${r}`;await S(i,JSON.stringify(n.payload),this.apiKey)&&unlinkSync(s);}catch{}}cacheEvent(e){try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let t=join(this.cacheDir,`${Date.now()}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(t,JSON.stringify(e,null,2)),this.scheduleRetry();}catch{}}scheduleRetry(){if(this.retryScheduled)return;this.retryScheduled=true;let e=Math.min(lt*Math.pow(2,this.retryAttempts),ut);setTimeout(()=>{this.retryScheduled=false,this.flushCache().then(()=>{this.retryAttempts=0;}).catch(()=>{this.retryAttempts++;});},e).unref();}truncateInput(e){let t=JSON.stringify(e);return t.length<=$?e:{_truncated:true,_original_size:t.length,_preview:t.substring(0,$)+"..."}}};var ft=1800*1e3;function J(){return process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow")}function gt(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{let a=fileURLToPath(import.meta.url);return join(dirname(a),"..","..")}catch{return process.cwd()}}function yt(){let a=gt(),e=[join(a,"dist","command-registry.json"),join(a,"command-registry.json")],t=[];try{for(let s of e){if(existsSync(s)){let o=JSON.parse(readFileSync(s,"utf8")).commands;if(o)return o}t.push(`${s}: ${existsSync(s)?"found":"missing"}`);}}catch(s){t.push(`error: ${s.message}`);}try{writeFileSync(join(J(),"hook-debug.log"),JSON.stringify({ts:Date.now(),pluginRoot:a,CLAUDE_PLUGIN_ROOT:process.env.CLAUDE_PLUGIN_ROOT??"(unset)",cwd:process.cwd(),paths:t},null,2));}catch{}return null}function x(a){let e=yt();return e?e[a]?.mcpTools??[]:[]}var _=class{constructor(e){this.stateDir=J();}detect(){let e=join(this.stateDir,"current-skill.json");if(!existsSync(e))return null;try{let t=readFileSync(e,"utf8"),s=JSON.parse(t);return !s.registeredAt||Date.now()-s.registeredAt>ft?(this.endExecution(),null):s}catch{return null}}startExecution(e,t){existsSync(this.stateDir)||mkdirSync(this.stateDir,{recursive:true});let s=this.getCurrentExecutionId();if(s)return s;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{}}};var O=join(homedir(),".devflow"),j=join(O,"server-refs.json"),Rt="http://127.0.0.1:13337/api/health",Et=600*1e3,It=500;function Pt(){existsSync(O)||mkdirSync(O,{recursive:true});}function T(){try{if(existsSync(j))return JSON.parse(readFileSync(j,"utf-8"))}catch{}return {sessions:[],lastActivity:0}}function B(a){Pt(),writeFileSync(j,JSON.stringify(a));}var R=class{constructor(e){this.process=null;this.idleTimer=null;this.onShutdown=null;this.projectRoot=e;}setOnShutdown(e){this.onShutdown=e;}addRef(e){let t=T();t.sessions.includes(e)||t.sessions.push(e),t.lastActivity=Date.now(),B(t),this.resetIdleTimer();}removeRef(e){let t=T();t.sessions=t.sessions.filter(s=>s!==e),t.lastActivity=Date.now(),B(t),t.sessions.length===0&&this.resetIdleTimer();}get activeSessions(){return T().sessions.length}async ensureRunning(){return await this.healthCheck()?true:(await this.startServer(),this.waitForReady())}get isRunning(){return this.process!==null&&!this.process.killed}async stop(){this.idleTimer&&clearTimeout(this.idleTimer),this.process&&(this.process.kill("SIGTERM"),await new Promise(e=>{let t=setTimeout(()=>{this.process&&!this.process.killed&&this.process.kill("SIGKILL"),e();},5e3);this.process?this.process.on("exit",()=>{clearTimeout(t),e();}):(clearTimeout(t),e());}),this.process=null);}async startServer(){let e=join(this.projectRoot,"apps","server","dist","main.js"),t=join(this.projectRoot,"node_modules","@devflow-tools","server","dist","main.js"),s=existsSync(e)?e:t;this.process=spawn("node",[s],{cwd:this.projectRoot,env:{...process.env,NODE_ENV:process.env.NODE_ENV||"development"},stdio:["ignore","pipe","pipe"]}),this.process.stdout?.on("data",o=>{}),this.process.stderr?.on("data",o=>{}),this.process.on("exit",o=>{this.process=null,this.onShutdown&&this.onShutdown();}),this.process.on("error",()=>{this.process=null;});}async waitForReady(){let e=Date.now()+3e4;for(;Date.now()<e;){if(await this.healthCheck())return true;await new Promise(t=>setTimeout(t,It));}return false}healthCheck(){return new Promise(e=>{let t=new URL(Rt),s=request({hostname:t.hostname,port:t.port,path:t.pathname,method:"GET",timeout:2e3},o=>{e(o.statusCode===200);});s.on("error",()=>e(false)),s.on("timeout",()=>{s.destroy(),e(false);}),s.end();})}resetIdleTimer(){this.idleTimer&&clearTimeout(this.idleTimer),!(T().sessions.length>0)&&(this.idleTimer=setTimeout(()=>{this.stop();},Et).unref());}};var m=join(homedir(),".devflow","telemetry-buffer.jsonl"),W=500;function Dt(){let a=join(homedir(),".devflow");existsSync(a)||mkdirSync(a,{recursive:true});}var I=class{append(e){try{Dt();let t=[];existsSync(m)&&(t=readFileSync(m,"utf-8").split(`
2
+ `).filter(s=>s.trim())),t.push(JSON.stringify(e)),t.length>W&&(t=t.slice(t.length-W)),writeFileSync(m,t.join(`
3
+ `)+`
4
+ `);}catch{}}readAll(){try{return existsSync(m)?readFileSync(m,"utf-8").split(`
5
+ `).filter(e=>e.trim()):[]}catch{return []}}clear(){try{existsSync(m)&&writeFileSync(m,"");}catch{}}get size(){return this.readAll().length}};var v=join(homedir(),".devflow","hook-daemon.sock");join(homedir(),".devflow");var jt=600*1e3,Y={Agent:"get_project_context",Bash:"get_project_context",Glob:"get_project_context",Grep:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function At(a){switch(a){case "WebSearch":case "WebFetch":return 1;case "Agent":return 3;case "Grep":case "Glob":return 3;case "Bash":return 5;default:return 2}}function Ut(a){switch(a){case "Read":case "Glob":return "file_read";case "Write":case "Edit":case "NotebookEdit":return "file_write";case "Bash":return "bash_command";case "Agent":return "subagent";case "Skill":return "skill_invoke";default:return "tool_use"}}function tt(a){let e=createHash("sha256").update(a).digest("hex").slice(0,16);return join(process.env.TMPDIR||"/tmp",".devflow-receipts",`${e}.json`)}function Z(a){try{let e=tt(a);return existsSync(e)?JSON.parse(readFileSync(e,"utf-8")):{}}catch{return {}}}function P(a,e){try{let t=tt(a),s=join(process.env.TMPDIR||"/tmp",".devflow-receipts");existsSync(s)||mkdirSync(s,{recursive:!0}),writeFileSync(t,JSON.stringify(e));}catch{}}var U=class{constructor(e){this.sessionId=null;this.eventMap=[];this.idleTimer=null;this.projectRoot=e,this.projectName=e.split("/").pop()||"unknown",this.telemetry=new w,this.skillManager=new _,this.serverManager=new R(e),this.telemetryBuffer=new I;}async start(){try{existsSync(v)&&unlinkSync(v);}catch{}let e=createServer(t=>{this.resetIdleTimer();let s="";t.on("data",async o=>{s+=o.toString();let n=s.split(`
6
+ `);s=n.pop()||"";for(let r of n)if(r.trim())try{let i=JSON.parse(r),c=await this.handleRequest(i);c!==null&&t.write(JSON.stringify(c)+`
7
+ `);}catch(i){t.write(JSON.stringify({error:i.message})+`
8
+ `);}}),t.on("error",()=>{});});e.on("error",t=>{console.error("[devflow-daemon] Server error:",t.message);}),e.listen(v,()=>{console.error("[devflow-daemon] Listening on",v);}),this.flushBuffer(),this.resetIdleTimer();}resetIdleTimer(){this.idleTimer&&clearTimeout(this.idleTimer),this.idleTimer=setTimeout(()=>{console.error("[devflow-daemon] Idle timeout, exiting"),this.shutdown();},jt).unref();}async handleRequest(e){switch(e.type){case "pre-tool-use":return this.handlePreToolUse(e.input);case "post-tool-use":return this.handlePostToolUse(e.input);case "stop":return await this.handleStop(),{status:"ok"};default:return {error:"unknown request type"}}}async handlePreToolUse(e){try{if(!e||e.trim()==="")return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}};let t=JSON.parse(e);if(this.sessionId||(this.sessionId=`session:${this.projectName}:${Date.now().toString(36)}`,this.telemetry.sendSessionStart(this.sessionId,this.projectRoot,Date.now()).catch(()=>{})),t.tool_name==="Skill"){let l=t.tool_input?.skill;if(l&&l.startsWith("devflow:")){let D=x(l),y=this.skillManager.startExecution(l,{required_mcp_tools:D});this.telemetry.sendExecutionStart(y,this.sessionId,l,Date.now()).catch(()=>{});}}let s=this.skillManager.detect(),o=t.tool_name.startsWith("mcp__"),n=o?t.tool_name.replace(/^mcp__[^_]+__/,""):void 0,r=s?.required_mcp_tools??[],i=r.length>0,c=i&&!o&&t.tool_name!=="Skill",p;if(c)p={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:`\u5EFA\u8BAE\u5148\u8C03\u7528 ${r[0]||"get_project_context"} \u83B7\u53D6\u9879\u76EE\u4E0A\u4E0B\u6587\uFF0C\u7ED3\u679C\u4F1A\u66F4\u7CBE\u786E\u3002`}};else if(!o&&t.tool_name!=="Skill"){let D=Z(this.projectRoot).bypassCount??0,y=Y[t.tool_name],et=y?At(t.tool_name):1/0;y&&D>=et?p={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:`\u5DF2\u591A\u6B21\u4F7F\u7528 ${t.tool_name}\uFF0C\u5EFA\u8BAE\u5207\u6362\u5230 ${y} \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`}}:p={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}};}else p={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}};let g=this.skillManager.getCurrentExecutionId()??this.sessionId,f={eventId:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`,executionId:g,sessionId:this.sessionId,timestamp:Date.now(),toolName:t.tool_name,toolType:o?"mcp":t.tool_name==="Agent"?"subagent":"direct",isMcpTool:o,mcpToolName:n,mcpEnforced:o&&i,mcpFallback:!o&&i,kind:Ut(t.tool_name),input:t.tool_input,duration:0,blocked:p&&p.hookSpecificOutput?.permissionDecision==="deny"};return this.telemetry.sendEvent(f).then(l=>{l&&this.eventMap.push({toolName:t.tool_name,eventId:l,timestamp:f.timestamp});}).catch(()=>{}),p}catch{return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}}}popEventFromMap(e){let t=this.eventMap.filter(o=>o.toolName===e);if(t.length===0)return null;t.sort((o,n)=>Math.abs(o.timestamp-Date.now())-Math.abs(n.timestamp-Date.now()));let s=t[0];return this.eventMap=this.eventMap.filter(o=>o.eventId!==s.eventId),s}enforcePostToolUse(e){if(!Y[e])return null;let t=Z(this.projectRoot),s=t.lastMcpCall??0;if(Math.floor(Date.now()/1e3)-s<30)return null;let n=t.bypassCount??0;if(n===0)return P(this.projectRoot,{...t,bypassCount:1}),null;let r=n+1;P(this.projectRoot,{...t,bypassCount:r});let i;switch(e){case "Agent":i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u4F7F\u7528\u4E86 Agent \u5B50\u4EE3\u7406\u6267\u884C\u641C\u7D22\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Bash":i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Bash \u547D\u4EE4\u641C\u7D22\u4EE3\u7801\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Glob":i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Glob \u5339\u914D\u6587\u4EF6\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Grep":i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Grep \u641C\u7D22\u4EE3\u7801\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "WebSearch":case "WebFetch":i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86\u7F51\u7EDC\u641C\u7D22\u3002\u4E0B\u6B21\u8BF7\u7528 get_knowledge MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;default:i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u8BF7\u4F7F\u7528 DevFlow MCP \u5DE5\u5177\u66FF\u4EE3\u76F4\u63A5\u8C03\u7528\u3002`;}return r>=3?i=`${i} \u5DF2\u8FBE\u5230\u6700\u5927\u8FDD\u89C4\u6B21\u6570\uFF0C\u540E\u7EED\u7ED5\u8FC7\u5C06\u88AB\u786C\u963B\u6B62\u3002`:r>=2?i=`${i} \u4E0B\u6B21\u8FDD\u89C4\u5C06\u88AB\u963B\u6B62\u3002`:i=`${i} \u8BF7\u7ACB\u5373\u7EA0\u6B63\u3002`,i}async handlePostToolUse(e){if(!e)return null;let t;try{t=JSON.parse(e);}catch{return null}let s=t.tool_name||"",o=t.tool_input||{},n=t.tool_response;if(s==="Skill"){let h=o?.skill;if(h&&h.startsWith("devflow:")){let f=x(h);this.skillManager.startExecution(h,{required_mcp_tools:f}),P(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}}s.startsWith("mcp__")&&P(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});let r=this.popEventFromMap(s);if(r&&this.sessionId){let h=typeof n=="string"?n:JSON.stringify(n),f=h.length>5e3?{_truncated:true,_originalSize:h.length,text:h.slice(0,5e3)}:n,l=Date.now()-r.timestamp;this.sendHttp("/api/telemetry/tool-call/output",{eventId:r.eventId,sessionId:this.sessionId,output:f,duration:l,completedAt:Date.now()});}let i=typeof n=="object"&&n!==null?n:null,c=i?.exitCode,p=i?.stderr;s==="Bash"&&c!==void 0&&c!==0&&this.sendHttp(`/api/memory/events?rootPath=${encodeURIComponent(this.projectRoot)}`,{id:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:this.sessionId??`session:${this.projectName}`,tool:s,command:o.command||s,exitCode:c,stderr:p??null,durationMs:0,createdAt:Date.now()});let g=this.enforcePostToolUse(s);return g?(console.error("[devflow-daemon] Enforcement:",g),{hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:JSON.stringify(g)}}):null}sendHttp(e,t){let s=JSON.stringify(t),o=new URL(e,"http://127.0.0.1:13337"),n=request({hostname:o.hostname,port:o.port,path:o.pathname+o.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":"dev-key-000000","Content-Length":Buffer.byteLength(s)},timeout:5e3},r=>{r.statusCode===200&&this.flushBuffer();});n.on("error",()=>{this.telemetryBuffer.append({path:e,data:t,timestamp:Date.now()});}),n.write(s),n.end();}flushBuffer(){let e=this.telemetryBuffer.readAll();if(e.length!==0){for(let t of e)try{let s=JSON.parse(t),o=JSON.stringify(s.data),n=new URL(s.path,"http://127.0.0.1:13337"),r=request({hostname:n.hostname,port:n.port,path:n.pathname+n.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":"dev-key-000000","Content-Length":Buffer.byteLength(o)},timeout:5e3},()=>{});r.on("error",()=>{}),r.write(o),r.end();}catch{}this.telemetryBuffer.clear();}}async handleStop(){this.sessionId&&(this.sendHttp(`/api/telemetry/sessions/${this.sessionId}/close`,{finishedAt:Date.now()}),this.serverManager.removeRef(this.sessionId)),this.sendHttp(`/api/memory/process?rootPath=${encodeURIComponent(this.projectRoot)}`,{}),await this.telemetry.flushCache().catch(()=>{}),this.shutdown();}shutdown(){this.idleTimer&&clearTimeout(this.idleTimer);try{unlinkSync(v);}catch{}this.serverManager.stop(),process.exit(0);}},Lt=process.argv[2]||process.env.CLAUDE_PROJECT_DIR||process.cwd(),Ft=new U(Lt);Ft.start().catch(a=>{console.error("[devflow-daemon] Failed to start:",a),process.exit(1);});
@@ -1,2 +1,2 @@
1
- import {request}from'http';import {existsSync,readFileSync,writeFileSync,mkdirSync}from'fs';import {join}from'path';import {homedir}from'os';import {createHash}from'crypto';var T=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",L=process.env.DEVFLOW_API_KEY||"dev-key-000000",v=process.env.CLAUDE_PROJECT_DIR||process.cwd(),C=join(homedir(),".devflow","session-id"),w=join(homedir(),".devflow","event-map.json"),F=new Set(["Agent","Bash","Glob","Grep","WebSearch","WebFetch"]);function b(r,s){return new Promise(t=>{let c=JSON.stringify(s),o=new URL(r,T),n=request({hostname:o.hostname,port:o.port,path:o.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":L,"Content-Length":Buffer.byteLength(c)},timeout:5e3},()=>t());n.on("error",()=>t()),n.write(c),n.end();})}function J(){try{if(existsSync(C))return readFileSync(C,"utf-8").trim()}catch{}return null}function W(r,s){try{if(!existsSync(w))return null;let t=JSON.parse(readFileSync(w,"utf-8")),c=t.filter(l=>l.toolName===r);if(c.length===0)return null;let o=s??Date.now();c.sort((l,d)=>Math.abs(l.timestamp-o)-Math.abs(d.timestamp-o));let n=c[0],e=t.findIndex(l=>l.eventId===n.eventId);return e!==-1&&t.splice(e,1),writeFileSync(w,JSON.stringify(t)),{eventId:n.eventId,timestamp:n.timestamp}}catch{return null}}function P(){let r=createHash("sha256").update(v).digest("hex").slice(0,16);return join(process.env.TMPDIR||"/tmp",".devflow-receipts",`${r}.json`)}function U(){try{let r=P();return existsSync(r)?JSON.parse(readFileSync(r,"utf-8")):{}}catch{return {}}}function k(r){try{let s=P(),t=join(process.env.TMPDIR||"/tmp",".devflow-receipts");existsSync(t)||mkdirSync(t,{recursive:!0}),writeFileSync(s,JSON.stringify(r));}catch{}}function B(r){if(!F.has(r))return null;let s=U(),t=s.lastMcpCall??0;if(Math.floor(Date.now()/1e3)-t<30)return null;let o=s.bypassCount??0;if(o===0)return k({...s,bypassCount:1}),null;let n=o+1;k({...s,bypassCount:n});let e;switch(r){case "Agent":e=`\u7B2C ${n} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u4F7F\u7528\u4E86 Agent \u5B50\u4EE3\u7406\u6267\u884C\u641C\u7D22\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Bash":e=`\u7B2C ${n} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Bash \u547D\u4EE4\u641C\u7D22\u4EE3\u7801\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Glob":e=`\u7B2C ${n} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Glob \u5339\u914D\u6587\u4EF6\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Grep":e=`\u7B2C ${n} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Grep \u641C\u7D22\u4EE3\u7801\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "WebSearch":case "WebFetch":e=`\u7B2C ${n} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86\u7F51\u7EDC\u641C\u7D22\u3002\u4E0B\u6B21\u8BF7\u7528 get_knowledge MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;default:e=`\u7B2C ${n} \u6B21\u8FDD\u89C4\uFF1A\u8BF7\u4F7F\u7528 DevFlow MCP \u5DE5\u5177\u66FF\u4EE3\u76F4\u63A5\u8C03\u7528\u3002`;}return n>=3?e=`${e} \u5DF2\u8FBE\u5230\u6700\u5927\u8FDD\u89C4\u6B21\u6570\uFF0C\u540E\u7EED\u7ED5\u8FC7\u5C06\u88AB\u786C\u963B\u6B62\u3002`:n>=2?e=`${e} \u4E0B\u6B21\u8FDD\u89C4\u5C06\u88AB\u963B\u6B62\u3002`:e=`${e} \u8BF7\u7ACB\u5373\u7EA0\u6B63\u3002`,e}async function q(r){if(!r)return null;let s;try{s=JSON.parse(r);}catch{return null}let t=s.tool_name||"",c=s.tool_input||{},o=s.tool_response||{},n=J(),e=W(t,Date.now());if(e&&n){let a=typeof o=="string"?o:JSON.stringify(o),h=a.length>5e3?{_truncated:true,_originalSize:a.length,text:a.slice(0,5e3)}:o,i=Date.now()-e.timestamp;b("/api/telemetry/tool-call/output",{eventId:e.eventId,sessionId:n,output:h,duration:i,completedAt:Date.now()}).catch(()=>{});}let l=typeof c=="object"&&c.command?c.command:"",d=typeof o=="object"&&o!==null?o:null,f=d?.exitCode,R=d?.stderr;if(t==="Bash"&&f!==void 0&&f!==0){let a={id:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:n??`session:${v.split("/").pop()||"default"}`,tool:t,command:l||t,exitCode:f,stderr:R??null,durationMs:0,createdAt:Date.now()};b(`/api/memory/events?rootPath=${encodeURIComponent(v)}`,a).catch(()=>{});}let M=["get_project_context","get_knowledge","get_memory","search_symbol","get_dependency_graph","react_diagnose_bug","react_new_component","react_audit_performance","vue_diagnose_bug","nest_diagnose_bug","nextjs_diagnose_bug","electron_diagnose_bug","graphql_diagnose_bug","run_workflow"],u=t.replace("mcp__devflow__","");if(t.startsWith("mcp__devflow__")&&M.includes(u)){let a=typeof o=="object"&&o!==null?o:null,h=["files","results","data","result","memories","nodes","chunks"],i=0,p=a?.data??a;if(Array.isArray(p))i=p.length;else if(typeof p=="object"&&p!==null){let y=p;for(let I of h)Array.isArray(y[I])&&(i+=y[I].length);i===0&&Object.keys(y).length>0&&(i=1);}let j=JSON.stringify(o),D=Buffer.byteLength(j,"utf8"),N={id:`metric:${Date.now()}:${Math.random().toString(36).slice(2,11)}`,toolCallEventId:e?.eventId??`unknown:${Date.now()}`,sessionId:n??"",toolName:u,query:typeof c=="object"&&c.query?c.query:void 0,status:a?.error?"error":i===0?"empty":"success",resultCount:i,resultSizeBytes:D,latencyMs:e?Date.now()-e.timestamp:0,engine:u.startsWith("get_project_context")||u==="search_symbol"||u==="get_dependency_graph"?"codegraph":u==="get_knowledge"?"knowledge":u==="get_memory"?"memory":void 0};b("/api/telemetry/tool-metrics",N).catch(()=>{});}let _=B(t);return _&&console.error("[devflow] Enforcement:",_),_}if(process.argv[1]?.endsWith("post-tool-use")||process.argv[1]?.endsWith("post-tool-use.js")){let r="";process.stdin.setEncoding("utf8"),process.stdin.on("data",s=>{r+=s;}),process.stdin.on("end",async()=>{let s=await q(r.trim()||process.argv[2]||"");if(s){let t=JSON.stringify(s);console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:t}}));}}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),5e3).unref();}
2
- export{q as handlePostToolUse};
1
+ import {request}from'http';import {existsSync,readFileSync,writeFileSync,mkdirSync}from'fs';import {join,dirname}from'path';import {homedir}from'os';import {createHash}from'crypto';import {fileURLToPath}from'url';var W=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",$=process.env.DEVFLOW_API_KEY||"dev-key-000000",I=process.env.CLAUDE_PROJECT_DIR||process.cwd(),D=join(homedir(),".devflow","session-id"),b=join(homedir(),".devflow","event-map.json"),F=new Set(["Agent","Bash","Glob","Grep","WebSearch","WebFetch"]);function R(e,n){return new Promise(t=>{let o=JSON.stringify(n),s=new URL(e,W),i=request({hostname:s.hostname,port:s.port,path:s.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":$,"Content-Length":Buffer.byteLength(o)},timeout:5e3},()=>t());i.on("error",()=>t()),i.write(o),i.end();})}function q(){try{if(existsSync(D))return readFileSync(D,"utf-8").trim()}catch{}return null}function B(e,n){try{if(!existsSync(b))return null;let t=JSON.parse(readFileSync(b,"utf-8")),o=t.filter(u=>u.toolName===e);if(o.length===0)return null;let s=n??Date.now();o.sort((u,f)=>Math.abs(u.timestamp-s)-Math.abs(f.timestamp-s));let i=o[0],a=t.findIndex(u=>u.eventId===i.eventId);return a!==-1&&t.splice(a,1),writeFileSync(b,JSON.stringify(t)),{eventId:i.eventId,timestamp:i.timestamp}}catch{return null}}var _=join(homedir(),".devflow");function K(){existsSync(_)||mkdirSync(_,{recursive:true});}function V(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{let e=fileURLToPath(import.meta.url);return join(dirname(e),"..","..")}catch{return process.cwd()}}function G(){let e=V(),n=[join(e,"dist","command-registry.json"),join(e,"command-registry.json")];for(let t of n)try{if(existsSync(t)){let o=JSON.parse(readFileSync(t,"utf8")).commands;if(o)return o}}catch{}return null}function Y(e){let n=G();return n?n[e]?.mcpTools??[]:[]}function z(e){K();let n=join(_,"current-execution-id");if(existsSync(n))return;let t=`exec_${Date.now()}_${Math.random().toString(36).slice(2,11)}`;writeFileSync(n,t),writeFileSync(join(_,"current-skill.json"),JSON.stringify({name:e,required_mcp_tools:Y(e),registeredAt:Date.now()}));}function P(){let e=createHash("sha256").update(I).digest("hex").slice(0,16);return join(process.env.TMPDIR||"/tmp",".devflow-receipts",`${e}.json`)}function H(){try{let e=P();return existsSync(e)?JSON.parse(readFileSync(e,"utf-8")):{}}catch{return {}}}function O(e){try{let n=P(),t=join(process.env.TMPDIR||"/tmp",".devflow-receipts");existsSync(t)||mkdirSync(t,{recursive:!0}),writeFileSync(n,JSON.stringify(e));}catch{}}function X(e){if(!F.has(e))return null;let n=H(),t=(n.bypassCount??0)+1;return O({...n,bypassCount:t}),t>20&&t%10===0?`MCP \u5DE5\u5177\u4F7F\u7528\u7387\u4F4E\uFF1A${t} \u6B21\u76F4\u63A5\u5DE5\u5177\u8C03\u7528\u3002\u5EFA\u8BAE\u4F7F\u7528 get_project_context \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`:null}async function Q(e){if(!e)return null;let n;try{n=JSON.parse(e);}catch{return null}let t=n.tool_name||"",o=n.tool_input||{},s=n.tool_response||{},i=q();if(t==="Skill"){let r=typeof o=="object"&&o.skill?o.skill:"";r&&r.startsWith("devflow:")&&(z(r),O({lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0}));}t.startsWith("mcp__")&&O({lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});let a=B(t,Date.now());if(a&&i){let r=typeof s=="string"?s:JSON.stringify(s),v=r.length>5e3?{_truncated:true,_originalSize:r.length,text:r.slice(0,5e3)}:s,l=Date.now()-a.timestamp;R("/api/telemetry/tool-call/output",{eventId:a.eventId,sessionId:i,output:v,duration:l,completedAt:Date.now()}).catch(()=>{});}let u=typeof o=="object"&&o.command?o.command:"",f=typeof s=="object"&&s!==null?s:null,h=f?.exitCode,C=f?.stderr;if(t==="Bash"&&h!==void 0&&h!==0){let r={id:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:i??`session:${I.split("/").pop()||"default"}`,tool:t,command:u||t,exitCode:h,stderr:C??null,durationMs:0,createdAt:Date.now()};R(`/api/memory/events?rootPath=${encodeURIComponent(I)}`,r).catch(()=>{});}let T=["get_project_context","get_knowledge","get_memory","search_symbol","get_dependency_graph","react_diagnose_bug","react_new_component","react_audit_performance","vue_diagnose_bug","nest_diagnose_bug","nextjs_diagnose_bug","electron_diagnose_bug","graphql_diagnose_bug","run_workflow"],d=t.replace("mcp__devflow__","");if(t.startsWith("mcp__devflow__")&&T.includes(d)){let r=typeof s=="object"&&s!==null?s:null,v=["files","results","data","result","memories","nodes","chunks"],l=0,m=r?.data??r;if(Array.isArray(m))l=m.length;else if(typeof m=="object"&&m!==null){let S=m;for(let E of v)Array.isArray(S[E])&&(l+=S[E].length);l===0&&Object.keys(S).length>0&&(l=1);}let j=JSON.stringify(s),x=Buffer.byteLength(j,"utf8"),M={id:`metric:${Date.now()}:${Math.random().toString(36).slice(2,11)}`,toolCallEventId:a?.eventId??`unknown:${Date.now()}`,sessionId:i??"",toolName:d,query:typeof o=="object"&&o.query?o.query:void 0,status:r?.error?"error":l===0?"empty":"success",resultCount:l,resultSizeBytes:x,latencyMs:a?Date.now()-a.timestamp:0,engine:d.startsWith("get_project_context")||d==="search_symbol"||d==="get_dependency_graph"?"codegraph":d==="get_knowledge"?"knowledge":d==="get_memory"?"memory":void 0};R("/api/telemetry/tool-metrics",M).catch(()=>{});}let w=X(t);return w&&console.error("[devflow] Enforcement:",w),w}if(process.argv[1]?.endsWith("post-tool-use")||process.argv[1]?.endsWith("post-tool-use.js")){let e="";process.stdin.setEncoding("utf8"),process.stdin.on("data",n=>{e+=n;}),process.stdin.on("end",async()=>{let n=await Q(e.trim()||process.argv[2]||"");if(n){let t=JSON.stringify(n);console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:t}}));}}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),5e3).unref();}
2
+ export{Q as handlePostToolUse};
@@ -1,2 +1,2 @@
1
- import {existsSync,readdirSync,readFileSync,unlinkSync,mkdirSync,writeFileSync}from'fs';import {join}from'path';import {homedir}from'os';import {request}from'http';var I=1e4,q=3e4,W=300*1e3;function m(o,t,e){return new Promise(n=>{try{let r=new URL(o),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",a=>c.push(a)),s.on("end",()=>{let a=Buffer.concat(c).toString();n(s.statusCode!=null&&s.statusCode>=200&&s.statusCode<300?a:null);});});i.on("error",()=>n(null)),i.on("timeout",()=>{i.destroy(),n(null);}),i.write(t),i.end();}catch{n(null);}})}var f=class{constructor(t){this.retryScheduled=false;this.retryAttempts=0;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??process.env.DEVFLOW_API_KEY??"dev-key-000000",this.cacheDir=t?.cacheDir??join(homedir(),".devflow","telemetry-cache");}async sendEvent(t){let e=this.truncateInput(t.input),n={...t,input:e},r=`${this.apiUrl}/api/telemetry/tool-call`,i=await m(r,JSON.stringify(n),this.apiKey);if(i)try{let s=JSON.parse(i),c=s.data?.eventId??s.eventId??null;if(c)return c}catch{}return this.cacheEvent({type:"tool_call",payload:n}),null}async sendExecutionStart(t,e,n,r){let i={executionId:t,sessionId:e,skillName:n,startedAt:r},s=`${this.apiUrl}/api/telemetry/skill-execution/start`;await m(s,JSON.stringify(i),this.apiKey)||this.cacheEvent({type:"execution_start",payload:i});}async sendSessionStart(t,e,n){let r={id:t,projectRoot:e,startedAt:n,label:e.split("/").pop()??"unknown"},i=`${this.apiUrl}/api/telemetry/sessions`;return await m(i,JSON.stringify(r),this.apiKey)!==null}async flushCache(){if(!existsSync(this.cacheDir))return;let t=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json"));for(let e of t)try{let n=join(this.cacheDir,e),r=readFileSync(n,"utf8"),i=JSON.parse(r),s=i.type==="execution_start"?"/api/telemetry/skill-execution/start":"/api/telemetry/tool-call",c=`${this.apiUrl}${s}`;await m(c,JSON.stringify(i.payload),this.apiKey)&&unlinkSync(n);}catch{}}cacheEvent(t){try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let e=join(this.cacheDir,`${Date.now()}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(e,JSON.stringify(t,null,2)),this.scheduleRetry();}catch{}}scheduleRetry(){if(this.retryScheduled)return;this.retryScheduled=true;let t=Math.min(q*Math.pow(2,this.retryAttempts),W);setTimeout(()=>{this.retryScheduled=false,this.flushCache().then(()=>{this.retryAttempts=0;}).catch(()=>{this.retryAttempts++;});},t).unref();}truncateInput(t){let e=JSON.stringify(t);return e.length<=I?t:{_truncated:true,_original_size:e.length,_preview:e.substring(0,I)+"..."}}};var V=1800*1e3;function X(){return process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow")}var g=class{constructor(t){this.stateDir=X();}detect(){let t=join(this.stateDir,"current-skill.json");if(!existsSync(t))return null;try{let e=readFileSync(t,"utf8"),n=JSON.parse(e);return !n.registeredAt||Date.now()-n.registeredAt>V?(this.endExecution(),null):n}catch{return null}}startExecution(t,e){existsSync(this.stateDir)||mkdirSync(this.stateDir,{recursive:true});let n=this.getCurrentExecutionId();if(n)return n;let r=`exec_${Date.now()}_${Math.random().toString(36).slice(2,11)}`;return writeFileSync(join(this.stateDir,"current-execution-id"),r),writeFileSync(join(this.stateDir,"current-skill.json"),JSON.stringify({name:t,required_mcp_tools:e?.required_mcp_tools??[],registeredAt:Date.now()})),r}getCurrentExecutionId(){let t=join(this.stateDir,"current-execution-id");if(!existsSync(t))return null;try{return readFileSync(t,"utf8").trim()}catch{return null}}endExecution(){let t=join(this.stateDir,"current-skill.json"),e=join(this.stateDir,"current-execution-id");try{existsSync(t)&&unlinkSync(t);}catch{}try{existsSync(e)&&unlinkSync(e);}catch{}}};function d(){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}}function G(o){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:o}}}var h=join(homedir(),".devflow");function A(){existsSync(h)||mkdirSync(h,{recursive:true});}function Z(){try{let o=join(h,"session-id");if(existsSync(o))return readFileSync(o,"utf-8").trim()}catch{}return null}function Q(o){A(),writeFileSync(join(h,"session-id"),o);}function tt(o,t,e){A();let n=join(h,"event-map.json"),r=[];try{existsSync(n)&&(r=JSON.parse(readFileSync(n,"utf-8")));}catch{}r.push({toolName:o,eventId:t,timestamp:e}),writeFileSync(n,JSON.stringify(r));}function et(o){switch(o){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 nt(o){try{if(!o||o.trim()==="")return d();let t=JSON.parse(o),e=process.env.CLAUDE_PROJECT_DIR||process.cwd(),n=e.split("/").pop()||"unknown",r=new f,i=Z();i||(i=`session:${n}:${Date.now().toString(36)}`,Q(i),r.sendSessionStart(i,e,Date.now()).catch(()=>{}));let s=new g;if(t.tool_name==="Skill"){let l=t.tool_input?.skill;if(l&&l.startsWith("devflow:")){let C=s.startExecution(l);r.sendExecutionStart(C,i,l,Date.now()).catch(()=>{});}}let c=s.detect(),a=t.tool_name.startsWith("mcp__devflow__"),M=a?t.tool_name.replace("mcp__devflow__",""):void 0,w=c?.required_mcp_tools??[],_=w.length>0,D=_&&!a,S;D?S=G(`Skill "${c?.name||"unknown"}" requires MCP tools. Use one of: ${w.join(", ")}`):S=d();let R=s.getCurrentExecutionId()??i,E={eventId:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`,executionId:R,sessionId:i,timestamp:Date.now(),toolName:t.tool_name,toolType:a?"mcp":t.tool_name==="Agent"?"subagent":"direct",isMcpTool:a,mcpToolName:M,mcpEnforced:a&&_,mcpFallback:!a&&_,kind:et(t.tool_name),input:t.tool_input,duration:0,blocked:D};return r.sendEvent(E).then(l=>{l&&tt(t.tool_name,l,E.timestamp);}).catch(()=>{}),S}catch{return d()}}if(process.argv[1]?.endsWith("pre-tool-use")||process.argv[1]?.endsWith("pre-tool-use.js")){let o="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{o+=t;}),process.stdin.on("end",async()=>{let t=await nt(o);console.log(JSON.stringify(t));}),process.stdin.on("error",()=>{console.log(JSON.stringify(d())),process.exit(0);}),setTimeout(()=>{console.log(JSON.stringify(d())),process.exit(0);},4e3).unref();}
2
- export{nt as handlePreToolUse};
1
+ import {appendFileSync,existsSync,readdirSync,readFileSync,unlinkSync,mkdirSync,writeFileSync}from'fs';import {join,dirname}from'path';import {homedir}from'os';import {createHash}from'crypto';import {request}from'http';import {fileURLToPath}from'url';var N=1e4,Y=3e4,z=300*1e3;function y(e,t,o){return new Promise(n=>{try{let r=new URL(e),i=request({hostname:r.hostname,port:r.port||80,path:r.pathname+r.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":o,"Content-Length":Buffer.byteLength(t)},timeout:5e3},s=>{let a=[];s.on("data",p=>a.push(p)),s.on("end",()=>{let p=Buffer.concat(a).toString();n(s.statusCode!=null&&s.statusCode>=200&&s.statusCode<300?p:null);});});i.on("error",()=>n(null)),i.on("timeout",()=>{i.destroy(),n(null);}),i.write(t),i.end();}catch{n(null);}})}var S=class{constructor(t){this.retryScheduled=false;this.retryAttempts=0;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??process.env.DEVFLOW_API_KEY??"dev-key-000000",this.cacheDir=t?.cacheDir??join(homedir(),".devflow","telemetry-cache");}async sendEvent(t){let o=this.truncateInput(t.input),n={...t,input:o},r=`${this.apiUrl}/api/telemetry/tool-call`,i=await y(r,JSON.stringify(n),this.apiKey);if(i)try{let s=JSON.parse(i),a=s.data?.eventId??s.eventId??null;if(a)return a}catch{}return this.cacheEvent({type:"tool_call",payload:n}),null}async sendExecutionStart(t,o,n,r){let i={executionId:t,sessionId:o,skillName:n,startedAt:r},s=`${this.apiUrl}/api/telemetry/skill-execution/start`;await y(s,JSON.stringify(i),this.apiKey)||this.cacheEvent({type:"execution_start",payload:i});}async sendSessionStart(t,o,n){let r={id:t,projectRoot:o,startedAt:n,label:o.split("/").pop()??"unknown"},i=`${this.apiUrl}/api/telemetry/sessions`;return await y(i,JSON.stringify(r),this.apiKey)!==null}async flushCache(){if(!existsSync(this.cacheDir))return;let t=readdirSync(this.cacheDir).filter(o=>o.endsWith(".json"));for(let o of t)try{let n=join(this.cacheDir,o),r=readFileSync(n,"utf8"),i=JSON.parse(r),s=i.type==="execution_start"?"/api/telemetry/skill-execution/start":"/api/telemetry/tool-call",a=`${this.apiUrl}${s}`;await y(a,JSON.stringify(i.payload),this.apiKey)&&unlinkSync(n);}catch{}}cacheEvent(t){try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let o=join(this.cacheDir,`${Date.now()}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(o,JSON.stringify(t,null,2)),this.scheduleRetry();}catch{}}scheduleRetry(){if(this.retryScheduled)return;this.retryScheduled=true;let t=Math.min(Y*Math.pow(2,this.retryAttempts),z);setTimeout(()=>{this.retryScheduled=false,this.flushCache().then(()=>{this.retryAttempts=0;}).catch(()=>{this.retryAttempts++;});},t).unref();}truncateInput(t){let o=JSON.stringify(t);return o.length<=N?t:{_truncated:true,_original_size:o.length,_preview:o.substring(0,N)+"..."}}};var nt=1800*1e3;function P(){return process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow")}function ot(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{let e=fileURLToPath(import.meta.url);return join(dirname(e),"..","..")}catch{return process.cwd()}}function rt(){let e=ot(),t=[join(e,"dist","command-registry.json"),join(e,"command-registry.json")],o=[];try{for(let n of t){if(existsSync(n)){let r=JSON.parse(readFileSync(n,"utf8")).commands;if(r)return r}o.push(`${n}: ${existsSync(n)?"found":"missing"}`);}}catch(n){o.push(`error: ${n.message}`);}try{writeFileSync(join(P(),"hook-debug.log"),JSON.stringify({ts:Date.now(),pluginRoot:e,CLAUDE_PLUGIN_ROOT:process.env.CLAUDE_PLUGIN_ROOT??"(unset)",cwd:process.cwd(),paths:o},null,2));}catch{}return null}function A(e){let t=rt();return t?t[e]?.mcpTools??[]:[]}var k=class{constructor(t){this.stateDir=P();}detect(){let t=join(this.stateDir,"current-skill.json");if(!existsSync(t))return null;try{let o=readFileSync(t,"utf8"),n=JSON.parse(o);return !n.registeredAt||Date.now()-n.registeredAt>nt?(this.endExecution(),null):n}catch{return null}}startExecution(t,o){existsSync(this.stateDir)||mkdirSync(this.stateDir,{recursive:true});let n=this.getCurrentExecutionId();if(n)return n;let r=`exec_${Date.now()}_${Math.random().toString(36).slice(2,11)}`;return writeFileSync(join(this.stateDir,"current-execution-id"),r),writeFileSync(join(this.stateDir,"current-skill.json"),JSON.stringify({name:t,required_mcp_tools:o?.required_mcp_tools??[],registeredAt:Date.now()})),r}getCurrentExecutionId(){let t=join(this.stateDir,"current-execution-id");if(!existsSync(t))return null;try{return readFileSync(t,"utf8").trim()}catch{return null}}endExecution(){let t=join(this.stateDir,"current-skill.json"),o=join(this.stateDir,"current-execution-id");try{existsSync(t)&&unlinkSync(t);}catch{}try{existsSync(o)&&unlinkSync(o);}catch{}}};function m(){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}}function U(e){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:e}}}var g=join(homedir(),".devflow");function M(){existsSync(g)||mkdirSync(g,{recursive:true});}function lt(){try{let e=join(g,"session-id");if(existsSync(e))return readFileSync(e,"utf-8").trim()}catch{}return null}function ut(e){M(),writeFileSync(join(g,"session-id"),e);}function pt(e,t,o){M();let n=join(g,"event-map.json"),r=[];try{existsSync(n)&&(r=JSON.parse(readFileSync(n,"utf-8")));}catch{}r.push({toolName:e,eventId:t,timestamp:o}),writeFileSync(n,JSON.stringify(r));}function dt(e){switch(e){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"}}var j={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge",Bash:"get_project_context"};function ht(e){switch(e){case "WebSearch":case "WebFetch":return 1;case "Agent":return 3;case "Grep":case "Glob":return 3;case "Bash":return 5;default:return 2}}function mt(){let e=process.env.CLAUDE_PROJECT_DIR||process.cwd(),t=createHash("sha256").update(e).digest("hex").slice(0,16);return join(process.env.TMPDIR||"/tmp",".devflow-receipts",`${t}.json`)}function $(){try{let e=mt();return existsSync(e)?JSON.parse(readFileSync(e,"utf-8")):{}}catch{return {}}}function gt(e){let t=j[e];if(!t)return null;let n=$().bypassCount??0,r=ht(e);return n<r?null:`Direct tool "${e}" blocked (${n} prior violations). Use mcp__devflow__${t} instead.`}async function ft(e){try{if(!e||e.trim()==="")return m();let t=JSON.parse(e),o=Date.now(),n=process.env.CLAUDE_PROJECT_DIR||process.cwd(),r=n.split("/").pop()||"unknown",i=new S,s=lt();s||(s=`session:${r}:${Date.now().toString(36)}`,ut(s),i.sendSessionStart(s,n,Date.now()).catch(()=>{}));let a=new k;if(t.tool_name==="Skill"){let c=t.tool_input?.skill;if(c&&c.startsWith("devflow:")){let _=a.startExecution(c,{required_mcp_tools:A(c)});i.sendExecutionStart(_,s,c,Date.now()).catch(()=>{});}}let p=a.detect(),u=t.tool_name.startsWith("mcp__"),F=u?t.tool_name.replace(/^mcp__[^_]+__/,""):void 0,w=p?.required_mcp_tools??[],D=w.length>0,E=D&&!u&&t.tool_name!=="Skill",d;if(E){let c=w[0]||"get_project_context";d=U(`\u5EFA\u8BAE\u5148\u8C03\u7528 ${c} \u83B7\u53D6\u9879\u76EE\u4E0A\u4E0B\u6587\uFF0C\u7ED3\u679C\u4F1A\u66F4\u7CBE\u786E\u3002`);}else if(!u&&t.tool_name!=="Skill")if(gt(t.tool_name)){let _=j[t.tool_name]||"get_project_context";d=U(`\u5DF2\u591A\u6B21\u4F7F\u7528 ${t.tool_name}\uFF0C\u5EFA\u8BAE\u5207\u6362\u5230 ${_} \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`);}else d=m();else d=m();let J=a.getCurrentExecutionId()??s,I={eventId:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`,executionId:J,sessionId:s,timestamp:Date.now(),toolName:t.tool_name,toolType:u?"mcp":t.tool_name==="Agent"?"subagent":"direct",isMcpTool:u,mcpToolName:F,mcpEnforced:u&&D,mcpFallback:!u&&D,kind:dt(t.tool_name),input:t.tool_input,duration:0,blocked:d.hookSpecificOutput.permissionDecision==="deny"};i.sendEvent(I).then(c=>{c&&pt(t.tool_name,c,I.timestamp);}).catch(()=>{});try{let c=$(),_={ts:o,tool:t.tool_name,skill:p?.name??"(none)",requiredTools:w,isMcp:u,enforce:E,stateless:!E&&!u&&t.tool_name!=="Skill",bypassCount:c.bypassCount??0,decision:d.hookSpecificOutput.permissionDecision,env_CLAUDE_PLUGIN_ROOT:process.env.CLAUDE_PLUGIN_ROOT??"(unset)",cwd:process.cwd()},W=join(g,"hook-debug.jsonl");appendFileSync(W,JSON.stringify(_)+`
2
+ `);}catch{}return d}catch{return m()}}if(process.argv[1]?.endsWith("pre-tool-use")||process.argv[1]?.endsWith("pre-tool-use.js")){let e="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{e+=t;}),process.stdin.on("end",async()=>{let t=await ft(e);console.log(JSON.stringify(t));}),process.stdin.on("error",()=>{console.log(JSON.stringify(m())),process.exit(0);}),setTimeout(()=>{console.log(JSON.stringify(m())),process.exit(0);},4e3).unref();}export{ft as handlePreToolUse};
@@ -1,20 +1,20 @@
1
1
  {
2
- "name": "@devflow-tools/claude-code-plugin",
3
- "description": "DevFlow — Developer Intelligence Platform: project context, code graph, knowledge base, memory engine, and workflow automation with 14 domain plugins",
4
- "version": "0.8.1",
5
- "author": {
6
- "name": "DevFlow"
7
- },
8
- "homepage": "https://github.com/shilongfeicool/dev-flow",
9
- "repository": "https://github.com/shilongfeicool/dev-flow",
10
- "license": "MIT",
11
- "keywords": [
12
- "devflow",
13
- "context",
14
- "knowledge-base",
15
- "code-graph",
16
- "memory",
17
- "workflow",
18
- "development"
19
- ]
20
- }
2
+ "name": "devflow",
3
+ "description": "DevFlow \u2014 Developer Intelligence Platform: project context, code graph, knowledge base, memory engine, and workflow automation with 14 domain plugins",
4
+ "version": "0.12.6",
5
+ "author": {
6
+ "name": "DevFlow"
7
+ },
8
+ "homepage": "https://github.com/shilongfeicool/dev-flow",
9
+ "repository": "https://github.com/shilongfeicool/dev-flow",
10
+ "license": "MIT",
11
+ "keywords": [
12
+ "devflow",
13
+ "context",
14
+ "knowledge-base",
15
+ "code-graph",
16
+ "memory",
17
+ "workflow",
18
+ "development"
19
+ ]
20
+ }
@@ -3,6 +3,44 @@ name: devflow:animation
3
3
  description: Animation 动画专家 — CSS 动画、Framer Motion、GSAP、Lottie、性能优化
4
4
  ---
5
5
 
6
+ ## 工作流(必须按顺序执行)
7
+
8
+ ### Step 1: 获取精确上下文
9
+ 调用 `mcp__devflow__get_project_context` 获取与用户问题相关的代码、记忆和知识库信息。
10
+ 这一步确保你拿到的是项目真实状态,而非凭空猜测。
11
+
12
+ ### Step 2: 分析
13
+ 基于 Step 1 返回的上下文,分析问题根因或设计方案。
14
+ 此时可以自由使用 Read 查看具体文件。
15
+
16
+ ### Step 3: 执行
17
+ 根据分析结果修改代码,逐文件编辑。
18
+
19
+ ### Step 4: 验证
20
+ 运行类型检查和测试,确保改动正确。
21
+
22
+ ## 可用工具
23
+
24
+ | 工具 | 何时调用 |
25
+ |------|----------|
26
+ | `mcp__devflow__get_project_context` | 每次任务第一步(必须) |
27
+ | `mcp__devflow__animation_suggest` | 按需调用 |
28
+
29
+ ## 规则
30
+ - 永远从 `mcp__devflow__get_project_context` 开始——你不知道项目里有什么
31
+ - 上下文返回后,用 Read 确认关键文件
32
+ - 改完代码后跑类型检查
33
+
34
+ ## 规则
35
+ - 永远从 `get_project_context` 开始——你不知道项目里有什么
36
+ - 上下文返回后,用 Read 确认关键文件
37
+ - 改完代码后跑类型检查
38
+
39
+ ## 规则
40
+ - 永远从 `animation_suggest` 开始——你不知道项目里有什么
41
+ - 上下文返回后,用 Read 确认关键文件
42
+ - 改完代码后跑类型检查
43
+
6
44
  # ✨ ANIMATION
7
45
 
8
46
  你是一个动画专家。精通 CSS Animation、Framer Motion、GSAP。动画必须 60fps 且支持 prefers-reduced-motion。
@@ -29,10 +67,3 @@ description: Animation 动画专家 — CSS 动画、Framer Motion、GSAP、Lott
29
67
 
30
68
  - animation-docs: 2024 — https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animations
31
69
 
32
- ## 工作流
33
-
34
- | 场景 | 触发方式 |
35
- |------|----------|
36
- | 动画审计:性能检查 → 可访问性 → 实现质量 | 调用 MCP 工具 `animation_audit:animation` |
37
- | 新建动画:需求分析 → 选型建议 → 实现 → 性能验证 | 调用 MCP 工具 `animation_new:animation` |
38
- | 动画 GPU 迁移:扫描 → 替换为 GPU 属性 → 验证 | 调用 MCP 工具 `animation_migrate:gpu-animation` |
@@ -1,36 +1,53 @@
1
1
  ---
2
2
  name: devflow:context
3
3
  description: 检索项目上下文 — 自然语言查询相关代码、符号、依赖关系
4
- required_mcp_tools:
5
- - get_project_context
6
- - search_symbol
7
4
  ---
8
- ## MANDATORY FIRST STEP - Skill Registration
9
5
 
10
- > **IMPORTANT: Before ANY other action, execute the registration command below. This is NON-NEGOTIABLE.**
6
+ ## 工作流(必须按顺序执行)
11
7
 
12
- ```bash
13
- 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
14
- ```
8
+ ### Step 1: 获取精确上下文
9
+ 调用 `mcp__devflow__get_project_context` 获取与用户问题相关的代码、记忆和知识库信息。
10
+ 这一步确保你拿到的是项目真实状态,而非凭空猜测。
15
11
 
16
- After registration, you **MUST** use ONLY these MCP tools for all work in this skill:
17
- - `mcp__devflow__get_project_context`
18
- - `mcp__devflow__search_symbol`
12
+ ### Step 2: 分析
13
+ 基于 Step 1 返回的上下文,分析问题根因或设计方案。
14
+ 此时可以自由使用 Read 查看具体文件。
19
15
 
20
- **Never use direct tools** (Read, Write, Edit, Bash, Grep, etc.) for tasks that can be handled by the MCP tools above.
16
+ ### Step 3: 执行
17
+ 根据分析结果修改代码,逐文件编辑。
21
18
 
22
- When the skill execution is complete, clean up:
23
- ```bash
24
- rm -f ~/.devflow/current-skill.json ~/.devflow/current-execution-id
25
- ```
19
+ ### Step 4: 验证
20
+ 运行类型检查和测试,确保改动正确。
21
+
22
+ ## 可用工具
23
+
24
+ | 工具 | 何时调用 |
25
+ |------|----------|
26
+ | `mcp__devflow__get_project_context` | 每次任务第一步(必须) |
27
+ | `mcp__devflow__get_project_context` | 按需调用 |
28
+ | `mcp__devflow__search_symbol` | 按需调用 |
26
29
 
30
+ ## 规则
31
+ - 永远从 `mcp__devflow__get_project_context` 开始——你不知道项目里有什么
32
+ - 上下文返回后,用 Read 确认关键文件
33
+ - 改完代码后跑类型检查
34
+
35
+ ## 规则
36
+ - 永远从 `get_project_context` 开始——你不知道项目里有什么
37
+ - 上下文返回后,用 Read 确认关键文件
38
+ - 改完代码后跑类型检查
39
+
40
+ ## 规则
41
+ - 永远从 `get_project_context` 开始——你不知道项目里有什么
42
+ - 上下文返回后,用 Read 确认关键文件
43
+ - 改完代码后跑类型检查
27
44
 
28
45
  # /devflow:context
29
46
 
30
47
  ## 执行
31
48
 
32
49
  1. 如果用户的查询包含中文,先将其中的中文关键词翻译为英文代码术语(空格分隔)
33
- 2. 调用 `get_project_context` MCP 工具:
50
+ 2. 调用 `mcp__devflow__get_project_context` MCP 工具:
34
51
 
35
52
  ```
36
53
  query = 用户的原始查询内容(包含中文)
@@ -38,6 +55,8 @@ expandedTerms = 你翻译的英文关键词(如 "code review audit module")
38
55
  mode = "fast" (首次调用;如需更深入结果,再改用 "deep")
39
56
  ```
40
57
 
58
+ 也可以使用 `mcp__devflow__search_symbol` 按符号名搜索代码。
59
+
41
60
  示例:
42
61
  - 用户查询 "代码审核模块的流程" → expandedTerms = "code review audit module workflow pipeline process"
43
62
  - 用户查询 "用户登录认证逻辑" → expandedTerms = "user login authentication auth logic service handler"
@@ -3,6 +3,44 @@ name: devflow:css
3
3
  description: CSS 专家 — 布局调试、响应式审查、BEM 规范、性能优化
4
4
  ---
5
5
 
6
+ ## 工作流(必须按顺序执行)
7
+
8
+ ### Step 1: 获取精确上下文
9
+ 调用 `mcp__devflow__get_project_context` 获取与用户问题相关的代码、记忆和知识库信息。
10
+ 这一步确保你拿到的是项目真实状态,而非凭空猜测。
11
+
12
+ ### Step 2: 分析
13
+ 基于 Step 1 返回的上下文,分析问题根因或设计方案。
14
+ 此时可以自由使用 Read 查看具体文件。
15
+
16
+ ### Step 3: 执行
17
+ 根据分析结果修改代码,逐文件编辑。
18
+
19
+ ### Step 4: 验证
20
+ 运行类型检查和测试,确保改动正确。
21
+
22
+ ## 可用工具
23
+
24
+ | 工具 | 何时调用 |
25
+ |------|----------|
26
+ | `mcp__devflow__get_project_context` | 每次任务第一步(必须) |
27
+ | `mcp__devflow__css_audit_selectors` | 按需调用 |
28
+
29
+ ## 规则
30
+ - 永远从 `mcp__devflow__get_project_context` 开始——你不知道项目里有什么
31
+ - 上下文返回后,用 Read 确认关键文件
32
+ - 改完代码后跑类型检查
33
+
34
+ ## 规则
35
+ - 永远从 `get_project_context` 开始——你不知道项目里有什么
36
+ - 上下文返回后,用 Read 确认关键文件
37
+ - 改完代码后跑类型检查
38
+
39
+ ## 规则
40
+ - 永远从 `css_audit_selectors` 开始——你不知道项目里有什么
41
+ - 上下文返回后,用 Read 确认关键文件
42
+ - 改完代码后跑类型检查
43
+
6
44
  # 🎨 CSS
7
45
 
8
46
  你是一个 CSS 专家。使用现代布局方案(Flexbox/Grid),遵循 BEM 命名,移动优先响应式设计。遇到问题先查 MDN 文档。
@@ -29,10 +67,3 @@ description: CSS 专家 — 布局调试、响应式审查、BEM 规范、性能
29
67
 
30
68
  - mdn-css: 2024 — https://developer.mozilla.org/en-US/docs/Web/CSS
31
69
 
32
- ## 工作流
33
-
34
- | 场景 | 触发方式 |
35
- |------|----------|
36
- | 布局调试:检查元素定位 → 弹性/网格属性 → 修复 | 调用 MCP 工具 `css_debug:layout` |
37
- | 响应式审查:断点检查 → 媒体查询 → 流体方案 | 调用 MCP 工具 `css_audit:responsive` |
38
- | BEM 命名审查:扫描类名 → 规范检查 → 修复建议 | 调用 MCP 工具 `css_review:bem` |
@@ -3,6 +3,44 @@ name: devflow:docker
3
3
  description: Docker 专家 — 镜像优化、Compose 编排、安全扫描、多阶段构建
4
4
  ---
5
5
 
6
+ ## 工作流(必须按顺序执行)
7
+
8
+ ### Step 1: 获取精确上下文
9
+ 调用 `mcp__devflow__get_project_context` 获取与用户问题相关的代码、记忆和知识库信息。
10
+ 这一步确保你拿到的是项目真实状态,而非凭空猜测。
11
+
12
+ ### Step 2: 分析
13
+ 基于 Step 1 返回的上下文,分析问题根因或设计方案。
14
+ 此时可以自由使用 Read 查看具体文件。
15
+
16
+ ### Step 3: 执行
17
+ 根据分析结果修改代码,逐文件编辑。
18
+
19
+ ### Step 4: 验证
20
+ 运行类型检查和测试,确保改动正确。
21
+
22
+ ## 可用工具
23
+
24
+ | 工具 | 何时调用 |
25
+ |------|----------|
26
+ | `mcp__devflow__get_project_context` | 每次任务第一步(必须) |
27
+ | `mcp__devflow__docker_dockerize_project` | 按需调用 |
28
+
29
+ ## 规则
30
+ - 永远从 `mcp__devflow__get_project_context` 开始——你不知道项目里有什么
31
+ - 上下文返回后,用 Read 确认关键文件
32
+ - 改完代码后跑类型检查
33
+
34
+ ## 规则
35
+ - 永远从 `get_project_context` 开始——你不知道项目里有什么
36
+ - 上下文返回后,用 Read 确认关键文件
37
+ - 改完代码后跑类型检查
38
+
39
+ ## 规则
40
+ - 永远从 `docker_dockerize_project` 开始——你不知道项目里有什么
41
+ - 上下文返回后,用 Read 确认关键文件
42
+ - 改完代码后跑类型检查
43
+
6
44
  # 🐳 DOCKER
7
45
 
8
46
  你是一个 Docker 专家。使用多阶段构建优化镜像,遵循安全和性能最佳实践。遇到问题先查官方文档。
@@ -32,11 +70,3 @@ description: Docker 专家 — 镜像优化、Compose 编排、安全扫描、
32
70
 
33
71
  - docker-docs: 26.0.0 — https://docs.docker.com/reference/
34
72
 
35
- ## 工作流
36
-
37
- | 场景 | 触发方式 |
38
- |------|----------|
39
- | 项目 Docker 化:分析项目 → 多阶段 Dockerfile → Compose → 构建验证 | 调用 MCP 工具 `docker_dockerize_project` |
40
- | 镜像优化:体积分析 → 层优化 → 多阶段重构 | 调用 MCP 工具 `docker_optimize:image` |
41
- | 安全扫描:漏洞检测 → 配置审查 → 修复建议 | 调用 MCP 工具 `docker_scan:security` |
42
- | 多服务 Compose 编排:依赖分析 → 网络设计 → 环境管理 | 调用 MCP 工具 `docker_setup:compose` |
@@ -3,6 +3,44 @@ name: devflow:electron
3
3
  description: Electron 专家 — IPC 通信、窗口管理、原生模块、自动更新、安全配置
4
4
  ---
5
5
 
6
+ ## 工作流(必须按顺序执行)
7
+
8
+ ### Step 1: 获取精确上下文
9
+ 调用 `mcp__devflow__get_project_context` 获取与用户问题相关的代码、记忆和知识库信息。
10
+ 这一步确保你拿到的是项目真实状态,而非凭空猜测。
11
+
12
+ ### Step 2: 分析
13
+ 基于 Step 1 返回的上下文,分析问题根因或设计方案。
14
+ 此时可以自由使用 Read 查看具体文件。
15
+
16
+ ### Step 3: 执行
17
+ 根据分析结果修改代码,逐文件编辑。
18
+
19
+ ### Step 4: 验证
20
+ 运行类型检查和测试,确保改动正确。
21
+
22
+ ## 可用工具
23
+
24
+ | 工具 | 何时调用 |
25
+ |------|----------|
26
+ | `mcp__devflow__get_project_context` | 每次任务第一步(必须) |
27
+ | `mcp__devflow__electron_diagnose_bug` | 按需调用 |
28
+
29
+ ## 规则
30
+ - 永远从 `mcp__devflow__get_project_context` 开始——你不知道项目里有什么
31
+ - 上下文返回后,用 Read 确认关键文件
32
+ - 改完代码后跑类型检查
33
+
34
+ ## 规则
35
+ - 永远从 `get_project_context` 开始——你不知道项目里有什么
36
+ - 上下文返回后,用 Read 确认关键文件
37
+ - 改完代码后跑类型检查
38
+
39
+ ## 规则
40
+ - 永远从 `electron_diagnose_bug` 开始——你不知道项目里有什么
41
+ - 上下文返回后,用 Read 确认关键文件
42
+ - 改完代码后跑类型检查
43
+
6
44
  # ⚛️ ELECTRON
7
45
 
8
46
  你是一个 Electron 专家。使用 contextBridge + IPC 安全通信模式。严格遵循 Electron 安全最佳实践。
@@ -32,11 +70,3 @@ description: Electron 专家 — IPC 通信、窗口管理、原生模块、自
32
70
 
33
71
  - electron-docs: 30.0.0 — https://www.electronjs.org/docs/latest/
34
72
 
35
- ## 工作流
36
-
37
- | 场景 | 触发方式 |
38
- |------|----------|
39
- | 新建 IPC 通道:定义 API → Preload 暴露 → 主进程处理 | 调用 MCP 工具 `electron_new:ipc` |
40
- | 安全审计:配置检查 → IPC 暴露面 → CSP → 第三方依赖 | 调用 MCP 工具 `electron_audit:security` |
41
- | 自动更新配置:electron-updater → 发布渠道 → 更新 UI | 调用 MCP 工具 `electron_setup:auto-update` |
42
- | Electron Bug 诊断:进程分类 → IPC 追踪 → 原生调试 | 调用 MCP 工具 `electron_diagnose_bug` |
@@ -3,6 +3,44 @@ name: devflow:git
3
3
  description: Git 专家 — 提交规范、分支审查、Changelog 生成、冲突解决
4
4
  ---
5
5
 
6
+ ## 工作流(必须按顺序执行)
7
+
8
+ ### Step 1: 获取精确上下文
9
+ 调用 `mcp__devflow__get_project_context` 获取与用户问题相关的代码、记忆和知识库信息。
10
+ 这一步确保你拿到的是项目真实状态,而非凭空猜测。
11
+
12
+ ### Step 2: 分析
13
+ 基于 Step 1 返回的上下文,分析问题根因或设计方案。
14
+ 此时可以自由使用 Read 查看具体文件。
15
+
16
+ ### Step 3: 执行
17
+ 根据分析结果修改代码,逐文件编辑。
18
+
19
+ ### Step 4: 验证
20
+ 运行类型检查和测试,确保改动正确。
21
+
22
+ ## 可用工具
23
+
24
+ | 工具 | 何时调用 |
25
+ |------|----------|
26
+ | `mcp__devflow__get_project_context` | 每次任务第一步(必须) |
27
+ | `mcp__devflow__git_analyze_repo` | 按需调用 |
28
+
29
+ ## 规则
30
+ - 永远从 `mcp__devflow__get_project_context` 开始——你不知道项目里有什么
31
+ - 上下文返回后,用 Read 确认关键文件
32
+ - 改完代码后跑类型检查
33
+
34
+ ## 规则
35
+ - 永远从 `get_project_context` 开始——你不知道项目里有什么
36
+ - 上下文返回后,用 Read 确认关键文件
37
+ - 改完代码后跑类型检查
38
+
39
+ ## 规则
40
+ - 永远从 `git_analyze_repo` 开始——你不知道项目里有什么
41
+ - 上下文返回后,用 Read 确认关键文件
42
+ - 改完代码后跑类型检查
43
+
6
44
  # 🔀 GIT
7
45
 
8
46
  你是一个 Git 工作流专家。遵循 Conventional Commits 规范和 GitFlow 分支策略。
@@ -32,11 +70,3 @@ description: Git 专家 — 提交规范、分支审查、Changelog 生成、冲
32
70
  - conventional-commits: 1.0.0 — https://www.conventionalcommits.org/en/v1.0.0/
33
71
  - git-scm-docs: 2.45.0 — https://git-scm.com/docs
34
72
 
35
- ## 工作流
36
-
37
- | 场景 | 触发方式 |
38
- |------|----------|
39
- | 规范化提交: | 调用 MCP 工具 `git_commit` |
40
- | 发布流程: | 调用 MCP 工具 `git_release` |
41
- | 分支清理: | 调用 MCP 工具 `git_audit:branches` |
42
- | 冲突解决: | 调用 MCP 工具 `git_resolve:conflicts` |