@devflow-tools/devflow 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 (30) hide show
  1. package/dist/fallback.html +106 -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/plugin/.claude-plugin/plugin.json +19 -19
  7. package/dist/plugin/hooks.json +52 -0
  8. package/dist/plugin/skills/devflow/SKILL.md +20 -0
  9. package/dist/plugin/skills/devflow:animation/SKILL.md +69 -0
  10. package/dist/plugin/skills/devflow:context/SKILL.md +63 -0
  11. package/dist/plugin/skills/devflow:css/SKILL.md +69 -0
  12. package/dist/plugin/skills/devflow:docker/SKILL.md +72 -0
  13. package/dist/plugin/skills/devflow:doctor/SKILL.md +17 -0
  14. package/dist/plugin/skills/devflow:electron/SKILL.md +72 -0
  15. package/dist/plugin/skills/devflow:git/SKILL.md +72 -0
  16. package/dist/plugin/skills/devflow:graph/SKILL.md +55 -0
  17. package/dist/plugin/skills/devflow:graphql/SKILL.md +72 -0
  18. package/dist/plugin/skills/devflow:knowledge/SKILL.md +55 -0
  19. package/dist/plugin/skills/devflow:memory/SKILL.md +54 -0
  20. package/dist/plugin/skills/devflow:nest/SKILL.md +72 -0
  21. package/dist/plugin/skills/devflow:nextjs/SKILL.md +70 -0
  22. package/dist/plugin/skills/devflow:performance/SKILL.md +72 -0
  23. package/dist/plugin/skills/devflow:react/SKILL.md +54 -0
  24. package/dist/plugin/skills/devflow:start/SKILL.md +50 -0
  25. package/dist/plugin/skills/devflow:tailwind/SKILL.md +69 -0
  26. package/dist/plugin/skills/devflow:taro/SKILL.md +71 -0
  27. package/dist/plugin/skills/devflow:ui-layout/SKILL.md +69 -0
  28. package/dist/plugin/skills/devflow:vue/SKILL.md +76 -0
  29. package/dist/plugin/skills/devflow:workflow/SKILL.md +55 -0
  30. package/package.json +13 -13
