@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.
Files changed (42) hide show
  1. package/dist/command-registry.json +234 -0
  2. package/dist/hooks/hook-client.js +6 -6
  3. package/dist/hooks/hook-daemon.js +7 -7
  4. package/dist/hooks/post-tool-use.js +2 -2
  5. package/dist/hooks/pre-tool-use.js +2 -2
  6. package/dist/skills/devflow:animation/SKILL.md +63 -0
  7. package/dist/skills/devflow:css/SKILL.md +63 -0
  8. package/dist/skills/devflow:docker/SKILL.md +67 -0
  9. package/dist/skills/devflow:electron/SKILL.md +67 -0
  10. package/dist/skills/devflow:git/SKILL.md +67 -0
  11. package/dist/skills/devflow:graphql/SKILL.md +68 -0
  12. package/dist/skills/devflow:nest/SKILL.md +68 -0
  13. package/dist/skills/devflow:nextjs/SKILL.md +65 -0
  14. package/dist/skills/devflow:performance/SKILL.md +67 -0
  15. package/dist/skills/devflow:react/SKILL.md +85 -0
  16. package/dist/skills/devflow:tailwind/SKILL.md +65 -0
  17. package/dist/skills/devflow:taro/SKILL.md +66 -0
  18. package/dist/skills/devflow:ui-layout/SKILL.md +63 -0
  19. package/dist/skills/devflow:vue/SKILL.md +74 -0
  20. package/hooks/post-tool-use +6 -2
  21. package/hooks/session-start +33 -22
  22. package/package.json +3 -3
  23. package/skills/devflow:animation/SKILL.md +38 -7
  24. package/skills/devflow:context/SKILL.md +36 -17
  25. package/skills/devflow:css/SKILL.md +38 -7
  26. package/skills/devflow:docker/SKILL.md +38 -8
  27. package/skills/devflow:electron/SKILL.md +38 -8
  28. package/skills/devflow:git/SKILL.md +38 -8
  29. package/skills/devflow:graph/SKILL.md +33 -15
  30. package/skills/devflow:graphql/SKILL.md +39 -8
  31. package/skills/devflow:knowledge/SKILL.md +33 -15
  32. package/skills/devflow:memory/SKILL.md +34 -16
  33. package/skills/devflow:nest/SKILL.md +39 -8
  34. package/skills/devflow:nextjs/SKILL.md +39 -7
  35. package/skills/devflow:performance/SKILL.md +38 -8
  36. package/skills/devflow:react/SKILL.md +16 -12
  37. package/skills/devflow:start/SKILL.md +24 -17
  38. package/skills/devflow:tailwind/SKILL.md +38 -7
  39. package/skills/devflow:taro/SKILL.md +38 -8
  40. package/skills/devflow:ui-layout/SKILL.md +38 -7
  41. package/skills/devflow:vue/SKILL.md +40 -9
  42. 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"),s=process.argv[2],u=process.argv[3];function t(){s==="pre-tool-use"&&console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}})),process.exit(0);}var o=createConnection(f,()=>{if(s==="stop")o.write(JSON.stringify({type:"stop"})+`
2
- `);else if(s==="post-tool-use")o.write(JSON.stringify({type:"post-tool-use",input:u||""})+`
3
- `);else if(s==="pre-tool-use"){let e="";process.stdin.setEncoding("utf8"),process.stdin.on("data",n=>{e+=n;}),process.stdin.on("end",()=>{o.write(JSON.stringify({type:"pre-tool-use",input:e})+`
4
- `);}),process.stdin.on("error",()=>t());}else t();}),i="";o.on("data",e=>{if(i+=e.toString(),i.includes(`
5
- `)){let n=i.split(`
6
- `).filter(Boolean);for(let r of n)process.stdout.write(r+`
1
+ import {createConnection}from'net';import {join}from'path';import {homedir}from'os';var f=join(homedir(),".devflow","hook-daemon.sock"),n=process.argv[2],d=process.argv[3];function t(){process.exit(1);}var o=createConnection(f,()=>{if(n==="stop")o.write(JSON.stringify({type:"stop"})+`
2
+ `);else if(n==="post-tool-use")o.write(JSON.stringify({type:"post-tool-use",input:d||""})+`
3
+ `);else if(n==="pre-tool-use"){let e="";process.stdin.setEncoding("utf8"),process.stdin.on("data",s=>{e+=s;}),process.stdin.on("end",()=>{o.write(JSON.stringify({type:"pre-tool-use",input:e})+`
4
+ `);}),process.stdin.on("error",()=>t());}else t();}),r="";o.on("data",e=>{if(r+=e.toString(),r.includes(`
5
+ `)){let s=r.split(`
6
+ `).filter(Boolean);for(let i of s)process.stdout.write(i+`
7
7
  `);o.end();}});o.on("error",()=>t());o.on("close",()=>process.exit(0));setTimeout(()=>t(),4e3).unref();
