@devflow-tools/claude-code-plugin 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.
- package/dist/command-registry.json +84 -19
- package/dist/hooks/hook-client.js +6 -6
- package/dist/hooks/hook-daemon.js +8 -4
- package/dist/hooks/post-tool-use.js +2 -2
- package/dist/hooks/pre-tool-use.js +2 -2
- package/dist/skills/devflow:animation/SKILL.md +63 -0
- package/dist/skills/devflow:css/SKILL.md +63 -0
- package/dist/skills/devflow:git/SKILL.md +67 -0
- package/dist/skills/devflow:graphql/SKILL.md +4 -4
- package/dist/skills/devflow:nest/SKILL.md +4 -4
- package/dist/skills/devflow:nextjs/SKILL.md +4 -4
- package/dist/skills/devflow:react/SKILL.md +6 -6
- package/dist/skills/devflow:tailwind/SKILL.md +4 -2
- package/dist/skills/devflow:ui-layout/SKILL.md +63 -0
- package/dist/skills/devflow:vue/SKILL.md +4 -4
- package/hooks/post-tool-use +6 -2
- package/hooks/session-start +33 -22
- package/package.json +3 -3
- package/skills/devflow:animation/SKILL.md +38 -7
- package/skills/devflow:context/SKILL.md +36 -17
- package/skills/devflow:css/SKILL.md +38 -7
- package/skills/devflow:docker/SKILL.md +38 -8
- package/skills/devflow:electron/SKILL.md +38 -8
- package/skills/devflow:git/SKILL.md +38 -8
- package/skills/devflow:graph/SKILL.md +33 -15
- package/skills/devflow:graphql/SKILL.md +39 -8
- package/skills/devflow:knowledge/SKILL.md +33 -15
- package/skills/devflow:memory/SKILL.md +34 -16
- package/skills/devflow:nest/SKILL.md +39 -8
- package/skills/devflow:nextjs/SKILL.md +39 -7
- package/skills/devflow:performance/SKILL.md +38 -8
- package/skills/devflow:react/SKILL.md +16 -12
- package/skills/devflow:tailwind/SKILL.md +38 -7
- package/skills/devflow:taro/SKILL.md +38 -8
- package/skills/devflow:ui-layout/SKILL.md +38 -7
- package/skills/devflow:vue/SKILL.md +40 -9
- package/skills/devflow:workflow/SKILL.md +33 -15
|
@@ -64,11 +64,11 @@
|
|
|
64
64
|
"devflow:react": {
|
|
65
65
|
"mcpTools": [
|
|
66
66
|
"react_diagnose_bug",
|
|
67
|
-
"react_new_feature",
|
|
68
67
|
"react_audit_performance",
|
|
68
|
+
"react_new_component",
|
|
69
69
|
"react_review_hooks",
|
|
70
70
|
"react_refactor_component",
|
|
71
|
-
"
|
|
71
|
+
"react_new_feature"
|
|
72
72
|
],
|
|
73
73
|
"blockedNative": [
|
|
74
74
|
"grep",
|
|
@@ -85,8 +85,8 @@
|
|
|
85
85
|
"devflow:vue": {
|
|
86
86
|
"mcpTools": [
|
|
87
87
|
"vue_diagnose_bug",
|
|
88
|
-
"
|
|
89
|
-
"
|
|
88
|
+
"vue_debug_reactivity",
|
|
89
|
+
"vue_new_component"
|
|
90
90
|
],
|
|
91
91
|
"blockedNative": [
|
|
92
92
|
"grep",
|
|
@@ -100,9 +100,10 @@
|
|
|
100
100
|
"WebFetch"
|
|
101
101
|
]
|
|
102
102
|
},
|
|
103
|
-
"devflow:
|
|
103
|
+
"devflow:nest": {
|
|
104
104
|
"mcpTools": [
|
|
105
|
-
"
|
|
105
|
+
"nest_diagnose_bug",
|
|
106
|
+
"nest_new_module"
|
|
106
107
|
],
|
|
107
108
|
"blockedNative": [
|
|
108
109
|
"grep",
|
|
@@ -116,9 +117,10 @@
|
|
|
116
117
|
"WebFetch"
|
|
117
118
|
]
|
|
118
119
|
},
|
|
119
|
-
"devflow:
|
|
120
|
+
"devflow:nextjs": {
|
|
120
121
|
"mcpTools": [
|
|
121
|
-
"
|
|
122
|
+
"nextjs_diagnose_bug",
|
|
123
|
+
"nextjs_new_page"
|
|
122
124
|
],
|
|
123
125
|
"blockedNative": [
|
|
124
126
|
"grep",
|
|
@@ -132,10 +134,10 @@
|
|
|
132
134
|
"WebFetch"
|
|
133
135
|
]
|
|
134
136
|
},
|
|
135
|
-
"devflow:
|
|
137
|
+
"devflow:graphql": {
|
|
136
138
|
"mcpTools": [
|
|
137
|
-
"
|
|
138
|
-
"
|
|
139
|
+
"graphql_diagnose_bug",
|
|
140
|
+
"graphql_new_graphql_type"
|
|
139
141
|
],
|
|
140
142
|
"blockedNative": [
|
|
141
143
|
"grep",
|
|
@@ -149,10 +151,25 @@
|
|
|
149
151
|
"WebFetch"
|
|
150
152
|
]
|
|
151
153
|
},
|
|
152
|
-
"devflow:
|
|
154
|
+
"devflow:git": {
|
|
155
|
+
"mcpTools": [
|
|
156
|
+
"git_analyze_repo"
|
|
157
|
+
],
|
|
158
|
+
"blockedNative": [
|
|
159
|
+
"grep",
|
|
160
|
+
"find",
|
|
161
|
+
"rg",
|
|
162
|
+
"ag",
|
|
163
|
+
"Grep",
|
|
164
|
+
"Glob",
|
|
165
|
+
"Explore",
|
|
166
|
+
"WebSearch",
|
|
167
|
+
"WebFetch"
|
|
168
|
+
]
|
|
169
|
+
},
|
|
170
|
+
"devflow:electron": {
|
|
153
171
|
"mcpTools": [
|
|
154
|
-
"
|
|
155
|
-
"graphql_diagnose_bug"
|
|
172
|
+
"electron_diagnose_bug"
|
|
156
173
|
],
|
|
157
174
|
"blockedNative": [
|
|
158
175
|
"grep",
|
|
@@ -198,10 +215,9 @@
|
|
|
198
215
|
"WebFetch"
|
|
199
216
|
]
|
|
200
217
|
},
|
|
201
|
-
"devflow:
|
|
218
|
+
"devflow:animation": {
|
|
202
219
|
"mcpTools": [
|
|
203
|
-
"
|
|
204
|
-
"nextjs_diagnose_bug"
|
|
220
|
+
"animation_suggest"
|
|
205
221
|
],
|
|
206
222
|
"blockedNative": [
|
|
207
223
|
"grep",
|
|
@@ -215,9 +231,58 @@
|
|
|
215
231
|
"WebFetch"
|
|
216
232
|
]
|
|
217
233
|
},
|
|
218
|
-
"devflow:
|
|
234
|
+
"devflow:css": {
|
|
219
235
|
"mcpTools": [
|
|
220
|
-
"
|
|
236
|
+
"css_audit_selectors"
|
|
237
|
+
],
|
|
238
|
+
"blockedNative": [
|
|
239
|
+
"grep",
|
|
240
|
+
"find",
|
|
241
|
+
"rg",
|
|
242
|
+
"ag",
|
|
243
|
+
"Grep",
|
|
244
|
+
"Glob",
|
|
245
|
+
"Explore",
|
|
246
|
+
"WebSearch",
|
|
247
|
+
"WebFetch"
|
|
248
|
+
]
|
|
249
|
+
},
|
|
250
|
+
"devflow:tailwind": {
|
|
251
|
+
"mcpTools": [
|
|
252
|
+
"tailwind_new_component",
|
|
253
|
+
"tailwind_optimize_classes"
|
|
254
|
+
],
|
|
255
|
+
"blockedNative": [
|
|
256
|
+
"grep",
|
|
257
|
+
"find",
|
|
258
|
+
"rg",
|
|
259
|
+
"ag",
|
|
260
|
+
"Grep",
|
|
261
|
+
"Glob",
|
|
262
|
+
"Explore",
|
|
263
|
+
"WebSearch",
|
|
264
|
+
"WebFetch"
|
|
265
|
+
]
|
|
266
|
+
},
|
|
267
|
+
"devflow:taro": {
|
|
268
|
+
"mcpTools": [
|
|
269
|
+
"taro_new_page"
|
|
270
|
+
],
|
|
271
|
+
"blockedNative": [
|
|
272
|
+
"grep",
|
|
273
|
+
"find",
|
|
274
|
+
"rg",
|
|
275
|
+
"ag",
|
|
276
|
+
"Grep",
|
|
277
|
+
"Glob",
|
|
278
|
+
"Explore",
|
|
279
|
+
"WebSearch",
|
|
280
|
+
"WebFetch"
|
|
281
|
+
]
|
|
282
|
+
},
|
|
283
|
+
"devflow:ui-layout": {
|
|
284
|
+
"mcpTools": [
|
|
285
|
+
"ui_layout_generate"
|
|
221
286
|
],
|
|
222
287
|
"blockedNative": [
|
|
223
288
|
"grep",
|
|
@@ -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"),
|
|
2
|
-
`);else if(
|
|
3
|
-
`);else if(
|
|
4
|
-
`);}),process.stdin.on("error",()=>t());}else t();}),
|
|
5
|
-
`)){let
|
|
6
|
-
`).filter(Boolean);for(let
|
|
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
|
|
2
|
-
`)
|
|
3
|
-
`)
|
|
4
|
-
`);}
|
|
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
|
|
2
|
-
export{
|
|
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
|
|
2
|
-
export{
|
|
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};
|
|
@@ -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,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: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` |
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
name: devflow:graphql
|
|
3
3
|
description: GraphQL 专家 — Schema 设计、N+1 检测、Resolver 优化、安全审计
|
|
4
4
|
required_mcp_tools:
|
|
5
|
-
- graphql_new_graphql_type
|
|
6
5
|
- graphql_diagnose_bug
|
|
6
|
+
- graphql_new_graphql_type
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
<HARD-GATE>
|
|
10
10
|
收到 devflow:graphql 时,必须先调用以下 MCP 工具之一:
|
|
11
|
-
|
|
11
|
+
graphql_diagnose_bug / graphql_new_graphql_type
|
|
12
12
|
</HARD-GATE>
|
|
13
13
|
|
|
14
14
|
## MANDATORY FIRST STEP - Skill Registration
|
|
@@ -16,12 +16,12 @@ graphql_new_graphql_type / graphql_diagnose_bug
|
|
|
16
16
|
> **IMPORTANT: Before ANY other action, execute the registration command below. This is NON-NEGOTIABLE.**
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
mkdir -p ~/.devflow && echo '{"name":"devflow:graphql","required_mcp_tools":["
|
|
19
|
+
mkdir -p ~/.devflow && echo '{"name":"devflow:graphql","required_mcp_tools":["graphql_diagnose_bug","graphql_new_graphql_type"],"description":"devflow:graphql skill","registeredAt":'"$(date +%s)000"'}' > ~/.devflow/current-skill.json
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
After registration, you **MUST** use ONLY these MCP tools for all work in this skill:
|
|
23
|
-
- `mcp__devflow__graphql_new_graphql_type`
|
|
24
23
|
- `mcp__devflow__graphql_diagnose_bug`
|
|
24
|
+
- `mcp__devflow__graphql_new_graphql_type`
|
|
25
25
|
|
|
26
26
|
**Never use direct tools** (Read, Write, Edit, Bash, Grep, etc.) for tasks that can be handled by the MCP tools above.
|
|
27
27
|
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
name: devflow:nest
|
|
3
3
|
description: NestJS 专家 — 模块架构、守卫调试、微服务配置、Swagger 生成
|
|
4
4
|
required_mcp_tools:
|
|
5
|
-
- nest_new_module
|
|
6
5
|
- nest_diagnose_bug
|
|
6
|
+
- nest_new_module
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
<HARD-GATE>
|
|
10
10
|
收到 devflow:nest 时,必须先调用以下 MCP 工具之一:
|
|
11
|
-
|
|
11
|
+
nest_diagnose_bug / nest_new_module
|
|
12
12
|
</HARD-GATE>
|
|
13
13
|
|
|
14
14
|
## MANDATORY FIRST STEP - Skill Registration
|
|
@@ -16,12 +16,12 @@ nest_new_module / nest_diagnose_bug
|
|
|
16
16
|
> **IMPORTANT: Before ANY other action, execute the registration command below. This is NON-NEGOTIABLE.**
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
mkdir -p ~/.devflow && echo '{"name":"devflow:nest","required_mcp_tools":["
|
|
19
|
+
mkdir -p ~/.devflow && echo '{"name":"devflow:nest","required_mcp_tools":["nest_diagnose_bug","nest_new_module"],"description":"devflow:nest skill","registeredAt":'"$(date +%s)000"'}' > ~/.devflow/current-skill.json
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
After registration, you **MUST** use ONLY these MCP tools for all work in this skill:
|
|
23
|
-
- `mcp__devflow__nest_new_module`
|
|
24
23
|
- `mcp__devflow__nest_diagnose_bug`
|
|
24
|
+
- `mcp__devflow__nest_new_module`
|
|
25
25
|
|
|
26
26
|
**Never use direct tools** (Read, Write, Edit, Bash, Grep, etc.) for tasks that can be handled by the MCP tools above.
|
|
27
27
|
|