@devflow-tools/claude-code-plugin 0.16.10 → 0.16.12

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.
@@ -1,2 +1,15 @@
1
- import {request}from'http';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {existsSync,readFileSync,rmSync,mkdirSync,writeFileSync,renameSync}from'fs';import {join}from'path';import {randomUUID}from'crypto';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';function a(s){return s??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function m(s){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(a(s))}function x(s){return Buffer.from(s,"utf8").toString("base64url")}var i=class{constructor(e){this.projectRoot=e;this.sessions=new Map;this.sessionsDir=join(m(e),"hook-sessions");}registerSession(e){let t=e.trim();if(!t)throw new Error("session_id_required");let n=this.get(t);if(n)return n.lastActivityAt=Date.now(),this.persist(n),n;let r=Date.now(),o={sessionId:t,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:r,lastActivityAt:r};return this.sessions.set(t,o),this.persist(o),o}startExecution(e,t,n){let r=this.registerSession(e);return (!r.executionId||r.skillName!==t)&&(r.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),r.skillName=t,r.requiredMcpTools=[...new Set(n)],r.lastActivityAt=Date.now(),this.persist(r),r}get(e){let t=e.trim();if(!t)return null;let n=this.sessions.get(t);if(n)return n;let r=this.snapshotPath(t);if(!existsSync(r))return null;try{let o=JSON.parse(readFileSync(r,"utf8"));return o.sessionId!==t||o.projectRoot!==this.projectRoot?null:(o.requiredMcpTools=Array.isArray(o.requiredMcpTools)?o.requiredMcpTools:[],this.sessions.set(t,o),o)}catch{return null}}completeExecution(e){let t=this.get(e);if(!t)return null;let n={...t,requiredMcpTools:[...t.requiredMcpTools]};return delete t.executionId,delete t.skillName,t.requiredMcpTools=[],t.lastActivityAt=Date.now(),this.persist(t),n}removeSession(e){let t=e.trim();t&&(this.sessions.delete(t),rmSync(this.snapshotPath(t),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(e){return join(this.sessionsDir,`${x(e)}.json`)}persist(e){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let t=this.snapshotPath(e.sessionId),n=`${t}.${process.pid}.tmp`;writeFileSync(n,JSON.stringify(e),{mode:384}),renameSync(n,t);}};function M(s){return `DevFlow memory decision required for ${s.turnId}. Before the final response, semantically decide whether this turn contains durable project knowledge. Call mcp__devflow__memory_commit_turn with evidence-bound candidates, or mcp__devflow__memory_skip_turn with a concrete reason. Do not claim memory success without a canonical receipt.`}function p(s,e,t){if(t)return null;let n=openGlobalDevFlowDatabase();try{let r=n.getPendingMemoryTurn(s,e);return !r||!n.markMemoryTurnStopPrompted(r.turnId)?null:{decision:"block",reason:M(r)}}finally{n.close();}}var T=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",k=getLocalApiKey();function D(s,e){return new Promise(t=>{let n=JSON.stringify(e),r=new URL(s,T),o=request({hostname:r.hostname,port:r.port,path:r.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":k,"Content-Length":Buffer.byteLength(n)},timeout:5e3},()=>t());o.on("error",()=>t()),o.write(n),o.end();})}async function b(s,e=a()){let t;try{t=JSON.parse(s);}catch{return null}let n=t.session_id?.trim();if(!n)return null;let r=p(e,n,t.stop_hook_active===true);if(r)return r;if(t.stop_hook_active)return null;let o=new i(e).completeExecution(n);if(o?.executionId){let c=openGlobalDevFlowDatabase();try{c.reconcileSkillExecution(o.executionId,"completed"),c.deleteContextReceipt(e,n,o.executionId);}finally{c.close();}}return D(`/api/telemetry/sessions/${encodeURIComponent(n)}/tick`,{timestamp:Date.now()}).catch(()=>{}),null}if(process.argv[1]?.endsWith("stop")||process.argv[1]?.endsWith("stop.js")){let s="";process.stdin.setEncoding("utf8"),process.stdin.on("data",e=>{s+=e;}),process.stdin.on("end",async()=>{let e=await b(s.trim()||process.argv[2]||"");e&&console.log(JSON.stringify(e)),process.exit(0);}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),4e3).unref();}
2
- export{b as handleStop};
1
+ import {request}from'http';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {existsSync as existsSync$1,readFileSync as readFileSync$1,rmSync,mkdirSync as mkdirSync$1,writeFileSync as writeFileSync$1,linkSync,readdirSync,renameSync as renameSync$1}from'fs';import {join as join$1}from'path';import {randomUUID,createHash as createHash$1}from'crypto';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {createHash}from'node:crypto';import {readFileSync,openSync,constants,fstatSync,closeSync,mkdirSync,existsSync,writeFileSync,renameSync,unlinkSync,lstatSync,readSync}from'node:fs';import {join,resolve}from'node:path';import'@devflow-tools/memory-engine';import'os';function y(n){return n??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function b(n){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(y(n))}function D(n){return Buffer.from(n,"utf8").toString("base64url")}function oe(n,e,t){let r=e?.trim();if(r){let o=/^[a-zA-Z0-9:._-]{1,192}$/.test(r)?r:`tool-${createHash$1("sha256").update(r.slice(0,1024)).digest("hex").slice(0,32)}`;return `${n}:${o}`}return `${n}:legacy-${t}-${randomUUID().slice(0,8)}`}function N(n){return `${n}:legacy-journal`}var g=class{constructor(e){this.projectRoot=e;this.sessions=new Map;this.sessionsDir=join$1(b(e),"hook-sessions");}registerSession(e){let t=e.trim();if(!t)throw new Error("session_id_required");let r=this.get(t);if(r)return r.lastActivityAt=Date.now(),this.persist(r),r;let o=Date.now(),i={sessionId:t,projectRoot:this.projectRoot,requiredMcpTools:[],evidenceObligations:[],evidenceDegradations:[],evidenceOutcomes:[],startedAt:o,lastActivityAt:o};return this.sessions.set(t,i),this.persist(i),i}startExecution(e,t,r){let o=this.registerSession(e);return (!o.executionId||o.skillName!==t)&&(o.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),o.skillName=t,o.requiredMcpTools=[...new Set(r)],o.lastActivityAt=Date.now(),this.persist(o),o}get(e){let t=e.trim();if(!t)return null;let r=this.sessions.get(t);if(r)return this.replayEvidenceJournal(r),r;let o=this.snapshotPath(t);if(!existsSync$1(o))return null;try{let i=JSON.parse(readFileSync$1(o,"utf8"));return i.sessionId!==t||i.projectRoot!==this.projectRoot?null:(i.requiredMcpTools=Array.isArray(i.requiredMcpTools)?i.requiredMcpTools:[],i.evidenceObligations=T(i.evidenceObligations),i.evidenceDegradations=P(i.evidenceDegradations),i.evidenceOutcomes=$(i.evidenceOutcomes),this.replayEvidenceJournal(i),this.sessions.set(t,i),i)}catch{return null}}completeExecution(e){let t=this.get(e);if(!t)return null;let r=structuredClone(t);return delete t.executionId,delete t.skillName,t.requiredMcpTools=[],t.lastActivityAt=Date.now(),this.persist(t),r}addEvidenceObligation(e,t,r=Date.now(),o){let i=this.registerSession(e),a=(o?void 0:i.evidenceObligations.find(d=>d.contractId===t.id))?.obligationId??oe(t.id,o,r);this.appendEvidenceEvent(e,{kind:"add",obligationId:a,contractId:t.id,at:r,contract:t},`add\0${a}`),this.replayEvidenceJournal(i);let c=i.evidenceObligations.find(d=>d.obligationId===a);return c?(i.lastActivityAt=Date.now(),this.persist(i),structuredClone(c)):null}listEvidenceObligations(e){return (this.get(e)?.evidenceObligations??[]).map(t=>structuredClone(t))}markEvidenceCorrection(e,t,r,o,i=Date.now()){let s=this.get(e),a=s?.evidenceObligations.find(u=>u.obligationId===t);if(!s||!a)return null;let c=/^[a-f0-9]{64}$/.test(o)?o:"";if(!c)return null;this.appendEvidenceEvent(e,{kind:"correction",obligationId:t,contractId:a.contractId,at:i,fingerprint:c,violations:r.slice(0,16).map(u=>u.slice(0,240))},`correction\0${t}\0${c}`),this.replayEvidenceJournal(s),s.lastActivityAt=i,this.persist(s);let d=s.evidenceObligations.find(u=>u.obligationId===t);return d?structuredClone(d):null}resolveEvidenceObligation(e,t){let r=this.get(e);if(!r)return false;let o=r.evidenceObligations.find(s=>s.obligationId===t);if(!o)return false;let i=Date.now();return this.appendEvidenceEvent(e,{kind:"resolve",obligationId:t,contractId:o.contractId,at:i},`resolve\0${t}`),this.replayEvidenceJournal(r),r.lastActivityAt=i,this.persist(r),true}degradeEvidenceObligation(e,t,r,o=Date.now()){let i=this.get(e),s=i?.evidenceObligations.find(c=>c.obligationId===t);if(!i||!s)return false;let a=r.slice(0,240);return this.appendEvidenceEvent(e,{kind:"degrade",obligationId:t,contractId:s.contractId,at:o,reason:a},`degrade\0${t}\0${a}`),this.replayEvidenceJournal(i),i.lastActivityAt=o,this.persist(i),true}removeSession(e){let t=e.trim();t&&(this.sessions.delete(t),rmSync(this.snapshotPath(t),{force:true}),rmSync(this.evidenceJournalDir(t),{recursive:true,force:true}));}list(){return [...this.sessions.values()]}snapshotPath(e){return join$1(this.sessionsDir,`${D(e)}.json`)}evidenceJournalDir(e){return join$1(this.sessionsDir,"evidence-journal",D(e))}appendEvidenceEvent(e,t,r){let o=this.evidenceJournalDir(e);mkdirSync$1(o,{recursive:true,mode:448});let i=createHash$1("sha256").update(r).digest("hex"),s=join$1(o,`${t.kind}-${i}.json`);if(existsSync$1(s))return;let a=join$1(o,`.${process.pid}.${randomUUID()}.tmp`);writeFileSync$1(a,JSON.stringify(t),{mode:384});try{linkSync(a,s);}catch(c){if(c.code!=="EEXIST")throw c}finally{rmSync(a,{force:true});}}replayEvidenceJournal(e){e.evidenceObligations=T(e.evidenceObligations),e.evidenceDegradations=P(e.evidenceDegradations),e.evidenceOutcomes=$(e.evidenceOutcomes);let t=this.evidenceJournalDir(e.sessionId);if(!existsSync$1(t))return;let r;try{r=readdirSync(t).filter(i=>/^(?:add|correction|resolve|degrade)-[a-f0-9]{64}\.json$/.test(i)).slice(0,4096);}catch{return}let o=r.flatMap(i=>{try{let s=readFileSync$1(join$1(t,i),"utf8");if(Buffer.byteLength(s,"utf8")>64*1024)return [];let a=ie(JSON.parse(s));return a?[a]:[]}catch{return []}}).sort((i,s)=>i.at-s.at||j(i.kind)-j(s.kind));for(let i of o)this.applyEvidenceEvent(e,i);e.evidenceObligations=e.evidenceObligations.slice(-8),e.evidenceDegradations=e.evidenceDegradations.slice(-20),e.evidenceOutcomes=e.evidenceOutcomes.slice(-20);}applyEvidenceEvent(e,t){let r=e.evidenceOutcomes.find(s=>s.obligationId===t.obligationId);if(t.kind==="add"){if(r)return;let s=e.evidenceObligations.find(a=>a.obligationId===t.obligationId);s?(s.contract=t.contract,s.promptedAt=Math.min(s.promptedAt,t.at)):e.evidenceObligations.push({obligationId:t.obligationId,contractId:t.contractId,contract:t.contract,promptedAt:t.at,attempt:0,correctionRequested:false,violations:[]});return}let o=e.evidenceObligations.find(s=>s.obligationId===t.obligationId);if(t.kind==="correction"){if(!o||r||o.correctionFingerprint===t.fingerprint)return;o.attempt+=1,o.correctionRequested=true,o.correctionRequestedAt=t.at,o.correctionFingerprint=t.fingerprint,o.violations=t.violations;return}let i=o?.attempt??0;e.evidenceObligations=e.evidenceObligations.filter(s=>s.obligationId!==t.obligationId),!r&&(t.kind==="degrade"&&e.evidenceDegradations.push({obligationId:t.obligationId,contractId:t.contractId,reason:t.reason,degradedAt:t.at,attempt:i}),e.evidenceOutcomes.push({obligationId:t.obligationId,contractId:t.contractId,status:t.kind==="resolve"?"resolved":"degraded",completedAt:t.at,attempt:i,...t.kind==="degrade"?{reason:t.reason}:{}}));}persist(e){mkdirSync$1(this.sessionsDir,{recursive:true,mode:448});let t=this.snapshotPath(e.sessionId),r=`${t}.${process.pid}.${randomUUID()}.tmp`;writeFileSync$1(r,JSON.stringify(e),{mode:384}),renameSync$1(r,t);}};function T(n){return Array.isArray(n)?n.flatMap(e=>{if(!e||typeof e!="object")return [];let t=e;if(typeof t.contractId!="string"||t.contractId.length===0||t.contractId.length>128||typeof t.promptedAt!="number"||typeof t.attempt!="number"||typeof t.correctionRequested!="boolean"||!Array.isArray(t.violations)||!L(t.contract))return [];let r=typeof t.obligationId=="string"&&t.obligationId.length>0&&t.obligationId.length<=384?t.obligationId:N(t.contractId),o=typeof t.correctionTranscriptHash=="string"?t.correctionTranscriptHash:void 0,i=t.correctionFingerprint??o;return i!==void 0&&!/^[a-f0-9]{64}$/.test(i)?[]:[{...t,obligationId:r,correctionFingerprint:i}]}).slice(-8):[]}function P(n){return Array.isArray(n)?n.filter(e=>{if(!e||typeof e!="object")return false;let t=e;return typeof t.obligationId=="string"&&typeof t.contractId=="string"&&typeof t.reason=="string"&&typeof t.degradedAt=="number"&&typeof t.attempt=="number"}).slice(-20):[]}function $(n){return Array.isArray(n)?n.filter(e=>{if(!e||typeof e!="object")return false;let t=e;return typeof t.obligationId=="string"&&typeof t.contractId=="string"&&(t.status==="resolved"||t.status==="degraded")&&typeof t.completedAt=="number"&&typeof t.attempt=="number"&&(t.reason===void 0||typeof t.reason=="string")}).slice(-20):[]}function ie(n){if(!n||typeof n!="object")return null;let e=n;if(!["add","correction","resolve","degrade"].includes(e.kind??"")||typeof e.contractId!="string"||e.contractId.length>128||typeof e.at!="number")return null;let t=typeof e.obligationId=="string"&&e.obligationId.length>0&&e.obligationId.length<=384?e.obligationId:N(e.contractId);if(e.kind==="add")return L(e.contract)?{...e,obligationId:t}:null;if(e.kind==="correction"){let r=typeof e.fingerprint=="string"?e.fingerprint:e.transcriptHash;return typeof r=="string"&&/^[a-f0-9]{64}$/.test(r)&&Array.isArray(e.violations)&&e.violations.every(o=>typeof o=="string"&&o.length<=240)?{...e,obligationId:t,fingerprint:r}:null}return e.kind==="resolve"?{...e,obligationId:t}:typeof e.reason=="string"&&e.reason.length<=240?{...e,obligationId:t}:null}function j(n){return n==="add"?0:n==="correction"?1:2}function L(n){if(!n||typeof n!="object")return false;let e=n,t=e.requiredSections,r=e.prohibitedClaims;return e.version==="1.0"&&typeof e.id=="string"&&/^[a-zA-Z0-9][a-zA-Z0-9:._-]{0,127}$/.test(e.id)&&typeof e.runtimeProfilingOccurred=="boolean"&&typeof e.samplingEvidenceOccurred=="boolean"&&Array.isArray(t)&&t.length===3&&t[0]==="\u5DE5\u5177\u53D1\u73B0"&&t[1]==="\u6A21\u578B\u5047\u8BBE"&&t[2]==="\u9700\u8981\u8FD0\u884C\u65F6\u9A8C\u8BC1"&&Array.isArray(r)&&r.length<=16&&r.every(o=>typeof o=="string"&&o.length<=128)&&typeof e.canonicalReport=="string"&&Buffer.byteLength(e.canonicalReport,"utf8")<=48*1024&&!!e.correctionPolicy&&e.correctionPolicy.maxAttempts===1&&e.correctionPolicy.repeatedViolation==="fail_open"}var w=1,me="memory-snapshot-v1.json",pe=512*1024,J=256*1024,q=2e3,ge=500,fe=10,he=240,ye=300*1e3,be=720*60*60*1e3,ve=2147483647;function Se(n){return n.currentUid===void 0?{ownerSafe:true,modeSafe:true}:{ownerSafe:n.ownerUid===n.currentUid,modeSafe:(n.mode&18)===0}}function h(n){return resolve(n)}function v(n){return createHash("sha256").update(h(n)).digest("hex").slice(0,16)}function we(n,e={}){let t=v(n),r=e.stateRoot?join(e.stateRoot,t):getProjectStateDir(h(n));return join(r,me)}function Ie(n,e){let r=(n instanceof Error?n.message:"").replace(/[\r\n\t]+/gu," ").trim().slice(0,160);return r?`${e}:${r}`:e}function C(n,e){return Array.isArray(n)&&n.length<=e&&n.every(t=>typeof t=="string"&&t.length>0&&t.length<=512)}function _e(n){if(!n||typeof n!="object"||Array.isArray(n))return false;let e=n;return typeof e.id=="string"&&e.id.length>0&&e.id.length<=512&&typeof e.kind=="string"&&e.kind.length>0&&e.kind.length<=128&&(e.family===void 0||typeof e.family=="string"&&e.family.length>0&&e.family.length<=256)&&(e.entity===void 0||typeof e.entity=="string"&&e.entity.length>0&&e.entity.length<=256)&&C(e.memoryIds,100)&&(e.status==="withheld"||e.status==="resolved")&&(e.summary===void 0||typeof e.summary=="string"&&e.summary.length<=2e3)}function Ee(n){if(!n||typeof n!="object"||Array.isArray(n))return false;let e=n;return typeof e.reason=="string"&&e.reason.length>0&&e.reason.length<=512&&(e.stale===void 0||typeof e.stale=="boolean")}function W(n,e,t){if(!n||typeof n!="object"||Array.isArray(n))return null;let r=n,o=h(e),i=v(o),s=r.generation===void 0?0:r.generation;return r.version!==w||r.projectRoot!==o||r.projectHash!==i||typeof r.markdown!="string"||Buffer.byteLength(r.markdown,"utf8")>J||!C(r.memoryIds,q)||!Array.isArray(r.conflicts)||r.conflicts.length>ge||!r.conflicts.every(_e)||typeof r.generatedAt!="number"||!Number.isFinite(r.generatedAt)||r.generatedAt<0||r.generatedAt>t+ye||!Number.isInteger(s)||s<0||s>ve||!["ready","pending","degraded","disabled"].includes(String(r.status))||r.degradation!==void 0&&!Ee(r.degradation)?null:{...r,generation:s}}function ke(n,e,t,r,o=0){let i=h(n);return {version:w,projectRoot:i,projectHash:v(i),markdown:"",memoryIds:[],conflicts:[],generatedAt:r,generation:o,status:e,degradation:{reason:t}}}function Re(n,e={}){let t=e.now??Date.now(),r=we(n,e),o;try{o=openSync(r,constants.O_RDONLY|constants.O_NOFOLLOW);let i=fstatSync(o);if(!i.isFile()||i.size<=0||i.size>pe)throw new Error("cache_size_invalid");let s=Se({...typeof process.getuid=="function"?{currentUid:process.getuid()}:{},ownerUid:i.uid,mode:i.mode});if(!s.ownerSafe)throw new Error("cache_owner_invalid");if(!s.modeSafe)throw new Error("cache_permissions_unsafe");let a=JSON.parse(readFileSync(o,"utf8")),c=W(a,n,t);if(!c)throw new Error("cache_schema_invalid");return c.status==="ready"&&t-c.generatedAt>be?{snapshot:{...c,status:"degraded",degradation:{reason:"cache_stale",stale:!0}},source:"cache",valid:!0,stale:!0}:{snapshot:c,source:"cache",valid:!0,stale:!1}}catch(i){let a=i.code==="ENOENT";return {snapshot:ke(n,a?"pending":"degraded",a?"cache_missing":Ie(i,"cache_invalid"),t),source:"none",valid:false,stale:false}}finally{if(o!==void 0)try{closeSync(o);}catch{}}}function Ce(n){if(!n)return true;try{return JSON.parse(readFileSync(n,"utf8")).sessionStart?.injectMemories??!0}catch{return true}}function Me(n,e,t){if(!e.trim())return null;try{let r=JSON.parse(e);if(r.status!=="ok"&&r.status!=="degraded")return null;if(r.enabled===!1){let i=h(n);return {version:w,projectRoot:i,projectHash:v(i),markdown:"",memoryIds:[],conflicts:[],generatedAt:t,generation:0,status:"disabled"}}let o=W(r.snapshot,n,t);if(o)return o;if(r.status==="ok"&&typeof r.markdown=="string"&&r.markdown.length>0&&Buffer.byteLength(r.markdown,"utf8")<=J){let i=h(n);return {version:w,projectRoot:i,projectHash:v(i),markdown:r.markdown,memoryIds:C(r.memoryIds,q)?r.memoryIds:[],conflicts:[],generatedAt:t,generation:0,status:"ready"}}}catch{}return null}function R(n,e){return [...(n??e).replace(/[\r\n\t|]+/gu," ").trim()||e].slice(0,he).join("")}function xe(n){if(n.length===0)return "";let e=n.slice(0,fe),t=e.map(r=>{let o=R(r.family,"unknown"),i=R(r.entity,"unknown"),s=R(r.summary,"Explicit choice required.");return `- family=${o}; entity=${i}; status=${r.status}; summary=${s}`});return n.length>e.length&&t.push(`- ${n.length-e.length} additional conflict(s) omitted from SessionStart output.`),`## Withheld preference conflicts (${n.length})
2
+ ${t.join(`
3
+ `)}`}function Oe(n){let e=n.now??Date.now();if(Ce(n.configPath)===false)return "";let t=Me(n.projectRoot,n.daemonResponse??"",e),r=t?{snapshot:t,source:"daemon"}:Re(n.projectRoot,{...n.cacheOptions,now:e}),{snapshot:o}=r;if(o.status==="disabled")return `## Project memory
4
+ DevFlow memory snapshot=pending; source=${r.source}; reason=previous_snapshot_disabled. Memory availability is not yet known; do not conclude that the project has no memories.`;let i=new Date(o.generatedAt).toISOString(),s=o.degradation?.reason?`; reason=${o.degradation.reason}`:"",a=`DevFlow memory snapshot=${o.status}; source=${r.source}; generatedAt=${i}${s}.`,c=xe(o.conflicts);return o.markdown?[o.markdown,c,a].filter(Boolean).join(`
5
+ `):["## Project memory",c,`${a} Memory availability is not yet known; do not conclude that the project has no memories.`].filter(Boolean).join(`
6
+ `)}if(process.env.DEVFLOW_MEMORY_SNAPSHOT_CLI==="1"){let[,,n,e,t]=process.argv;n!=="session-start"||!e?process.exitCode=2:process.stdout.write(Oe({projectRoot:e,configPath:t,daemonResponse:process.env.DEVFLOW_MEMORY_RESPONSE}));}function De(n){return `DevFlow memory decision required for ${n.turnId}. Before the final response, semantically decide whether this turn contains durable project knowledge. Call mcp__devflow__memory_commit_turn with evidence-bound candidates, or mcp__devflow__memory_skip_turn with a concrete reason. Do not claim memory success without a canonical receipt.`}function B(n,e,t){let r=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:50});try{let o=r.getPendingMemoryTurn(n,e);return !o||!r.markMemoryTurnStopPrompted(o.turnId)?null:{decision:"block",reason:De(o)}}finally{r.close();}}var Ne=512*1024,Le=500;var ze=["\u5DE5\u5177\u53D1\u73B0","\u6A21\u578B\u5047\u8BBE","\u9700\u8981\u8FD0\u884C\u65F6\u9A8C\u8BC1"];function K(n){let e=n.runtimeStore??new g(n.projectRoot),t=e.listEvidenceObligations(n.sessionId);if(t.length===0)return null;let r=Ye(n.transcriptPath,t);if(!r.answer){let s=r.degradationReason??"transcript_answer_unavailable";for(let a of t)e.degradeEvidenceObligation(n.sessionId,a.obligationId,s);return null}let o=r.fingerprint;if(!o){for(let s of t)e.degradeEvidenceObligation(n.sessionId,s.obligationId,"transcript_identity_unavailable");return null}let i=[];for(let s of t){let a=He(r.answer,[s]);if(a.length===0){e.resolveEvidenceObligation(n.sessionId,s.obligationId);continue}if(!s.correctionFingerprint){e.markEvidenceCorrection(n.sessionId,s.obligationId,a,o),i.push({obligation:s,violations:a});continue}if(s.correctionFingerprint===o){i.push({obligation:s,violations:a});continue}e.degradeEvidenceObligation(n.sessionId,s.obligationId,"evidence_contract_unresolved");}return i.length===0?null:Ue(i)}function Ue(n){let e=[...new Set(n.flatMap(r=>r.violations))],t=[...new Map(n.map(r=>[r.obligation.contractId,r.obligation.contract.canonicalReport])).values()];return {decision:"block",reason:["DevFlow evidence contract violation. Correct the final performance answer once before stopping.",`Violations: ${e.join("; ")}`,"Use this canonical report (you may add clearly labeled hypotheses or runtime verification steps, but no unsupported measured claims):",t.join(`
7
+
8
+ ---
9
+
10
+ `)].join(`
11
+
12
+ `)}}function He(n,e){let t=new Set,r=new Set(e.flatMap(c=>c.contract.requiredSections)),o=Je(n.split(/\r?\n/)),i=et(o.headingLines);for(let c of r)i.found.has(c)||t.add(`missing_section:${c}`);if(e.every(c=>c.contract.runtimeProfilingOccurred))return [...t];let a=e.every(c=>c.contract.samplingEvidenceOccurred);for(let c=0;c<o.claimLines.length;c++){let d=o.claimLines[c].trim();if(!d||We(d))continue;let u=i.byLine.get(c);for(let l of Xe(d))Ge(l,u,a,t);}return [...t]}function Je(n){let e=false,t=[],r=[];for(let o of n){if(/^\s*```/.test(o)){e=!e,t.push(""),r.push(o.replace(/^\s*```+[^\s`]*/,"").replace(/```+\s*$/,"").trim());continue}t.push(e?"":o.replace(/`[^`]*`/g,"").trim()),r.push(o.replace(/`([^`]*)`/g,(i,s)=>qe(s)?"":s).trim());}return {headingLines:t,claimLines:r}}function qe(n){let e=n.trim().slice(0,256);return !e||/\s/.test(e)||n.trim().length>256?false:/^[A-Za-z_$][\w$.-]*$/.test(e)||/^(['"])[^'"`]*\1$/.test(e)}function We(n){if(n.length>2048)return false;let e=n.replace(/^\s*(?:[-*+]|\d+[.)])\s+/,"").trim();return e?Be(e)||/^(?:\/\/|\/\*|\*\/|\*\s|#!)/.test(e)||/^(?:import|export|const|let|var|function|class|type|interface|enum|namespace|declare)\b/.test(e)||/^(?:async\s+)?(?:function\s+)?[A-Za-z_$][\w$]*\s*=\s*/.test(e)||/^[A-Za-z_$][\w$.[\]'"-]*\s*(?:=|\+=|-=|\*=|\/=|\?\?=|&&=|\|\|=)\s*/.test(e)||/^[A-Za-z_$'"-][\w$.'"-]*\s*:\s*[^:]+[,}]?$/.test(e)||/=>/.test(e)||/^(?:await\s+|return\s+|throw\s+|new\s+)?[A-Za-z_$][\w$.]*\s*\([^)]*\)\s*;?$/.test(e)||/^(['"])[\s\S]*\1[;,]?$/.test(e)?true:/[{}()[\]=<>]\s*;?$/.test(e)&&/(?:;|=>|\b(?:return|new|await)\b)/.test(e):false}function Be(n){if(n.length>1024)return false;let e="(?:--[A-Za-z0-9_-]+|(?:-webkit-|-moz-|-ms-)?(?:animation(?:-[a-z-]+)?|transition(?:-[a-z-]+)?|opacity|transform|will-change|contain|content-visibility|display|position|top|right|bottom|left|width|height|min-(?:width|height)|max-(?:width|height)|margin(?:-[a-z]+)?|padding(?:-[a-z]+)?|color|background(?:-[a-z-]+)?|border(?:-[a-z-]+)?|font(?:-[a-z-]+)?|line-height|z-index|overflow(?:-[xy])?))";return new RegExp(`^${e}\\s*:\\s*\\S[^;]{0,960};$`,"i").test(n)}function Ge(n,e,t,r){let o=/\bP0\b/gi;for(let m of n.matchAll(o))V(n,m.index??0,m[0],"p0")||r.add("unsupported_p0_severity");let i=/(?:中高|高)(?:风险|优先级)|严重(?:问题|风险)|\bhigh[- ](?:risk|severity|priority)\b/gi;for(let m of n.matchAll(i))V(n,m.index??0,m[0],"high")||r.add("unsupported_high_severity");let s=/(?:性能)?瓶颈|卡顿|掉帧|CPU\s*(?:热点|尖峰)|\bbottleneck\b|\bjank\b|\bdropped frames?\b|\bCPU hotspot\b/gi;for(let m of n.matchAll(s))Ke(n,m.index??0,m[0])||(/(?:瓶颈|bottleneck)/i.test(m[0])?r.add("unsupported_confirmed_bottleneck"):r.add("unsupported_runtime_effect"));let a=/\b\d{1,3}(?:\.\d+)?\s*%/.test(n),c=/(?:预计|预期|估计|提升|降低|减少|节省|改善|收益|加速|improve|reduce|faster|gain|benefit|speedup|expected|estimated)/i.test(n),d=/\b\d+(?:\.\d+)?\s*[x×]\b/i.test(n);(a||d)&&c&&r.add(a?"unsupported_benefit_percentage":"unsupported_performance_number");let u=/\b\d+(?:\.\d+)?(?:\s*[-–~]\s*\d+(?:\.\d+)?)?\s*(?:ms|fps|hz)\b/i.test(n)||/\d+(?:\.\d+)?(?:\s*[-–~]\s*\d+(?:\.\d+)?)?\s*(?:毫秒|次\s*\/\s*秒|帧)/.test(n),l=e==="\u9700\u8981\u8FD0\u884C\u65F6\u9A8C\u8BC1"&&/(?:采集|记录|测量|收集|collect|measure|record)/i.test(n)&&!/(?:预计|预期|估计|约|大约|通常|平均|每(?:隔)?|达到|提升|降低|减少|节省|改善|improve|reduce|faster|expected|estimated|average|typically|every)/i.test(n);a&&!c&&!l&&r.add("unsupported_benefit_percentage"),u&&!l&&r.add("unsupported_performance_number");let p=/(?:requestAnimationFrame|\bRAF\b)/i.test(n),I=/(?:建议|应该|应当|使用|添加|采用|切换|包裹|优化|节流|\buse\b|\badopt\b|\brecommend\b|\bswitch\b|\bwrap\b|\bthrottl(?:e|ing)\b)/i.test(n),_=/(?:不要|不得|不能|不应|避免|不建议|不推荐).{0,16}(?:使用|采用|添加|切换|包裹|节流)?.{0,8}(?:requestAnimationFrame|\bRAF\b)|\b(?:do\s+not|don't|never|avoid)\s+(?:use|adopt|recommend|switch|wrap|throttle)?.{0,8}(?:requestAnimationFrame|RAF)\b/i.test(n);p&&I&&!_&&!t&&r.add("raf_without_sampling_evidence");}function V(n,e,t,r){let o=n.slice(Math.max(0,e-64),e),i=n.slice(e+t.length,e+t.length+28),s=Ve(o),a=/^(?:\s+)?(?:未发现|不存在|并不存在|未被发现)/.test(i)||/^(?:\s+)?(?:was\s+|is\s+)?not found\b/i.test(i);return s||a?true:r==="high"?/(?:未发现|没有发现|不存在|无证据(?:表明|证明)?)\s*(?:任何|明确的)?\s*P0\s*(?:或|和|及|、)\s*$/i.test(o)||/\b(?:no evidence (?:of|for)|no)\s+P0\s+(?:or|and)\s+$/i.test(o):false}function Ve(n){if(/(?:未发现|没有发现|不存在|无证据(?:表明|证明)?|不是|并非|未被)\s*(?:任何|明确的)?\s*(?:(?:问题|发现|证据|项|内容)\s*)?(?:(?:属于|(?:被)?(?:评|判定|认定|标记|列|视|定|归(?:类)?)为)\s*)?$/i.test(n)||/\b(?:no evidence (?:of|for)|no|not|isn't|is\s+not)\s+(?:an?\s+)?(?:(?:issues?|findings?|evidence|items?)\s+)?(?:(?:classified|rated|marked|labelled|labeled)(?:\s+as)?\s+)?$/i.test(n))return true;let t=/(?:不得|不能|不应|不宜|不要)\s*(?:被\s*)?(?:(?:直接|轻易|简单|明确|正式|合理|据此|当前|目前)\s*){0,2}(?:(?:评|判定|认定|标记|列|视|定|归(?:类)?)为|属于)\s*$/i,r=/\b(?:cannot|can't|must not|should not|shouldn't|do not|don't)\s+(?:(?:reasonably|directly|formally|currently)\s+){0,2}(?:be\s+)?(?:classified|labeled|labelled|rated|marked)(?:\s+as)?\s+$/i;return t.test(n)||r.test(n)}function Ke(n,e,t){let r=n.slice(Math.max(0,e-40),e),o=n.slice(e+t.length,e+t.length+32),i=/(?:是否|待验证|候选|可能|潜在|未观察到|没有观察到|未发现|不存在|无证据(?:表明)?|不能(?:据此)?确认|尚未(?:确认|证明)|验证(?:是否)?|测量(?:是否)?|采集|记录|收集).{0,16}$/i.test(r)||/\b(?:whether|measure|validate|candidate|potential|possible|may|might|no|no evidence(?: of| for)?|not observed|unobserved|collect|record).{0,18}$/i.test(r),s=/^(?:是否|有待验证|待验证|候选|可能|潜在|指标|需要(?:验证|测量)|需(?:验证|测量))/.test(o)||/^(?:\s+)?(?:candidate|potential|possible|metric|metrics|to (?:measure|validate)|needs? (?:measurement|validation)|is (?:a )?(?:candidate|potential))/i.test(o);return i||s}function Xe(n){return n.split(/(?:[。!?;;,,]|\b(?:but|however|yet|nevertheless|nonetheless)\b|但是|但|然而|却|不过|可是)/i).map(e=>e.trim()).filter(Boolean)}function Ye(n,e){if(!n||n.length>4096)return {degradationReason:"transcript_path_missing"};let t;try{let r=lstatSync(n);if(!r.isFile()||r.isSymbolicLink())return {degradationReason:"transcript_path_unsafe"};t=openSync(n,constants.O_RDONLY|(constants.O_NOFOLLOW??0));let o=fstatSync(t);if(!o.isFile()||o.dev!==r.dev||o.ino!==r.ino)return {degradationReason:"transcript_path_changed"};let i=Math.min(o.size,Ne),s=Buffer.alloc(i),a=0;for(;a<i;){let p=readSync(t,s,a,i-a,o.size-i+a);if(p===0)break;a+=p;}let c=s.subarray(0,a).toString("utf8");o.size>i&&(c=c.slice(Math.max(0,c.indexOf(`
13
+ `)+1)));let d=c.split(/\r?\n/).slice(-Le),l=Ze(d,Math.min(...e.map(p=>p.promptedAt))).at(-1);return l?.text?{answer:l.text,fingerprint:l.fingerprint}:{degradationReason:"transcript_answer_unavailable"}}catch{return {degradationReason:"transcript_unreadable"}}finally{t!==void 0&&closeSync(t);}}function Ze(n,e){let t=[];for(let r=0;r<n.length;r++){let o=n[r];if(o.trim())try{let i=JSON.parse(o),s=i.message;if(!s||typeof s!="object"||s.role!=="assistant")continue;let a=s,c=Qe(i.timestamp);if(c>0&&c+5e3<e)continue;let d=s.content,u=typeof d=="string"?d:Array.isArray(d)?d.filter(l=>l&&typeof l=="object"&&l.type==="text").map(l=>l.text).filter(l=>typeof l=="string").join(`
14
+ `):"";if(u.trim()){let l=u.trim(),p=typeof a.id=="string"&&a.id?`message:${a.id}`:typeof i.uuid=="string"&&i.uuid?`uuid:${i.uuid}`:`record:${String(i.timestamp??"")}:${r}:${createHash("sha256").update(o).digest("hex")}`,I=createHash("sha256").update(l).digest("hex"),_=createHash("sha256").update(`${p}\0${I}`).digest("hex");t.push({text:l,timestamp:c,fingerprint:_});}}catch{continue}}return t}function Qe(n){if(typeof n=="number"&&Number.isFinite(n))return n;if(typeof n!="string")return 0;let e=Date.parse(n);return Number.isFinite(e)?e:0}function et(n){let e=new Set,t=new Map,r;for(let o=0;o<n.length;o++){let i=n[o].trim().replace(/^#{1,6}\s*/,"").replace(/^\*\*(.*)\*\*$/,"$1").replace(/^(?:[一二三123][、..]\s*)/,"").replace(/[::]\s*$/,"").trim(),s=ze.find(a=>i===a);s&&(r=s,e.add(s)),t.set(o,r);}return {found:e,byLine:t}}function Z(n,e){let t=it(n);mkdirSync(t,{recursive:true,mode:448});let r=join(t,`${e.receiptId}.json`);if(existsSync(r))return e.receiptId;let o=`${r}.${process.pid}.tmp`;writeFileSync(o,JSON.stringify(e),{mode:384});try{renameSync(o,r);}catch(i){try{unlinkSync(o);}catch{}if(!existsSync(r))throw i}return e.receiptId}function it(n){return join(b(n),"session-finalize-pending")}process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337";getLocalApiKey();function Q(n){let e={committed:0,skipped:0,pending:0},t=[];for(let r of n)e[r.status]+=1,r.receiptId&&t.push(r.receiptId);return {memoryReceiptIds:[...new Set(t)],missingMemoryDecisionCount:e.pending,memoryTurnOutcomes:e}}function dt(n,e=y(),t=true,r,o=true){let i;try{i=JSON.parse(n);}catch{return null}let s=i.session_id?.trim();if(!s)return null;let a=`session-close:${createHash("sha256").update(`${e}\0${s}`).digest("hex").slice(0,24)}`,c={inputJson:n,sessionId:s,cleanupSharedState:t,executionId:r},d;try{d=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:50});let u=d.requestSessionClosure({receiptId:a,projectRoot:e,sessionId:s,payload:c}),l=d.getWorkByIdempotencyKey(a);return {status:u.state==="closed"||u.state==="closed_with_pending_work"?"completed":"accepted",receiptId:a,workState:l?.state??"pending",closureState:u.state,pendingWorkCount:u.pendingWorkCount}}catch(u){if(!o)throw u;return Z(e,{receiptId:a,...c}),{status:"accepted",receiptId:a,workState:"spooled",closureState:"closing",pendingWorkCount:1}}finally{d?.close();}}if(process.argv[1]?.endsWith("session-end")||process.argv[1]?.endsWith("session-end.js")){let n=console.log.bind(console);console.log=console.error.bind(console),console.info=console.error.bind(console);let e="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{e+=t;}),process.stdin.on("end",async()=>{let t=dt(e.trim()||process.argv[2]||"");t&&n(JSON.stringify(t)),process.exit(0);}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),1e4).unref();}var pt=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",gt=getLocalApiKey();function ft(n,e){return new Promise(t=>{let r=JSON.stringify(e),o=new URL(n,pt),i=request({hostname:o.hostname,port:o.port,path:o.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":gt,"Content-Length":Buffer.byteLength(r)},timeout:5e3},()=>t());i.on("error",()=>t()),i.write(r),i.end();})}async function ht(n,e=y()){let t;try{t=JSON.parse(n);}catch{return null}let r=t.session_id?.trim();if(!r)return null;let o=new g(e),i=B(e,r,t.stop_hook_active===true);if(i)return i;let s=K({projectRoot:e,sessionId:r,transcriptPath:t.transcript_path,runtimeStore:o});if(s)return s;let a=o.completeExecution(r);if(a?.executionId){let c=openGlobalDevFlowDatabase();try{let d=c.listSessionMemoryTurnsForReconciliation(e,r);c.reconcileSkillExecution(a.executionId,"completed",Date.now(),{...Q(d),evidenceDegradations:a.evidenceDegradations,evidenceContractOutcomes:a.evidenceOutcomes}),c.deleteContextReceipt(e,r,a.executionId);}finally{c.close();}}return ft(`/api/telemetry/sessions/${encodeURIComponent(r)}/tick`,{timestamp:Date.now()}).catch(()=>{}),null}if(process.argv[1]?.endsWith("stop")||process.argv[1]?.endsWith("stop.js")){let n=console.log.bind(console);console.log=console.error.bind(console),console.info=console.error.bind(console);let e="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{e+=t;}),process.stdin.on("end",async()=>{let t=await ht(e.trim()||process.argv[2]||"");t&&n(JSON.stringify(t)),process.exit(0);}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),4e3).unref();}
15
+ export{ht as handleStop};
@@ -1,5 +1,14 @@
1
- import {createHash}from'node:crypto';import {mkdirSync,appendFileSync}from'node:fs';import {homedir as homedir$1}from'node:os';import {dirname,join as join$1}from'node:path';import {MemoryGate}from'@devflow-tools/memory-engine';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {existsSync,readFileSync,rmSync,mkdirSync as mkdirSync$1,writeFileSync,renameSync,readdirSync,unlinkSync}from'fs';import {join,dirname as dirname$1}from'path';import {randomUUID}from'crypto';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {fileURLToPath}from'url';import {request}from'http';import {homedir}from'os';var G=/^\/(?:devflow:)?[\w-]+\b\s*/iu,W=/\b(?:(?:do\s+not|don't|dont|never|not)(?:\s+need\s+to)?|no\s+need\s+to)\s+(?:please\s+)?(?:remember|memorize)\b|(?:不要|别|不用|无需|不必|不需要)(?:再)?(?:记住|记|记忆)/iu,z=/(?:^|[.!?]\s*)(?:(?:please|kindly)\s+)?(?:remember|memorize|save\s+this|store\s+this|note\s+that)(?:\s+(?:that|to))?[\s:,-]*(.+)$/iu,K=/(?:^|[。!?;,,]\s*)(?:(?:请|麻烦|务必|一定要|帮我|帮忙|需要)\s*)?(?:记住|记一下|记下来|记|记录一下|记录下来|保存一下|保存下来)(?:这(?:件事|一点|个信息|些内容))?[\s::,,]*(.+)$/iu;function p(o){let t=o.trim().replace(G,"").trim();return !t||W.test(t)?null:(K.exec(t)??z.exec(t))?.[1]?.trim().replace(/^[::,,\s]+/u,"")||null}function V(o){let t=createHash("sha256").update(o).digest("hex").slice(0,16),e=process.env.DEVFLOW_STATE_DIR||join$1(homedir$1(),".devflow","state",t);return join$1(e,"memory-intents.jsonl")}function w(o,t){let e=V(o);mkdirSync(dirname(e),{recursive:true}),appendFileSync(e,`${JSON.stringify(t)}
2
- `,{mode:384});}var _="WARNING: DevFlow is in degraded mode (daemon unreachable): 4-Gate enforcement and memory prefetch cache are unavailable. Run `devflow doctor` for details.";function b(){return process.env.DEVFLOW_HOOK_DEGRADED==="1"}function Y(o){return o??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function I(o){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(Y(o))}function oe(o){return Buffer.from(o,"utf8").toString("base64url")}var h=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(I(t),"hook-sessions");}registerSession(t){let e=t.trim();if(!e)throw new Error("session_id_required");let r=this.get(e);if(r)return r.lastActivityAt=Date.now(),this.persist(r),r;let n=Date.now(),s={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:n,lastActivityAt:n};return this.sessions.set(e,s),this.persist(s),s}startExecution(t,e,r){let n=this.registerSession(t);return (!n.executionId||n.skillName!==e)&&(n.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),n.skillName=e,n.requiredMcpTools=[...new Set(r)],n.lastActivityAt=Date.now(),this.persist(n),n}get(t){let e=t.trim();if(!e)return null;let r=this.sessions.get(e);if(r)return r;let n=this.snapshotPath(e);if(!existsSync(n))return null;try{let s=JSON.parse(readFileSync(n,"utf8"));return s.sessionId!==e||s.projectRoot!==this.projectRoot?null:(s.requiredMcpTools=Array.isArray(s.requiredMcpTools)?s.requiredMcpTools:[],this.sessions.set(e,s),s)}catch{return null}}completeExecution(t){let e=this.get(t);if(!e)return null;let r={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),r}removeSession(t){let e=t.trim();e&&(this.sessions.delete(e),rmSync(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(t){return join(this.sessionsDir,`${oe(t)}.json`)}persist(t){mkdirSync$1(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(t.sessionId),r=`${e}.${process.pid}.tmp`;writeFileSync(r,JSON.stringify(t),{mode:384}),renameSync(r,e);}};function se(o){let t=o.trim().replace(/^\//,"");if(!t.startsWith("devflow:"))return null;let e=t.slice(8).replace(/^devflow-/,"");return /^[a-z0-9][a-z0-9-]*$/i.test(e)?`devflow:${e.toLowerCase()}`:null}function R(o){let t=o.trimStart().match(/^\/(devflow:[a-z0-9][a-z0-9-]*)\b/i);if(!t)return null;let e=se(t[1]);return e?{rawName:t[1],skillName:e}:null}var ue=2,me=1;function de(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join(dirname$1(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function T(o=de()){let t=[join(o,"dist","command-registry.json"),join(o,"command-registry.json")];for(let e of t)try{if(!existsSync(e))continue;let r=JSON.parse(readFileSync(e,"utf8"));if(r.version!==ue&&r.version!==me||!pe(r.commands)){console.error(`[devflow] command registry contract mismatch: ${e}`);continue}return r.commands}catch(r){console.error(`[devflow] command registry load failed: ${r.message}`);}return null}function pe(o){return !o||typeof o!="object"||Array.isArray(o)?false:Object.values(o).every(t=>{if(!t||typeof t!="object"||Array.isArray(t))return false;let e=t;return k(e.mcpTools)&&k(e.blockedNative)&&(e.source===void 0||e.source==="core"||e.source==="plugin")&&(e.requiresContext===void 0||typeof e.requiresContext=="boolean")&&(e.evidenceMode===void 0||["static","runtime","mixed"].includes(e.evidenceMode))})}function k(o){return Array.isArray(o)&&o.every(t=>typeof t=="string"&&t.length>0)}function M(o){let t=T();return t?t[o]?.mcpTools??[]:[]}var N=1e4,Se=3e4,g=500;function De(o,t,e){return new Promise(r=>{try{let n=new URL(o),s=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},i=>{let c=[];i.on("data",a=>c.push(a)),i.on("end",()=>{let a=Buffer.concat(c).toString();r(i.statusCode!=null&&i.statusCode>=200&&i.statusCode<300?a:null);});});s.on("error",()=>r(null)),s.on("timeout",()=>{s.destroy(),r(null);}),s.write(t),s.end();}catch{r(null);}})}var f=class{constructor(t){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??getLocalApiKey(),this.cacheDir=t?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=t?.legacyCacheDir??(t?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=t?.database??null,this.ownsDatabase=!t?.database;}async sendEvent(t){let e={...t,input:this.truncateInput(t.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),t.eventId):null}async sendExecutionStart(t,e,r,n,s=process.env.CLAUDE_PROJECT_DIR??process.cwd(),i=[]){let c={executionId:t,sessionId:e,skillName:r,startedAt:n,projectRoot:s,requiredMcpTools:i};this.commitOrCache("execution_start",c)&&this.postHttp("/api/telemetry/skill-execution/start",c);}async sendExecutionComplete(t,e="completed",r=Date.now(),n){let s={executionId:t,status:e,finishedAt:r,metadata:n},i=this.commitOrCache("execution_complete",s);return i&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",s),i}async sendSessionStart(t,e,r){let n={id:t,projectRoot:e,startedAt:r,label:e.split("/").pop()??"unknown"},s=this.commitOrCache("session_start",n);return s&&this.postHttp("/api/telemetry/sessions",n),s}async completeEvent(t){let e=this.commitOrCache("complete_event",t);return e&&(this.postHttp("/api/telemetry/tool-call/output",t),this.scheduleMetricAggregation()),e}async endSession(t,e=Date.now()){let r={sessionId:t,finishedAt:e},n=this.commitOrCache("session_end",r);return n&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),n}async flushCache(){try{let r=this.getDatabase(),n=r.listTelemetryFailures({unresolvedOnly:!0,limit:g}).reverse();for(let s of n)try{this.applyOperation(s.operation,s.payload),r.resolveTelemetryFailure(s.id);}catch{}r.trimTelemetryFailures(g);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(r=>!!r))].filter(r=>existsSync(r)).flatMap(r=>readdirSync(r).filter(n=>n.endsWith(".json")).map(n=>{let s=join(r,n);try{return {cacheFile:s,envelope:we(JSON.parse(readFileSync(s,"utf8")),n)}}catch{return null}})).filter(r=>r!==null).sort((r,n)=>r.envelope.timestamp-n.envelope.timestamp||P(r.envelope.operation)-P(n.envelope.operation));for(let{cacheFile:r,envelope:n}of e)try{let s=this.getDatabase();s.insertTelemetryFailure({id:n.id,operation:n.operation,payload:n.payload,error:n.failure,createdAt:n.timestamp}),this.applyOperation(n.operation,n.payload),s.resolveTelemetryFailure(n.id),unlinkSync(r);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(t,e){try{return this.applyOperation(t,e),!0}catch(r){return this.cacheOperation(t,e,r),false}}applyOperation(t,e){let r=this.getDatabase();switch(t){case "tool_call":this.ensureParentSession(r,e),r.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(r,e),r.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running",requiredMcpTools:e.requiredMcpTools});return;case "execution_complete":r.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":r.ensureSession(e),r.insertRun({id:j(e.id),source:"hook",tool:"session",input:{projectRoot:e.projectRoot},status:"active",startedAt:e.startedAt,tokenUsed:0,metadata:{sessionId:e.id,projectRoot:e.projectRoot}});return;case "complete_event":if(!r.updateToolCallEvent(e.eventId,{output:e.output===void 0?void 0:JSON.stringify(e.output),error:e.error,duration:e.duration}))throw new Error(`Tool call event ${e.eventId} is not available for completion`);return;case "session_end":r.closeSession(e.sessionId,e.finishedAt),r.updateRun(j(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(t,e){let r=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!r)throw new Error("Canonical session ID is required for telemetry");let n=typeof e.projectRoot=="string"&&e.projectRoot.trim()?e.projectRoot:typeof e.input?.projectRoot=="string"&&e.input.projectRoot.trim()?e.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";t.ensureSession({id:r,projectRoot:n,label:n==="unknown"?void 0:n.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(t,e,r){let n=Date.now(),s={id:`failure:${n}:${Math.random().toString(36).slice(2,11)}`,operation:t,payload:e,failure:r instanceof Error?r.message:String(r),timestamp:n};try{let i=this.getDatabase();i.insertTelemetryFailure({id:s.id,operation:t,payload:e,error:s.failure,createdAt:n}),i.trimTelemetryFailures(g),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync$1(this.cacheDir,{recursive:!0});let i=join(this.cacheDir,`${n}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(i,JSON.stringify(s,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let t=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of t.slice(0,Math.max(0,t.length-g)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},Se).unref());}scheduleMetricAggregation(){if(this.metricAggregationScheduled||process.env.DEVFLOW_HOOK_DEGRADED==="1")return;this.metricAggregationScheduled=true,setTimeout(()=>{this.metricAggregationScheduled=false;try{this.getDatabase().aggregatePendingToolMetrics(50)===50&&this.scheduleMetricAggregation();}catch{}},25).unref();}postHttp(t,e){De(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=N?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,N)}...`}}};function P(o){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(o)}function j(o){return `hook-run:${o}`}function we(o,t){if(!o||typeof o!="object")throw new Error("Invalid telemetry cache envelope");let e=o;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let r=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!r)throw new Error("Unknown legacy telemetry cache operation");let n=e.payload??{},s=Number(n.timestamp??n.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:r,payload:n,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:s}}var be=/^(?:ok(?:ay)?|yes|no|thanks?|thank you|continue|好的?|可以|行|是|否|谢谢|继续|收到|明白了?)[.!。!\s]*$/iu;function xe(o){let t=o.trim();return t.length>0&&!be.test(t)}function Re(o,t,e=Date.now()){let r=createHash("sha256").update(t.trim()).digest("hex"),n=createHash("sha256").update(`${o}\0${e}\0${r}`).digest("hex").slice(0,24);return {turnId:`turn:${n}`,eventId:`memory-turn:${n}`,promptHash:r,createdAt:e}}function ke(o,t,e=[]){return `memory-receipt:${createHash("sha256").update(`${o}\0${t}\0${[...e].sort().join("\0")}`).digest("hex").slice(0,24)}`}function Te(o){return `DevFlow memory decision required for ${o.turnId}. Before the final response, semantically decide whether this turn contains durable project knowledge. Call mcp__devflow__memory_commit_turn with evidence-bound candidates, or mcp__devflow__memory_skip_turn with a concrete reason. Do not claim memory success without a canonical receipt.`}function F(o){let t=o.memoryIds.length>0?o.memoryIds.join(","):"none";return `DevFlow canonical memory receipt: status=${o.status}; receiptId=${o.receiptId??"missing"}; turnId=${o.turnId}; memoryIds=${t}; source=${o.source??"unknown"}.`}async function $(o){if(!xe(o.prompt))return null;let t=openGlobalDevFlowDatabase();try{let e=createHash("sha256").update(o.prompt.trim()).digest("hex"),r=t.listMemoryTurns(o.projectRoot,o.sessionId,5).find(u=>u.promptHash===e&&Date.now()-u.createdAt<1e4),n=r?{turnId:r.turnId,eventId:r.eventId,promptHash:r.promptHash,createdAt:r.createdAt}:Re(o.sessionId,o.prompt),s=r??t.beginMemoryTurn({...n,projectRoot:o.projectRoot,sessionId:o.sessionId});if(s.status!=="pending")return {turn:s,additionalContext:F(s),skipSkillRouting:s.source==="explicit_intent"};await o.memory.recordUserMessage(o.prompt.slice(0,4e3),o.sessionId,o.prompt.slice(0,200)),await o.memory.recordEvent({id:n.eventId,sessionId:o.sessionId,tool:"UserPromptSubmit",kind:"user_message",payload:{prompt:o.prompt.slice(0,4e3),turnId:n.turnId},createdAt:n.createdAt});let i=p(o.prompt);if(!i)return {turn:s,additionalContext:Te(s),skipSkillRouting:!1};let c=await o.memory.saveExplicitMemoryIntent(i,o.sessionId,n.eventId),a=ke(s.turnId,"committed",[c.id]);return s=t.commitMemoryTurn({turnId:s.turnId,receiptId:a,memoryIds:[c.id],source:"explicit_intent",reason:"explicit_user_request"}),{turn:s,explicitContent:i,additionalContext:F(s),skipSkillRouting:!0}}finally{t.close();}}var m=process.env.CLAUDE_PROJECT_DIR||process.cwd();async function Ce(o){let t=b()?[_]:[],e;try{e=JSON.parse(o);}catch{return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t.length?{additionalContext:t.join(`
3
- `)}:{}}}}let r=e.prompt||"";if(!r.trim())return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t.length?{additionalContext:t.join(`
4
- `)}:{}}};let n=e.session_id?.trim(),s=R(r),i=new MemoryGate(m),c=false;try{if(await i.forceWarmUp(),n){let a=await $({projectRoot:m,sessionId:n,prompt:r,memory:i});a&&(t.push(a.additionalContext),c=a.skipSkillRouting);}}catch{let a=p(r);if(a){try{let u=openGlobalDevFlowDatabase();try{let l=n?u.getPendingMemoryTurn(m,n):null;w(m,{content:a,sessionId:n,turnId:l?.turnId,eventId:l?.eventId,createdAt:Date.now()});}finally{u.close();}}catch{}t.push("DevFlow explicit memory save failed; no canonical receipt was created. Do not claim that the content was remembered."),c=true;}}finally{i.close();}if(n&&s&&!c){let a=new h(m),u=a.get(n)?.executionId,l=a.startExecution(n,s.skillName,M(s.skillName));if(l.executionId&&l.executionId!==u){let v=new f;try{await v.sendSessionStart(n,m,l.startedAt),await v.sendExecutionStart(l.executionId,n,s.skillName,l.lastActivityAt,m,l.requiredMcpTools);}finally{v.close();}}}return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t.length?{additionalContext:t.join(`
5
- `)}:{}}}}if(process.argv[1]?.endsWith("user-prompt-submit")||process.argv[1]?.endsWith("user-prompt-submit.js")){let o="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{o+=t;}),process.stdin.on("end",async()=>{let t=await Ce(o.trim()||process.argv[2]||"");console.log(JSON.stringify(t)),process.exit(0);}),process.stdin.on("error",()=>{console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}})),process.exit(0);}),setTimeout(()=>{console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}})),process.exit(0);},3e4).unref();}export{Ce as handleUserPromptSubmit};
1
+ import {MemoryGate}from'@devflow-tools/memory-engine';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {existsSync,readFileSync as readFileSync$1,rmSync,mkdirSync as mkdirSync$1,writeFileSync as writeFileSync$1,linkSync,readdirSync,renameSync as renameSync$1,unlinkSync}from'fs';import {join as join$1,dirname}from'path';import {randomUUID as randomUUID$1,createHash as createHash$1}from'crypto';import {formatSkillRuntimeOverlay,createSkillRuntimeOverlay,loadConfig,getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {fileURLToPath}from'url';import {request}from'http';import {homedir}from'os';import {createHash,randomUUID}from'node:crypto';import {readFileSync,openSync,constants,fstatSync,closeSync,mkdirSync,writeFileSync,renameSync,chmodSync,fsyncSync,rmSync as rmSync$1,unlinkSync as unlinkSync$1,lstatSync}from'node:fs';import {join,resolve,dirname as dirname$1,isAbsolute,basename}from'node:path';var X="WARNING: DevFlow is in degraded mode (daemon unreachable): 4-Gate enforcement and memory prefetch cache are unavailable. Run `devflow doctor` for details.";function Z(){return process.env.DEVFLOW_HOOK_DEGRADED==="1"}function We(n){return n??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function S(n){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(We(n))}function ne(n){return Buffer.from(n,"utf8").toString("base64url")}function Be(n,e,t){let r=e?.trim();if(r){let o=/^[a-zA-Z0-9:._-]{1,192}$/.test(r)?r:`tool-${createHash$1("sha256").update(r.slice(0,1024)).digest("hex").slice(0,32)}`;return `${n}:${o}`}return `${n}:legacy-${t}-${randomUUID$1().slice(0,8)}`}function ce(n){return `${n}:legacy-journal`}var k=class{constructor(e){this.projectRoot=e;this.sessions=new Map;this.sessionsDir=join$1(S(e),"hook-sessions");}registerSession(e){let t=e.trim();if(!t)throw new Error("session_id_required");let r=this.get(t);if(r)return r.lastActivityAt=Date.now(),this.persist(r),r;let o=Date.now(),i={sessionId:t,projectRoot:this.projectRoot,requiredMcpTools:[],evidenceObligations:[],evidenceDegradations:[],evidenceOutcomes:[],startedAt:o,lastActivityAt:o};return this.sessions.set(t,i),this.persist(i),i}startExecution(e,t,r){let o=this.registerSession(e);return (!o.executionId||o.skillName!==t)&&(o.executionId=`exec_${Date.now()}_${randomUUID$1().slice(0,8)}`),o.skillName=t,o.requiredMcpTools=[...new Set(r)],o.lastActivityAt=Date.now(),this.persist(o),o}get(e){let t=e.trim();if(!t)return null;let r=this.sessions.get(t);if(r)return this.replayEvidenceJournal(r),r;let o=this.snapshotPath(t);if(!existsSync(o))return null;try{let i=JSON.parse(readFileSync$1(o,"utf8"));return i.sessionId!==t||i.projectRoot!==this.projectRoot?null:(i.requiredMcpTools=Array.isArray(i.requiredMcpTools)?i.requiredMcpTools:[],i.evidenceObligations=re(i.evidenceObligations),i.evidenceDegradations=oe(i.evidenceDegradations),i.evidenceOutcomes=ie(i.evidenceOutcomes),this.replayEvidenceJournal(i),this.sessions.set(t,i),i)}catch{return null}}completeExecution(e){let t=this.get(e);if(!t)return null;let r=structuredClone(t);return delete t.executionId,delete t.skillName,t.requiredMcpTools=[],t.lastActivityAt=Date.now(),this.persist(t),r}addEvidenceObligation(e,t,r=Date.now(),o){let i=this.registerSession(e),a=(o?void 0:i.evidenceObligations.find(d=>d.contractId===t.id))?.obligationId??Be(t.id,o,r);this.appendEvidenceEvent(e,{kind:"add",obligationId:a,contractId:t.id,at:r,contract:t},`add\0${a}`),this.replayEvidenceJournal(i);let c=i.evidenceObligations.find(d=>d.obligationId===a);return c?(i.lastActivityAt=Date.now(),this.persist(i),structuredClone(c)):null}listEvidenceObligations(e){return (this.get(e)?.evidenceObligations??[]).map(t=>structuredClone(t))}markEvidenceCorrection(e,t,r,o,i=Date.now()){let s=this.get(e),a=s?.evidenceObligations.find(l=>l.obligationId===t);if(!s||!a)return null;let c=/^[a-f0-9]{64}$/.test(o)?o:"";if(!c)return null;this.appendEvidenceEvent(e,{kind:"correction",obligationId:t,contractId:a.contractId,at:i,fingerprint:c,violations:r.slice(0,16).map(l=>l.slice(0,240))},`correction\0${t}\0${c}`),this.replayEvidenceJournal(s),s.lastActivityAt=i,this.persist(s);let d=s.evidenceObligations.find(l=>l.obligationId===t);return d?structuredClone(d):null}resolveEvidenceObligation(e,t){let r=this.get(e);if(!r)return false;let o=r.evidenceObligations.find(s=>s.obligationId===t);if(!o)return false;let i=Date.now();return this.appendEvidenceEvent(e,{kind:"resolve",obligationId:t,contractId:o.contractId,at:i},`resolve\0${t}`),this.replayEvidenceJournal(r),r.lastActivityAt=i,this.persist(r),true}degradeEvidenceObligation(e,t,r,o=Date.now()){let i=this.get(e),s=i?.evidenceObligations.find(c=>c.obligationId===t);if(!i||!s)return false;let a=r.slice(0,240);return this.appendEvidenceEvent(e,{kind:"degrade",obligationId:t,contractId:s.contractId,at:o,reason:a},`degrade\0${t}\0${a}`),this.replayEvidenceJournal(i),i.lastActivityAt=o,this.persist(i),true}removeSession(e){let t=e.trim();t&&(this.sessions.delete(t),rmSync(this.snapshotPath(t),{force:true}),rmSync(this.evidenceJournalDir(t),{recursive:true,force:true}));}list(){return [...this.sessions.values()]}snapshotPath(e){return join$1(this.sessionsDir,`${ne(e)}.json`)}evidenceJournalDir(e){return join$1(this.sessionsDir,"evidence-journal",ne(e))}appendEvidenceEvent(e,t,r){let o=this.evidenceJournalDir(e);mkdirSync$1(o,{recursive:true,mode:448});let i=createHash$1("sha256").update(r).digest("hex"),s=join$1(o,`${t.kind}-${i}.json`);if(existsSync(s))return;let a=join$1(o,`.${process.pid}.${randomUUID$1()}.tmp`);writeFileSync$1(a,JSON.stringify(t),{mode:384});try{linkSync(a,s);}catch(c){if(c.code!=="EEXIST")throw c}finally{rmSync(a,{force:true});}}replayEvidenceJournal(e){e.evidenceObligations=re(e.evidenceObligations),e.evidenceDegradations=oe(e.evidenceDegradations),e.evidenceOutcomes=ie(e.evidenceOutcomes);let t=this.evidenceJournalDir(e.sessionId);if(!existsSync(t))return;let r;try{r=readdirSync(t).filter(i=>/^(?:add|correction|resolve|degrade)-[a-f0-9]{64}\.json$/.test(i)).slice(0,4096);}catch{return}let o=r.flatMap(i=>{try{let s=readFileSync$1(join$1(t,i),"utf8");if(Buffer.byteLength(s,"utf8")>64*1024)return [];let a=Ve(JSON.parse(s));return a?[a]:[]}catch{return []}}).sort((i,s)=>i.at-s.at||se(i.kind)-se(s.kind));for(let i of o)this.applyEvidenceEvent(e,i);e.evidenceObligations=e.evidenceObligations.slice(-8),e.evidenceDegradations=e.evidenceDegradations.slice(-20),e.evidenceOutcomes=e.evidenceOutcomes.slice(-20);}applyEvidenceEvent(e,t){let r=e.evidenceOutcomes.find(s=>s.obligationId===t.obligationId);if(t.kind==="add"){if(r)return;let s=e.evidenceObligations.find(a=>a.obligationId===t.obligationId);s?(s.contract=t.contract,s.promptedAt=Math.min(s.promptedAt,t.at)):e.evidenceObligations.push({obligationId:t.obligationId,contractId:t.contractId,contract:t.contract,promptedAt:t.at,attempt:0,correctionRequested:false,violations:[]});return}let o=e.evidenceObligations.find(s=>s.obligationId===t.obligationId);if(t.kind==="correction"){if(!o||r||o.correctionFingerprint===t.fingerprint)return;o.attempt+=1,o.correctionRequested=true,o.correctionRequestedAt=t.at,o.correctionFingerprint=t.fingerprint,o.violations=t.violations;return}let i=o?.attempt??0;e.evidenceObligations=e.evidenceObligations.filter(s=>s.obligationId!==t.obligationId),!r&&(t.kind==="degrade"&&e.evidenceDegradations.push({obligationId:t.obligationId,contractId:t.contractId,reason:t.reason,degradedAt:t.at,attempt:i}),e.evidenceOutcomes.push({obligationId:t.obligationId,contractId:t.contractId,status:t.kind==="resolve"?"resolved":"degraded",completedAt:t.at,attempt:i,...t.kind==="degrade"?{reason:t.reason}:{}}));}persist(e){mkdirSync$1(this.sessionsDir,{recursive:true,mode:448});let t=this.snapshotPath(e.sessionId),r=`${t}.${process.pid}.${randomUUID$1()}.tmp`;writeFileSync$1(r,JSON.stringify(e),{mode:384}),renameSync$1(r,t);}};function re(n){return Array.isArray(n)?n.flatMap(e=>{if(!e||typeof e!="object")return [];let t=e;if(typeof t.contractId!="string"||t.contractId.length===0||t.contractId.length>128||typeof t.promptedAt!="number"||typeof t.attempt!="number"||typeof t.correctionRequested!="boolean"||!Array.isArray(t.violations)||!de(t.contract))return [];let r=typeof t.obligationId=="string"&&t.obligationId.length>0&&t.obligationId.length<=384?t.obligationId:ce(t.contractId),o=typeof t.correctionTranscriptHash=="string"?t.correctionTranscriptHash:void 0,i=t.correctionFingerprint??o;return i!==void 0&&!/^[a-f0-9]{64}$/.test(i)?[]:[{...t,obligationId:r,correctionFingerprint:i}]}).slice(-8):[]}function oe(n){return Array.isArray(n)?n.filter(e=>{if(!e||typeof e!="object")return false;let t=e;return typeof t.obligationId=="string"&&typeof t.contractId=="string"&&typeof t.reason=="string"&&typeof t.degradedAt=="number"&&typeof t.attempt=="number"}).slice(-20):[]}function ie(n){return Array.isArray(n)?n.filter(e=>{if(!e||typeof e!="object")return false;let t=e;return typeof t.obligationId=="string"&&typeof t.contractId=="string"&&(t.status==="resolved"||t.status==="degraded")&&typeof t.completedAt=="number"&&typeof t.attempt=="number"&&(t.reason===void 0||typeof t.reason=="string")}).slice(-20):[]}function Ve(n){if(!n||typeof n!="object")return null;let e=n;if(!["add","correction","resolve","degrade"].includes(e.kind??"")||typeof e.contractId!="string"||e.contractId.length>128||typeof e.at!="number")return null;let t=typeof e.obligationId=="string"&&e.obligationId.length>0&&e.obligationId.length<=384?e.obligationId:ce(e.contractId);if(e.kind==="add")return de(e.contract)?{...e,obligationId:t}:null;if(e.kind==="correction"){let r=typeof e.fingerprint=="string"?e.fingerprint:e.transcriptHash;return typeof r=="string"&&/^[a-f0-9]{64}$/.test(r)&&Array.isArray(e.violations)&&e.violations.every(o=>typeof o=="string"&&o.length<=240)?{...e,obligationId:t,fingerprint:r}:null}return e.kind==="resolve"?{...e,obligationId:t}:typeof e.reason=="string"&&e.reason.length<=240?{...e,obligationId:t}:null}function se(n){return n==="add"?0:n==="correction"?1:2}function de(n){if(!n||typeof n!="object")return false;let e=n,t=e.requiredSections,r=e.prohibitedClaims;return e.version==="1.0"&&typeof e.id=="string"&&/^[a-zA-Z0-9][a-zA-Z0-9:._-]{0,127}$/.test(e.id)&&typeof e.runtimeProfilingOccurred=="boolean"&&typeof e.samplingEvidenceOccurred=="boolean"&&Array.isArray(t)&&t.length===3&&t[0]==="\u5DE5\u5177\u53D1\u73B0"&&t[1]==="\u6A21\u578B\u5047\u8BBE"&&t[2]==="\u9700\u8981\u8FD0\u884C\u65F6\u9A8C\u8BC1"&&Array.isArray(r)&&r.length<=16&&r.every(o=>typeof o=="string"&&o.length<=128)&&typeof e.canonicalReport=="string"&&Buffer.byteLength(e.canonicalReport,"utf8")<=48*1024&&!!e.correctionPolicy&&e.correctionPolicy.maxAttempts===1&&e.correctionPolicy.repeatedViolation==="fail_open"}function Ye(n){let e=n.trim().replace(/^\//,"");if(!e.startsWith("devflow:"))return null;let t=e.slice(8).replace(/^devflow-/,"");return /^[a-z0-9][a-z0-9-]*$/i.test(t)?`devflow:${t.toLowerCase()}`:null}function ue(n){let e=n.trimStart().match(/^\/(devflow:[a-z0-9][a-z0-9-]*)\b/i);if(!e)return null;let t=Ye(e[1]);return t?{rawName:e[1],skillName:t}:null}var tt=2,nt=1;function rt(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join$1(dirname(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function le(n=rt()){let e=[join$1(n,"dist","command-registry.json"),join$1(n,"command-registry.json")];for(let t of e)try{if(!existsSync(t))continue;let r=JSON.parse(readFileSync$1(t,"utf8"));if(r.version!==tt&&r.version!==nt||!ot(r.commands)){console.error(`[devflow] command registry contract mismatch: ${t}`);continue}return r.commands}catch(r){console.error(`[devflow] command registry load failed: ${r.message}`);}return null}function ot(n){return !n||typeof n!="object"||Array.isArray(n)?false:Object.values(n).every(e=>{if(!e||typeof e!="object"||Array.isArray(e))return false;let t=e;return P(t.mcpTools)&&P(t.blockedNative)&&(t.source===void 0||t.source==="core"||t.source==="plugin")&&(t.requiresContext===void 0||typeof t.requiresContext=="boolean")&&(t.runtimePackages===void 0||P(t.runtimePackages))&&(t.evidenceMode===void 0||["static","runtime","mixed"].includes(t.evidenceMode))})}function P(n){return Array.isArray(n)&&n.every(e=>typeof e=="string"&&e.length>0)}function me(n){let e=le();return e?e[n]?.mcpTools??[]:[]}var he=1e4,lt=3e4,E=500;function mt(n,e,t){return new Promise(r=>{try{let o=new URL(n),i=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},s=>{let a=[];s.on("data",c=>a.push(c)),s.on("end",()=>{let c=Buffer.concat(a).toString();r(s.statusCode!=null&&s.statusCode>=200&&s.statusCode<300?c:null);});});i.on("error",()=>r(null)),i.on("timeout",()=>{i.destroy(),r(null);}),i.write(e),i.end();}catch{r(null);}})}var M=class{constructor(e){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=e?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=e?.apiKey??getLocalApiKey(),this.cacheDir=e?.cacheDir??join$1(process.env.DEVFLOW_STATE_DIR??join$1(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=e?.legacyCacheDir??(e?.cacheDir?null:join$1(homedir(),".devflow","telemetry-cache")),this.database=e?.database??null,this.ownsDatabase=!e?.database;let t=e?.busyTimeoutMs??Number(process.env.DEVFLOW_TELEMETRY_DB_BUSY_TIMEOUT_MS);this.busyTimeoutMs=Number.isSafeInteger(t)&&t>=0?t:100;}async sendEvent(e){let t={...e,input:this.truncateInput(e.input)};return this.commitOrCache("tool_call",t)?(this.postHttp("/api/telemetry/tool-call",t),e.eventId):null}async sendExecutionStart(e,t,r,o,i=process.env.CLAUDE_PROJECT_DIR??process.cwd(),s=[]){let a={executionId:e,sessionId:t,skillName:r,startedAt:o,projectRoot:i,requiredMcpTools:s};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(e,t="completed",r=Date.now(),o){let i={executionId:e,status:t,finishedAt:r,metadata:o},s=this.commitOrCache("execution_complete",i);return s&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",i),s}async sendSessionStart(e,t,r){let o={id:e,projectRoot:t,startedAt:r,label:t.split("/").pop()??"unknown"},i=this.commitOrCache("session_start",o);return i&&this.postHttp("/api/telemetry/sessions",o),i}async completeEvent(e){let t=this.commitOrCache("complete_event",e);return t&&(this.postHttp("/api/telemetry/tool-call/output",e),this.scheduleMetricAggregation()),t}async endSession(e,t=Date.now()){let r={sessionId:e,finishedAt:t},o=this.commitOrCache("session_end",r);return o&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(e)}/close`,{finishedAt:t})),o}async flushCache(){try{let r=this.getDatabase(),o=r.listTelemetryFailures({unresolvedOnly:!0,limit:E}).reverse();for(let i of o)try{this.applyOperation(i.operation,i.payload),r.resolveTelemetryFailure(i.id);}catch{}r.trimTelemetryFailures(E);}catch{}let t=[...new Set([this.cacheDir,this.legacyCacheDir].filter(r=>!!r))].filter(r=>existsSync(r)).flatMap(r=>readdirSync(r).filter(o=>o.endsWith(".json")).map(o=>{let i=join$1(r,o);try{return {cacheFile:i,envelope:pt(JSON.parse(readFileSync$1(i,"utf8")),o)}}catch{return null}})).filter(r=>r!==null).sort((r,o)=>r.envelope.timestamp-o.envelope.timestamp||be(r.envelope.operation)-be(o.envelope.operation));for(let{cacheFile:r,envelope:o}of t)try{let i=this.getDatabase();i.insertTelemetryFailure({id:o.id,operation:o.operation,payload:o.payload,error:o.failure,createdAt:o.timestamp}),this.applyOperation(o.operation,o.payload),i.resolveTelemetryFailure(o.id),unlinkSync(r);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:this.busyTimeoutMs}),this.database}commitOrCache(e,t){try{return this.applyOperation(e,t),!0}catch(r){return this.cacheOperation(e,t,r),false}}applyOperation(e,t){let r=this.getDatabase();switch(e){case "tool_call":this.ensureParentSession(r,t),r.insertToolCallEvent(t);return;case "execution_start":this.ensureParentSession(r,t),r.insertSkillExecution({executionId:t.executionId,sessionId:t.sessionId,skillName:t.skillName,startedAt:t.startedAt,status:"running",requiredMcpTools:t.requiredMcpTools});return;case "execution_complete":r.reconcileSkillExecution(t.executionId,t.status,t.finishedAt,t.metadata);return;case "session_start":r.ensureSession(t),r.insertRun({id:ve(t.id),source:"hook",tool:"session",input:{projectRoot:t.projectRoot},status:"active",startedAt:t.startedAt,tokenUsed:0,metadata:{sessionId:t.id,projectRoot:t.projectRoot}});return;case "complete_event":if(!r.updateToolCallEvent(t.eventId,{output:t.output===void 0?void 0:JSON.stringify(t.output),error:t.error,duration:t.duration}))throw new Error(`Tool call event ${t.eventId} is not available for completion`);return;case "session_end":r.closeSession(t.sessionId,t.finishedAt),r.updateRun(ve(t.sessionId),{status:"completed",finishedAt:t.finishedAt});return}}ensureParentSession(e,t){let r=typeof t.sessionId=="string"?t.sessionId.trim():"";if(!r)throw new Error("Canonical session ID is required for telemetry");let o=typeof t.projectRoot=="string"&&t.projectRoot.trim()?t.projectRoot:typeof t.input?.projectRoot=="string"&&t.input.projectRoot.trim()?t.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";e.ensureSession({id:r,projectRoot:o,label:o==="unknown"?void 0:o.split("/").pop(),startedAt:Number(t.startedAt??t.timestamp??Date.now())});}cacheOperation(e,t,r){let o=Date.now(),i={id:`failure:${o}:${Math.random().toString(36).slice(2,11)}`,operation:e,payload:t,failure:r instanceof Error?r.message:String(r),timestamp:o};try{let s=this.getDatabase();s.insertTelemetryFailure({id:i.id,operation:e,payload:t,error:i.failure,createdAt:o}),s.trimTelemetryFailures(E),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync$1(this.cacheDir,{recursive:!0});let s=join$1(this.cacheDir,`${o}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync$1(s,JSON.stringify(i,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let e=readdirSync(this.cacheDir).filter(t=>t.endsWith(".json")).sort();for(let t of e.slice(0,Math.max(0,e.length-E)))unlinkSync(join$1(this.cacheDir,t));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},lt).unref());}scheduleMetricAggregation(){if(this.metricAggregationScheduled||process.env.DEVFLOW_HOOK_DEGRADED==="1")return;this.metricAggregationScheduled=true,setTimeout(()=>{this.metricAggregationScheduled=false;try{this.getDatabase().aggregatePendingToolMetrics(50)===50&&this.scheduleMetricAggregation();}catch{}},25).unref();}postHttp(e,t){mt(`${this.apiUrl}${e}`,JSON.stringify(t),this.apiKey);}truncateInput(e){let t=JSON.stringify(e);return t===void 0||t.length<=he?e:{_truncated:true,_original_size:t.length,_preview:`${t.substring(0,he)}...`}}};function be(n){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(n)}function ve(n){return `hook-run:${n}`}function pt(n,e){if(!n||typeof n!="object")throw new Error("Invalid telemetry cache envelope");let t=n;if(typeof t.operation=="string"&&t.payload!==void 0)return t;let r=t.type==="tool_call"?"tool_call":t.type==="execution_start"?"execution_start":null;if(!r)throw new Error("Unknown legacy telemetry cache operation");let o=t.payload??{},i=Number(o.timestamp??o.startedAt??Date.now());return {id:`legacy-cache:${e}`,operation:r,payload:o,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:i}}var gt=/^\/(?:devflow:)?[\w-]+\b\s*/iu,ft=/\b(?:(?:do\s+not|don't|dont|never|not)(?:\s+need\s+to)?|no\s+need\s+to)\s+(?:please\s+)?(?:remember|memorize)\b|(?:不要|别|不用|无需|不必|不需要)(?:再)?(?:记住|记|记忆)/iu,yt=/(?:^|[.!?]\s*)(?:(?:please|kindly)\s+)?(?:remember|memorize|save\s+this|store\s+this|note\s+that)(?:\s+(?:that|to))?[\s:,-]*(.+)$/iu,ht=/(?:^|[。!?;,,]\s*)(?:(?:请|麻烦|务必|一定要|帮我|帮忙|需要)\s*)?(?:记住|记一下|记下来|记|记录一下|记录下来|保存一下|保存下来)(?:这(?:件事|一点|个信息|些内容))?[\s::,,]*(.+)$/iu;function R(n){let e=n.trim().replace(gt,"").trim();return !e||ft.test(e)?null:(ht.exec(e)??yt.exec(e))?.[1]?.trim().replace(/^[::,,\s]+/u,"")||null}var w=1,W="memory-snapshot-v1.json",Ce=512*1024,Ae=256*1024,Te=2e3,kt=500,Et=10,Mt=240,Rt=300*1e3,Dt=720*60*60*1e3,je=2147483647,Ot=100,_e=250,xt=3e4,Ct=10,D=new Map;function $e(n){return n.currentUid===void 0?{ownerSafe:true,modeSafe:true}:{ownerSafe:n.ownerUid===n.currentUid,modeSafe:(n.mode&18)===0}}function p(n){return resolve(n)}function v(n){return createHash("sha256").update(p(n)).digest("hex").slice(0,16)}function G(n,e={}){let t=v(n),r=e.stateRoot?join(e.stateRoot,t):getProjectStateDir(p(n));return join(r,W)}function x(n,e){let r=(n instanceof Error?n.message:"").replace(/[\r\n\t]+/gu," ").trim().slice(0,160);return r?`${e}:${r}`:e}function z(n,e){return Array.isArray(n)&&n.length<=e&&n.every(t=>typeof t=="string"&&t.length>0&&t.length<=512)}function At(n){if(!n||typeof n!="object"||Array.isArray(n))return false;let e=n;return typeof e.id=="string"&&e.id.length>0&&e.id.length<=512&&typeof e.kind=="string"&&e.kind.length>0&&e.kind.length<=128&&(e.family===void 0||typeof e.family=="string"&&e.family.length>0&&e.family.length<=256)&&(e.entity===void 0||typeof e.entity=="string"&&e.entity.length>0&&e.entity.length<=256)&&z(e.memoryIds,100)&&(e.status==="withheld"||e.status==="resolved")&&(e.summary===void 0||typeof e.summary=="string"&&e.summary.length<=2e3)}function Tt(n){if(!n||typeof n!="object"||Array.isArray(n))return false;let e=n;return typeof e.reason=="string"&&e.reason.length>0&&e.reason.length<=512&&(e.stale===void 0||typeof e.stale=="boolean")}function K(n,e,t){if(!n||typeof n!="object"||Array.isArray(n))return null;let r=n,o=p(e),i=v(o),s=r.generation===void 0?0:r.generation;return r.version!==w||r.projectRoot!==o||r.projectHash!==i||typeof r.markdown!="string"||Buffer.byteLength(r.markdown,"utf8")>Ae||!z(r.memoryIds,Te)||!Array.isArray(r.conflicts)||r.conflicts.length>kt||!r.conflicts.every(At)||typeof r.generatedAt!="number"||!Number.isFinite(r.generatedAt)||r.generatedAt<0||r.generatedAt>t+Rt||!Number.isInteger(s)||s<0||s>je||!["ready","pending","degraded","disabled"].includes(String(r.status))||r.degradation!==void 0&&!Tt(r.degradation)?null:{...r,generation:s}}function B(n,e,t,r,o=0){let i=p(n);return {version:w,projectRoot:i,projectHash:v(i),markdown:"",memoryIds:[],conflicts:[],generatedAt:r,generation:o,status:e,degradation:{reason:t}}}function h(n,e={}){let t=e.now??Date.now(),r=G(n,e),o;try{o=openSync(r,constants.O_RDONLY|constants.O_NOFOLLOW);let i=fstatSync(o);if(!i.isFile()||i.size<=0||i.size>Ce)throw new Error("cache_size_invalid");let s=$e({...typeof process.getuid=="function"?{currentUid:process.getuid()}:{},ownerUid:i.uid,mode:i.mode});if(!s.ownerSafe)throw new Error("cache_owner_invalid");if(!s.modeSafe)throw new Error("cache_permissions_unsafe");let a=JSON.parse(readFileSync(o,"utf8")),c=K(a,n,t);if(!c)throw new Error("cache_schema_invalid");return c.status==="ready"&&t-c.generatedAt>Dt?{snapshot:{...c,status:"degraded",degradation:{reason:"cache_stale",stale:!0}},source:"cache",valid:!0,stale:!0}:{snapshot:c,source:"cache",valid:!0,stale:!1}}catch(i){let a=i.code==="ENOENT";return {snapshot:B(n,a?"pending":"degraded",a?"cache_missing":x(i,"cache_invalid"),t),source:"none",valid:false,stale:false}}finally{if(o!==void 0)try{closeSync(o);}catch{}}}function ke(n){if(!Number.isInteger(n)||n<0||n>=je)throw new Error("memory_snapshot_generation_exhausted");return n+1}function jt(n){let e=dirname$1(n);mkdirSync(e,{recursive:true,mode:448}),chmodSync(e,448);}function $t(n){jt(n);let e=`${n}.lock`,t={pid:process.pid,createdAt:Date.now(),token:randomUUID()},r=JSON.stringify(t),o;try{return o=openSync(e,"wx",384),writeFileSync(o,r),fsyncSync(o),{fd:o,path:e,contents:r}}catch(i){if(o!==void 0){try{closeSync(o);}catch{}try{unlinkSync$1(e);}catch{}}if(i.code==="EEXIST")return null;throw i}}function Pt(n){if(!n||typeof n!="object"||Array.isArray(n))return false;let e=n;return Number.isInteger(e.pid)&&Number(e.pid)>0&&typeof e.createdAt=="number"&&Number.isFinite(e.createdAt)&&typeof e.token=="string"&&e.token.length>0}function Nt(n){try{return process.kill(n,0),!1}catch(e){return e.code==="ESRCH"}}function Ft(n){return !n.canonicalCachePath||!n.directoryIsSafe||!n.lockIsRegularFile||!n.metadataValid||!n.contentsUnchanged||!n.ownerDead&&!n.stale?false:n.currentUid!==void 0?n.directoryUid===n.currentUid&&n.lockUid===n.currentUid:true}function Lt(n){return isAbsolute(n)&&resolve(n)===n&&basename(n)===W&&/^[a-f0-9]{16}$/u.test(basename(dirname$1(n)))}function Ut(n,e){let t=`${n}.lock`;try{let r=lstatSync(dirname$1(n)),o=lstatSync(t),i=readFileSync(t,"utf8"),s=null;try{let Y=JSON.parse(i);Pt(Y)&&(s=Y);}catch{}let a=s?.createdAt??o.mtimeMs,c=Date.now()-a>e,d=s?Nt(s.pid):!1,l=readFileSync(t,"utf8")===i,u=typeof process.getuid=="function"?process.getuid():void 0,g=$e({...u!==void 0?{currentUid:u}:{},ownerUid:r.uid,mode:r.mode});return Ft({canonicalCachePath:Lt(n),directoryIsSafe:r.isDirectory()&&!r.isSymbolicLink()&&g.modeSafe,lockIsRegularFile:o.isFile()&&!o.isSymbolicLink(),metadataValid:s!==null,ownerDead:d,stale:c,contentsUnchanged:l,...u!==void 0?{currentUid:u}:{},directoryUid:r.uid,lockUid:o.uid})?(unlinkSync$1(t),!0):!1}catch{return false}}function Ht(n){try{closeSync(n.fd);}catch{}try{readFileSync(n.path,"utf8")===n.contents&&unlinkSync$1(n.path);}catch{}}function Jt(n){return new Promise(e=>setTimeout(e,n))}function F(n,e,t,r){let o=Number.isFinite(n)?Math.floor(n):e;return Math.min(r,Math.max(t,o))}async function qt(n,e){let t=F(e.lockTimeoutMs,Ot,0,_e),r=F(e.lockRetryMs,Ct,1,_e),o=F(e.lockStaleMs,xt,0,Number.MAX_SAFE_INTEGER),i=Date.now()+t;for(;;){let s=$t(n);if(s)return s;if(Ut(n,o))continue;let a=i-Date.now();if(a<=0)throw new Error("memory_snapshot_cache_lock_timeout");await Jt(Math.min(r,a));}}async function Ee(n,e,t){let r=await qt(n,e);try{return await t()}finally{Ht(r);}}function Me(n,e,t,r){let o=r.now??Date.now(),i=p(n),s={version:w,projectRoot:i,projectHash:v(i),markdown:e.markdown,memoryIds:e.memoryIds??[],conflicts:e.conflicts??[],generatedAt:o,generation:t,status:e.status??"ready",...e.degradation?{degradation:e.degradation}:{}};if(!K(s,n,o))throw new Error("memory_snapshot_payload_invalid");let a=JSON.stringify(s);if(Buffer.byteLength(a,"utf8")>Ce)throw new Error("memory_snapshot_cache_too_large");let c=G(n,r),d=dirname$1(c);mkdirSync(d,{recursive:true,mode:448}),chmodSync(d,448);let l=join(d,`.${W}.${process.pid}.${randomUUID()}.tmp`),u;try{u=openSync(l,"wx",384),writeFileSync(u,a),fsyncSync(u),closeSync(u),u=void 0,chmodSync(l,384),renameSync(l,c),chmodSync(c,384);let g;try{g=openSync(d,"r");try{fsyncSync(g);}catch{}}finally{g!==void 0&&closeSync(g);}return s}finally{if(u!==void 0)try{closeSync(u);}catch{}rmSync$1(l,{force:true});}}function Wt(n,e){let r=(D.get(n)??Promise.resolve()).catch(()=>{}).then(e),o=r.then(()=>{},()=>{});return D.set(n,o),r.finally(()=>{D.get(n)===o&&D.delete(n);})}function L(n,e,t,r,o){return n.valid?n.snapshot.generation>o?{...n,persisted:false}:{...n,snapshot:{...n.snapshot,status:"degraded",degradation:{reason:t,stale:n.stale||void 0}},persisted:false}:{snapshot:B(e,"degraded",t,r,o),source:"none",valid:false,stale:false,persisted:false}}async function Gt(n){let e=G(n.projectRoot,n.cacheOptions);return Wt(e,async()=>{let t=h(n.projectRoot,n.cacheOptions),r=t.valid?t.snapshot.generation:0,o;try{o=await n.load();}catch(i){let s=x(i,`refresh_failed:${n.reason}`);try{return await Ee(e,n.cacheOptions??{},()=>{let a=h(n.projectRoot,n.cacheOptions);if(a.valid)return L(a,n.projectRoot,s,n.cacheOptions?.now??Date.now(),r);let c=ke(r),d=B(n.projectRoot,"degraded",s,n.cacheOptions?.now??Date.now(),c);return {snapshot:Me(n.projectRoot,d,c,n.cacheOptions??{}),source:"cache",valid:!0,stale:!1,persisted:!0}})}catch(a){let c=h(n.projectRoot,n.cacheOptions);return L(c,n.projectRoot,x(a,s),n.cacheOptions?.now??Date.now(),r)}}try{return await Ee(e,n.cacheOptions??{},()=>{let i=h(n.projectRoot,n.cacheOptions);if(i.valid&&i.snapshot.generation>r)return {...i,persisted:!1};let s=ke(Math.max(r,i.valid?i.snapshot.generation:0));return {snapshot:Me(n.projectRoot,o,s,n.cacheOptions??{}),source:"cache",valid:!0,stale:!1,persisted:!0}})}catch(i){let s=h(n.projectRoot,n.cacheOptions);return L(s,n.projectRoot,x(i,`refresh_commit_failed:${n.reason}`),n.cacheOptions?.now??Date.now(),r)}})}async function Pe(n){return Gt({projectRoot:n.projectRoot,reason:n.reason,cacheOptions:n.cacheOptions,load:async()=>{if(n.injectConfig===false)return {status:"disabled",markdown:"",memoryIds:[],conflicts:[]};let e=typeof n.injectConfig=="object"?n.injectConfig:{},t=Number.isFinite(e.topN)?Math.max(0,Math.floor(e.topN)):10,r=Number.isFinite(e.budgetTokens)?Math.max(1,Math.floor(e.budgetTokens)):800,o=await n.memory.getAll({purpose:"session_bootstrap",budgetTokens:r,limit:t}),s=Number.isInteger(o.truncatedCount)&&o.truncatedCount>=0?o.truncatedCount:null,a=o.memories.length===0&&s!==0,c=s!==null&&s>0?"snapshot_results_truncated":"snapshot_truncation_unknown",d=(o.conflicts??[]).map(u=>({id:u.id,kind:u.kind,family:u.family,entity:u.entity,memoryIds:u.memoryIds,status:u.status,...u.summary?{summary:u.summary}:{}})),l=a?`## Project memory
2
+ DevFlow memory snapshot=degraded; reason=${c}. Memory result completeness is not authoritative; do not conclude that the project has no memories.`:o.memories.length===0&&d.length>0?`## Project memory
3
+ ${d.length} explicit preference conflict(s) are withheld from automatic guidance; an explicit choice is required.`:o.memories.length===0?`## Project memory
4
+ \u672C\u9879\u76EE\u6682\u65E0\u8BB0\u5FC6`:`## Project memory (auto-injected, ${o.memories.length} items, ${o.tokenCount} tokens)
5
+ ${o.memories.map(u=>`- **${u.title||u.type}**: ${u.content} (confidence ${u.confidence.toFixed(2)}, ${u.source})`).join(`
6
+ `)}`;return {status:a?"degraded":"ready",markdown:l,memoryIds:o.memories.map(u=>u.id),conflicts:d,...a?{degradation:{reason:c}}:{}}}})}function zt(n){if(!n)return true;try{return JSON.parse(readFileSync(n,"utf8")).sessionStart?.injectMemories??!0}catch{return true}}function Kt(n,e,t){if(!e.trim())return null;try{let r=JSON.parse(e);if(r.status!=="ok"&&r.status!=="degraded")return null;if(r.enabled===!1){let i=p(n);return {version:w,projectRoot:i,projectHash:v(i),markdown:"",memoryIds:[],conflicts:[],generatedAt:t,generation:0,status:"disabled"}}let o=K(r.snapshot,n,t);if(o)return o;if(r.status==="ok"&&typeof r.markdown=="string"&&r.markdown.length>0&&Buffer.byteLength(r.markdown,"utf8")<=Ae){let i=p(n);return {version:w,projectRoot:i,projectHash:v(i),markdown:r.markdown,memoryIds:z(r.memoryIds,Te)?r.memoryIds:[],conflicts:[],generatedAt:t,generation:0,status:"ready"}}}catch{}return null}function U(n,e){return [...(n??e).replace(/[\r\n\t|]+/gu," ").trim()||e].slice(0,Mt).join("")}function Bt(n){if(n.length===0)return "";let e=n.slice(0,Et),t=e.map(r=>{let o=U(r.family,"unknown"),i=U(r.entity,"unknown"),s=U(r.summary,"Explicit choice required.");return `- family=${o}; entity=${i}; status=${r.status}; summary=${s}`});return n.length>e.length&&t.push(`- ${n.length-e.length} additional conflict(s) omitted from SessionStart output.`),`## Withheld preference conflicts (${n.length})
7
+ ${t.join(`
8
+ `)}`}function Vt(n){let e=n.now??Date.now();if(zt(n.configPath)===false)return "";let t=Kt(n.projectRoot,n.daemonResponse??"",e),r=t?{snapshot:t,source:"daemon"}:h(n.projectRoot,{...n.cacheOptions,now:e}),{snapshot:o}=r;if(o.status==="disabled")return `## Project memory
9
+ DevFlow memory snapshot=pending; source=${r.source}; reason=previous_snapshot_disabled. Memory availability is not yet known; do not conclude that the project has no memories.`;let i=new Date(o.generatedAt).toISOString(),s=o.degradation?.reason?`; reason=${o.degradation.reason}`:"",a=`DevFlow memory snapshot=${o.status}; source=${r.source}; generatedAt=${i}${s}.`,c=Bt(o.conflicts);return o.markdown?[o.markdown,c,a].filter(Boolean).join(`
10
+ `):["## Project memory",c,`${a} Memory availability is not yet known; do not conclude that the project has no memories.`].filter(Boolean).join(`
11
+ `)}if(process.env.DEVFLOW_MEMORY_SNAPSHOT_CLI==="1"){let[,,n,e,t]=process.argv;n!=="session-start"||!e?process.exitCode=2:process.stdout.write(Vt({projectRoot:e,configPath:t,daemonResponse:process.env.DEVFLOW_MEMORY_RESPONSE}));}var Xt=/^(?:ok(?:ay)?|yes|no|thanks?|thank you|continue|好的?|可以|行|是|否|谢谢|继续|收到|明白了?)[.!。!\s]*$/iu;function Zt(n){let e=n.trim();return e.length>0&&!Xt.test(e)}function Qt(n,e,t=Date.now()){let r=createHash("sha256").update(e.trim()).digest("hex"),o=createHash("sha256").update(`${n}\0${t}\0${r}`).digest("hex").slice(0,24);return {turnId:`turn:${o}`,eventId:`memory-turn:${o}`,promptHash:r,createdAt:t}}function en(n,e,t=[]){return `memory-receipt:${createHash("sha256").update(`${n}\0${e}\0${[...t].sort().join("\0")}`).digest("hex").slice(0,24)}`}function Fe(n){return `DevFlow memory decision required for ${n.turnId}. Before the final response, semantically decide whether this turn contains durable project knowledge. Call mcp__devflow__memory_commit_turn with evidence-bound candidates, or mcp__devflow__memory_skip_turn with a concrete reason. Do not claim memory success without a canonical receipt.`}function V(n){let e=n.memoryIds.length>0?n.memoryIds.join(","):"none",t=n.status==="committed"?"accepted":n.status,r=n.status==="committed"?"; indexStatus=pending":"",o=n.status==="committed"&&n.source==="explicit_intent"?"; memoryOnlyCompleted=true; skipProjectContext=true; skipMemoryTool=true":"";return `DevFlow canonical memory receipt: status=${t}; receiptId=${n.receiptId??"missing"}; turnId=${n.turnId}; memoryIds=${e}; source=${n.source??"unknown"}${r}${o}.`}function Le(n){if(!Zt(n.prompt))return null;let e=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:50});try{let t=n.prompt.trim().slice(0,4e3),r=createHash("sha256").update(t).digest("hex"),o=e.listMemoryTurns(n.projectRoot,n.sessionId,5).find(d=>d.promptHash===r&&Date.now()-d.createdAt<1e4),i=o?{turnId:o.turnId,eventId:o.eventId,promptHash:o.promptHash,createdAt:o.createdAt}:Qt(n.sessionId,t),s=o??e.beginMemoryTurn({...i,projectRoot:n.projectRoot,sessionId:n.sessionId});if(s.status!=="pending")return {turn:s,additionalContext:V(s),skipSkillRouting:s.source==="explicit_intent"};let a=R(t)??void 0,c=a?"commit_explicit":"capture_prompt";return e.enqueueWork({idempotencyKey:`memory-turn:${c}:${s.turnId}`,kind:a?"memory.explicit_commit":"memory.turn_capture",projectRoot:n.projectRoot,sessionId:n.sessionId,turnId:s.turnId,payload:{operation:c,turnId:s.turnId,eventId:s.eventId,sessionId:n.sessionId,prompt:t,explicitContent:a,createdAt:s.createdAt}}),{turn:s,explicitContent:a,additionalContext:a?`DevFlow explicit memory is durably queued for ${s.turnId}, but no canonical receipt exists yet. Do not claim that it was remembered.`:Fe(s),skipSkillRouting:!!a}}finally{e.close();}}async function Ue(n){let{payload:e,memory:t,projectRoot:r}=n,o=openGlobalDevFlowDatabase();try{let i=o.getMemoryTurn(e.turnId);if(!i)throw new Error(`Memory turn ${e.turnId} does not exist`);if(i.status!=="pending")return {turn:i,additionalContext:V(i),skipSkillRouting:i.source==="explicit_intent"};if(await t.recordUserMessage(e.prompt,e.sessionId,e.prompt.slice(0,200)),await t.recordEvent({id:e.eventId,sessionId:e.sessionId,tool:"UserPromptSubmit",kind:"user_message",payload:{prompt:e.prompt,turnId:e.turnId},createdAt:e.createdAt}),e.operation==="capture_prompt"||!e.explicitContent)return {turn:i,additionalContext:Fe(i),skipSkillRouting:!1};let s=await t.saveExplicitMemoryIntent(e.explicitContent,e.sessionId,e.eventId),a=en(i.turnId,"committed",[s.id]),c=o.commitMemoryTurn({turnId:i.turnId,receiptId:a,memoryIds:[s.id],source:"explicit_intent",reason:"explicit_user_request"});o.enqueueWork({idempotencyKey:`vector:${r}:${s.id}`,kind:"memory.vector_backfill",projectRoot:r,sessionId:e.sessionId,turnId:c.turnId,payload:{observationId:s.id},maxAttempts:20});try{await Pe({projectRoot:r,memory:t,injectConfig:loadConfig(r).sessionStart?.injectMemories,reason:"explicit_memory_commit"});}catch(d){console.error("[devflow] Memory snapshot refresh failed after canonical commit:",d.message);}return {turn:c,explicitContent:e.explicitContent,additionalContext:V(c),skipSkillRouting:!0}}finally{o.close();}}function Je(n,e){let t=sn(n);mkdirSync(t,{recursive:true,mode:448});let r=`memory-turn-spool-${Date.now()}-${process.pid}-${randomUUID()}`,o=join(t,`${r}.json`),i=`${o}.tmp`;return writeFileSync(i,JSON.stringify({...e,prompt:e.prompt.trim().slice(0,4e3)}),{mode:384}),renameSync(i,o),r}function sn(n){return join(S(n),"memory-turn-pending")}var m=process.env.CLAUDE_PROJECT_DIR||process.cwd();async function un(n){let e=Z()?[X]:[],t;try{t=JSON.parse(n);}catch{return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...e.length?{additionalContext:e.join(`
12
+ `)}:{}}}}let r=t.prompt||"";if(!r.trim())return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...e.length?{additionalContext:e.join(`
13
+ `)}:{}}};let o=t.session_id?.trim(),i=ue(r),s=false;if(o)try{let a=Le({projectRoot:m,sessionId:o,prompt:r});if(a){let c=a;if(a.explicitContent&&a.turn.status==="pending"){let d=new MemoryGate(m);try{c=await Ue({projectRoot:m,payload:{operation:"commit_explicit",turnId:a.turn.turnId,eventId:a.turn.eventId,sessionId:o,prompt:r.trim().slice(0,4e3),explicitContent:a.explicitContent,createdAt:a.turn.createdAt},memory:d});}catch(l){console.error("[devflow] Explicit memory commit deferred:",l.message);}finally{d.close();}}e.push(c.additionalContext),s=c.skipSkillRouting;}}catch(a){s=!!R(r);try{let c=Je(m,{sessionId:o,prompt:r,createdAt:Date.now()});e.push(`DevFlow memory turn status=accepted; durable=true; receiptId=${c}; canonicalMemory=false; SQLite queue replay pending. Do not claim memory success without a canonical receipt.`),console.error("[devflow] Memory turn spooled after database contention:",a.message);}catch(c){e.push("DevFlow memory turn status=degraded; durable=false; no canonical receipt exists. Do not claim memory success."),console.error("[devflow] Memory turn persistence unavailable:",c.message);}}if(o&&i&&!s){let a=new k(m),c=a.get(o)?.executionId,d=a.startExecution(o,i.skillName,me(i.skillName));if(d.executionId&&d.executionId!==c){let u=new M;try{await u.sendSessionStart(o,m,d.startedAt),await u.sendExecutionStart(d.executionId,o,i.skillName,d.lastActivityAt,m,d.requiredMcpTools);}finally{u.close();}}let l=formatSkillRuntimeOverlay(createSkillRuntimeOverlay(i.skillName,m));l&&e.push(l);}return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...e.length?{additionalContext:e.join(`
14
+ `)}:{}}}}if(process.argv[1]?.endsWith("user-prompt-submit")||process.argv[1]?.endsWith("user-prompt-submit.js")){let n=console.log.bind(console);console.log=console.error.bind(console),console.info=console.error.bind(console);let e="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{e+=t;}),process.stdin.on("end",async()=>{let t=await un(e.trim()||process.argv[2]||"");n(JSON.stringify(t)),process.exit(0);}),process.stdin.on("error",()=>{n(JSON.stringify({hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}})),process.exit(0);}),setTimeout(()=>{n(JSON.stringify({hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}})),process.exit(0);},3e4).unref();}export{un as handleUserPromptSubmit};
package/hooks/hooks.json CHANGED
@@ -51,7 +51,7 @@
51
51
  {
52
52
  "type": "command",
53
53
  "command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/user-prompt-submit\"",
54
- "timeout": 30
54
+ "timeout": 5
55
55
  }
56
56
  ]
57
57
  }
@@ -85,7 +85,8 @@
85
85
  "hooks": [
86
86
  {
87
87
  "type": "command",
88
- "command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/session-end\""
88
+ "command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/session-end\"",
89
+ "timeout": 5
89
90
  }
90
91
  ]
91
92
  }
@@ -4,58 +4,10 @@ set -euo pipefail
4
4
  PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-$(pwd)}"
5
5
  SESSION_START_INPUT="$(cat)"
6
6
 
7
- # Try to get project summary from devflow doctor
8
- SUMMARY=""
9
- DOCTOR_OUTPUT=""
10
-
11
- # Try global devflow first, then npx fallback
12
- if command -v devflow &>/dev/null; then
13
- DOCTOR_OUTPUT=$(devflow doctor --json 2>/dev/null || echo "")
14
- elif command -v npx &>/dev/null; then
15
- DOCTOR_OUTPUT=$(npx --yes @devflow-tools/cli doctor --json 2>/dev/null || echo "")
16
- fi
17
-
18
- if [ -n "$DOCTOR_OUTPUT" ] && echo "$DOCTOR_OUTPUT" | python3 -c "import sys,json; json.load(sys.stdin)" 2>/dev/null; then
19
- SUMMARY=$(
20
- DEVFLOW_DOCTOR_OUTPUT="$DOCTOR_OUTPUT" python3 - 2>/dev/null <<'PY'
21
- import json, os
22
- from pathlib import Path
23
-
24
- d = json.loads(os.environ.get('DEVFLOW_DOCTOR_OUTPUT', '{}'))
25
- metrics = {}
26
- red_sections = []
27
- for section in d.get('sections', []):
28
- if section.get('status') == 'red':
29
- red_sections.append(str(section.get('name', '')).strip())
30
- for metric in section.get('metrics', []):
31
- key = metric.get('key')
32
- if key:
33
- metrics[key] = metric.get('value')
34
-
35
- # One-release compatibility with the previous top-level doctor schema.
36
- legacy_project = d.get('project', {}) if isinstance(d.get('project'), dict) else {}
37
- project_root = metrics.get('canonical_project') or legacy_project.get('rootPath') or ''
38
- project_name = legacy_project.get('name') or d.get('projectName') or (Path(project_root).name if project_root else '')
39
- parts = ['DevFlow 已就绪']
40
- if project_name:
41
- parts.append(f'项目 {project_name}')
42
- nodes = metrics.get('codegraph_nodes')
43
- if isinstance(nodes, (int, float)):
44
- parts.append(f'代码图 {int(nodes)} 个节点')
45
- plugin_source = metrics.get('active_skill_source')
46
- if isinstance(plugin_source, str) and plugin_source:
47
- parts.append(f'技能来源 {plugin_source}')
48
- if red_sections:
49
- parts.append('需处理 ' + ', '.join(x for x in red_sections if x))
50
- print('。'.join(parts) + '。使用 /devflow:context 查代码,/devflow:react 获取 React 专家能力。')
51
- PY
52
- ) || SUMMARY=""
53
- if [ -z "$SUMMARY" ]; then
54
- SUMMARY="DevFlow 插件已加载。Doctor 状态暂不可用,运行 devflow doctor 查看详情。"
55
- fi
56
- else
57
- SUMMARY="DevFlow 插件已加载。如尚未初始化项目,请运行 devflow init。使用 /devflow 查看可用命令。"
58
- fi
7
+ # SessionStart must not synchronously run Doctor. Full diagnostics can touch
8
+ # several databases and exceed the lifecycle acknowledgement budget.
9
+ PROJECT_NAME="$(basename "$PROJECT_ROOT")"
10
+ SUMMARY="DevFlow 已就绪。项目 ${PROJECT_NAME}。使用 /devflow 查看全部已安装能力,并按项目技术栈选择对应的 /devflow:* 领域 Skill;运行 devflow doctor 查看完整健康状态。"
59
11
 
60
12
  # Generate session token for MCP Gateway authentication
61
13
  # Clean up stale receipt from previous session
@@ -130,7 +82,7 @@ MCP_PROTOCOL="
130
82
  不要跳过这一步。不要用 Explore、Agent、Grep 或 Bash 代替。
131
83
 
132
84
  ### Step 2: 按需调用领域工具
133
- 根据技能类型调用对应的 MCP 工具:
85
+ 根据已加载 Skill 和 command registry 调用对应的 MCP 工具。以下仅为高频示例,完整能力以 /devflow 列表和 command registry 为准:
134
86
  - React 性能问题 → mcp__devflow__react_audit_performance(参数:{\"projectRoot\": \"\${CLAUDE_PROJECT_DIR}\"})
135
87
  - React Bug → mcp__devflow__react_diagnose_bug
136
88
  - React Hooks 审查 → mcp__devflow__react_review_hooks
@@ -148,6 +100,9 @@ MCP_PROTOCOL="
148
100
  - 如果 MCP 工具返回错误,重试一次;再失败则回退到直接读文件"
149
101
 
150
102
  DEVFLOW_DAEMON_REACHABLE=0
103
+ DEVFLOW_DAEMON_LAUNCHING=0
104
+ DEVFLOW_SESSION_DURABLE=0
105
+ DEVFLOW_SESSION_RECEIPT=""
151
106
  DAEMON_SESSION_RESPONSE=""
152
107
  run_lifecycle() {
153
108
  local PROJECT_HASH=""
@@ -179,36 +134,53 @@ run_lifecycle() {
179
134
  [ -n "$SESSION_START_INPUT" ] || return 0
180
135
  [ -f "$CLIENT_JS" ] || return 1
181
136
  if response=$(DEVFLOW_HOOK_CLIENT_TIMEOUT_MS=250 \
182
- DEVFLOW_HOOK_CLIENT_RETRY_DELAY_MS=50 \
137
+ DEVFLOW_HOOK_CLIENT_RETRY_DELAY_MS=0 \
183
138
  DEVFLOW_HOOK_CLIENT_SUPPRESS_FALLBACK_LOG="$suppress_fallback" \
184
139
  node "$CLIENT_JS" session-start "$SESSION_START_INPUT" 2>> "$DAEMON_LOG"); then
185
140
  DEVFLOW_DAEMON_REACHABLE=1
141
+ DEVFLOW_SESSION_DURABLE=1
186
142
  DAEMON_SESSION_RESPONSE="$response"
187
143
  return 0
188
144
  fi
189
145
  return 1
190
146
  }
191
147
 
148
+ queue_session_start() {
149
+ [ -n "$SESSION_START_INPUT" ] || return 1
150
+ [ -n "$PROJECT_HASH" ] || return 1
151
+ local queue_dir="${HOME}/.devflow/state/${PROJECT_HASH}/session-start-pending"
152
+ local receipt_id="session-start-spool-${PROJECT_HASH}-$(date +%s)-$$"
153
+ local temp_file="${queue_dir}/.${receipt_id}.tmp"
154
+ local target_file="${queue_dir}/${receipt_id}.json"
155
+ mkdir -p "$queue_dir" 2>/dev/null || return 1
156
+ if (umask 077 && printf '%s' "$SESSION_START_INPUT" > "$temp_file") 2>/dev/null \
157
+ && mv "$temp_file" "$target_file" 2>/dev/null; then
158
+ DEVFLOW_SESSION_DURABLE=1
159
+ DEVFLOW_SESSION_RECEIPT="$receipt_id"
160
+ return 0
161
+ fi
162
+ rm -f "$temp_file" 2>/dev/null || true
163
+ return 1
164
+ }
165
+
192
166
  if [ -n "$SESSION_START_INPUT" ] && register_session 1; then
193
167
  :
194
168
  elif [ -f "$DAEMON_JS" ] && command -v nohup >/dev/null 2>&1 && command -v node >/dev/null 2>&1; then
195
- if [ -n "$SESSION_START_INPUT" ]; then
169
+ if [ -n "$SESSION_START_INPUT" ] && queue_session_start; then
170
+ nohup node "$DAEMON_JS" "$PROJECT_ROOT" </dev/null >> "$DAEMON_LOG" 2>&1 &
171
+ elif [ -n "$SESSION_START_INPUT" ]; then
196
172
  nohup node "$DAEMON_JS" "$PROJECT_ROOT" "$SESSION_START_INPUT" </dev/null >> "$DAEMON_LOG" 2>&1 &
197
173
  else
198
174
  nohup node "$DAEMON_JS" "$PROJECT_ROOT" </dev/null >> "$DAEMON_LOG" 2>&1 &
199
175
  fi
200
176
  DAEMON_PID=$!
177
+ DEVFLOW_DAEMON_LAUNCHING=1
201
178
  echo "[devflow] Hook daemon started for ${PROJECT_ROOT} (PID ${DAEMON_PID})" >&2 || true
202
-
203
- if [ -n "$SESSION_START_INPUT" ]; then
204
- for _ in 1 2 3 4 5 6 7 8; do
205
- register_session 1 && break
206
- sleep 0.05
207
- done
208
- fi
209
179
  fi
210
180
 
211
- if [ -n "$SESSION_START_INPUT" ] && [ "$DEVFLOW_DAEMON_REACHABLE" != "1" ]; then
181
+ if [ -n "$SESSION_START_INPUT" ] \
182
+ && [ "$DEVFLOW_DAEMON_REACHABLE" != "1" ] \
183
+ && [ "$DEVFLOW_DAEMON_LAUNCHING" != "1" ]; then
212
184
  register_session 0 || true
213
185
  fi
214
186
 
@@ -221,30 +193,38 @@ run_lifecycle() {
221
193
  run_lifecycle || true
222
194
 
223
195
  DEGRADED_CONTEXT=""
224
- if [ "$DEVFLOW_DAEMON_REACHABLE" != "1" ]; then
196
+ if [ "$DEVFLOW_DAEMON_LAUNCHING" = "1" ]; then
197
+ if [ "$DEVFLOW_SESSION_DURABLE" = "1" ]; then
198
+ DEGRADED_CONTEXT="
199
+
200
+ DevFlow SessionStart status=accepted; durable=true; receiptId=${DEVFLOW_SESSION_RECEIPT:-daemon-receipt}; daemon registration is pending while the runtime warms."
201
+ else
202
+ DEGRADED_CONTEXT="
203
+
204
+ WARNING: DevFlow daemon launch was requested, but SessionStart persistence was not confirmed. Continue in degraded mode and do not treat lifecycle registration as completed."
205
+ fi
206
+ elif [ "$DEVFLOW_DAEMON_REACHABLE" != "1" ]; then
225
207
  DEGRADED_CONTEXT="
226
208
 
227
209
  WARNING: DevFlow is in degraded mode (daemon unreachable): 4-Gate enforcement is unavailable, memory prefetch cache is unavailable, and daemon-shared Hook state is unavailable. Run devflow doctor for details."
228
210
  fi
229
211
 
230
- MEMORY_SNAPSHOT=$'## Project memory\n本项目暂无记忆'
212
+ MEMORY_SNAPSHOT=$'## Project memory\nDevFlow memory snapshot=pending; source=none; reason=runtime_warming. Memory availability is not yet known; do not conclude that the project has no memories.'
231
213
  CONFIG_FILE="${PROJECT_ROOT}/.devflow/config.json"
232
214
  MEMORY_RESPONSE=""
233
- if [ -f "${CLIENT_JS:-}" ] && command -v node >/dev/null 2>&1; then
234
- MEMORY_RESPONSE=$(DEVFLOW_HOOK_CLIENT_TIMEOUT_MS=1000 node "$CLIENT_JS" memory-snapshot </dev/null 2>/dev/null || echo "")
215
+ if { [ "$DEVFLOW_DAEMON_REACHABLE" = "1" ] || [ -z "$SESSION_START_INPUT" ]; } \
216
+ && [ -f "${CLIENT_JS:-}" ] \
217
+ && command -v node >/dev/null 2>&1; then
218
+ MEMORY_RESPONSE=$(DEVFLOW_HOOK_CLIENT_TIMEOUT_MS=75 \
219
+ DEVFLOW_HOOK_CLIENT_RETRY_DELAY_MS=0 \
220
+ node "$CLIENT_JS" memory-snapshot </dev/null 2>/dev/null || echo "")
235
221
  fi
236
- if command -v python3 >/dev/null 2>&1; then
237
- MEMORY_SNAPSHOT=$(DEVFLOW_MEMORY_RESPONSE="$MEMORY_RESPONSE" python3 - "$CONFIG_FILE" 2>/dev/null <<'PY'
238
- import json,os,sys
239
- try: cfg=json.load(open(sys.argv[1])).get('sessionStart',{}).get('injectMemories',True)
240
- except Exception: cfg=True
241
- if cfg is False: print(''); raise SystemExit
242
- try: response=json.loads(os.environ.get('DEVFLOW_MEMORY_RESPONSE',''))
243
- except Exception: response={}
244
- markdown = response.get('markdown')
245
- print(markdown or '## Project memory\n本项目暂无记忆')
246
- PY
247
- ) || MEMORY_SNAPSHOT=$'## Project memory\n本项目暂无记忆'
222
+ MEMORY_CACHE_JS="${PLUGIN_ROOT}/dist/hooks/memory-snapshot-cache.js"
223
+ if [ -f "$MEMORY_CACHE_JS" ] && command -v node >/dev/null 2>&1; then
224
+ MEMORY_SNAPSHOT=$(DEVFLOW_MEMORY_SNAPSHOT_CLI=1 \
225
+ DEVFLOW_MEMORY_RESPONSE="$MEMORY_RESPONSE" \
226
+ node "$MEMORY_CACHE_JS" session-start "$PROJECT_ROOT" "$CONFIG_FILE" 2>/dev/null) \
227
+ || MEMORY_SNAPSHOT=$'## Project memory\nDevFlow memory snapshot=degraded; source=none; reason=cache_reader_failed. Memory availability is not yet known; do not conclude that the project has no memories.'
248
228
  fi
249
229
 
250
230
  ONBOARDING_SNAPSHOT=""
@@ -267,16 +247,20 @@ fi
267
247
 
268
248
  DAEMON_CONTEXT=""
269
249
  if [ -n "$DAEMON_SESSION_RESPONSE" ] && command -v python3 >/dev/null 2>&1; then
270
- DAEMON_CONTEXT=$(DEVFLOW_DAEMON_RESPONSE="$DAEMON_SESSION_RESPONSE" python3 -c "import json,os; print(json.loads(os.environ.get('DEVFLOW_DAEMON_RESPONSE','{}')).get('additionalContext',''))" 2>/dev/null || echo "")
250
+ DAEMON_CONTEXT=$(DEVFLOW_DAEMON_RESPONSE="$DAEMON_SESSION_RESPONSE" python3 -c "import json,os; d=json.loads(os.environ.get('DEVFLOW_DAEMON_RESPONSE','{}')); status=d.get('status'); receipt=d.get('receiptId'); work=d.get('workState'); parts=[]; parts.append(f'DevFlow SessionStart status={status}; durable=true; receiptId={receipt}; workState={work}.' if status in ('accepted','completed') and receipt else ''); parts.append(str(d.get('additionalContext',''))); print('\\n'.join(x for x in parts if x))" 2>/dev/null || echo "")
251
+ [ -z "$DAEMON_CONTEXT" ] || DAEMON_CONTEXT=$'\n\n'"${DAEMON_CONTEXT}"
271
252
  fi
272
253
 
273
254
  SUMMARY="${SUMMARY}${DEGRADED_CONTEXT}${DAEMON_CONTEXT}"$'\n\n'"${ONBOARDING_SNAPSHOT}"$'\n\n'"${MEMORY_SNAPSHOT}${MCP_PROTOCOL}"$'\n\n'"${GLOBAL_CONSTRAINT}${GATEWAY_STATUS}"
274
255
 
275
- if command -v python3 >/dev/null 2>&1 \
256
+ if command -v node >/dev/null 2>&1 \
257
+ && ESCAPED=$(printf '%s\n' "$SUMMARY" | node -e "let s=''; process.stdin.setEncoding('utf8'); process.stdin.on('data',c=>s+=c); process.stdin.on('end',()=>process.stdout.write(JSON.stringify(s)))" 2>/dev/null); then
258
+ :
259
+ elif command -v python3 >/dev/null 2>&1 \
276
260
  && ESCAPED=$(printf '%s\n' "$SUMMARY" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))" 2>/dev/null); then
277
261
  :
278
262
  else
279
- ESCAPED='"DevFlow SessionStart 已完成。"'
263
+ ESCAPED='"DevFlow SessionStart status=degraded; memory snapshot=pending; runtime helpers unavailable. Memory availability is not yet known."'
280
264
  fi
281
265
 
282
266
  printf '{\n "hookSpecificOutput": {\n "hookEventName": "SessionStart",\n "additionalContext": %s\n }\n}\n' "$ESCAPED"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devflow-tools/claude-code-plugin",
3
- "version": "0.16.10",
3
+ "version": "0.16.12",
4
4
  "description": "DevFlow — Developer Intelligence Platform for Claude Code",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -8,18 +8,18 @@
8
8
  "deploy": "npx tsx scripts/inject-gates.ts --deploy"
9
9
  },
10
10
  "dependencies": {
11
- "@devflow-tools/database": "0.16.10",
12
- "@devflow-tools/memory-engine": "0.16.10",
13
- "@devflow-tools/sdk": "0.16.10",
14
- "@devflow-tools/telemetry": "0.16.10",
15
- "@devflow-tools/workflow-engine": "0.16.10"
11
+ "@devflow-tools/database": "0.16.12",
12
+ "@devflow-tools/memory-engine": "0.16.12",
13
+ "@devflow-tools/sdk": "0.16.12",
14
+ "@devflow-tools/telemetry": "0.16.12",
15
+ "@devflow-tools/workflow-engine": "0.16.12"
16
16
  },
17
17
  "allowScripts": {
18
18
  "better-sqlite3@12.11.1": true,
19
19
  "sharp@0.32.6": true
20
20
  },
21
21
  "devDependencies": {
22
- "@devflow-tools/mcp-server": "0.16.10"
22
+ "@devflow-tools/mcp-server": "0.16.12"
23
23
  },
24
24
  "keywords": [
25
25
  "pi-package",
@@ -40,5 +40,5 @@
40
40
  "hooks",
41
41
  "skills"
42
42
  ],
43
- "gitHead": "964af33767bf232606e63d973b3a29d7d53c8119"
43
+ "gitHead": "d4a018f3031a3570c794c253f8edeff405956133"
44
44
  }
@@ -25,7 +25,9 @@ description: Animation 动画专家 — CSS 动画、Framer Motion、GSAP、Lott
25
25
  - 使用 AnimatePresence 处理 React 组件退出动画
26
26
  - Lottie 动画控制文件大小 < 50KB
27
27
 
28
- ## 当前版本
28
+ ## 知识来源
29
+
30
+ 以下版本描述知识源,不代表目标项目版本;目标项目的 package.json 与 lockfile 证据始终优先。
29
31
 
30
32
  - animation-docs: 2024 — https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animations
31
33
 
@@ -25,7 +25,9 @@ description: CSS 专家 — 布局调试、响应式审查、BEM 规范、性能
25
25
  - 使用 rem/em 做相对单位
26
26
  - 不确定属性行为时先查 MDN 文档确认
27
27
 
28
- ## 当前版本
28
+ ## 知识来源
29
+
30
+ 以下版本描述知识源,不代表目标项目版本;目标项目的 package.json 与 lockfile 证据始终优先。
29
31
 
30
32
  - mdn-css: 2024 — https://developer.mozilla.org/en-US/docs/Web/CSS
31
33
 
@@ -28,7 +28,9 @@ description: Docker 专家 — 镜像优化、Compose 编排、安全扫描、
28
28
  - 不在镜像层中写入敏感信息
29
29
  - 修复安全问题时先查 Docker 官方文档确认方案
30
30
 
31
- ## 当前版本
31
+ ## 知识来源
32
+
33
+ 以下版本描述知识源,不代表目标项目版本;目标项目的 package.json 与 lockfile 证据始终优先。
32
34
 
33
35
  - docker-docs: 26.0.0 — https://docs.docker.com/reference/
34
36
 
@@ -14,4 +14,4 @@ required_mcp_tools: []
14
14
  - Context/Graph/Memory/Knowledge/Workflow 引擎是否就绪
15
15
  - 已安装哪些领域插件
16
16
  - 是否有已知配置问题
17
- **MCP Gateway 策略**: 调用前会校验 Session Token 和调用合法性。无有效收据的情况下直接使用原生工具(grep/Glob/WebSearch)将被 PreToolUse Hook 拦截。收据有效期 30 秒,过期后需重新通过 MCP 工具获取。
17
+ **MCP Gateway 策略**: 调用前会校验 Session Token 和调用合法性。在支持 PreToolUse Hook 的 Claude Code 等宿主中,无有效收据的原生工具调用会被拦截;不提供生命周期 Hook 的宿主不会声称已执行原生拦截,而是依赖 canonical MCP receipts 和显式 capability degradation。收据有效期 30 秒,过期后需重新通过 MCP 工具获取。
@@ -28,7 +28,9 @@ description: Electron 专家 — IPC 通信、窗口管理、原生模块、自
28
28
  - 避免使用 remote 模块(已废弃)
29
29
  - 构建使用 electron-builder 并配置代码签名
30
30
 
31
- ## 当前版本
31
+ ## 知识来源
32
+
33
+ 以下版本描述知识源,不代表目标项目版本;目标项目的 package.json 与 lockfile 证据始终优先。
32
34
 
33
35
  - electron-docs: 30.0.0 — https://www.electronjs.org/docs/latest/
34
36
 
@@ -27,7 +27,9 @@ description: Git 专家 — 提交规范、分支审查、Changelog 生成、冲
27
27
  - PR 必须关联 Issue
28
28
  - 解决冲突前先理解两边变更的意图
29
29
 
30
- ## 当前版本
30
+ ## 知识来源
31
+
32
+ 以下版本描述知识源,不代表目标项目版本;目标项目的 package.json 与 lockfile 证据始终优先。
31
33
 
32
34
  - conventional-commits: 1.0.0 — https://www.conventionalcommits.org/en/v1.0.0/
33
35
  - git-scm-docs: 2.45.0 — https://git-scm.com/docs