@@ -0,0 +1,106 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>DevFlow</title>
7
+ <style>
8
+ * { margin: 0; padding: 0; box-sizing: border-box; }
9
+ body {
10
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
11
+ background: #0a0a0a;
12
+ color: #fafafa;
13
+ display: flex;
14
+ align-items: center;
15
+ justify-content: center;
16
+ min-height: 100vh;
17
+ }
18
+ .container {
19
+ text-align: center;
20
+ padding: 2rem;
21
+ }
22
+ .logo {
23
+ font-size: 3rem;
24
+ font-weight: 700;
25
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
26
+ -webkit-background-clip: text;
27
+ -webkit-text-fill-color: transparent;
28
+ margin-bottom: 1rem;
29
+ }
30
+ .status {
31
+ color: #a1a1aa;
32
+ font-size: 1.1rem;
33
+ margin-bottom: 2rem;
34
+ }
35
+ .spinner {
36
+ width: 40px;
37
+ height: 40px;
38
+ border: 3px solid #27272a;
39
+ border-top-color: #667eea;
40
+ border-radius: 50%;
41
+ animation: spin 0.8s linear infinite;
42
+ margin: 0 auto 1.5rem;
43
+ }
44
+ @keyframes spin {
45
+ to { transform: rotate(360deg); }
46
+ }
47
+ .info {
48
+ color: #52525b;
49
+ font-size: 0.85rem;
50
+ margin-top: 2rem;
51
+ }
52
+ .server-ok {
53
+ color: #22c55e;
54
+ }
55
+ .server-wait {
56
+ color: #eab308;
57
+ }
58
+ </style>
59
+ </head>
60
+ <body>
61
+ <div class="container">
62
+ <div class="logo">DevFlow</div>
63
+ <div class="spinner" id="spinner"></div>
64
+ <div class="status" id="status">Preparing dashboard...</div>
65
+ <div class="info" id="info">Checking server status...</div>
66
+ </div>
67
+ <script>
68
+ let attempts = 0;
69
+ const maxAttempts = 60;
70
+
71
+ async function checkStatus() {
72
+ attempts++;
73
+ const statusEl = document.getElementById('status');
74
+ const infoEl = document.getElementById('info');
75
+ const spinnerEl = document.getElementById('spinner');
76
+
77
+ try {
78
+ const res = await fetch('/api/projects/active', {
79
+ headers: { 'x-api-key': 'dev-key-000000' }
80
+ });
81
+
82
+ if (res.ok) {
83
+ infoEl.textContent = 'Server is running. Dashboard will appear when download completes.';
84
+ infoEl.className = 'info server-ok';
85
+ } else {
86
+ infoEl.textContent = 'Waiting for server...';
87
+ infoEl.className = 'info server-wait';
88
+ }
89
+ } catch {
90
+ infoEl.textContent = 'Waiting for server...';
91
+ infoEl.className = 'info server-wait';
92
+ }
93
+
94
+ if (attempts < maxAttempts) {
95
+ setTimeout(checkStatus, 3000);
96
+ } else {
97
+ statusEl.textContent = 'Dashboard download is taking longer than expected';
98
+ spinnerEl.style.display = 'none';
99
+ infoEl.textContent = 'The server is running. You can use the API at /api. The dashboard will appear when ready.';
100
+ }
101
+ }
102
+
103
+ checkStatus();
104
+ </script>
105
+ </body>
106
+ </html>
@@ -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};
@@ -1,20 +1,20 @@
1
1
  {
2
- "name": "@devflow-tools/claude-code-plugin",
3
- "description": "DevFlow — Developer Intelligence Platform: project context, code graph, knowledge base, memory engine, and workflow automation with 14 domain plugins",
4
- "version": "0.8.1",
5
- "author": {
6
- "name": "DevFlow"
7
- },
8
- "homepage": "https://github.com/shilongfeicool/dev-flow",
9
- "repository": "https://github.com/shilongfeicool/dev-flow",
10
- "license": "MIT",
11
- "keywords": [
12
- "devflow",
13
- "context",
14
- "knowledge-base",
15
- "code-graph",
16
- "memory",
17
- "workflow",
18
- "development"
19
- ]
20
- }
2
+ "name": "devflow",
3
+ "description": "DevFlow \u2014 Developer Intelligence Platform: project context, code graph, knowledge base, memory engine, and workflow automation with 14 domain plugins",
4
+ "version": "0.12.6",
5
+ "author": {
6
+ "name": "DevFlow"
7
+ },
8
+ "homepage": "https://github.com/shilongfeicool/dev-flow",
9
+ "repository": "https://github.com/shilongfeicool/dev-flow",
10
+ "license": "MIT",
11
+ "keywords": [
12
+ "devflow",
13
+ "context",
14
+ "knowledge-base",
15
+ "code-graph",
16
+ "memory",
17
+ "workflow",
18
+ "development"
19
+ ]
20
+ }
@@ -0,0 +1,52 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "matcher": "",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/session-start\"",
10
+ "async": false
11
+ }
12
+ ]
13
+ }
14
+ ],
15
+ "PreToolUse": [
16
+ {
17
+ "matcher": "*",
18
+ "hooks": [
19
+ {
20
+ "type": "command",
21
+ "command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/pre-tool-use\"",
22
+ "async": false
23
+ }
24
+ ]
25
+ }
26
+ ],
27
+ "PostToolUse": [
28
+ {
29
+ "matcher": "*",
30
+ "hooks": [
31
+ {
32
+ "type": "command",
33
+ "command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/post-tool-use\"",
34
+ "async": false
35
+ }
36
+ ]
37
+ }
38
+ ],
39
+ "Stop": [
40
+ {
41
+ "matcher": "",
42
+ "hooks": [
43
+ {
44
+ "type": "command",
45
+ "command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/stop\"",
46
+ "async": false
47
+ }
48
+ ]
49
+ }
50
+ ]
51
+ }
52
+ }
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: devflow
3
+ description: DevFlow — 项目开发智能平台。代码检索、图谱、记忆、工作流一站式工具集。
4
+ required_mcp_tools: []
5
+ ---
6
+
7
+ # DevFlow
8
+
9
+ 你已接入 DevFlow 开发智能平台。收到 /devflow: 前缀命令时,只做一件事:调用对应的 MCP 工具。
10
+
11
+ | 命令 | 只调用这个工具 |
12
+ |------|-------------|
13
+ | /devflow:context | get_project_context |
14
+ | /devflow:graph | get_dependency_graph |
15
+ | /devflow:memory | get_memory |
16
+ | /devflow:knowledge | get_knowledge |
17
+ | /devflow:workflow | run_workflow |
18
+ | /devflow:doctor | get_project_context |
19
+
20
+ 领域插件(/devflow:react, /devflow:vue, /devflow:nextjs, /devflow:nest, /devflow:tailwind, /devflow:docker, /devflow:electron, /devflow:graphql, /devflow:git, /devflow:performance, /devflow:animation, /devflow:css, /devflow:taro, /devflow:ui-layout)— 每个都有对应的 MCP 工具。
@@ -0,0 +1,69 @@
1
+ ---
2
+ name: devflow:animation
3
+ description: Animation 动画专家 — CSS 动画、Framer Motion、GSAP、Lottie、性能优化
4
+ ---
5
+
6
+ ## 工作流(必须按顺序执行)
7
+
8
+ ### Step 1: 获取精确上下文
9
+ 调用 `mcp__devflow__get_project_context` 获取与用户问题相关的代码、记忆和知识库信息。
10
+ 这一步确保你拿到的是项目真实状态,而非凭空猜测。
11
+
12
+ ### Step 2: 分析
13
+ 基于 Step 1 返回的上下文,分析问题根因或设计方案。
14
+ 此时可以自由使用 Read 查看具体文件。
15
+
16
+ ### Step 3: 执行
17
+ 根据分析结果修改代码,逐文件编辑。
18
+
19
+ ### Step 4: 验证
20
+ 运行类型检查和测试,确保改动正确。
21
+
22
+ ## 可用工具
23
+
24
+ | 工具 | 何时调用 |
25
+ |------|----------|
26
+ | `mcp__devflow__get_project_context` | 每次任务第一步(必须) |
27
+ | `mcp__devflow__animation_suggest` | 按需调用 |
28
+
29
+ ## 规则
30
+ - 永远从 `mcp__devflow__get_project_context` 开始——你不知道项目里有什么
31
+ - 上下文返回后,用 Read 确认关键文件
32
+ - 改完代码后跑类型检查
33
+
34
+ ## 规则
35
+ - 永远从 `get_project_context` 开始——你不知道项目里有什么
36
+ - 上下文返回后,用 Read 确认关键文件
37
+ - 改完代码后跑类型检查
38
+
39
+ ## 规则
40
+ - 永远从 `animation_suggest` 开始——你不知道项目里有什么
41
+ - 上下文返回后,用 Read 确认关键文件
42
+ - 改完代码后跑类型检查
43
+
44
+ # ✨ ANIMATION
45
+
46
+ 你是一个动画专家。精通 CSS Animation、Framer Motion、GSAP。动画必须 60fps 且支持 prefers-reduced-motion。
47
+
48
+ ## 适用场景
49
+
50
+ **动画审计:性能检查 → 可访问性 → 实现质量**
51
+
52
+ **新建动画:需求分析 → 选型建议 → 实现 → 性能验证**
53
+
54
+ **动画 GPU 迁移:扫描 → 替换为 GPU 属性 → 验证**
55
+
56
+ ## 核心规则
57
+
58
+ - 动画属性只使用 transform 和 opacity(GPU 加速)
59
+ - 过渡动画 200-300ms,入场 300-500ms
60
+ - 使用 ease-out 入场、ease-in 退场
61
+ - 尊重 prefers-reduced-motion,提供减弱方案
62
+ - 避免同时动画过多元素(批量编排使用 staggerChildren)
63
+ - 使用 AnimatePresence 处理 React 组件退出动画
64
+ - Lottie 动画控制文件大小 < 50KB
65
+
66
+ ## 当前版本
67
+
68
+ - animation-docs: 2024 — https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animations
69
+
@@ -0,0 +1,63 @@
1
+ ---
2
+ name: devflow:context
3
+ description: 检索项目上下文 — 自然语言查询相关代码、符号、依赖关系
4
+ ---
5
+
6
+ ## 工作流(必须按顺序执行)
7
+
8
+ ### Step 1: 获取精确上下文
9
+ 调用 `mcp__devflow__get_project_context` 获取与用户问题相关的代码、记忆和知识库信息。
10
+ 这一步确保你拿到的是项目真实状态,而非凭空猜测。
11
+
12
+ ### Step 2: 分析
13
+ 基于 Step 1 返回的上下文,分析问题根因或设计方案。
14
+ 此时可以自由使用 Read 查看具体文件。
15
+
16
+ ### Step 3: 执行
17
+ 根据分析结果修改代码,逐文件编辑。
18
+
19
+ ### Step 4: 验证
20
+ 运行类型检查和测试,确保改动正确。
21
+
22
+ ## 可用工具
23
+
24
+ | 工具 | 何时调用 |
25
+ |------|----------|
26
+ | `mcp__devflow__get_project_context` | 每次任务第一步(必须) |
27
+ | `mcp__devflow__get_project_context` | 按需调用 |
28
+ | `mcp__devflow__search_symbol` | 按需调用 |
29
+
30
+ ## 规则
31
+ - 永远从 `mcp__devflow__get_project_context` 开始——你不知道项目里有什么
32
+ - 上下文返回后,用 Read 确认关键文件
33
+ - 改完代码后跑类型检查
34
+
35
+ ## 规则
36
+ - 永远从 `get_project_context` 开始——你不知道项目里有什么
37
+ - 上下文返回后,用 Read 确认关键文件
38
+ - 改完代码后跑类型检查
39
+
40
+ ## 规则
41
+ - 永远从 `get_project_context` 开始——你不知道项目里有什么
42
+ - 上下文返回后,用 Read 确认关键文件
43
+ - 改完代码后跑类型检查
44
+
45
+ # /devflow:context
46
+
47
+ ## 执行
48
+
49
+ 1. 如果用户的查询包含中文,先将其中的中文关键词翻译为英文代码术语(空格分隔)
50
+ 2. 调用 `mcp__devflow__get_project_context` MCP 工具:
51
+
52
+ ```
53
+ query = 用户的原始查询内容(包含中文)
54
+ expandedTerms = 你翻译的英文关键词(如 "code review audit module")
55
+ mode = "fast" (首次调用;如需更深入结果,再改用 "deep")
56
+ ```
57
+
58
+ 也可以使用 `mcp__devflow__search_symbol` 按符号名搜索代码。
59
+
60
+ 示例:
61
+ - 用户查询 "代码审核模块的流程" → expandedTerms = "code review audit module workflow pipeline process"
62
+ - 用户查询 "用户登录认证逻辑" → expandedTerms = "user login authentication auth logic service handler"
63
+ - 用户查询 "数据库缓存策略" → expandedTerms = "database db cache memo redis storage strategy"