@@ -1,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 O=1e4,nt=3e4,rt=300*1e3;function v(a,t,e){return new Promise(s=>{try{let n=new URL(a),r=request({hostname:n.hostname,port:n.port||80,path:n.pathname+n.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(t)},timeout:5e3},o=>{let i=[];o.on("data",c=>i.push(c)),o.on("end",()=>{let c=Buffer.concat(i).toString();s(o.statusCode!=null&&o.statusCode>=200&&o.statusCode<300?c:null);});});r.on("error",()=>s(null)),r.on("timeout",()=>{r.destroy(),s(null);}),r.write(t),r.end();}catch{s(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 e=this.truncateInput(t.input),s={...t,input:e},n=`${this.apiUrl}/api/telemetry/tool-call`,r=await v(n,JSON.stringify(s),this.apiKey);if(r)try{let o=JSON.parse(r),i=o.data?.eventId??o.eventId??null;if(i)return i}catch{}return this.cacheEvent({type:"tool_call",payload:s}),null}async sendExecutionStart(t,e,s,n){let r={executionId:t,sessionId:e,skillName:s,startedAt:n},o=`${this.apiUrl}/api/telemetry/skill-execution/start`;await v(o,JSON.stringify(r),this.apiKey)||this.cacheEvent({type:"execution_start",payload:r});}async sendSessionStart(t,e,s){let n={id:t,projectRoot:e,startedAt:s,label:e.split("/").pop()??"unknown"},r=`${this.apiUrl}/api/telemetry/sessions`;return await v(r,JSON.stringify(n),this.apiKey)!==null}async flushCache(){if(!existsSync(this.cacheDir))return;let t=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json"));for(let e of t)try{let s=join(this.cacheDir,e),n=readFileSync(s,"utf8"),r=JSON.parse(n),o=r.type==="execution_start"?"/api/telemetry/skill-execution/start":"/api/telemetry/tool-call",i=`${this.apiUrl}${o}`;await v(i,JSON.stringify(r.payload),this.apiKey)&&unlinkSync(s);}catch{}}cacheEvent(t){try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let e=join(this.cacheDir,`${Date.now()}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(e,JSON.stringify(t,null,2)),this.scheduleRetry();}catch{}}scheduleRetry(){if(this.retryScheduled)return;this.retryScheduled=true;let t=Math.min(nt*Math.pow(2,this.retryAttempts),rt);setTimeout(()=>{this.retryScheduled=false,this.flushCache().then(()=>{this.retryAttempts=0;}).catch(()=>{this.retryAttempts++;});},t).unref();}truncateInput(t){let e=JSON.stringify(t);return e.length<=O?t:{_truncated:true,_original_size:e.length,_preview:e.substring(0,O)+"..."}}};var at=1800*1e3;function ct(){return process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow")}var w=class{constructor(t){this.stateDir=ct();}detect(){let t=join(this.stateDir,"current-skill.json");if(!existsSync(t))return null;try{let e=readFileSync(t,"utf8"),s=JSON.parse(e);return !s.registeredAt||Date.now()-s.registeredAt>at?(this.endExecution(),null):s}catch{return null}}startExecution(t,e){existsSync(this.stateDir)||mkdirSync(this.stateDir,{recursive:true});let s=this.getCurrentExecutionId();if(s)return s;let n=`exec_${Date.now()}_${Math.random().toString(36).slice(2,11)}`;return writeFileSync(join(this.stateDir,"current-execution-id"),n),writeFileSync(join(this.stateDir,"current-skill.json"),JSON.stringify({name:t,required_mcp_tools:e?.required_mcp_tools??[],registeredAt:Date.now()})),n}getCurrentExecutionId(){let t=join(this.stateDir,"current-execution-id");if(!existsSync(t))return null;try{return readFileSync(t,"utf8").trim()}catch{return null}}endExecution(){let t=join(this.stateDir,"current-skill.json"),e=join(this.stateDir,"current-execution-id");try{existsSync(t)&&unlinkSync(t);}catch{}try{existsSync(e)&&unlinkSync(e);}catch{}}};var D=join(homedir(),".devflow"),b=join(D,"server-refs.json"),ft="http://127.0.0.1:13337/api/health",yt=600*1e3,gt=500;function vt(){existsSync(D)||mkdirSync(D,{recursive:true});}function _(){try{if(existsSync(b))return JSON.parse(readFileSync(b,"utf-8"))}catch{}return {sessions:[],lastActivity:0}}function F(a){vt(),writeFileSync(b,JSON.stringify(a));}var k=class{constructor(t){this.process=null;this.idleTimer=null;this.onShutdown=null;this.projectRoot=t;}setOnShutdown(t){this.onShutdown=t;}addRef(t){let e=_();e.sessions.includes(t)||e.sessions.push(t),e.lastActivity=Date.now(),F(e),this.resetIdleTimer();}removeRef(t){let e=_();e.sessions=e.sessions.filter(s=>s!==t),e.lastActivity=Date.now(),F(e),e.sessions.length===0&&this.resetIdleTimer();}get activeSessions(){return _().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(t=>{let e=setTimeout(()=>{this.process&&!this.process.killed&&this.process.kill("SIGKILL"),t();},5e3);this.process?this.process.on("exit",()=>{clearTimeout(e),t();}):(clearTimeout(e),t());}),this.process=null);}async startServer(){let t=join(this.projectRoot,"apps","server","dist","main.js"),e=join(this.projectRoot,"node_modules","@devflow-tools","server","dist","main.js"),s=existsSync(t)?t:e;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",n=>{}),this.process.stderr?.on("data",n=>{}),this.process.on("exit",n=>{this.process=null,this.onShutdown&&this.onShutdown();}),this.process.on("error",()=>{this.process=null;});}async waitForReady(){let t=Date.now()+3e4;for(;Date.now()<t;){if(await this.healthCheck())return true;await new Promise(e=>setTimeout(e,gt));}return false}healthCheck(){return new Promise(t=>{let e=new URL(ft),s=request({hostname:e.hostname,port:e.port,path:e.pathname,method:"GET",timeout:2e3},n=>{t(n.statusCode===200);});s.on("error",()=>t(false)),s.on("timeout",()=>{s.destroy(),t(false);}),s.end();})}resetIdleTimer(){this.idleTimer&&clearTimeout(this.idleTimer),!(_().sessions.length>0)&&(this.idleTimer=setTimeout(()=>{this.stop();},yt).unref());}};var p=join(homedir(),".devflow","telemetry-buffer.jsonl"),B=500;function wt(){let a=join(homedir(),".devflow");existsSync(a)||mkdirSync(a,{recursive:true});}var E=class{append(t){try{wt();let e=[];existsSync(p)&&(e=readFileSync(p,"utf-8").split(`
2
- `).filter(s=>s.trim())),e.push(JSON.stringify(t)),e.length>B&&(e=e.slice(e.length-B)),writeFileSync(p,e.join(`
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(p)?readFileSync(p,"utf-8").split(`
5
- `).filter(t=>t.trim()):[]}catch{return []}}clear(){try{existsSync(p)&&writeFileSync(p,"");}catch{}}get size(){return this.readAll().length}};var f=join(homedir(),".devflow","hook-daemon.sock");join(homedir(),".devflow");var Rt=600*1e3,Pt=new Set(["Agent","Bash","Glob","Grep","WebSearch","WebFetch"]);function Dt(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 V(a){let t=createHash("sha256").update(a).digest("hex").slice(0,16);return join(process.env.TMPDIR||"/tmp",".devflow-receipts",`${t}.json`)}function bt(a){try{let t=V(a);return existsSync(t)?JSON.parse(readFileSync(t,"utf-8")):{}}catch{return {}}}function W(a,t){try{let e=V(a),s=join(process.env.TMPDIR||"/tmp",".devflow-receipts");existsSync(s)||mkdirSync(s,{recursive:!0}),writeFileSync(e,JSON.stringify(t));}catch{}}var C=class{constructor(t){this.sessionId=null;this.eventMap=[];this.idleTimer=null;this.projectRoot=t,this.projectName=t.split("/").pop()||"unknown",this.telemetry=new S,this.skillManager=new w,this.serverManager=new k(t),this.telemetryBuffer=new E;}async start(){try{existsSync(f)&&unlinkSync(f);}catch{}let t=createServer(e=>{this.resetIdleTimer();let s="";e.on("data",async n=>{s+=n.toString();let r=s.split(`
6
- `);s=r.pop()||"";for(let o of r)if(o.trim())try{let i=JSON.parse(o),c=await this.handleRequest(i);c!==null&&e.write(JSON.stringify(c)+`
7
- `);}catch(i){e.write(JSON.stringify({error:i.message})+`
8
- `);}}),e.on("error",()=>{});});t.on("error",e=>{console.error("[devflow-daemon] Server error:",e.message);}),t.listen(f,()=>{console.error("[devflow-daemon] Listening on",f);}),this.flushBuffer(),this.resetIdleTimer();}resetIdleTimer(){this.idleTimer&&clearTimeout(this.idleTimer),this.idleTimer=setTimeout(()=>{console.error("[devflow-daemon] Idle timeout, exiting"),this.shutdown();},Rt).unref();}async handleRequest(t){switch(t.type){case "pre-tool-use":return this.handlePreToolUse(t.input);case "post-tool-use":return this.handlePostToolUse(t.input);case "stop":return await this.handleStop(),{status:"ok"};default:return {error:"unknown request type"}}}async handlePreToolUse(t){try{if(!t||t.trim()==="")return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}};let e=JSON.parse(t);if(this.sessionId||(this.sessionId=`session:${this.projectName}:${Date.now().toString(36)}`,this.telemetry.sendSessionStart(this.sessionId,this.projectRoot,Date.now()).catch(()=>{})),e.tool_name==="Skill"){let l=e.tool_input?.skill;if(l&&l.startsWith("devflow:")){let X=this.skillManager.startExecution(l);this.telemetry.sendExecutionStart(X,this.sessionId,l,Date.now()).catch(()=>{});}}let s=this.skillManager.detect(),n=e.tool_name.startsWith("mcp__devflow__"),r=n?e.tool_name.replace("mcp__devflow__",""):void 0,o=s?.required_mcp_tools??[],i=o.length>0,c=i&&!n,h;c?h={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:`Skill "${s?.name||"unknown"}" requires MCP tools. Use one of: ${o.join(", ")}`}}:h={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}};let d=this.skillManager.getCurrentExecutionId()??this.sessionId,g={eventId:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`,executionId:d,sessionId:this.sessionId,timestamp:Date.now(),toolName:e.tool_name,toolType:n?"mcp":e.tool_name==="Agent"?"subagent":"direct",isMcpTool:n,mcpToolName:r,mcpEnforced:n&&i,mcpFallback:!n&&i,kind:Dt(e.tool_name),input:e.tool_input,duration:0,blocked:c};return this.telemetry.sendEvent(g).then(l=>{l&&this.eventMap.push({toolName:e.tool_name,eventId:l,timestamp:g.timestamp});}).catch(()=>{}),h}catch{return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}}}popEventFromMap(t){let e=this.eventMap.filter(n=>n.toolName===t);if(e.length===0)return null;e.sort((n,r)=>Math.abs(n.timestamp-Date.now())-Math.abs(r.timestamp-Date.now()));let s=e[0];return this.eventMap=this.eventMap.filter(n=>n.eventId!==s.eventId),s}enforcePostToolUse(t){if(!Pt.has(t))return null;let e=bt(this.projectRoot),s=e.lastMcpCall??0;if(Math.floor(Date.now()/1e3)-s<30)return null;let r=e.bypassCount??0;if(r===0)return W(this.projectRoot,{...e,bypassCount:1}),null;let o=r+1;W(this.projectRoot,{...e,bypassCount:o});let i;switch(t){case "Agent":i=`\u7B2C ${o} \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 ${o} \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 ${o} \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 ${o} \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 ${o} \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 ${o} \u6B21\u8FDD\u89C4\uFF1A\u8BF7\u4F7F\u7528 DevFlow MCP \u5DE5\u5177\u66FF\u4EE3\u76F4\u63A5\u8C03\u7528\u3002`;}return o>=3?i=`${i} \u5DF2\u8FBE\u5230\u6700\u5927\u8FDD\u89C4\u6B21\u6570\uFF0C\u540E\u7EED\u7ED5\u8FC7\u5C06\u88AB\u786C\u963B\u6B62\u3002`:o>=2?i=`${i} \u4E0B\u6B21\u8FDD\u89C4\u5C06\u88AB\u963B\u6B62\u3002`:i=`${i} \u8BF7\u7ACB\u5373\u7EA0\u6B63\u3002`,i}async handlePostToolUse(t){if(!t)return null;let e;try{e=JSON.parse(t);}catch{return null}let s=e.tool_name||"",n=e.tool_input||{},r=e.tool_response,o=this.popEventFromMap(s);if(o&&this.sessionId){let y=typeof r=="string"?r:JSON.stringify(r),g=y.length>5e3?{_truncated:true,_originalSize:y.length,text:y.slice(0,5e3)}:r,l=Date.now()-o.timestamp;this.sendHttp("/api/telemetry/tool-call/output",{eventId:o.eventId,sessionId:this.sessionId,output:g,duration:l,completedAt:Date.now()});}let i=typeof r=="object"&&r!==null?r:null,c=i?.exitCode,h=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:n.command||s,exitCode:c,stderr:h??null,durationMs:0,createdAt:Date.now()});let d=this.enforcePostToolUse(s);return d?(console.error("[devflow-daemon] Enforcement:",d),{hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:JSON.stringify(d)}}):null}sendHttp(t,e){let s=JSON.stringify(e),n=new URL(t,"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(s)},timeout:5e3},o=>{o.statusCode===200&&this.flushBuffer();});r.on("error",()=>{this.telemetryBuffer.append({path:t,data:e,timestamp:Date.now()});}),r.write(s),r.end();}flushBuffer(){let t=this.telemetryBuffer.readAll();if(t.length!==0){for(let e of t)try{let s=JSON.parse(e),n=JSON.stringify(s.data),r=new URL(s.path,"http://127.0.0.1:13337"),o=request({hostname:r.hostname,port:r.port,path:r.pathname+r.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":"dev-key-000000","Content-Length":Buffer.byteLength(n)},timeout:5e3},()=>{});o.on("error",()=>{}),o.write(n),o.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(f);}catch{}this.serverManager.stop(),process.exit(0);}},Mt=process.argv[2]||process.env.CLAUDE_PROJECT_DIR||process.cwd(),Ct=new C(Mt);Ct.start().catch(a=>{console.error("[devflow-daemon] Failed to start:",a),process.exit(1);});
4
+ `);}catch{}}readAll(){try{return existsSync(m)?readFileSync(m,"utf-8").split(`
5
+ `).filter(e=>e.trim()):[]}catch{return []}}clear(){try{existsSync(m)&&writeFileSync(m,"");}catch{}}get size(){return this.readAll().length}};var v=join(homedir(),".devflow","hook-daemon.sock");join(homedir(),".devflow");var jt=600*1e3,Y={Agent:"get_project_context",Bash:"get_project_context",Glob:"get_project_context",Grep:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function At(a){switch(a){case "WebSearch":case "WebFetch":return 1;case "Agent":return 3;case "Grep":case "Glob":return 3;case "Bash":return 5;default:return 2}}function Ut(a){switch(a){case "Read":case "Glob":return "file_read";case "Write":case "Edit":case "NotebookEdit":return "file_write";case "Bash":return "bash_command";case "Agent":return "subagent";case "Skill":return "skill_invoke";default:return "tool_use"}}function tt(a){let e=createHash("sha256").update(a).digest("hex").slice(0,16);return join(process.env.TMPDIR||"/tmp",".devflow-receipts",`${e}.json`)}function Z(a){try{let e=tt(a);return existsSync(e)?JSON.parse(readFileSync(e,"utf-8")):{}}catch{return {}}}function P(a,e){try{let t=tt(a),s=join(process.env.TMPDIR||"/tmp",".devflow-receipts");existsSync(s)||mkdirSync(s,{recursive:!0}),writeFileSync(t,JSON.stringify(e));}catch{}}var U=class{constructor(e){this.sessionId=null;this.eventMap=[];this.idleTimer=null;this.projectRoot=e,this.projectName=e.split("/").pop()||"unknown",this.telemetry=new w,this.skillManager=new _,this.serverManager=new R(e),this.telemetryBuffer=new I;}async start(){try{existsSync(v)&&unlinkSync(v);}catch{}let e=createServer(t=>{this.resetIdleTimer();let s="";t.on("data",async o=>{s+=o.toString();let n=s.split(`
6
+ `);s=n.pop()||"";for(let r of n)if(r.trim())try{let i=JSON.parse(r),c=await this.handleRequest(i);c!==null&&t.write(JSON.stringify(c)+`
7
+ `);}catch(i){t.write(JSON.stringify({error:i.message})+`
8
+ `);}}),t.on("error",()=>{});});e.on("error",t=>{console.error("[devflow-daemon] Server error:",t.message);}),e.listen(v,()=>{console.error("[devflow-daemon] Listening on",v);}),this.flushBuffer(),this.resetIdleTimer();}resetIdleTimer(){this.idleTimer&&clearTimeout(this.idleTimer),this.idleTimer=setTimeout(()=>{console.error("[devflow-daemon] Idle timeout, exiting"),this.shutdown();},jt).unref();}async handleRequest(e){switch(e.type){case "pre-tool-use":return this.handlePreToolUse(e.input);case "post-tool-use":return this.handlePostToolUse(e.input);case "stop":return await this.handleStop(),{status:"ok"};default:return {error:"unknown request type"}}}async handlePreToolUse(e){try{if(!e||e.trim()==="")return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}};let t=JSON.parse(e);if(this.sessionId||(this.sessionId=`session:${this.projectName}:${Date.now().toString(36)}`,this.telemetry.sendSessionStart(this.sessionId,this.projectRoot,Date.now()).catch(()=>{})),t.tool_name==="Skill"){let l=t.tool_input?.skill;if(l&&l.startsWith("devflow:")){let D=x(l),y=this.skillManager.startExecution(l,{required_mcp_tools:D});this.telemetry.sendExecutionStart(y,this.sessionId,l,Date.now()).catch(()=>{});}}let s=this.skillManager.detect(),o=t.tool_name.startsWith("mcp__"),n=o?t.tool_name.replace(/^mcp__[^_]+__/,""):void 0,r=s?.required_mcp_tools??[],i=r.length>0,c=i&&!o&&t.tool_name!=="Skill",p;if(c)p={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:`\u5EFA\u8BAE\u5148\u8C03\u7528 ${r[0]||"get_project_context"} \u83B7\u53D6\u9879\u76EE\u4E0A\u4E0B\u6587\uFF0C\u7ED3\u679C\u4F1A\u66F4\u7CBE\u786E\u3002`}};else if(!o&&t.tool_name!=="Skill"){let D=Z(this.projectRoot).bypassCount??0,y=Y[t.tool_name],et=y?At(t.tool_name):1/0;y&&D>=et?p={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:`\u5DF2\u591A\u6B21\u4F7F\u7528 ${t.tool_name}\uFF0C\u5EFA\u8BAE\u5207\u6362\u5230 ${y} \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`}}:p={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}};}else p={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}};let g=this.skillManager.getCurrentExecutionId()??this.sessionId,f={eventId:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`,executionId:g,sessionId:this.sessionId,timestamp:Date.now(),toolName:t.tool_name,toolType:o?"mcp":t.tool_name==="Agent"?"subagent":"direct",isMcpTool:o,mcpToolName:n,mcpEnforced:o&&i,mcpFallback:!o&&i,kind:Ut(t.tool_name),input:t.tool_input,duration:0,blocked:p&&p.hookSpecificOutput?.permissionDecision==="deny"};return this.telemetry.sendEvent(f).then(l=>{l&&this.eventMap.push({toolName:t.tool_name,eventId:l,timestamp:f.timestamp});}).catch(()=>{}),p}catch{return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}}}popEventFromMap(e){let t=this.eventMap.filter(o=>o.toolName===e);if(t.length===0)return null;t.sort((o,n)=>Math.abs(o.timestamp-Date.now())-Math.abs(n.timestamp-Date.now()));let s=t[0];return this.eventMap=this.eventMap.filter(o=>o.eventId!==s.eventId),s}enforcePostToolUse(e){if(!Y[e])return null;let t=Z(this.projectRoot),s=t.lastMcpCall??0;if(Math.floor(Date.now()/1e3)-s<30)return null;let n=t.bypassCount??0;if(n===0)return P(this.projectRoot,{...t,bypassCount:1}),null;let r=n+1;P(this.projectRoot,{...t,bypassCount:r});let i;switch(e){case "Agent":i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u4F7F\u7528\u4E86 Agent \u5B50\u4EE3\u7406\u6267\u884C\u641C\u7D22\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Bash":i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Bash \u547D\u4EE4\u641C\u7D22\u4EE3\u7801\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Glob":i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Glob \u5339\u914D\u6587\u4EF6\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Grep":i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Grep \u641C\u7D22\u4EE3\u7801\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "WebSearch":case "WebFetch":i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86\u7F51\u7EDC\u641C\u7D22\u3002\u4E0B\u6B21\u8BF7\u7528 get_knowledge MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;default:i=`\u7B2C ${r} \u6B21\u8FDD\u89C4\uFF1A\u8BF7\u4F7F\u7528 DevFlow MCP \u5DE5\u5177\u66FF\u4EE3\u76F4\u63A5\u8C03\u7528\u3002`;}return r>=3?i=`${i} \u5DF2\u8FBE\u5230\u6700\u5927\u8FDD\u89C4\u6B21\u6570\uFF0C\u540E\u7EED\u7ED5\u8FC7\u5C06\u88AB\u786C\u963B\u6B62\u3002`:r>=2?i=`${i} \u4E0B\u6B21\u8FDD\u89C4\u5C06\u88AB\u963B\u6B62\u3002`:i=`${i} \u8BF7\u7ACB\u5373\u7EA0\u6B63\u3002`,i}async handlePostToolUse(e){if(!e)return null;let t;try{t=JSON.parse(e);}catch{return null}let s=t.tool_name||"",o=t.tool_input||{},n=t.tool_response;if(s==="Skill"){let h=o?.skill;if(h&&h.startsWith("devflow:")){let f=x(h);this.skillManager.startExecution(h,{required_mcp_tools:f}),P(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}}s.startsWith("mcp__")&&P(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});let r=this.popEventFromMap(s);if(r&&this.sessionId){let h=typeof n=="string"?n:JSON.stringify(n),f=h.length>5e3?{_truncated:true,_originalSize:h.length,text:h.slice(0,5e3)}:n,l=Date.now()-r.timestamp;this.sendHttp("/api/telemetry/tool-call/output",{eventId:r.eventId,sessionId:this.sessionId,output:f,duration:l,completedAt:Date.now()});}let i=typeof n=="object"&&n!==null?n:null,c=i?.exitCode,p=i?.stderr;s==="Bash"&&c!==void 0&&c!==0&&this.sendHttp(`/api/memory/events?rootPath=${encodeURIComponent(this.projectRoot)}`,{id:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:this.sessionId??`session:${this.projectName}`,tool:s,command:o.command||s,exitCode:c,stderr:p??null,durationMs:0,createdAt:Date.now()});let g=this.enforcePostToolUse(s);return g?(console.error("[devflow-daemon] Enforcement:",g),{hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:JSON.stringify(g)}}):null}sendHttp(e,t){let s=JSON.stringify(t),o=new URL(e,"http://127.0.0.1:13337"),n=request({hostname:o.hostname,port:o.port,path:o.pathname+o.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":"dev-key-000000","Content-Length":Buffer.byteLength(s)},timeout:5e3},r=>{r.statusCode===200&&this.flushBuffer();});n.on("error",()=>{this.telemetryBuffer.append({path:e,data:t,timestamp:Date.now()});}),n.write(s),n.end();}flushBuffer(){let e=this.telemetryBuffer.readAll();if(e.length!==0){for(let t of e)try{let s=JSON.parse(t),o=JSON.stringify(s.data),n=new URL(s.path,"http://127.0.0.1:13337"),r=request({hostname:n.hostname,port:n.port,path:n.pathname+n.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":"dev-key-000000","Content-Length":Buffer.byteLength(o)},timeout:5e3},()=>{});r.on("error",()=>{}),r.write(o),r.end();}catch{}this.telemetryBuffer.clear();}}async handleStop(){this.sessionId&&(this.sendHttp(`/api/telemetry/sessions/${this.sessionId}/close`,{finishedAt:Date.now()}),this.serverManager.removeRef(this.sessionId)),this.sendHttp(`/api/memory/process?rootPath=${encodeURIComponent(this.projectRoot)}`,{}),await this.telemetry.flushCache().catch(()=>{}),this.shutdown();}shutdown(){this.idleTimer&&clearTimeout(this.idleTimer);try{unlinkSync(v);}catch{}this.serverManager.stop(),process.exit(0);}},Lt=process.argv[2]||process.env.CLAUDE_PROJECT_DIR||process.cwd(),Ft=new U(Lt);Ft.start().catch(a=>{console.error("[devflow-daemon] Failed to start:",a),process.exit(1);});
@@ -1,2 +1,2 @@
1
- import {request}from'http';import {existsSync,readFileSync,writeFileSync,mkdirSync}from'fs';import {join}from'path';import {homedir}from'os';import {createHash}from'crypto';var T=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",L=process.env.DEVFLOW_API_KEY||"dev-key-000000",v=process.env.CLAUDE_PROJECT_DIR||process.cwd(),C=join(homedir(),".devflow","session-id"),w=join(homedir(),".devflow","event-map.json"),F=new Set(["Agent","Bash","Glob","Grep","WebSearch","WebFetch"]);function b(r,s){return new Promise(t=>{let c=JSON.stringify(s),o=new URL(r,T),n=request({hostname:o.hostname,port:o.port,path:o.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":L,"Content-Length":Buffer.byteLength(c)},timeout:5e3},()=>t());n.on("error",()=>t()),n.write(c),n.end();})}function J(){try{if(existsSync(C))return readFileSync(C,"utf-8").trim()}catch{}return null}function W(r,s){try{if(!existsSync(w))return null;let t=JSON.parse(readFileSync(w,"utf-8")),c=t.filter(l=>l.toolName===r);if(c.length===0)return null;let o=s??Date.now();c.sort((l,d)=>Math.abs(l.timestamp-o)-Math.abs(d.timestamp-o));let n=c[0],e=t.findIndex(l=>l.eventId===n.eventId);return e!==-1&&t.splice(e,1),writeFileSync(w,JSON.stringify(t)),{eventId:n.eventId,timestamp:n.timestamp}}catch{return null}}function P(){let r=createHash("sha256").update(v).digest("hex").slice(0,16);return join(process.env.TMPDIR||"/tmp",".devflow-receipts",`${r}.json`)}function U(){try{let r=P();return existsSync(r)?JSON.parse(readFileSync(r,"utf-8")):{}}catch{return {}}}function k(r){try{let s=P(),t=join(process.env.TMPDIR||"/tmp",".devflow-receipts");existsSync(t)||mkdirSync(t,{recursive:!0}),writeFileSync(s,JSON.stringify(r));}catch{}}function B(r){if(!F.has(r))return null;let s=U(),t=s.lastMcpCall??0;if(Math.floor(Date.now()/1e3)-t<30)return null;let o=s.bypassCount??0;if(o===0)return k({...s,bypassCount:1}),null;let n=o+1;k({...s,bypassCount:n});let e;switch(r){case "Agent":e=`\u7B2C ${n} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u4F7F\u7528\u4E86 Agent \u5B50\u4EE3\u7406\u6267\u884C\u641C\u7D22\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Bash":e=`\u7B2C ${n} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Bash \u547D\u4EE4\u641C\u7D22\u4EE3\u7801\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Glob":e=`\u7B2C ${n} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Glob \u5339\u914D\u6587\u4EF6\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Grep":e=`\u7B2C ${n} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Grep \u641C\u7D22\u4EE3\u7801\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "WebSearch":case "WebFetch":e=`\u7B2C ${n} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86\u7F51\u7EDC\u641C\u7D22\u3002\u4E0B\u6B21\u8BF7\u7528 get_knowledge MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;default:e=`\u7B2C ${n} \u6B21\u8FDD\u89C4\uFF1A\u8BF7\u4F7F\u7528 DevFlow MCP \u5DE5\u5177\u66FF\u4EE3\u76F4\u63A5\u8C03\u7528\u3002`;}return n>=3?e=`${e} \u5DF2\u8FBE\u5230\u6700\u5927\u8FDD\u89C4\u6B21\u6570\uFF0C\u540E\u7EED\u7ED5\u8FC7\u5C06\u88AB\u786C\u963B\u6B62\u3002`:n>=2?e=`${e} \u4E0B\u6B21\u8FDD\u89C4\u5C06\u88AB\u963B\u6B62\u3002`:e=`${e} \u8BF7\u7ACB\u5373\u7EA0\u6B63\u3002`,e}async function q(r){if(!r)return null;let s;try{s=JSON.parse(r);}catch{return null}let t=s.tool_name||"",c=s.tool_input||{},o=s.tool_response||{},n=J(),e=W(t,Date.now());if(e&&n){let a=typeof o=="string"?o:JSON.stringify(o),h=a.length>5e3?{_truncated:true,_originalSize:a.length,text:a.slice(0,5e3)}:o,i=Date.now()-e.timestamp;b("/api/telemetry/tool-call/output",{eventId:e.eventId,sessionId:n,output:h,duration:i,completedAt:Date.now()}).catch(()=>{});}let l=typeof c=="object"&&c.command?c.command:"",d=typeof o=="object"&&o!==null?o:null,f=d?.exitCode,R=d?.stderr;if(t==="Bash"&&f!==void 0&&f!==0){let a={id:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:n??`session:${v.split("/").pop()||"default"}`,tool:t,command:l||t,exitCode:f,stderr:R??null,durationMs:0,createdAt:Date.now()};b(`/api/memory/events?rootPath=${encodeURIComponent(v)}`,a).catch(()=>{});}let M=["get_project_context","get_knowledge","get_memory","search_symbol","get_dependency_graph","react_diagnose_bug","react_new_component","react_audit_performance","vue_diagnose_bug","nest_diagnose_bug","nextjs_diagnose_bug","electron_diagnose_bug","graphql_diagnose_bug","run_workflow"],u=t.replace("mcp__devflow__","");if(t.startsWith("mcp__devflow__")&&M.includes(u)){let a=typeof o=="object"&&o!==null?o:null,h=["files","results","data","result","memories","nodes","chunks"],i=0,p=a?.data??a;if(Array.isArray(p))i=p.length;else if(typeof p=="object"&&p!==null){let y=p;for(let I of h)Array.isArray(y[I])&&(i+=y[I].length);i===0&&Object.keys(y).length>0&&(i=1);}let j=JSON.stringify(o),D=Buffer.byteLength(j,"utf8"),N={id:`metric:${Date.now()}:${Math.random().toString(36).slice(2,11)}`,toolCallEventId:e?.eventId??`unknown:${Date.now()}`,sessionId:n??"",toolName:u,query:typeof c=="object"&&c.query?c.query:void 0,status:a?.error?"error":i===0?"empty":"success",resultCount:i,resultSizeBytes:D,latencyMs:e?Date.now()-e.timestamp:0,engine:u.startsWith("get_project_context")||u==="search_symbol"||u==="get_dependency_graph"?"codegraph":u==="get_knowledge"?"knowledge":u==="get_memory"?"memory":void 0};b("/api/telemetry/tool-metrics",N).catch(()=>{});}let _=B(t);return _&&console.error("[devflow] Enforcement:",_),_}if(process.argv[1]?.endsWith("post-tool-use")||process.argv[1]?.endsWith("post-tool-use.js")){let r="";process.stdin.setEncoding("utf8"),process.stdin.on("data",s=>{r+=s;}),process.stdin.on("end",async()=>{let s=await q(r.trim()||process.argv[2]||"");if(s){let t=JSON.stringify(s);console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:t}}));}}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),5e3).unref();}
2
- export{q as handlePostToolUse};
1
+ import {request}from'http';import {existsSync,readFileSync,writeFileSync,mkdirSync}from'fs';import {join,dirname}from'path';import {homedir}from'os';import {createHash}from'crypto';import {fileURLToPath}from'url';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 I=1e4,q=3e4,W=300*1e3;function m(o,t,e){return new Promise(n=>{try{let r=new URL(o),i=request({hostname:r.hostname,port:r.port||80,path:r.pathname+r.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(t)},timeout:5e3},s=>{let c=[];s.on("data",a=>c.push(a)),s.on("end",()=>{let a=Buffer.concat(c).toString();n(s.statusCode!=null&&s.statusCode>=200&&s.statusCode<300?a:null);});});i.on("error",()=>n(null)),i.on("timeout",()=>{i.destroy(),n(null);}),i.write(t),i.end();}catch{n(null);}})}var f=class{constructor(t){this.retryScheduled=false;this.retryAttempts=0;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??process.env.DEVFLOW_API_KEY??"dev-key-000000",this.cacheDir=t?.cacheDir??join(homedir(),".devflow","telemetry-cache");}async sendEvent(t){let e=this.truncateInput(t.input),n={...t,input:e},r=`${this.apiUrl}/api/telemetry/tool-call`,i=await m(r,JSON.stringify(n),this.apiKey);if(i)try{let s=JSON.parse(i),c=s.data?.eventId??s.eventId??null;if(c)return c}catch{}return this.cacheEvent({type:"tool_call",payload:n}),null}async sendExecutionStart(t,e,n,r){let i={executionId:t,sessionId:e,skillName:n,startedAt:r},s=`${this.apiUrl}/api/telemetry/skill-execution/start`;await m(s,JSON.stringify(i),this.apiKey)||this.cacheEvent({type:"execution_start",payload:i});}async sendSessionStart(t,e,n){let r={id:t,projectRoot:e,startedAt:n,label:e.split("/").pop()??"unknown"},i=`${this.apiUrl}/api/telemetry/sessions`;return await m(i,JSON.stringify(r),this.apiKey)!==null}async flushCache(){if(!existsSync(this.cacheDir))return;let t=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json"));for(let e of t)try{let n=join(this.cacheDir,e),r=readFileSync(n,"utf8"),i=JSON.parse(r),s=i.type==="execution_start"?"/api/telemetry/skill-execution/start":"/api/telemetry/tool-call",c=`${this.apiUrl}${s}`;await m(c,JSON.stringify(i.payload),this.apiKey)&&unlinkSync(n);}catch{}}cacheEvent(t){try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let e=join(this.cacheDir,`${Date.now()}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(e,JSON.stringify(t,null,2)),this.scheduleRetry();}catch{}}scheduleRetry(){if(this.retryScheduled)return;this.retryScheduled=true;let t=Math.min(q*Math.pow(2,this.retryAttempts),W);setTimeout(()=>{this.retryScheduled=false,this.flushCache().then(()=>{this.retryAttempts=0;}).catch(()=>{this.retryAttempts++;});},t).unref();}truncateInput(t){let e=JSON.stringify(t);return e.length<=I?t:{_truncated:true,_original_size:e.length,_preview:e.substring(0,I)+"..."}}};var V=1800*1e3;function X(){return process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow")}var g=class{constructor(t){this.stateDir=X();}detect(){let t=join(this.stateDir,"current-skill.json");if(!existsSync(t))return null;try{let e=readFileSync(t,"utf8"),n=JSON.parse(e);return !n.registeredAt||Date.now()-n.registeredAt>V?(this.endExecution(),null):n}catch{return null}}startExecution(t,e){existsSync(this.stateDir)||mkdirSync(this.stateDir,{recursive:true});let n=this.getCurrentExecutionId();if(n)return n;let r=`exec_${Date.now()}_${Math.random().toString(36).slice(2,11)}`;return writeFileSync(join(this.stateDir,"current-execution-id"),r),writeFileSync(join(this.stateDir,"current-skill.json"),JSON.stringify({name:t,required_mcp_tools:e?.required_mcp_tools??[],registeredAt:Date.now()})),r}getCurrentExecutionId(){let t=join(this.stateDir,"current-execution-id");if(!existsSync(t))return null;try{return readFileSync(t,"utf8").trim()}catch{return null}}endExecution(){let t=join(this.stateDir,"current-skill.json"),e=join(this.stateDir,"current-execution-id");try{existsSync(t)&&unlinkSync(t);}catch{}try{existsSync(e)&&unlinkSync(e);}catch{}}};function d(){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}}function G(o){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:o}}}var h=join(homedir(),".devflow");function A(){existsSync(h)||mkdirSync(h,{recursive:true});}function Z(){try{let o=join(h,"session-id");if(existsSync(o))return readFileSync(o,"utf-8").trim()}catch{}return null}function Q(o){A(),writeFileSync(join(h,"session-id"),o);}function tt(o,t,e){A();let n=join(h,"event-map.json"),r=[];try{existsSync(n)&&(r=JSON.parse(readFileSync(n,"utf-8")));}catch{}r.push({toolName:o,eventId:t,timestamp:e}),writeFileSync(n,JSON.stringify(r));}function et(o){switch(o){case "Read":case "Glob":return "file_read";case "Write":case "Edit":case "NotebookEdit":return "file_write";case "Bash":return "bash_command";case "Agent":return "subagent";case "Skill":return "skill_invoke";default:return "tool_use"}}async function nt(o){try{if(!o||o.trim()==="")return d();let t=JSON.parse(o),e=process.env.CLAUDE_PROJECT_DIR||process.cwd(),n=e.split("/").pop()||"unknown",r=new f,i=Z();i||(i=`session:${n}:${Date.now().toString(36)}`,Q(i),r.sendSessionStart(i,e,Date.now()).catch(()=>{}));let s=new g;if(t.tool_name==="Skill"){let l=t.tool_input?.skill;if(l&&l.startsWith("devflow:")){let C=s.startExecution(l);r.sendExecutionStart(C,i,l,Date.now()).catch(()=>{});}}let c=s.detect(),a=t.tool_name.startsWith("mcp__devflow__"),M=a?t.tool_name.replace("mcp__devflow__",""):void 0,w=c?.required_mcp_tools??[],_=w.length>0,D=_&&!a,S;D?S=G(`Skill "${c?.name||"unknown"}" requires MCP tools. Use one of: ${w.join(", ")}`):S=d();let R=s.getCurrentExecutionId()??i,E={eventId:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`,executionId:R,sessionId:i,timestamp:Date.now(),toolName:t.tool_name,toolType:a?"mcp":t.tool_name==="Agent"?"subagent":"direct",isMcpTool:a,mcpToolName:M,mcpEnforced:a&&_,mcpFallback:!a&&_,kind:et(t.tool_name),input:t.tool_input,duration:0,blocked:D};return r.sendEvent(E).then(l=>{l&&tt(t.tool_name,l,E.timestamp);}).catch(()=>{}),S}catch{return d()}}if(process.argv[1]?.endsWith("pre-tool-use")||process.argv[1]?.endsWith("pre-tool-use.js")){let o="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{o+=t;}),process.stdin.on("end",async()=>{let t=await nt(o);console.log(JSON.stringify(t));}),process.stdin.on("error",()=>{console.log(JSON.stringify(d())),process.exit(0);}),setTimeout(()=>{console.log(JSON.stringify(d())),process.exit(0);},4e3).unref();}
2
- export{nt as handlePreToolUse};
1
+ import {appendFileSync,existsSync,readdirSync,readFileSync,unlinkSync,mkdirSync,writeFileSync}from'fs';import {join,dirname}from'path';import {homedir}from'os';import {createHash}from'crypto';import {request}from'http';import {fileURLToPath}from'url';var N=1e4,Y=3e4,z=300*1e3;function y(e,t,o){return new Promise(n=>{try{let r=new URL(e),i=request({hostname:r.hostname,port:r.port||80,path:r.pathname+r.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":o,"Content-Length":Buffer.byteLength(t)},timeout:5e3},s=>{let a=[];s.on("data",p=>a.push(p)),s.on("end",()=>{let p=Buffer.concat(a).toString();n(s.statusCode!=null&&s.statusCode>=200&&s.statusCode<300?p:null);});});i.on("error",()=>n(null)),i.on("timeout",()=>{i.destroy(),n(null);}),i.write(t),i.end();}catch{n(null);}})}var S=class{constructor(t){this.retryScheduled=false;this.retryAttempts=0;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??process.env.DEVFLOW_API_KEY??"dev-key-000000",this.cacheDir=t?.cacheDir??join(homedir(),".devflow","telemetry-cache");}async sendEvent(t){let o=this.truncateInput(t.input),n={...t,input:o},r=`${this.apiUrl}/api/telemetry/tool-call`,i=await y(r,JSON.stringify(n),this.apiKey);if(i)try{let s=JSON.parse(i),a=s.data?.eventId??s.eventId??null;if(a)return a}catch{}return this.cacheEvent({type:"tool_call",payload:n}),null}async sendExecutionStart(t,o,n,r){let i={executionId:t,sessionId:o,skillName:n,startedAt:r},s=`${this.apiUrl}/api/telemetry/skill-execution/start`;await y(s,JSON.stringify(i),this.apiKey)||this.cacheEvent({type:"execution_start",payload:i});}async sendSessionStart(t,o,n){let r={id:t,projectRoot:o,startedAt:n,label:o.split("/").pop()??"unknown"},i=`${this.apiUrl}/api/telemetry/sessions`;return await y(i,JSON.stringify(r),this.apiKey)!==null}async flushCache(){if(!existsSync(this.cacheDir))return;let t=readdirSync(this.cacheDir).filter(o=>o.endsWith(".json"));for(let o of t)try{let n=join(this.cacheDir,o),r=readFileSync(n,"utf8"),i=JSON.parse(r),s=i.type==="execution_start"?"/api/telemetry/skill-execution/start":"/api/telemetry/tool-call",a=`${this.apiUrl}${s}`;await y(a,JSON.stringify(i.payload),this.apiKey)&&unlinkSync(n);}catch{}}cacheEvent(t){try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let o=join(this.cacheDir,`${Date.now()}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(o,JSON.stringify(t,null,2)),this.scheduleRetry();}catch{}}scheduleRetry(){if(this.retryScheduled)return;this.retryScheduled=true;let t=Math.min(Y*Math.pow(2,this.retryAttempts),z);setTimeout(()=>{this.retryScheduled=false,this.flushCache().then(()=>{this.retryAttempts=0;}).catch(()=>{this.retryAttempts++;});},t).unref();}truncateInput(t){let o=JSON.stringify(t);return o.length<=N?t:{_truncated:true,_original_size:o.length,_preview:o.substring(0,N)+"..."}}};var nt=1800*1e3;function P(){return process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow")}function ot(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{let e=fileURLToPath(import.meta.url);return join(dirname(e),"..","..")}catch{return process.cwd()}}function rt(){let e=ot(),t=[join(e,"dist","command-registry.json"),join(e,"command-registry.json")],o=[];try{for(let n of t){if(existsSync(n)){let r=JSON.parse(readFileSync(n,"utf8")).commands;if(r)return r}o.push(`${n}: ${existsSync(n)?"found":"missing"}`);}}catch(n){o.push(`error: ${n.message}`);}try{writeFileSync(join(P(),"hook-debug.log"),JSON.stringify({ts:Date.now(),pluginRoot:e,CLAUDE_PLUGIN_ROOT:process.env.CLAUDE_PLUGIN_ROOT??"(unset)",cwd:process.cwd(),paths:o},null,2));}catch{}return null}function A(e){let t=rt();return t?t[e]?.mcpTools??[]:[]}var k=class{constructor(t){this.stateDir=P();}detect(){let t=join(this.stateDir,"current-skill.json");if(!existsSync(t))return null;try{let o=readFileSync(t,"utf8"),n=JSON.parse(o);return !n.registeredAt||Date.now()-n.registeredAt>nt?(this.endExecution(),null):n}catch{return null}}startExecution(t,o){existsSync(this.stateDir)||mkdirSync(this.stateDir,{recursive:true});let n=this.getCurrentExecutionId();if(n)return n;let r=`exec_${Date.now()}_${Math.random().toString(36).slice(2,11)}`;return writeFileSync(join(this.stateDir,"current-execution-id"),r),writeFileSync(join(this.stateDir,"current-skill.json"),JSON.stringify({name:t,required_mcp_tools:o?.required_mcp_tools??[],registeredAt:Date.now()})),r}getCurrentExecutionId(){let t=join(this.stateDir,"current-execution-id");if(!existsSync(t))return null;try{return readFileSync(t,"utf8").trim()}catch{return null}}endExecution(){let t=join(this.stateDir,"current-skill.json"),o=join(this.stateDir,"current-execution-id");try{existsSync(t)&&unlinkSync(t);}catch{}try{existsSync(o)&&unlinkSync(o);}catch{}}};function m(){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}}function U(e){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:e}}}var g=join(homedir(),".devflow");function M(){existsSync(g)||mkdirSync(g,{recursive:true});}function lt(){try{let e=join(g,"session-id");if(existsSync(e))return readFileSync(e,"utf-8").trim()}catch{}return null}function ut(e){M(),writeFileSync(join(g,"session-id"),e);}function pt(e,t,o){M();let n=join(g,"event-map.json"),r=[];try{existsSync(n)&&(r=JSON.parse(readFileSync(n,"utf-8")));}catch{}r.push({toolName:e,eventId:t,timestamp:o}),writeFileSync(n,JSON.stringify(r));}function dt(e){switch(e){case "Read":case "Glob":return "file_read";case "Write":case "Edit":case "NotebookEdit":return "file_write";case "Bash":return "bash_command";case "Agent":return "subagent";case "Skill":return "skill_invoke";default:return "tool_use"}}var j={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge",Bash:"get_project_context"};function ht(e){switch(e){case "WebSearch":case "WebFetch":return 1;case "Agent":return 3;case "Grep":case "Glob":return 3;case "Bash":return 5;default:return 2}}function mt(){let e=process.env.CLAUDE_PROJECT_DIR||process.cwd(),t=createHash("sha256").update(e).digest("hex").slice(0,16);return join(process.env.TMPDIR||"/tmp",".devflow-receipts",`${t}.json`)}function $(){try{let e=mt();return existsSync(e)?JSON.parse(readFileSync(e,"utf-8")):{}}catch{return {}}}function gt(e){let t=j[e];if(!t)return null;let n=$().bypassCount??0,r=ht(e);return n<r?null:`Direct tool "${e}" blocked (${n} prior violations). Use mcp__devflow__${t} instead.`}async function ft(e){try{if(!e||e.trim()==="")return m();let t=JSON.parse(e),o=Date.now(),n=process.env.CLAUDE_PROJECT_DIR||process.cwd(),r=n.split("/").pop()||"unknown",i=new S,s=lt();s||(s=`session:${r}:${Date.now().toString(36)}`,ut(s),i.sendSessionStart(s,n,Date.now()).catch(()=>{}));let a=new k;if(t.tool_name==="Skill"){let c=t.tool_input?.skill;if(c&&c.startsWith("devflow:")){let _=a.startExecution(c,{required_mcp_tools:A(c)});i.sendExecutionStart(_,s,c,Date.now()).catch(()=>{});}}let p=a.detect(),u=t.tool_name.startsWith("mcp__"),F=u?t.tool_name.replace(/^mcp__[^_]+__/,""):void 0,w=p?.required_mcp_tools??[],D=w.length>0,E=D&&!u&&t.tool_name!=="Skill",d;if(E){let c=w[0]||"get_project_context";d=U(`\u5EFA\u8BAE\u5148\u8C03\u7528 ${c} \u83B7\u53D6\u9879\u76EE\u4E0A\u4E0B\u6587\uFF0C\u7ED3\u679C\u4F1A\u66F4\u7CBE\u786E\u3002`);}else if(!u&&t.tool_name!=="Skill")if(gt(t.tool_name)){let _=j[t.tool_name]||"get_project_context";d=U(`\u5DF2\u591A\u6B21\u4F7F\u7528 ${t.tool_name}\uFF0C\u5EFA\u8BAE\u5207\u6362\u5230 ${_} \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`);}else d=m();else d=m();let J=a.getCurrentExecutionId()??s,I={eventId:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`,executionId:J,sessionId:s,timestamp:Date.now(),toolName:t.tool_name,toolType:u?"mcp":t.tool_name==="Agent"?"subagent":"direct",isMcpTool:u,mcpToolName:F,mcpEnforced:u&&D,mcpFallback:!u&&D,kind:dt(t.tool_name),input:t.tool_input,duration:0,blocked:d.hookSpecificOutput.permissionDecision==="deny"};i.sendEvent(I).then(c=>{c&&pt(t.tool_name,c,I.timestamp);}).catch(()=>{});try{let c=$(),_={ts:o,tool:t.tool_name,skill:p?.name??"(none)",requiredTools:w,isMcp:u,enforce:E,stateless:!E&&!u&&t.tool_name!=="Skill",bypassCount:c.bypassCount??0,decision:d.hookSpecificOutput.permissionDecision,env_CLAUDE_PLUGIN_ROOT:process.env.CLAUDE_PLUGIN_ROOT??"(unset)",cwd:process.cwd()},W=join(g,"hook-debug.jsonl");appendFileSync(W,JSON.stringify(_)+`
2
+ `);}catch{}return d}catch{return m()}}if(process.argv[1]?.endsWith("pre-tool-use")||process.argv[1]?.endsWith("pre-tool-use.js")){let e="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{e+=t;}),process.stdin.on("end",async()=>{let t=await ft(e);console.log(JSON.stringify(t));}),process.stdin.on("error",()=>{console.log(JSON.stringify(m())),process.exit(0);}),setTimeout(()=>{console.log(JSON.stringify(m())),process.exit(0);},4e3).unref();}export{ft as handlePreToolUse};
@@ -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` |