@devflow-tools/claude-code-plugin 0.12.6 → 0.13.1
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 +234 -0
- package/dist/hooks/hook-client.js +6 -6
- package/dist/hooks/hook-daemon.js +7 -7
- 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:docker/SKILL.md +67 -0
- package/dist/skills/devflow:electron/SKILL.md +67 -0
- package/dist/skills/devflow:git/SKILL.md +67 -0
- package/dist/skills/devflow:graphql/SKILL.md +68 -0
- package/dist/skills/devflow:nest/SKILL.md +68 -0
- package/dist/skills/devflow:nextjs/SKILL.md +65 -0
- package/dist/skills/devflow:performance/SKILL.md +67 -0
- package/dist/skills/devflow:react/SKILL.md +85 -0
- package/dist/skills/devflow:tailwind/SKILL.md +65 -0
- package/dist/skills/devflow:taro/SKILL.md +66 -0
- package/dist/skills/devflow:ui-layout/SKILL.md +63 -0
- package/dist/skills/devflow:vue/SKILL.md +74 -0
- 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:start/SKILL.md +24 -17
- 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
|
@@ -60,6 +60,240 @@
|
|
|
60
60
|
"run_workflow"
|
|
61
61
|
],
|
|
62
62
|
"blockedNative": []
|
|
63
|
+
},
|
|
64
|
+
"devflow:react": {
|
|
65
|
+
"mcpTools": [
|
|
66
|
+
"react_diagnose_bug",
|
|
67
|
+
"react_new_feature",
|
|
68
|
+
"react_audit_performance",
|
|
69
|
+
"react_review_hooks",
|
|
70
|
+
"react_refactor_component",
|
|
71
|
+
"react_new_component"
|
|
72
|
+
],
|
|
73
|
+
"blockedNative": [
|
|
74
|
+
"grep",
|
|
75
|
+
"find",
|
|
76
|
+
"rg",
|
|
77
|
+
"ag",
|
|
78
|
+
"Grep",
|
|
79
|
+
"Glob",
|
|
80
|
+
"Explore",
|
|
81
|
+
"WebSearch",
|
|
82
|
+
"WebFetch"
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
"devflow:vue": {
|
|
86
|
+
"mcpTools": [
|
|
87
|
+
"vue_diagnose_bug",
|
|
88
|
+
"vue_new_component",
|
|
89
|
+
"vue_debug_reactivity"
|
|
90
|
+
],
|
|
91
|
+
"blockedNative": [
|
|
92
|
+
"grep",
|
|
93
|
+
"find",
|
|
94
|
+
"rg",
|
|
95
|
+
"ag",
|
|
96
|
+
"Grep",
|
|
97
|
+
"Glob",
|
|
98
|
+
"Explore",
|
|
99
|
+
"WebSearch",
|
|
100
|
+
"WebFetch"
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
"devflow:nest": {
|
|
104
|
+
"mcpTools": [
|
|
105
|
+
"nest_new_module",
|
|
106
|
+
"nest_diagnose_bug"
|
|
107
|
+
],
|
|
108
|
+
"blockedNative": [
|
|
109
|
+
"grep",
|
|
110
|
+
"find",
|
|
111
|
+
"rg",
|
|
112
|
+
"ag",
|
|
113
|
+
"Grep",
|
|
114
|
+
"Glob",
|
|
115
|
+
"Explore",
|
|
116
|
+
"WebSearch",
|
|
117
|
+
"WebFetch"
|
|
118
|
+
]
|
|
119
|
+
},
|
|
120
|
+
"devflow:nextjs": {
|
|
121
|
+
"mcpTools": [
|
|
122
|
+
"nextjs_new_page",
|
|
123
|
+
"nextjs_diagnose_bug"
|
|
124
|
+
],
|
|
125
|
+
"blockedNative": [
|
|
126
|
+
"grep",
|
|
127
|
+
"find",
|
|
128
|
+
"rg",
|
|
129
|
+
"ag",
|
|
130
|
+
"Grep",
|
|
131
|
+
"Glob",
|
|
132
|
+
"Explore",
|
|
133
|
+
"WebSearch",
|
|
134
|
+
"WebFetch"
|
|
135
|
+
]
|
|
136
|
+
},
|
|
137
|
+
"devflow:graphql": {
|
|
138
|
+
"mcpTools": [
|
|
139
|
+
"graphql_new_graphql_type",
|
|
140
|
+
"graphql_diagnose_bug"
|
|
141
|
+
],
|
|
142
|
+
"blockedNative": [
|
|
143
|
+
"grep",
|
|
144
|
+
"find",
|
|
145
|
+
"rg",
|
|
146
|
+
"ag",
|
|
147
|
+
"Grep",
|
|
148
|
+
"Glob",
|
|
149
|
+
"Explore",
|
|
150
|
+
"WebSearch",
|
|
151
|
+
"WebFetch"
|
|
152
|
+
]
|
|
153
|
+
},
|
|
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": {
|
|
171
|
+
"mcpTools": [
|
|
172
|
+
"electron_diagnose_bug"
|
|
173
|
+
],
|
|
174
|
+
"blockedNative": [
|
|
175
|
+
"grep",
|
|
176
|
+
"find",
|
|
177
|
+
"rg",
|
|
178
|
+
"ag",
|
|
179
|
+
"Grep",
|
|
180
|
+
"Glob",
|
|
181
|
+
"Explore",
|
|
182
|
+
"WebSearch",
|
|
183
|
+
"WebFetch"
|
|
184
|
+
]
|
|
185
|
+
},
|
|
186
|
+
"devflow:docker": {
|
|
187
|
+
"mcpTools": [
|
|
188
|
+
"docker_dockerize_project"
|
|
189
|
+
],
|
|
190
|
+
"blockedNative": [
|
|
191
|
+
"grep",
|
|
192
|
+
"find",
|
|
193
|
+
"rg",
|
|
194
|
+
"ag",
|
|
195
|
+
"Grep",
|
|
196
|
+
"Glob",
|
|
197
|
+
"Explore",
|
|
198
|
+
"WebSearch",
|
|
199
|
+
"WebFetch"
|
|
200
|
+
]
|
|
201
|
+
},
|
|
202
|
+
"devflow:performance": {
|
|
203
|
+
"mcpTools": [
|
|
204
|
+
"performance_audit_performance"
|
|
205
|
+
],
|
|
206
|
+
"blockedNative": [
|
|
207
|
+
"grep",
|
|
208
|
+
"find",
|
|
209
|
+
"rg",
|
|
210
|
+
"ag",
|
|
211
|
+
"Grep",
|
|
212
|
+
"Glob",
|
|
213
|
+
"Explore",
|
|
214
|
+
"WebSearch",
|
|
215
|
+
"WebFetch"
|
|
216
|
+
]
|
|
217
|
+
},
|
|
218
|
+
"devflow:animation": {
|
|
219
|
+
"mcpTools": [
|
|
220
|
+
"animation_suggest"
|
|
221
|
+
],
|
|
222
|
+
"blockedNative": [
|
|
223
|
+
"grep",
|
|
224
|
+
"find",
|
|
225
|
+
"rg",
|
|
226
|
+
"ag",
|
|
227
|
+
"Grep",
|
|
228
|
+
"Glob",
|
|
229
|
+
"Explore",
|
|
230
|
+
"WebSearch",
|
|
231
|
+
"WebFetch"
|
|
232
|
+
]
|
|
233
|
+
},
|
|
234
|
+
"devflow:css": {
|
|
235
|
+
"mcpTools": [
|
|
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
|
+
],
|
|
254
|
+
"blockedNative": [
|
|
255
|
+
"grep",
|
|
256
|
+
"find",
|
|
257
|
+
"rg",
|
|
258
|
+
"ag",
|
|
259
|
+
"Grep",
|
|
260
|
+
"Glob",
|
|
261
|
+
"Explore",
|
|
262
|
+
"WebSearch",
|
|
263
|
+
"WebFetch"
|
|
264
|
+
]
|
|
265
|
+
},
|
|
266
|
+
"devflow:taro": {
|
|
267
|
+
"mcpTools": [
|
|
268
|
+
"taro_new_page"
|
|
269
|
+
],
|
|
270
|
+
"blockedNative": [
|
|
271
|
+
"grep",
|
|
272
|
+
"find",
|
|
273
|
+
"rg",
|
|
274
|
+
"ag",
|
|
275
|
+
"Grep",
|
|
276
|
+
"Glob",
|
|
277
|
+
"Explore",
|
|
278
|
+
"WebSearch",
|
|
279
|
+
"WebFetch"
|
|
280
|
+
]
|
|
281
|
+
},
|
|
282
|
+
"devflow:ui-layout": {
|
|
283
|
+
"mcpTools": [
|
|
284
|
+
"ui_layout_generate"
|
|
285
|
+
],
|
|
286
|
+
"blockedNative": [
|
|
287
|
+
"grep",
|
|
288
|
+
"find",
|
|
289
|
+
"rg",
|
|
290
|
+
"ag",
|
|
291
|
+
"Grep",
|
|
292
|
+
"Glob",
|
|
293
|
+
"Explore",
|
|
294
|
+
"WebSearch",
|
|
295
|
+
"WebFetch"
|
|
296
|
+
]
|
|
63
297
|
}
|
|
64
298
|
}
|
|
65
299
|
}
|
|
@@ -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,8 +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';import {spawn}from'child_process';var
|
|
2
|
-
`).filter(s=>s.trim())),
|
|
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
3
|
`)+`
|
|
4
|
-
`);}catch{}}readAll(){try{return existsSync(
|
|
5
|
-
`).filter(
|
|
6
|
-
`);s=
|
|
7
|
-
`);}catch(i){
|
|
8
|
-
`);}}),
|
|
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';import {getLogWriter}from'@devflow-tools/telemetry';var W=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",$=process.env.DEVFLOW_API_KEY||"dev-key-000000",y=process.env.CLAUDE_PROJECT_DIR||process.cwd(),k=join(homedir(),".devflow","session-id"),S=join(homedir(),".devflow","event-map.json"),F=new Set(["Agent","Bash","Glob","Grep","WebSearch","WebFetch"]);function R(e,n){return new Promise(t=>{let r=JSON.stringify(n),o=new URL(e,W),s=request({hostname:o.hostname,port:o.port,path:o.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":$,"Content-Length":Buffer.byteLength(r)},timeout:5e3},()=>t());s.on("error",()=>t()),s.write(r),s.end();})}function q(){try{if(existsSync(k))return readFileSync(k,"utf-8").trim()}catch{}return null}function B(e,n){try{if(!existsSync(S))return null;let t=JSON.parse(readFileSync(S,"utf-8")),r=t.filter(i=>i.toolName===e);if(r.length===0)return null;let o=n??Date.now();r.sort((i,g)=>Math.abs(i.timestamp-o)-Math.abs(g.timestamp-o));let s=r[0],u=t.findIndex(i=>i.eventId===s.eventId);return u!==-1&&t.splice(u,1),writeFileSync(S,JSON.stringify(t)),{eventId:s.eventId,timestamp:s.timestamp}}catch{return null}}function E(e){if(!e)return 0;let n=e;if(typeof e=="string")try{n=JSON.parse(e);}catch{return 1}let t=n,r=["files","results","data","result","memories","nodes","chunks","findings","symbols"],o=t.data??t.structuredContent??t;if(Array.isArray(o))return o.length;if(typeof o=="object"&&o!==null){let s=o,u=0;for(let i of r)Array.isArray(s[i])&&(u+=s[i].length);for(let[,i]of Object.entries(s))i&&typeof i=="object"&&!Array.isArray(i)&&(u+=E(i));return u>0?u:Object.keys(s).length>0?1:0}return 0}var m=join(homedir(),".devflow");function K(){existsSync(m)||mkdirSync(m,{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 r=JSON.parse(readFileSync(t,"utf8")).commands;if(r)return r}}catch{}return null}function Y(e){let n=G();return n?n[e]?.mcpTools??[]:[]}function z(e){K();let n=join(m,"current-execution-id");if(existsSync(n))return;let t=`exec_${Date.now()}_${Math.random().toString(36).slice(2,11)}`;writeFileSync(n,t),writeFileSync(join(m,"current-skill.json"),JSON.stringify({name:e,required_mcp_tools:Y(e),registeredAt:Date.now()}));}function N(){let e=createHash("sha256").update(y).digest("hex").slice(0,16);return join(process.env.TMPDIR||"/tmp",".devflow-receipts",`${e}.json`)}function H(){try{let e=N();return existsSync(e)?JSON.parse(readFileSync(e,"utf-8")):{}}catch{return {}}}function I(e){try{let n=N(),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 I({...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||"",r=n.tool_input||{},o=n.tool_response||{},s=q();if(t==="Skill"){let c=typeof r=="object"&&r.skill?r.skill:"";c&&c.startsWith("devflow:")&&(z(c),I({lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0}));}t.startsWith("mcp__")&&I({lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});let u=getLogWriter(y),i=join(m,"current-execution-id"),g=existsSync(i)?readFileSync(i,"utf-8").trim():`auto-${Date.now()}`,l=B(t,Date.now()),p=typeof o=="object"&&o!==null?o:null,C=JSON.stringify(o),O=Buffer.byteLength(C,"utf8"),w=E(o);if(u.toolCall({tool:t,runId:g,stepId:`step-${g}`,input:r,output:o,duration:l?Date.now()-l.timestamp:0,success:!p?.error,resultCount:w,resultSizeBytes:O,error:p?.error}),l&&s){let c=typeof o=="string"?o:JSON.stringify(o),j=c.length>5e3?{_truncated:true,_originalSize:c.length,text:c.slice(0,5e3)}:o,x=Date.now()-l.timestamp;R("/api/telemetry/tool-call/output",{eventId:l.eventId,sessionId:s,output:j,duration:x,completedAt:Date.now()}).catch(()=>{});}if(t.startsWith("mcp__devflow__")){let c=t.replace("mcp__devflow__","");R("/api/telemetry/tool-metrics",{id:`metric:${Date.now()}:${Math.random().toString(36).slice(2,11)}`,toolCallEventId:l?.eventId??`unknown:${Date.now()}`,sessionId:s??"",toolName:c,query:typeof r=="object"&&r.query?r.query:void 0,status:p?.error?"error":w===0?"empty":"success",resultCount:w,resultSizeBytes:O,latencyMs:l?Date.now()-l.timestamp:0,engine:c.startsWith("get_project_context")||c==="search_symbol"||c==="get_dependency_graph"?"codegraph":c==="get_knowledge"?"knowledge":c==="get_memory"?"memory":void 0}).catch(()=>{});}let P=typeof r=="object"&&r.command?r.command:"",_=p?.exitCode,T=p?.stderr;if(t==="Bash"&&_!==void 0&&_!==0){let c={id:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:s??`session:${y.split("/").pop()||"default"}`,tool:t,command:P||t,exitCode:_,stderr:T??null,durationMs:0,createdAt:Date.now()};R(`/api/memory/events?rootPath=${encodeURIComponent(y)}`,c).catch(()=>{});}let v=X(t);return v&&console.error("[devflow] Enforcement:",v),v}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` |
|