@rulemetric/cli 0.7.32 → 0.7.34

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.
@@ -0,0 +1,2 @@
1
+ import{c as f}from"./chunk-VX2GJCQ4.js";import{a as o}from"./chunk-ZLLER3HP.js";import{createHash as w}from"node:crypto";import{mkdir as x,open as P,readFile as S,readdir as M,rename as C,stat as b,writeFile as _}from"node:fs/promises";import{homedir as A}from"node:os";import{basename as z,dirname as F,join as l}from"node:path";var $=3e4,E=5,I=8*1024*1024;function g(n){return w("sha256").update(`${n.externalSessionId}:${Math.round(n.mtimeMs)}:${n.size}`).digest("hex")}o(g,"fingerprint");async function k(n){try{return{fingerprints:JSON.parse(await S(n,"utf8")).fingerprints??{}}}catch{return{fingerprints:{}}}}o(k,"readState");async function O(n,i){let r=`${n}.${process.pid}.tmp`;await x(F(n),{recursive:!0}),await _(r,JSON.stringify(i)),await C(r,n)}o(O,"persistState");var h=class{constructor(i){this.options=i}static{o(this,"TranscriptSync")}lastSeenSize=new Map;lastSyncAt=new Map;inFlight=!1;async scan(){if(this.inFlight)return{discovered:0,imported:0,skipped:0};this.inFlight=!0;try{let i=await k(this.options.statePath),r=(await Promise.all(this.options.providers.map(t=>t.discover()))).flat().sort((t,p)=>p.mtimeMs-t.mtimeMs),s=Date.now(),e=[],a=0;for(let t of r){let p=g(t),d=this.lastSeenSize.get(t.path);if(this.lastSeenSize.set(t.path,t.size),i.fingerprints[t.path]===p){a++;continue}let m=this.options.minStableAgeMs??3e3;if(m>0&&s-t.mtimeMs<m){a++;continue}let v=d!==void 0&&d===t.size,u=this.lastSyncAt.get(t.path),T=u===void 0||s-u>=(this.options.activeResyncMs??$);if(!v&&!T){a++;continue}if(e.push(t),e.length>=(this.options.maxImportsPerScan??E))break}let c=0;for(let t of e){let p=g(t);if(t.size>(this.options.maxContentBytes??I)){i.fingerprints[t.path]=p,a++,this.options.log?.(`Transcript sync skipped oversized file: ${t.path}`);continue}let d=await S(t.path,"utf8");await this.options.importTranscript({content:d,tool:t.tool}),i.fingerprints[t.path]=p,this.lastSyncAt.set(t.path,Date.now()),c++,this.options.log?.(`Transcript sync imported ${t.tool} ${z(t.path)} (${t.externalSessionId}).`)}return e.length>0&&await O(this.options.statePath,i),{discovered:r.length,imported:c,skipped:a}}finally{this.inFlight=!1}}};async function D(n){let i;try{i=await P(n,"r");let r=Buffer.alloc(64*1024),{bytesRead:s}=await i.read(r,0,r.length,0),e=r.subarray(0,s).toString("utf8").split(`
2
+ `)[0];return e?JSON.parse(e):null}catch{return null}finally{await i?.close()}}o(D,"readFirstJsonLine");async function y(n,i,r){let s=await M(n,{withFileTypes:!0}).catch(()=>[]);for(let e of s){let a=l(n,e.name);if(e.isDirectory()){await y(a,i,r);continue}if(!e.isFile()||!e.name.startsWith("rollout-")||!e.name.endsWith(".jsonl"))continue;let[c,t]=await Promise.all([b(a).catch(()=>null),D(a)]);if(!c||c.mtimeMs<r||t?.type!=="session_meta")continue;let p=t.payload?.originator,d=t.payload?.id??t.payload?.session_id;!p?.toLowerCase().startsWith("codex")||!d||i.push({path:a,tool:"codex",externalSessionId:d,projectPath:t.payload?.cwd??null,mtimeMs:c.mtimeMs,size:c.size})}}o(y,"walkCodexSessions");function J(n=process.env.CODEX_HOME??l(A(),".codex"),i=14,r){return{name:"codex",async discover(){let s=typeof r=="function"?await r():r;if(s.mode==="off")return[];let e=[],a=Date.now()-Math.max(1,i)*24*60*60*1e3;return await y(l(n,"sessions"),e,a),s.mode==="all"?e:e.filter(c=>f(s,c.projectPath))}}}o(J,"createCodexTranscriptProvider");function U(n,i){let r=o(async()=>{try{let e=await n.scan();e.imported>0&&i.log?.(`Transcript sync imported/updated ${e.imported} session${e.imported===1?"":"s"}.`)}catch(e){i.log?.(`Transcript sync failed: ${e instanceof Error?e.message:String(e)}`)}},"run"),s=setInterval(()=>void r(),i.intervalMs??3e4);i.signal.addEventListener("abort",()=>clearInterval(s),{once:!0}),r()}o(U,"startTranscriptSync");export{h as a,J as b,U as c};
@@ -0,0 +1,24 @@
1
+ import{f as E}from"./chunk-MODFTCZY.js";import{b,e as y,f as k,h as M}from"./chunk-H2ORWIEY.js";import{a as u}from"./chunk-ZLLER3HP.js";var v=50,I=24e3,P=3,R=3,C=40,N=4e3,x=new Set(["fact","decision","correction","convention"]),L=`You are distilling DURABLE, PROJECT-SPECIFIC knowledge from an AI coding session so a future agent working on the SAME project starts smarter.
2
+
3
+ Extract ONLY facts / decisions / corrections / conventions that will STILL be true and useful in FUTURE sessions \u2014 architecture facts, deliberate design decisions, corrections of a wrong approach, or project conventions.
4
+
5
+ Do NOT extract:
6
+ - what the user asked for in THIS session, or the step-by-step actions taken
7
+ - anything specific to this one task that will not recur
8
+ - anything already covered by the existing memories listed below
9
+
10
+ Existing memories for this project (do NOT duplicate these):
11
+ {{EXISTING}}
12
+
13
+ Prefer FEWER, higher-quality memories \u2014 most sessions yield 0-2, many yield 0. A weak or redundant memory is worse than none.
14
+
15
+ Each memory has: "kind" (one of: fact, decision, correction, convention), a short "title" (<= 120 chars), and a 1-3 sentence "body".
16
+
17
+ Return STRICT JSON: {"memories": [{"kind": "...", "title": "...", "body": "..."}]} \u2014 use an empty array if nothing durable was learned.`;function A(t){return t.toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}u(A,"normalizeTitle");function O(t,n,f=R){let l=Array.isArray(t?.memories)?t.memories:[],g=new Set(n.map(A)),d=[];for(let m of l){if(d.length>=f)break;if(!m||typeof m!="object")continue;let o=m,r=typeof o.kind=="string"?o.kind.trim().toLowerCase():"",s=typeof o.title=="string"?o.title.trim():"",p=typeof o.body=="string"?o.body.trim():"";if(!x.has(r)||!s||!p||s.length>300||p.length>N)continue;let a=A(s);!a||g.has(a)||(g.add(a),d.push({kind:r,title:s,body:p}))}return d}u(O,"selectNewMemories");async function $(t,n){await M(`/api/sessions/${t}/enrich`,{analysis:{distilledAt:n}})}u($,"stampDistilledViaApi");async function F(t,n={}){let f=n.llm??E,l=n.now??Date.now,g=n.getSession??(async e=>{try{return await y(`/api/sessions/${e}`)}catch(i){if(i instanceof b&&i.statusCode===404)return null;throw i}}),d=n.getTurns??((e,i)=>y(`/api/sessions/${e}/events?limit=${i}&sort=desc&eventType=user_message,assistant_response&hasContent=true`)),m=n.getExistingTitles??(async(e,i)=>{let{memories:_}=await y(`/api/memories?projectId=${e}&status=active&limit=${i}`);return(_??[]).map(j=>j.title)}),o=n.createMemory??(async e=>{await k("/api/memories",{kind:e.kind,title:e.title,body:e.body,projectId:e.projectId,source:"distilled"})}),r=n.stamp??$,s=await g(t.sessionId);if(!s)return{skipped:"not_found"};if((s.analysis??{}).distilledAt)return{skipped:"already_distilled"};if(!s.projectId)return await r(t.sessionId,new Date(l()).toISOString()),{skipped:"no_project"};let a=s.projectId,S=(await d(t.sessionId,v)).reverse().map(e=>`${e.eventType==="user_message"?"USER":"ASSISTANT"}: ${e.content}`);if(S.length<P)return await r(t.sessionId,new Date(l()).toISOString()),{skipped:"too_short"};let c=S.join(`
18
+
19
+ `);if(c.length>I){let e=c.slice(0,Math.floor(I*.45)),i=c.slice(c.length-Math.floor(I*.45));c=`${e}
20
+
21
+ [... middle of session omitted ...]
22
+
23
+ ${i}`}let h=await m(a,C),D=L.replace("{{EXISTING}}",h.length?h.map(e=>`- ${e}`).join(`
24
+ `):"(none yet)"),T=await f(D,c,700);if(T===null)throw new Error("distill_memories: LLM backend unavailable or returned unparseable output");let w=O(T,h);for(let e of w)await o({...e,projectId:a});return await r(t.sessionId,new Date(l()).toISOString()),{created:w.length,candidates:w.length}}u(F,"distillMemories");export{O as a,F as b};
@@ -0,0 +1,2 @@
1
+ import{a as o}from"./chunk-ZLLER3HP.js";import{spawnSync as u}from"node:child_process";import{relative as l}from"node:path";function a(t,i){try{let e=u("git",t,{cwd:i,encoding:"utf-8",timeout:5e3});return{ok:e.status===0,stdout:(e.stdout??"").trim()}}catch{return{ok:!1,stdout:""}}}o(a,"git");function c(t,i){let e=i.filter(Boolean);return e.length===0?[]:a(["rev-parse","--is-inside-work-tree"],t).ok?e.filter(r=>!(a(["check-ignore","-q",r],t).ok||a(["ls-files","--error-unmatch",r],t).ok)):[]}o(c,"untrackedRepoArtifacts");function m(t,i){let e=c(t,i);if(e.length===0)return null;let r=e.map(n=>{let s=l(t,n);return s&&!s.startsWith("..")?s:n});return["",`[!!] ${r.length} file${r.length===1?"":"s"} written into this repo will show as untracked in git:`,...r.map(n=>` ${n}`),""," These are machine-local hook configs. RuleMetric does NOT edit your"," .gitignore \u2014 committing them is a legitimate choice for a shared team"," setup. To keep them out of git instead, add:","",...r.map(n=>` ${n}`),""].join(`
2
+ `)}o(m,"describeUntrackedRepoArtifacts");export{c as a,m as b};
@@ -0,0 +1 @@
1
+ import{a as s}from"./chunk-ZLLER3HP.js";import{readFile as v,stat as w}from"node:fs/promises";import{homedir as E}from"node:os";import{isAbsolute as O,join as d,resolve as S,sep as m}from"node:path";var l="capture.json";function R(e,o){return e==="~"?o:e.startsWith("~/")?d(o,e.slice(2)):e}s(R,"expandTilde");function x(e,o){return e.map(r=>typeof r=="string"?r.trim():"").filter(Boolean).map(r=>R(r,o)).filter(r=>O(r)).map(r=>S(r))}s(x,"normaliseRoots");function h(e){return{mode:"off",roots:[],source:"config",error:e}}s(h,"failClosed");async function y(e){let o=e.env??process.env,r=e.home??E(),t=o.RULEMETRIC_CODEX_CAPTURE?.trim().toLowerCase();if(t==="off"||t==="none"||t==="0")return{mode:"off",roots:[],source:"env"};if(t==="all"||t==="1")return{mode:"all",roots:[],source:"env"};let i=o.RULEMETRIC_CODEX_ROOTS?.trim();if(i){let g=x(i.split(","),r);return g.length>0?{mode:"allowlist",roots:g,source:"env"}:{mode:"off",roots:[],source:"env",error:"RULEMETRIC_CODEX_ROOTS had no absolute paths"}}let n=d(e.configDir,l),c;try{c=await v(n,"utf8")}catch{return{mode:"all",roots:[],source:"default"}}let u;try{u=JSON.parse(c)}catch{return h(`${l} is present but is not valid JSON \u2014 Codex capture is OFF until it parses.`)}let a=u?.codex;if(!a||typeof a!="object")return{mode:"all",roots:[],source:"default"};let f=typeof a.mode=="string"?a.mode.trim().toLowerCase():void 0;if(f==="off")return{mode:"off",roots:[],source:"config"};if(f==="all")return{mode:"all",roots:[],source:"config"};let p=Array.isArray(a.roots)?a.roots:[];if(f===void 0&&p.length===0)return{mode:"all",roots:[],source:"default"};let C=x(p,r);return C.length===0?h(`${l} selects an allowlist but lists no roots (absolute paths required) \u2014 Codex capture is OFF.`):{mode:"allowlist",roots:C,source:"config"}}s(y,"loadCodexCaptureScope");function $(e,o){if(e.mode==="off")return!1;if(e.mode==="all")return!0;if(!o)return!1;let r=S(o);return e.roots.some(t=>r===t||r.startsWith(t.endsWith(m)?t:t+m))}s($,"isPathInScope");function T(e){if(e.error)return`OFF \u2014 ${e.error}`;switch(e.mode){case"off":return`off (${e.source}) \u2014 no Codex transcripts are swept`;case"allowlist":return`scoped to ${e.roots.length} path${e.roots.length===1?"":"s"} (${e.source})`;default:return"all projects (unscoped)"}}s(T,"describeCodexCaptureScope");function _(e){let o=null,r=null;return async()=>{let t=d(e.configDir,l),i;try{let u=await w(t);i=`${u.mtimeMs}:${u.size}`}catch{i="absent"}if(o&&r===i)return o;let n=await y(e),c=o===null||o.mode!==n.mode||o.error!==n.error||o.roots.join("\0")!==n.roots.join("\0");return o=n,r=i,c&&e.onChange?.(n),n}}s(_,"createCodexScopeWatcher");export{l as a,y as b,$ as c,T as d,_ as e};
@@ -1 +1 @@
1
- import{c as C}from"./chunk-TIOKY2IA.js";import{b as I}from"./chunk-W2MOTOQE.js";import{a as R}from"./chunk-3FJT6LL4.js";import{a as T}from"./chunk-DAGDGAPU.js";import{a as A}from"./chunk-RSKQRVPJ.js";import{a as y}from"./chunk-JEBHSV2M.js";import{a as _}from"./chunk-L5ORQB25.js";import{b as H}from"./chunk-Q76LGSIU.js";import{a as M}from"./chunk-WEF5LPGS.js";import{a as S}from"./chunk-ZAWAPLIE.js";import{a as h}from"./chunk-AIEM6VXO.js";import{a as E}from"./chunk-BZUIMHHT.js";import{b}from"./chunk-5RBLIVBF.js";import{a as L}from"./chunk-LEXVC7VH.js";import{a as N}from"./chunk-5W55CRBJ.js";import{d as P}from"./chunk-JR4CWY3J.js";import{a as x}from"./chunk-UR6GZGFK.js";import{a as k}from"./chunk-UF6US7VU.js";import{d as w}from"./chunk-OPUI7CM2.js";import{b as f,e as v,f as p}from"./chunk-H2ORWIEY.js";import{a as s}from"./chunk-ZLLER3HP.js";var W={process_eval:h,process_insights:E,process_launch:I,process_send_message:A,process_conversation:S,process_session_goal:y,distill_memories:H,memory_ab:R,process_announcement:M,process_run_cleanup:T,cron_suggest_instructions:_,cron_refresh_skills:k,cron_refresh_pricing:x,cron_eval_autorun:N,cron_instruction_evolution:P,process_judge_review:L},D=Object.keys(W),F=new Set(["process_announcement","process_run_cleanup","memory_ab"]);function J(e=process.env){return e.DATABASE_URL?D:D.filter(n=>!F.has(n))}s(J,"availableCapabilities");var U=3e4,K=3e4,G=3e4,V=250,B=5e3,q=`thin-agent-${process.pid}`,g=null;function Q(e,n,r,l){let c;try{let d=w();c={claude:d.claudeBin!=null,codex:d.codexBin!=null,codexModel:d.codexConfiguredModel}}catch{}let i=s(d=>{p("/api/workers/heartbeat",{workerType:"cli-worker",clientName:q,clientVersion:l,workerVersion:"0.1.0",currentJobId:null,inFlightCount:0,lastError:null,status:d,metadata:{capabilities:e,mode:"thin-agent",llmEngines:c}}).then(u=>{let t=u?.advisory;t&&t.message!==g?(g=t.message,a(r,{event:"worker_advisory",code:t.code,message:t.message})):t||(g=null)}).catch(u=>{a(r,{event:"presence_heartbeat_error",error:u instanceof Error?u.message:String(u)})})},"send");i("alive");let o=setInterval(()=>i("alive"),G);n.addEventListener("abort",()=>{clearInterval(o),i("shutting_down")},{once:!0})}s(Q,"startPresenceHeartbeat");function a(e,n){try{e(JSON.stringify({ts:new Date().toISOString(),...n}))}catch{e(`[agent-loop] log_error event=${n.event}`)}}s(a,"structuredLog");function O(e,n){return n.aborted?Promise.resolve(!1):e<=0?Promise.resolve(!0):new Promise(r=>{let l=setTimeout(()=>{n.removeEventListener("abort",c),r(!0)},e),c=s(()=>{clearTimeout(l),n.removeEventListener("abort",c),r(!1)},"onAbort");n.addEventListener("abort",c,{once:!0})})}s(O,"abortableSleep");async function X(e,n){let r=`capabilities=${encodeURIComponent(e.join(","))}&waitMs=${n}`,l=await v(`/api/work/next?${r}`);return l==null||typeof l=="string"?null:l}s(X,"pollForWork");function z(e,n){return{logger:{info:s(r=>n(r),"info"),warn:s(r=>n(r),"warn"),error:s(r=>n(r),"error")},job:{attempts:e}}}s(z,"makeHelpers");async function Y(e,n,r){let l=W[e.kind],c=Date.now();if(a(r,{event:"claim",jobId:e.id,kind:e.kind,attempts:e.attempts}),!l){let o=`unknown kind: ${e.kind}`;a(r,{event:"fail",jobId:e.id,kind:e.kind,terminal:!0,error:o}),await p(`/api/work/${e.id}/fail`,{leaseToken:e.leaseToken,error:o,terminal:!0}).catch(()=>{});return}let i=setInterval(()=>{a(r,{event:"heartbeat",jobId:e.id}),p(`/api/work/${e.id}/heartbeat`,{leaseToken:e.leaseToken}).catch(o=>{a(r,{event:"heartbeat_error",jobId:e.id,error:o instanceof Error?o.message:String(o)})})},n);try{let o=z(e.attempts,r),d=await l(e.payload,o)??{};clearInterval(i),a(r,{event:"complete",jobId:e.id,kind:e.kind,durationMs:Date.now()-c}),await p(`/api/work/${e.id}/complete`,{leaseToken:e.leaseToken,result:d})}catch(o){clearInterval(i);let d=b(o),u=o instanceof Error?o.message:String(o);a(r,{event:"fail",jobId:e.id,kind:e.kind,durationMs:Date.now()-c,terminal:d,error:u}),await p(`/api/work/${e.id}/fail`,{leaseToken:e.leaseToken,error:u,terminal:d}).catch(t=>{a(r,{event:"fail_post_error",jobId:e.id,error:t instanceof Error?t.message:String(t)})})}}s(Y,"runOne");async function we(e){let n=e.log??(()=>{}),r=e.pollWaitMs??U,l=e.heartbeatIntervalMs??K,c=e.capabilities??J(),i=V;a(n,{event:"agent_start",capabilities:c,pollWaitMs:r,heartbeatIntervalMs:l}),Q(c,e.signal,n,e.version),C({signal:e.signal,log:n}).catch(t=>{a(n,{event:"tt_heartbeat_fatal",error:t instanceof Error?t.message:String(t)})});let o=5*6e4,d=0,u=s(()=>{if(!e.readInstalledVersion||!e.version)return!1;let t=Date.now();if(t-d<o)return!1;d=t;let m=e.readInstalledVersion();return!!(m&&m!==e.version)},"versionDrifted");for(;!e.signal.aborted;){if(u()){a(n,{event:"agent_version_drift",bootVersion:e.version,installedVersion:e.readInstalledVersion?.()??null}),e.onVersionDrift?.();break}let t=null;try{t=await X(c,r)}catch(m){let $=m instanceof f?m.statusCode:void 0;if(a(n,{event:"poll_error",status:$,error:m instanceof Error?m.message:String(m)}),!await O(i,e.signal))break;i=Math.min(i*2,B);continue}if(!t){if(a(n,{event:"poll_204"}),!await O(i,e.signal))break;i=Math.min(i*2,B);continue}i=V,await Y(t,l,n)}a(n,{event:"agent_stop"})}s(we,"runAgentLoop");export{W as a,D as b,J as c,X as d,we as e};
1
+ import{c as C}from"./chunk-TIOKY2IA.js";import{a as L}from"./chunk-LEXVC7VH.js";import{b as I}from"./chunk-W2MOTOQE.js";import{a as R}from"./chunk-3FJT6LL4.js";import{a as T}from"./chunk-DAGDGAPU.js";import{a as A}from"./chunk-RSKQRVPJ.js";import{a as y}from"./chunk-JEBHSV2M.js";import{a as k}from"./chunk-UF6US7VU.js";import{a as _}from"./chunk-L5ORQB25.js";import{b as H}from"./chunk-E3MQVVH6.js";import{a as M}from"./chunk-WEF5LPGS.js";import{a as S}from"./chunk-ZAWAPLIE.js";import{a as h}from"./chunk-AIEM6VXO.js";import{a as E}from"./chunk-BZUIMHHT.js";import{b}from"./chunk-5RBLIVBF.js";import{a as N}from"./chunk-5W55CRBJ.js";import{d as P}from"./chunk-JR4CWY3J.js";import{a as x}from"./chunk-UR6GZGFK.js";import{d as w}from"./chunk-OPUI7CM2.js";import{b as f,e as v,f as p}from"./chunk-H2ORWIEY.js";import{a as s}from"./chunk-ZLLER3HP.js";var W={process_eval:h,process_insights:E,process_launch:I,process_send_message:A,process_conversation:S,process_session_goal:y,distill_memories:H,memory_ab:R,process_announcement:M,process_run_cleanup:T,cron_suggest_instructions:_,cron_refresh_skills:k,cron_refresh_pricing:x,cron_eval_autorun:N,cron_instruction_evolution:P,process_judge_review:L},D=Object.keys(W),F=new Set(["process_announcement","process_run_cleanup","memory_ab"]);function J(e=process.env){return e.DATABASE_URL?D:D.filter(n=>!F.has(n))}s(J,"availableCapabilities");var U=3e4,K=3e4,G=3e4,V=250,B=5e3,q=`thin-agent-${process.pid}`,g=null;function Q(e,n,r,l){let c;try{let d=w();c={claude:d.claudeBin!=null,codex:d.codexBin!=null,codexModel:d.codexConfiguredModel}}catch{}let i=s(d=>{p("/api/workers/heartbeat",{workerType:"cli-worker",clientName:q,clientVersion:l,workerVersion:"0.1.0",currentJobId:null,inFlightCount:0,lastError:null,status:d,metadata:{capabilities:e,mode:"thin-agent",llmEngines:c}}).then(u=>{let t=u?.advisory;t&&t.message!==g?(g=t.message,a(r,{event:"worker_advisory",code:t.code,message:t.message})):t||(g=null)}).catch(u=>{a(r,{event:"presence_heartbeat_error",error:u instanceof Error?u.message:String(u)})})},"send");i("alive");let o=setInterval(()=>i("alive"),G);n.addEventListener("abort",()=>{clearInterval(o),i("shutting_down")},{once:!0})}s(Q,"startPresenceHeartbeat");function a(e,n){try{e(JSON.stringify({ts:new Date().toISOString(),...n}))}catch{e(`[agent-loop] log_error event=${n.event}`)}}s(a,"structuredLog");function O(e,n){return n.aborted?Promise.resolve(!1):e<=0?Promise.resolve(!0):new Promise(r=>{let l=setTimeout(()=>{n.removeEventListener("abort",c),r(!0)},e),c=s(()=>{clearTimeout(l),n.removeEventListener("abort",c),r(!1)},"onAbort");n.addEventListener("abort",c,{once:!0})})}s(O,"abortableSleep");async function X(e,n){let r=`capabilities=${encodeURIComponent(e.join(","))}&waitMs=${n}`,l=await v(`/api/work/next?${r}`);return l==null||typeof l=="string"?null:l}s(X,"pollForWork");function z(e,n){return{logger:{info:s(r=>n(r),"info"),warn:s(r=>n(r),"warn"),error:s(r=>n(r),"error")},job:{attempts:e}}}s(z,"makeHelpers");async function Y(e,n,r){let l=W[e.kind],c=Date.now();if(a(r,{event:"claim",jobId:e.id,kind:e.kind,attempts:e.attempts}),!l){let o=`unknown kind: ${e.kind}`;a(r,{event:"fail",jobId:e.id,kind:e.kind,terminal:!0,error:o}),await p(`/api/work/${e.id}/fail`,{leaseToken:e.leaseToken,error:o,terminal:!0}).catch(()=>{});return}let i=setInterval(()=>{a(r,{event:"heartbeat",jobId:e.id}),p(`/api/work/${e.id}/heartbeat`,{leaseToken:e.leaseToken}).catch(o=>{a(r,{event:"heartbeat_error",jobId:e.id,error:o instanceof Error?o.message:String(o)})})},n);try{let o=z(e.attempts,r),d=await l(e.payload,o)??{};clearInterval(i),a(r,{event:"complete",jobId:e.id,kind:e.kind,durationMs:Date.now()-c}),await p(`/api/work/${e.id}/complete`,{leaseToken:e.leaseToken,result:d})}catch(o){clearInterval(i);let d=b(o),u=o instanceof Error?o.message:String(o);a(r,{event:"fail",jobId:e.id,kind:e.kind,durationMs:Date.now()-c,terminal:d,error:u}),await p(`/api/work/${e.id}/fail`,{leaseToken:e.leaseToken,error:u,terminal:d}).catch(t=>{a(r,{event:"fail_post_error",jobId:e.id,error:t instanceof Error?t.message:String(t)})})}}s(Y,"runOne");async function we(e){let n=e.log??(()=>{}),r=e.pollWaitMs??U,l=e.heartbeatIntervalMs??K,c=e.capabilities??J(),i=V;a(n,{event:"agent_start",capabilities:c,pollWaitMs:r,heartbeatIntervalMs:l}),Q(c,e.signal,n,e.version),C({signal:e.signal,log:n}).catch(t=>{a(n,{event:"tt_heartbeat_fatal",error:t instanceof Error?t.message:String(t)})});let o=5*6e4,d=0,u=s(()=>{if(!e.readInstalledVersion||!e.version)return!1;let t=Date.now();if(t-d<o)return!1;d=t;let m=e.readInstalledVersion();return!!(m&&m!==e.version)},"versionDrifted");for(;!e.signal.aborted;){if(u()){a(n,{event:"agent_version_drift",bootVersion:e.version,installedVersion:e.readInstalledVersion?.()??null}),e.onVersionDrift?.();break}let t=null;try{t=await X(c,r)}catch(m){let $=m instanceof f?m.statusCode:void 0;if(a(n,{event:"poll_error",status:$,error:m instanceof Error?m.message:String(m)}),!await O(i,e.signal))break;i=Math.min(i*2,B);continue}if(!t){if(a(n,{event:"poll_204"}),!await O(i,e.signal))break;i=Math.min(i*2,B);continue}i=V,await Y(t,l,n)}a(n,{event:"agent_stop"})}s(we,"runAgentLoop");export{W as a,D as b,J as c,X as d,we as e};
@@ -1 +1 @@
1
- import{b,c as S}from"../chunk-V5U3E4BZ.js";import{a as X}from"../chunk-VLXCQ2DC.js";import"../chunk-I2DZGJRR.js";import{c as k,d as T,e as w}from"../chunk-MVM6OGAF.js";import"../chunk-KKDHD4XA.js";import"../chunk-SZ62CP55.js";import{d as H}from"../chunk-OPUI7CM2.js";import"../chunk-MODFTCZY.js";import"../chunk-PJ3WHQ67.js";import"../chunk-N7ODX6PO.js";import{a as _}from"../chunk-PPKPCXJD.js";import"../chunk-IZTIYR5Q.js";import"../chunk-V44SRRYR.js";import"../chunk-U6XEU573.js";import"../chunk-VLOX6KOM.js";import"../chunk-3AV3GTMP.js";import{a as R,e as O}from"../chunk-H2ORWIEY.js";import{a as A,g as h}from"../chunk-O6C2UGBY.js";import{a as m}from"../chunk-ZLLER3HP.js";import{spawnSync as E}from"node:child_process";import{existsSync as U,readFileSync as L}from"node:fs";import{join as B}from"node:path";var x=class u extends _{static{m(this,"Diagnostics")}static description='Diagnose capture / proxy / connectivity problems (e.g. a client-side "502 Bad Gateway")';static examples=["<%= config.bin %> diagnostics"];baseUrl(){return process.env.RULEMETRIC_API_URL??A().apiUrl??R}async checkDirect(){let t=process.env.HTTPS_PROXY,o=process.env.HTTP_PROXY;delete process.env.HTTPS_PROXY,delete process.env.HTTP_PROXY;let i=new AbortController,s=setTimeout(()=>i.abort(),8e3);try{return(await globalThis.fetch(`${this.baseUrl()}/health`,{signal:i.signal})).ok}catch{return!1}finally{clearTimeout(s),t!==void 0&&(process.env.HTTPS_PROXY=t),o!==void 0&&(process.env.HTTP_PROXY=o)}}configuredProxy(){if(process.env.HTTPS_PROXY)return process.env.HTTPS_PROXY;for(let t of["settings.local.json","settings.json"]){let o=B(process.cwd(),".claude",t);if(U(o))try{let i=JSON.parse(L(o,"utf8"));if(i.env?.HTTPS_PROXY)return i.env.HTTPS_PROXY}catch{}}return null}checkThroughProxy(t){let o=E("curl",["-sS","-x",t,"--max-time","8","-o","/dev/null","-w","%{http_code}",`${this.baseUrl()}/health`],{encoding:"utf8",stdio:["ignore","pipe","ignore"]});return o.error||typeof o.stdout!="string"?null:o.stdout.trim()==="200"}async run(){await this.parse(u);let t=process.version,o=this.configuredProxy(),i=o!=null,[s,g,v]=await Promise.all([this.checkDirect(),w(k),w(T)]),p=i&&o?this.checkThroughProxy(o):null,n=m((e,c,a)=>{let d=c===null?"\u2013":c?"[OK]":"[!!]";this.log(` ${d} ${e.padEnd(22)} ${a}`)},"line");this.log("RuleMetric diagnostics"),this.log(""),n("Node",!b(t),`${t}${b(t)?" (< 20.11 \u2014 breaks the bundled proxy)":""}`),n("API (direct)",s,`${this.baseUrl()}/health ${s?"reachable":"UNREACHABLE"}`),n("Authenticated",h(),h()?"yes":"no \u2014 run `rulemetric auth login`"),n("Proxy configured",i?!0:null,i?o:"no HTTPS_PROXY set"),i&&(n("Gateway :"+k,g,g?"listening":"NOT listening"),n("mitmproxy :"+T,v,v?"listening":"not listening (gateway should route direct)"),n("API (via proxy)",p,p===null?"inconclusive (curl unavailable)":p?"reachable":"FAILED \u2014 502/blocked through your proxy"));let l=H();n("claude CLI",l.claudeBin!=null,l.claudeBin??"not found \u2014 LLM features (grading, distill) will no-op"),n("codex CLI",l.codexBin!=null?!0:null,l.codexBin?`${l.codexBin}${l.codexConfiguredModel?` (model ${l.codexConfiguredModel})`:""}`:"not found \u2014 optional; enables the cross-vendor reviewer (evals validate-judge)");let r=null;if(h()&&s)try{r=await O("/api/workers/health")}catch{r=null}if(r){let e=r.worker;n("Worker",!!e.active,e.present?e.active?`online (last heartbeat ${e.ageSeconds??"?"}s ago${e.clientVersion?`, v${e.clientVersion}`:""})`:`NOT RUNNING \u2014 last heartbeat ${M(e.ageSeconds)} ago`:"never connected \u2014 no worker has ever reported in");let c=r.pending.reduce((a,d)=>a+d.count,0);if(c>0){let a=r.verdict.stuck.reduce((y,P)=>y+P.count,0),d=r.verdict.unclaimable.reduce((y,P)=>y+P.count,0);n("Job queue",a===0,`${c} pending`+(a>0?` \u2014 ${a} waiting on this worker`:"")+(d>0?` \u2014 ${d} need a worker with DATABASE_URL`:""))}else n("Job queue",!0,"empty")}else h()&&s&&n("Worker",null,"could not read /workers/health (older API?)");let $=X(process.cwd()),I={claude_code:"session hooks (+ proxy for instruction linking)",codex:"worker transcript sync of ~/.codex/sessions \u2014 no project config needed",cursor:"user hooks (~/.cursor/hooks.json)",copilot_agent:"workspace hooks",antigravity:"user hooks (~/.gemini/config/hooks.json)"};this.log(""),this.log(" Capture coverage (agents found on this machine):");for(let e of $)this.log(` \u2022 ${e.padEnd(15)} ${I[e]??"captured"}`);r&&!r.worker.active&&$.includes("codex")&&this.log(" ! Codex capture runs INSIDE the worker \u2014 it is stopped while the worker is down.");let f=S({nodeVersion:t,directApiOk:s,httpsProxySet:i,gatewayAlive:g,mitmproxyAlive:v,proxyPathOk:p});this.log("");let Y=f.severity==="ok"?"[OK]":f.severity==="warn"?"[!!]":"[XX]";this.log(`${Y} ${f.headline}`);for(let e of f.hints)this.log(` \u2022 ${e}`);if(r&&r.verdict.severity!=="ok"){let e=r.verdict.severity==="warn"?"[!!]":"[XX]";this.log(""),this.log(`${e} ${r.verdict.headline}`);for(let c of r.verdict.hints)this.log(` \u2022 ${c}`)}}};function M(u){if(u===null)return"never";if(u<90)return`${Math.round(u)}s`;let t=Math.round(u/60);return t<90?`${t}m`:`${Math.round(t/60)}h`}m(M,"formatAge");export{x as default};
1
+ import{b as A,c as I}from"../chunk-V5U3E4BZ.js";import{a as L}from"../chunk-VLXCQ2DC.js";import"../chunk-I2DZGJRR.js";import{c as b,d as x,e as $}from"../chunk-MVM6OGAF.js";import"../chunk-KKDHD4XA.js";import"../chunk-SZ62CP55.js";import{a as E,b as H,d as X}from"../chunk-VX2GJCQ4.js";import{d as U}from"../chunk-OPUI7CM2.js";import"../chunk-MODFTCZY.js";import"../chunk-PJ3WHQ67.js";import"../chunk-N7ODX6PO.js";import{a as C}from"../chunk-PPKPCXJD.js";import"../chunk-IZTIYR5Q.js";import"../chunk-V44SRRYR.js";import"../chunk-U6XEU573.js";import"../chunk-VLOX6KOM.js";import"../chunk-3AV3GTMP.js";import{a as O,e as S}from"../chunk-H2ORWIEY.js";import{a as _,g,l as w}from"../chunk-O6C2UGBY.js";import{a as v}from"../chunk-ZLLER3HP.js";import{spawnSync as B}from"node:child_process";import{existsSync as D,readFileSync as G}from"node:fs";import{join as Y}from"node:path";var R=class u extends C{static{v(this,"Diagnostics")}static description='Diagnose capture / proxy / connectivity problems (e.g. a client-side "502 Bad Gateway")';static examples=["<%= config.bin %> diagnostics"];baseUrl(){return process.env.RULEMETRIC_API_URL??_().apiUrl??O}async checkDirect(){let o=process.env.HTTPS_PROXY,t=process.env.HTTP_PROXY;delete process.env.HTTPS_PROXY,delete process.env.HTTP_PROXY;let i=new AbortController,s=setTimeout(()=>i.abort(),8e3);try{return(await globalThis.fetch(`${this.baseUrl()}/health`,{signal:i.signal})).ok}catch{return!1}finally{clearTimeout(s),o!==void 0&&(process.env.HTTPS_PROXY=o),t!==void 0&&(process.env.HTTP_PROXY=t)}}configuredProxy(){if(process.env.HTTPS_PROXY)return process.env.HTTPS_PROXY;for(let o of["settings.local.json","settings.json"]){let t=Y(process.cwd(),".claude",o);if(D(t))try{let i=JSON.parse(G(t,"utf8"));if(i.env?.HTTPS_PROXY)return i.env.HTTPS_PROXY}catch{}}return null}checkThroughProxy(o){let t=B("curl",["-sS","-x",o,"--max-time","8","-o","/dev/null","-w","%{http_code}",`${this.baseUrl()}/health`],{encoding:"utf8",stdio:["ignore","pipe","ignore"]});return t.error||typeof t.stdout!="string"?null:t.stdout.trim()==="200"}async run(){await this.parse(u);let o=process.version,t=this.configuredProxy(),i=t!=null,[s,p,P]=await Promise.all([this.checkDirect(),$(b),$(x)]),f=i&&t?this.checkThroughProxy(t):null,n=v((e,a,d)=>{let h=a===null?"\u2013":a?"[OK]":"[!!]";this.log(` ${h} ${e.padEnd(22)} ${d}`)},"line");this.log("RuleMetric diagnostics"),this.log(""),n("Node",!A(o),`${o}${A(o)?" (< 20.11 \u2014 breaks the bundled proxy)":""}`),n("API (direct)",s,`${this.baseUrl()}/health ${s?"reachable":"UNREACHABLE"}`),n("Authenticated",g(),g()?"yes":"no \u2014 run `rulemetric auth login`"),n("Proxy configured",i?!0:null,i?t:"no HTTPS_PROXY set"),i&&(n("Gateway :"+b,p,p?"listening":"NOT listening"),n("mitmproxy :"+x,P,P?"listening":"not listening (gateway should route direct)"),n("API (via proxy)",f,f===null?"inconclusive (curl unavailable)":f?"reachable":"FAILED \u2014 502/blocked through your proxy"));let l=U();n("claude CLI",l.claudeBin!=null,l.claudeBin??"not found \u2014 LLM features (grading, distill) will no-op"),n("codex CLI",l.codexBin!=null?!0:null,l.codexBin?`${l.codexBin}${l.codexConfiguredModel?` (model ${l.codexConfiguredModel})`:""}`:"not found \u2014 optional; enables the cross-vendor reviewer (evals validate-judge)");let r=null;if(g()&&s)try{r=await S("/api/workers/health")}catch{r=null}if(r){let e=r.worker;n("Worker",!!e.active,e.present?e.active?`online (last heartbeat ${e.ageSeconds??"?"}s ago${e.clientVersion?`, v${e.clientVersion}`:""})`:`NOT RUNNING \u2014 last heartbeat ${j(e.ageSeconds)} ago`:"never connected \u2014 no worker has ever reported in");let a=r.pending.reduce((d,h)=>d+h.count,0);if(a>0){let d=r.verdict.stuck.reduce((k,T)=>k+T.count,0),h=r.verdict.unclaimable.reduce((k,T)=>k+T.count,0);n("Job queue",d===0,`${a} pending`+(d>0?` \u2014 ${d} waiting on this worker`:"")+(h>0?` \u2014 ${h} need a worker with DATABASE_URL`:""))}else n("Job queue",!0,"empty")}else g()&&s&&n("Worker",null,"could not read /workers/health (older API?)");let y=L(process.cwd()),c=await H({configDir:w()}),M={claude_code:"session hooks (+ proxy for instruction linking)",codex:`worker transcript sync of ~/.codex/sessions \u2014 ${X(c)}`,cursor:"user hooks (~/.cursor/hooks.json)",copilot_agent:"workspace hooks",antigravity:"user hooks (~/.gemini/config/hooks.json)"};this.log(""),this.log(" Capture coverage (agents found on this machine):");for(let e of y)this.log(` \u2022 ${e.padEnd(15)} ${M[e]??"captured"}`);if(y.includes("codex"))if(c.error)this.log(` ! ${c.error}`);else if(c.mode==="allowlist")for(let e of c.roots)this.log(` \u21B3 ${e}`);else c.mode==="all"&&this.log(` \u21B3 scope it with ${Y(w(),E)} ({"codex":{"roots":["/path/to/repo"]}}) or RULEMETRIC_CODEX_CAPTURE=off`);r&&!r.worker.active&&y.includes("codex")&&c.mode!=="off"&&this.log(" ! Codex capture runs INSIDE the worker \u2014 it is stopped while the worker is down.");let m=I({nodeVersion:o,directApiOk:s,httpsProxySet:i,gatewayAlive:p,mitmproxyAlive:P,proxyPathOk:f});this.log("");let N=m.severity==="ok"?"[OK]":m.severity==="warn"?"[!!]":"[XX]";this.log(`${N} ${m.headline}`);for(let e of m.hints)this.log(` \u2022 ${e}`);if(r&&r.verdict.severity!=="ok"){let e=r.verdict.severity==="warn"?"[!!]":"[XX]";this.log(""),this.log(`${e} ${r.verdict.headline}`);for(let a of r.verdict.hints)this.log(` \u2022 ${a}`)}}};function j(u){if(u===null)return"never";if(u<90)return`${Math.round(u)}s`;let o=Math.round(u/60);return o<90?`${o}m`:`${Math.round(o/60)}h`}v(j,"formatAge");export{R as default};
@@ -1,3 +1,3 @@
1
- import{a as k,b as A,c as I}from"../../chunk-47HDPK73.js";import{e as O}from"../../chunk-DTMLVFNM.js";import"../../chunk-TIOKY2IA.js";import"../../chunk-W2MOTOQE.js";import"../../chunk-3FJT6LL4.js";import"../../chunk-DAGDGAPU.js";import"../../chunk-6QKI756T.js";import"../../chunk-RSKQRVPJ.js";import"../../chunk-JEBHSV2M.js";import"../../chunk-L5ORQB25.js";import"../../chunk-Q76LGSIU.js";import"../../chunk-WEF5LPGS.js";import"../../chunk-N7TGRFVZ.js";import{j as u}from"../../chunk-3DLVY7LD.js";import"../../chunk-ZAWAPLIE.js";import"../../chunk-AIEM6VXO.js";import"../../chunk-BZUIMHHT.js";import"../../chunk-QFVGUZDO.js";import"../../chunk-ZL4MZMUK.js";import"../../chunk-5RBLIVBF.js";import"../../chunk-LEXVC7VH.js";import"../../chunk-5W55CRBJ.js";import"../../chunk-JR4CWY3J.js";import"../../chunk-ZERPMWRT.js";import"../../chunk-UR6GZGFK.js";import"../../chunk-UF6US7VU.js";import"../../chunk-YQ56FQEO.js";import"../../chunk-JGYCLDYF.js";import"../../chunk-ZWKKFAIX.js";import"../../chunk-TDD7IOV2.js";import"../../chunk-MODXEGCU.js";import"../../chunk-7DFNBRLY.js";import"../../chunk-4VZLIGMS.js";import"../../chunk-3VYV6ZYB.js";import"../../chunk-OZQV5CDT.js";import"../../chunk-6UEFCMR2.js";import"../../chunk-CJRYYV76.js";import"../../chunk-BSJBNOJT.js";import"../../chunk-L2CFSMBC.js";import"../../chunk-Z72VHGV7.js";import"../../chunk-7AZKPX2J.js";import"../../chunk-CDY7TQFZ.js";import"../../chunk-V5MJ4ICC.js";import"../../chunk-WKXTY5AM.js";import"../../chunk-HMMO27U4.js";import"../../chunk-OPUI7CM2.js";import"../../chunk-MODFTCZY.js";import"../../chunk-PJ3WHQ67.js";import"../../chunk-N7ODX6PO.js";import{a as b}from"../../chunk-PPKPCXJD.js";import"../../chunk-IZTIYR5Q.js";import"../../chunk-V44SRRYR.js";import"../../chunk-U6XEU573.js";import"../../chunk-VLOX6KOM.js";import"../../chunk-3AV3GTMP.js";import{f as N}from"../../chunk-H2ORWIEY.js";import{e as P,l as g}from"../../chunk-O6C2UGBY.js";import{a as e}from"../../chunk-ZLLER3HP.js";import{trace as te,context as re}from"@opentelemetry/api";import{NodeSDK as M}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as V}from"@opentelemetry/exporter-trace-otlp-http";import{BatchSpanProcessor as $,ConsoleSpanExporter as j,SimpleSpanProcessor as q}from"@opentelemetry/sdk-trace-node";import{Resource as F}from"@opentelemetry/resources";import{ATTR_SERVICE_NAME as G,ATTR_SERVICE_VERSION as U}from"@opentelemetry/semantic-conventions";function L(t){if(!t)return{};let r={};for(let n of t.split(",")){let o=n.indexOf("=");if(o===-1)continue;let i=n.slice(0,o).trim(),a=n.slice(o+1).trim();i&&(r[i]=a)}return r}e(L,"parseOtlpHeaders");var l=null;function y(){return process.env.TELEMETRY_ENABLED!=="false"}e(y,"isTelemetryEnabled");function h(t){if(!y()||l!==null)return"off";let r=new F({[G]:t.serviceName,[U]:t.serviceVersion??"0.1.0"}),n=process.env.OTEL_EXPORTER_OTLP_ENDPOINT,o,i;if(n){let a=L(process.env.OTEL_EXPORTER_OTLP_HEADERS),d=new V({url:`${n}/v1/traces`,headers:a});i=new $(d),o="otlp"}else if(process.env.OTEL_SPAN_EXPORT==="none")i=void 0,o="silent";else{let a=new j;i=new q(a),o="console"}return l=new M({resource:r,spanProcessors:i?[i]:[]}),l.start(),o}e(h,"initTelemetry");async function f(){l&&(await l.shutdown(),l=null)}e(f,"shutdownTelemetry");import{SpanKind as fe,SpanStatusCode as me}from"@opentelemetry/api";import{trace as Ee,context as Se}from"@opentelemetry/api";import*as p from"@sentry/node";var E=!1;function S(t){if(E)return!1;let r=process.env.SENTRY_DSN;return r?(p.init({dsn:r,serverName:t.serviceName,environment:process.env.NODE_ENV??"development",tracesSampleRate:0}),E=!0,!0):!1}e(S,"initSentry");async function m(t){E&&await p.flush(t)}e(m,"flushSentry");import{existsSync as B,mkdirSync as H,readFileSync as w,unlinkSync as D,writeFileSync as C}from"node:fs";import{dirname as K,join as v}from"node:path";import{execFileSync as Y}from"node:child_process";var c=v(g(),"evals-agent.lock");function J(t){try{return process.kill(t,0),!0}catch(r){return r.code==="EPERM"}}e(J,"pidAlive");function z(t){if(!J(t))return!1;try{let r=Y("ps",["-p",String(t),"-o","command="],{encoding:"utf-8",stdio:["ignore","pipe","ignore"]});return/run\.js\s+evals\s+agent|evals\s+agent/.test(r)}catch{return!0}}e(z,"isLiveAgent");function Q(){try{H(K(c),{recursive:!0})}catch{}for(let t=0;t<2;t++)try{return C(c,String(process.pid),{flag:"wx"}),{ok:!0}}catch(r){if(r.code!=="EEXIST")throw r;let n=0;try{n=Number(w(c,"utf-8").trim())}catch{}if(n&&n!==process.pid&&z(n))return{ok:!1,holder:n};try{D(c)}catch{}}try{C(c,String(process.pid))}catch{}return{ok:!0}}e(Q,"acquireAgentLock");function T(){try{B(c)&&Number(w(c,"utf-8").trim())===process.pid&&D(c)}catch{}}e(T,"releaseAgentLock");var x=class t extends b{static{e(this,"EvalAgentCommand")}static description="The worker daemon: long-polls /api/work/next, claims jobs, and runs them locally. Installed as a launchd service by `rulemetric service install`.";static examples=["<%= config.bin %> evals agent"];async run(){await this.parse(t),this.requireAuth();let r=Q();if(!r.ok){this.log(`Another rulemetric evals agent is already running (pid ${r.holder}). Refusing to start a second instance.`);return}process.env.PG_APPLICATION_NAME||="rulemetric-agent",S({serviceName:"rulemetric-agent"})&&this.log("[sentry] error capture enabled");let o=h({serviceName:"rulemetric-agent"});o==="otlp"?this.log(`[otel] traces exporting to ${process.env.OTEL_EXPORTER_OTLP_ENDPOINT}`):o==="console"?this.log("[otel] cron spans printing to stdout (set OTEL_EXPORTER_OTLP_ENDPOINT to ship to a backend)"):this.log("[otel] disabled (TELEMETRY_ENABLED=false)"),!P()&&!process.env.RULEMETRIC_API_KEY&&this.error("No auth token found. Run `rulemetric auth login` first."),this.log("Starting thin agent (Phase 4b parallel-run)..."),this.log(`Long-polling /api/work/next. Press Ctrl+C to stop.
2
- `);let a=new AbortController,d=new k({providers:[A()],statePath:v(g(),"transcript-sync-state.json"),importTranscript:e(s=>N("/api/sessions/import",s),"importTranscript"),log:e(s=>this.log(`[transcript-sync] ${s}`),"log")});I(d,{signal:a.signal,log:e(s=>this.log(`[transcript-sync] ${s}`),"log")});let R=e(async s=>{this.log(`
3
- [${s}] shutting down agent...`),a.abort(),await Promise.allSettled([m(2e3),f(),u()]),T(),process.exit(0)},"shutdown");process.on("SIGINT",()=>{R("SIGINT")}),process.on("SIGTERM",()=>{R("SIGTERM")});let _=!1;try{await O({signal:a.signal,log:e(s=>this.log(s),"log"),version:this.config.version,readInstalledVersion:e(()=>{try{return JSON.parse(w(v(this.config.root,"package.json"),"utf-8")).version??null}catch{return null}},"readInstalledVersion"),onVersionDrift:e(()=>{_=!0},"onVersionDrift")}),_&&(this.log("[upgrade] Installed CLI version changed \u2014 restarting worker onto the new code."),await Promise.allSettled([m(2e3),f(),u()]),T(),process.exit(1))}finally{T(),await u()}}};export{x as default};
1
+ import{a as C,b as L,c as D}from"../../chunk-3HSEVXMQ.js";import{e as I}from"../../chunk-VXIRJ7YY.js";import"../../chunk-TIOKY2IA.js";import"../../chunk-LEXVC7VH.js";import"../../chunk-W2MOTOQE.js";import"../../chunk-3FJT6LL4.js";import"../../chunk-DAGDGAPU.js";import"../../chunk-6QKI756T.js";import"../../chunk-RSKQRVPJ.js";import"../../chunk-JEBHSV2M.js";import"../../chunk-UF6US7VU.js";import"../../chunk-L5ORQB25.js";import"../../chunk-E3MQVVH6.js";import"../../chunk-WEF5LPGS.js";import"../../chunk-N7TGRFVZ.js";import{j as d}from"../../chunk-3DLVY7LD.js";import"../../chunk-ZAWAPLIE.js";import"../../chunk-AIEM6VXO.js";import"../../chunk-BZUIMHHT.js";import"../../chunk-QFVGUZDO.js";import"../../chunk-ZL4MZMUK.js";import"../../chunk-5RBLIVBF.js";import"../../chunk-5W55CRBJ.js";import"../../chunk-JR4CWY3J.js";import"../../chunk-ZERPMWRT.js";import"../../chunk-UR6GZGFK.js";import"../../chunk-ZWKKFAIX.js";import"../../chunk-YQ56FQEO.js";import"../../chunk-JGYCLDYF.js";import"../../chunk-TDD7IOV2.js";import"../../chunk-MODXEGCU.js";import"../../chunk-7DFNBRLY.js";import"../../chunk-4VZLIGMS.js";import"../../chunk-3VYV6ZYB.js";import"../../chunk-OZQV5CDT.js";import"../../chunk-6UEFCMR2.js";import"../../chunk-CJRYYV76.js";import"../../chunk-BSJBNOJT.js";import"../../chunk-L2CFSMBC.js";import"../../chunk-Z72VHGV7.js";import"../../chunk-7AZKPX2J.js";import"../../chunk-CDY7TQFZ.js";import"../../chunk-V5MJ4ICC.js";import"../../chunk-WKXTY5AM.js";import"../../chunk-HMMO27U4.js";import{b as k,d as h,e as A}from"../../chunk-VX2GJCQ4.js";import"../../chunk-OPUI7CM2.js";import"../../chunk-MODFTCZY.js";import"../../chunk-PJ3WHQ67.js";import"../../chunk-N7ODX6PO.js";import{a as O}from"../../chunk-PPKPCXJD.js";import"../../chunk-IZTIYR5Q.js";import"../../chunk-V44SRRYR.js";import"../../chunk-U6XEU573.js";import"../../chunk-VLOX6KOM.js";import"../../chunk-3AV3GTMP.js";import{f as b}from"../../chunk-H2ORWIEY.js";import{e as P,l as f}from"../../chunk-O6C2UGBY.js";import{a as e}from"../../chunk-ZLLER3HP.js";import{trace as se,context as ie}from"@opentelemetry/api";import{NodeSDK as j}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as q}from"@opentelemetry/exporter-trace-otlp-http";import{BatchSpanProcessor as F,ConsoleSpanExporter as U,SimpleSpanProcessor as X}from"@opentelemetry/sdk-trace-node";import{Resource as B}from"@opentelemetry/resources";import{ATTR_SERVICE_NAME as H,ATTR_SERVICE_VERSION as K}from"@opentelemetry/semantic-conventions";function $(t){if(!t)return{};let r={};for(let n of t.split(",")){let s=n.indexOf("=");if(s===-1)continue;let i=n.slice(0,s).trim(),a=n.slice(s+1).trim();i&&(r[i]=a)}return r}e($,"parseOtlpHeaders");var p=null;function y(){return process.env.TELEMETRY_ENABLED!=="false"}e(y,"isTelemetryEnabled");function S(t){if(!y()||p!==null)return"off";let r=new B({[H]:t.serviceName,[K]:t.serviceVersion??"0.1.0"}),n=process.env.OTEL_EXPORTER_OTLP_ENDPOINT,s,i;if(n){let a=$(process.env.OTEL_EXPORTER_OTLP_HEADERS),l=new q({url:`${n}/v1/traces`,headers:a});i=new F(l),s="otlp"}else if(process.env.OTEL_SPAN_EXPORT==="none")i=void 0,s="silent";else{let a=new U;i=new X(a),s="console"}return p=new j({resource:r,spanProcessors:i?[i]:[]}),p.start(),s}e(S,"initTelemetry");async function m(){p&&(await p.shutdown(),p=null)}e(m,"shutdownTelemetry");import{SpanKind as he,SpanStatusCode as ye}from"@opentelemetry/api";import{trace as ve,context as we}from"@opentelemetry/api";import*as u from"@sentry/node";var E=!1;function T(t){if(E)return!1;let r=process.env.SENTRY_DSN;return r?(u.init({dsn:r,serverName:t.serviceName,environment:process.env.NODE_ENV??"development",tracesSampleRate:0}),E=!0,!0):!1}e(T,"initSentry");async function g(t){E&&await u.flush(t)}e(g,"flushSentry");import{existsSync as J,mkdirSync as W,readFileSync as R,unlinkSync as V,writeFileSync as M}from"node:fs";import{dirname as z,join as v}from"node:path";import{execFileSync as Q}from"node:child_process";var c=v(f(),"evals-agent.lock");function Z(t){try{return process.kill(t,0),!0}catch(r){return r.code==="EPERM"}}e(Z,"pidAlive");function ee(t){if(!Z(t))return!1;try{let r=Q("ps",["-p",String(t),"-o","command="],{encoding:"utf-8",stdio:["ignore","pipe","ignore"]});return/run\.js\s+evals\s+agent|evals\s+agent/.test(r)}catch{return!0}}e(ee,"isLiveAgent");function te(){try{W(z(c),{recursive:!0})}catch{}for(let t=0;t<2;t++)try{return M(c,String(process.pid),{flag:"wx"}),{ok:!0}}catch(r){if(r.code!=="EEXIST")throw r;let n=0;try{n=Number(R(c,"utf-8").trim())}catch{}if(n&&n!==process.pid&&ee(n))return{ok:!1,holder:n};try{V(c)}catch{}}try{M(c,String(process.pid))}catch{}return{ok:!0}}e(te,"acquireAgentLock");function x(){try{J(c)&&Number(R(c,"utf-8").trim())===process.pid&&V(c)}catch{}}e(x,"releaseAgentLock");var w=class t extends O{static{e(this,"EvalAgentCommand")}static description="The worker daemon: long-polls /api/work/next, claims jobs, and runs them locally. Installed as a launchd service by `rulemetric service install`.";static examples=["<%= config.bin %> evals agent"];async run(){await this.parse(t),this.requireAuth();let r=te();if(!r.ok){this.log(`Another rulemetric evals agent is already running (pid ${r.holder}). Refusing to start a second instance.`);return}process.env.PG_APPLICATION_NAME||="rulemetric-agent",T({serviceName:"rulemetric-agent"})&&this.log("[sentry] error capture enabled");let s=S({serviceName:"rulemetric-agent"});s==="otlp"?this.log(`[otel] traces exporting to ${process.env.OTEL_EXPORTER_OTLP_ENDPOINT}`):s==="console"?this.log("[otel] cron spans printing to stdout (set OTEL_EXPORTER_OTLP_ENDPOINT to ship to a backend)"):this.log("[otel] disabled (TELEMETRY_ENABLED=false)"),!P()&&!process.env.RULEMETRIC_API_KEY&&this.error("No auth token found. Run `rulemetric auth login` first."),this.log("Starting thin agent (Phase 4b parallel-run)..."),this.log(`Long-polling /api/work/next. Press Ctrl+C to stop.
2
+ `);let a=new AbortController,l=await k({configDir:f()});if(this.log(`[transcript-sync] Codex capture: ${h(l)}`),l.error)this.log(`[transcript-sync] WARNING: ${l.error}`);else if(l.mode==="allowlist")for(let o of l.roots)this.log(`[transcript-sync] \u2022 ${o}`);let G=new C({providers:[L(void 0,void 0,A({configDir:f(),onChange:e(o=>this.log(`[transcript-sync] Codex capture changed: ${h(o)}`),"onChange")}))],statePath:v(f(),"transcript-sync-state.json"),importTranscript:e(o=>b("/api/sessions/import",o),"importTranscript"),log:e(o=>this.log(`[transcript-sync] ${o}`),"log")});D(G,{signal:a.signal,log:e(o=>this.log(`[transcript-sync] ${o}`),"log")});let N=e(async o=>{this.log(`
3
+ [${o}] shutting down agent...`),a.abort(),await Promise.allSettled([g(2e3),m(),d()]),x(),process.exit(0)},"shutdown");process.on("SIGINT",()=>{N("SIGINT")}),process.on("SIGTERM",()=>{N("SIGTERM")});let _=!1;try{await I({signal:a.signal,log:e(o=>this.log(o),"log"),version:this.config.version,readInstalledVersion:e(()=>{try{return JSON.parse(R(v(this.config.root,"package.json"),"utf-8")).version??null}catch{return null}},"readInstalledVersion"),onVersionDrift:e(()=>{_=!0},"onVersionDrift")}),_&&(this.log("[upgrade] Installed CLI version changed \u2014 restarting worker onto the new code."),await Promise.allSettled([g(2e3),m(),d()]),x(),process.exit(1))}finally{x(),await d()}}};export{w as default};
@@ -1 +1 @@
1
- import{a as pt}from"../../chunk-6XEC6474.js";import{B as ut,E as dt,H as gt,I as ht,e as z,f as Q,g as Z,i as tt,j as et,k as st,n as ot,p as it,q as rt,r as at,u as nt,v as lt,y as ct}from"../../chunk-VLXCQ2DC.js";import{a as q,b as W,c as m,d as J,g as A}from"../../chunk-I2DZGJRR.js";import{c as s,g as X,h as D,i as F,k as O,l as V}from"../../chunk-MVM6OGAF.js";import{e as B}from"../../chunk-KKDHD4XA.js";import"../../chunk-SZ62CP55.js";import{a as f}from"../../chunk-PJ3WHQ67.js";import"../../chunk-N7ODX6PO.js";import{a as Y}from"../../chunk-XICLXXRH.js";import{a as M}from"../../chunk-PPKPCXJD.js";import"../../chunk-IZTIYR5Q.js";import{c as N}from"../../chunk-V44SRRYR.js";import"../../chunk-U6XEU573.js";import{b as L}from"../../chunk-VLOX6KOM.js";import"../../chunk-3AV3GTMP.js";import"../../chunk-H2ORWIEY.js";import{a as G}from"../../chunk-O6C2UGBY.js";import{a as v}from"../../chunk-ZLLER3HP.js";import{Flags as w}from"@oclif/core";import{execFileSync as ft,spawnSync as l}from"node:child_process";import{existsSync as c}from"node:fs";import{homedir as mt}from"node:os";import{join as _,dirname as Ct}from"node:path";function vt(){try{let d=ft("/bin/sh",["-c","command -v rulemetric"],{encoding:"utf-8"}).trim();if(d&&c(d))return Ct(d)}catch{}}v(vt,"resolveHookBinDir");function wt(){try{return ft("launchctl",["list","com.rulemetric.gateway"],{stdio:["ignore","pipe","pipe"]}),!0}catch{return!1}}v(wt,"isGatewayLaunchdManaged");var o=_(mt(),".mitmproxy","mitmproxy-ca-cert.pem"),R=class d extends M{static{v(this,"HooksInstall")}static description="Install session tracking hooks and context capture proxy for Claude Code";static examples=["<%= config.bin %> hooks install","<%= config.bin %> hooks install --global","<%= config.bin %> hooks install --no-proxy"];static flags={"no-proxy":w.boolean({default:!1,description:"Skip proxy/gateway configuration (hooks only)"}),"project-dir":w.string({description:"Project directory to install hooks in (defaults to cwd)"}),global:w.boolean({char:"g",default:!1,description:"Install hooks globally (~/.claude/settings.json) so all projects are tracked"}),"statusline-usage":w.boolean({default:!1,description:"Install the statusline shim so Claude Code pushes live rate_limits on every refresh (makes /usage rings update in real time without needing the proxy)"})};async run(){let{flags:i}=await this.parse(d),r=i.global,n=r?mt():i["project-dir"]??process.cwd(),x=_(n,".claude"),y=st(x,r),K=r?"settings.json":"settings.local.json",k=i["no-proxy"],P=!1,g=k?"no_proxy_flag":void 0,h=!1,S=[];r&&this.log(`Installing hooks globally \u2192 ${y}`);let{settings:a,recoveredBackup:$}=z(y);if($&&this.log(`[!!] ${y} was not valid JSON \u2014 backed it up to ${$} and continued with a fresh file.`),!r){let e=ot(_(x,"settings.json"),s);e.migrated&&(this.log("[OK] Moved RuleMetric machine-local state out of the shared .claude/settings.json (it now lives in .claude/settings.local.json)"),e.backupPath&&this.log(` Previous shared settings backed up \u2192 ${e.backupPath}`),e.removedStatusline&&c(q)&&!A(a)&&(a.statusLine=m))}let I=G();(I.accessToken||process.env.RULEMETRIC_ACCESS_TOKEN)&&!I.apiKey&&!process.env.RULEMETRIC_API_KEY&&await Y(this.log.bind(this),this.warn.bind(this),this.config.version);let yt=tt(a),C=vt();if(Z(a,C),this.log(yt?`[OK] Refreshed RuleMetric Claude Code session hooks \u2192 .claude/${K}`:`[OK] Installed RuleMetric Claude Code session hooks \u2192 .claude/${K}`),!k){let e=!!f("mitmdump");if(e||(this.log(""),this.log("[!!] mitmproxy not found \u2014 installing..."),S=["brew","pipx","uv"].filter(t=>!!f(t)),e=this.installMitmproxy(),e||(this.log("[!!] Could not install mitmproxy. Skipping proxy config."),this.log(" Install manually: pipx install mitmproxy"),this.log(" Then re-run: rulemetric hooks install"))),c(o))this.log("[OK] CA certificate found");else{this.log("[..] Generating CA certificate...");let t=f("mitmdump");t&&(l(t,["--set","listen_port=0","-q"],{timeout:5e3,stdio:"ignore"}),c(o)?this.log(`[OK] CA certificate generated at ${o}`):this.log("[!!] Could not generate CA cert"))}let u=c(o);u&&!this.isCACertTrusted()?(this.log("[..] Installing CA certificate into system trust store (requires admin)..."),h=this.trustCACert(),h?this.log("[OK] CA certificate trusted by system"):(this.log("[!!] Could not install CA cert automatically."),process.platform==="darwin"?this.log(` Run manually: sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ${o}`):process.platform==="linux"&&this.log(` Run manually: sudo cp ${o} /usr/local/share/ca-certificates/mitmproxy-ca.crt && sudo update-ca-certificates`))):u&&(this.log("[OK] CA certificate already trusted"),h=!0);let p=wt(),b=await X(s);if(p)this.log("[OK] Gateway managed by launchd (com.rulemetric.gateway)");else if(b.status==="ours-live")this.log("[OK] Gateway already running");else if(b.status==="foreign"&&F()){this.log("[..] Restarting gateway (running build predates the identity handshake)..."),V(),await new Promise(t=>setTimeout(t,500));try{let t=O(this.config.version);this.log(`[OK] Gateway restarted (PID ${t})`)}catch(t){this.log(`[!!] Could not restart gateway: ${t.message}`)}}else if(b.status!=="foreign")try{let t=O(this.config.version);this.log(`[OK] Gateway started (PID ${t})`)}catch(t){this.log(`[!!] Could not start gateway: ${t.message}`)}let j=await D(s,{timeoutMs:4e3});if(!u)g=e?"ca_not_generated":S.length===0?"no_package_manager":"mitmproxy_install_failed",this.log("[!!] No CA certificate \u2014 NOT setting HTTPS_PROXY (capture stays hooks-only)."),this.log(" Install mitmproxy, then re-run: rulemetric hooks install");else if(j.status==="foreign"&&!p){g="gateway_port_foreign";let t=B(s);this.log(`[!!] Port :${s} is held by a process that is NOT the RuleMetric gateway \u2014 NOT setting HTTPS_PROXY.`),this.log(t?` Holding the port: ${t}`:` Could not identify the holder (try: lsof -nP -iTCP:${s} -sTCP:LISTEN)`),this.log(" Capture still works via hooks. Free the port, then re-run: rulemetric hooks install")}else if(j.status==="dead")g="gateway_not_live",this.log(`[!!] Gateway is not answering on :${s} \u2014 NOT setting HTTPS_PROXY.`),this.log(" Capture still works via hooks. To enable deep (proxy) capture later:"),this.log(" rulemetric proxy start && rulemetric hooks install");else{let t=a.env??{};t.HTTPS_PROXY=`http://localhost:${s}`,t.NO_PROXY=et,c(o)&&(t.NODE_EXTRA_CA_CERTS=o),a.env=t,P=!0,this.log(`[OK] HTTPS_PROXY set to gateway on :${s} (verified live)`),nt(n,s,o)&&this.log("[OK] Cursor proxy configured \u2192 .cursor/settings.json"),lt(n,s,o)&&this.log("[OK] VS Code workspace proxy configured \u2192 .vscode/settings.json"),ut(s)&&this.log("[OK] VS Code user proxy configured \u2192 ~/Library/Application Support/Code/User/settings.json"),dt(s)&&this.log("[OK] Cursor user proxy configured \u2192 ~/Library/Application Support/Cursor/User/settings.json"),h?ct(s,o)&&this.log("[OK] HTTPS_PROXY set for GUI apps (launchctl + LaunchAgent)"):(g="ca_untrusted",this.log("[!!] CA not trusted \u2014 skipping machine-wide GUI proxy (would break native-app TLS)."),this.log(" Claude Code capture is unaffected. Trust the CA + re-run to enable cross-app capture."))}}if(!r){let e=it(n,C);e&&this.log(`[OK] Cursor hooks configured \u2192 ${e}`);let u=at(n,C);u&&this.log(`[OK] Copilot Agent hooks configured \u2192 ${u}`);let p=gt(n);p&&this.log(`[OK] Antigravity workspace hooks configured \u2192 ${p}`)}let E=ht();E&&this.log(`[OK] Antigravity user hooks configured \u2192 ${E}`);let H=rt(C);if(H&&this.log(`[OK] Cursor user hooks configured \u2192 ${H}`),i["statusline-usage"])if(A(a))this.log("[OK] Statusline shim already installed");else{let e=J(a);W(e),a.statusLine=m,e?this.log(`[OK] Statusline shim installed \u2192 ${m.command} (chains to: ${e})`):this.log(`[OK] Statusline shim installed \u2192 ${m.command}`),this.log(" Rate-limit rings will now update on Claude Code's statusline refresh cadence")}let{backupPath:U}=Q(y,a);U&&this.log(`[OK] Backed up previous settings \u2192 ${U}`),this.log(""),this.log("Setup complete! Next steps:"),r?(this.log(" 1. Install always-on API: rulemetric service install"),this.log(" 2. Start Claude Code in any project \u2014 all sessions tracked automatically"),this.log(""),this.log("The API service starts on login and restarts on crash.")):k?(this.log(" 1. Start Claude Code \u2014 sessions are captured by the hooks just installed"),this.log(""),this.log("No proxy means no mitmproxy / CA cert needed. The hooks capture full"),this.log("sessions on their own; re-run without --no-proxy to also capture system"),this.log("prompts for instruction-effectiveness linking.")):(this.log(" 1. Start Claude Code \u2014 sessions are captured by hooks immediately"),P?this.log(" 2. Full-depth capture is ON \u2014 the proxy is live on :"+s+"."):this.log(" 2. Enable full depth (proxy not yet live): rulemetric proxy start"),this.log(""),this.log("Hooks capture every session (lifecycle + transcript). The proxy adds the"),this.log("rendered system prompt (instruction-effectiveness linking) + cross-tool capture."),this.log(""),this.log("To capture traffic from other clients (Python, curl, etc.):"),this.log(' eval "$(rulemetric proxy env --all)"')),await L(),N("hooks_installed",{proxy:P,caTrusted:h,reason:g,installers:S,global:r},this.config.version);let T=await pt();this.log(""),T.available?this.log(`[OK] tmux found${T.version?` (v${T.version})`:""} \u2014 live send enabled`):this.log("[!!] tmux not found \u2014 live send will fall back to opening Terminal. Install with: brew install tmux")}installMitmproxy(){for(let[i,r]of[["pipx",["install","mitmproxy"]],["uv",["tool","install","mitmproxy"]],["brew",["install","mitmproxy"]]]){let n=f(i);if(n&&(this.log(` Installing via ${i}...`),l(n,[...r],{stdio:"inherit"}).status===0))return!0}return!1}isCACertTrusted(){return process.platform==="darwin"?l("security",["verify-cert","-c",o],{stdio:"pipe"}).status===0:process.platform==="linux"?c("/usr/local/share/ca-certificates/mitmproxy-ca.crt"):process.platform==="win32"?l("certutil",["-verify",o],{stdio:"pipe"}).status===0:!1}trustCACert(){return process.platform==="darwin"?l("sudo",["security","add-trusted-cert","-d","-r","trustRoot","-k","/Library/Keychains/System.keychain",o],{stdio:"inherit"}).status===0:process.platform==="linux"?l("sudo",["cp",o,"/usr/local/share/ca-certificates/mitmproxy-ca.crt"],{stdio:"inherit"}).status!==0?!1:l("sudo",["update-ca-certificates"],{stdio:"inherit"}).status===0:process.platform==="win32"?l("certutil",["-addstore","-f","Root",o],{stdio:"inherit"}).status===0:!1}};export{R as default};
1
+ import{b as mt}from"../../chunk-UGIUIZ2V.js";import{a as yt}from"../../chunk-6XEC6474.js";import{B as gt,E as ht,H as pt,I as ft,e as Z,f as tt,g as et,i as st,j as ot,k as it,n as rt,p as at,q as nt,r as lt,u as ct,v as ut,y as dt}from"../../chunk-VLXCQ2DC.js";import{a as J,b as z,c as m,d as Q,g as _}from"../../chunk-I2DZGJRR.js";import{c as s,g as F,h as V,i as q,k as A,l as W}from"../../chunk-MVM6OGAF.js";import{e as D}from"../../chunk-KKDHD4XA.js";import"../../chunk-SZ62CP55.js";import{a as f}from"../../chunk-PJ3WHQ67.js";import"../../chunk-N7ODX6PO.js";import{a as X}from"../../chunk-XICLXXRH.js";import{a as B}from"../../chunk-PPKPCXJD.js";import"../../chunk-IZTIYR5Q.js";import{c as Y}from"../../chunk-V44SRRYR.js";import"../../chunk-U6XEU573.js";import{b as M}from"../../chunk-VLOX6KOM.js";import"../../chunk-3AV3GTMP.js";import"../../chunk-H2ORWIEY.js";import{a as L}from"../../chunk-O6C2UGBY.js";import{a as w}from"../../chunk-ZLLER3HP.js";import{Flags as x}from"@oclif/core";import{execFileSync as Ct,spawnSync as l}from"node:child_process";import{existsSync as u}from"node:fs";import{homedir as vt}from"node:os";import{join as R,dirname as xt}from"node:path";function kt(){try{let g=Ct("/bin/sh",["-c","command -v rulemetric"],{encoding:"utf-8"}).trim();if(g&&u(g))return xt(g)}catch{}}w(kt,"resolveHookBinDir");function Pt(){try{return Ct("launchctl",["list","com.rulemetric.gateway"],{stdio:["ignore","pipe","pipe"]}),!0}catch{return!1}}w(Pt,"isGatewayLaunchdManaged");var o=R(vt(),".mitmproxy","mitmproxy-ca-cert.pem"),K=class g extends B{static{w(this,"HooksInstall")}static description="Install session tracking hooks and context capture proxy for Claude Code";static examples=["<%= config.bin %> hooks install","<%= config.bin %> hooks install --global","<%= config.bin %> hooks install --no-proxy"];static flags={"no-proxy":x.boolean({default:!1,description:"Skip proxy/gateway configuration (hooks only)"}),"project-dir":x.string({description:"Project directory to install hooks in (defaults to cwd)"}),global:x.boolean({char:"g",default:!1,description:"Install hooks globally (~/.claude/settings.json) so all projects are tracked"}),"statusline-usage":x.boolean({default:!1,description:"Install the statusline shim so Claude Code pushes live rate_limits on every refresh (makes /usage rings update in real time without needing the proxy)"})};async run(){let{flags:i}=await this.parse(g),r=i.global,a=r?vt():i["project-dir"]??process.cwd(),k=R(a,".claude"),y=it(k,r),$=r?"settings.json":"settings.local.json",P=i["no-proxy"],S=!1,h=P?"no_proxy_flag":void 0,p=!1,T=[];r&&this.log(`Installing hooks globally \u2192 ${y}`);let{settings:n,recoveredBackup:I}=Z(y);if(I&&this.log(`[!!] ${y} was not valid JSON \u2014 backed it up to ${I} and continued with a fresh file.`),!r){let e=rt(R(k,"settings.json"),s);e.migrated&&(this.log("[OK] Moved RuleMetric machine-local state out of the shared .claude/settings.json (it now lives in .claude/settings.local.json)"),e.backupPath&&this.log(` Previous shared settings backed up \u2192 ${e.backupPath}`),e.removedStatusline&&u(J)&&!_(n)&&(n.statusLine=m))}let E=L();(E.accessToken||process.env.RULEMETRIC_ACCESS_TOKEN)&&!E.apiKey&&!process.env.RULEMETRIC_API_KEY&&await X(this.log.bind(this),this.warn.bind(this),this.config.version);let wt=st(n),C=kt();if(et(n,C),this.log(wt?`[OK] Refreshed RuleMetric Claude Code session hooks \u2192 .claude/${$}`:`[OK] Installed RuleMetric Claude Code session hooks \u2192 .claude/${$}`),!P){let e=!!f("mitmdump");if(e||(this.log(""),this.log("[!!] mitmproxy not found \u2014 installing..."),T=["brew","pipx","uv"].filter(t=>!!f(t)),e=this.installMitmproxy(),e||(this.log("[!!] Could not install mitmproxy. Skipping proxy config."),this.log(" Install manually: pipx install mitmproxy"),this.log(" Then re-run: rulemetric hooks install"))),u(o))this.log("[OK] CA certificate found");else{this.log("[..] Generating CA certificate...");let t=f("mitmdump");t&&(l(t,["--set","listen_port=0","-q"],{timeout:5e3,stdio:"ignore"}),u(o)?this.log(`[OK] CA certificate generated at ${o}`):this.log("[!!] Could not generate CA cert"))}let c=u(o);c&&!this.isCACertTrusted()?(this.log("[..] Installing CA certificate into system trust store (requires admin)..."),p=this.trustCACert(),p?this.log("[OK] CA certificate trusted by system"):(this.log("[!!] Could not install CA cert automatically."),process.platform==="darwin"?this.log(` Run manually: sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ${o}`):process.platform==="linux"&&this.log(` Run manually: sudo cp ${o} /usr/local/share/ca-certificates/mitmproxy-ca.crt && sudo update-ca-certificates`))):c&&(this.log("[OK] CA certificate already trusted"),p=!0);let d=Pt(),O=await F(s);if(d)this.log("[OK] Gateway managed by launchd (com.rulemetric.gateway)");else if(O.status==="ours-live")this.log("[OK] Gateway already running");else if(O.status==="foreign"&&q()){this.log("[..] Restarting gateway (running build predates the identity handshake)..."),W(),await new Promise(t=>setTimeout(t,500));try{let t=A(this.config.version);this.log(`[OK] Gateway restarted (PID ${t})`)}catch(t){this.log(`[!!] Could not restart gateway: ${t.message}`)}}else if(O.status!=="foreign")try{let t=A(this.config.version);this.log(`[OK] Gateway started (PID ${t})`)}catch(t){this.log(`[!!] Could not start gateway: ${t.message}`)}let G=await V(s,{timeoutMs:4e3});if(!c)h=e?"ca_not_generated":T.length===0?"no_package_manager":"mitmproxy_install_failed",this.log("[!!] No CA certificate \u2014 NOT setting HTTPS_PROXY (capture stays hooks-only)."),this.log(" Install mitmproxy, then re-run: rulemetric hooks install");else if(G.status==="foreign"&&!d){h="gateway_port_foreign";let t=D(s);this.log(`[!!] Port :${s} is held by a process that is NOT the RuleMetric gateway \u2014 NOT setting HTTPS_PROXY.`),this.log(t?` Holding the port: ${t}`:` Could not identify the holder (try: lsof -nP -iTCP:${s} -sTCP:LISTEN)`),this.log(" Capture still works via hooks. Free the port, then re-run: rulemetric hooks install")}else if(G.status==="dead")h="gateway_not_live",this.log(`[!!] Gateway is not answering on :${s} \u2014 NOT setting HTTPS_PROXY.`),this.log(" Capture still works via hooks. To enable deep (proxy) capture later:"),this.log(" rulemetric proxy start && rulemetric hooks install");else{let t=n.env??{};t.HTTPS_PROXY=`http://localhost:${s}`,t.NO_PROXY=ot,u(o)&&(t.NODE_EXTRA_CA_CERTS=o),n.env=t,S=!0,this.log(`[OK] HTTPS_PROXY set to gateway on :${s} (verified live)`),ct(a,s,o)&&this.log("[OK] Cursor proxy configured \u2192 .cursor/settings.json"),ut(a,s,o)&&this.log("[OK] VS Code workspace proxy configured \u2192 .vscode/settings.json"),gt(s)&&this.log("[OK] VS Code user proxy configured \u2192 ~/Library/Application Support/Code/User/settings.json"),ht(s)&&this.log("[OK] Cursor user proxy configured \u2192 ~/Library/Application Support/Cursor/User/settings.json"),p?dt(s,o)&&this.log("[OK] HTTPS_PROXY set for GUI apps (launchctl + LaunchAgent)"):(h="ca_untrusted",this.log("[!!] CA not trusted \u2014 skipping machine-wide GUI proxy (would break native-app TLS)."),this.log(" Claude Code capture is unaffected. Trust the CA + re-run to enable cross-app capture."))}}let v=[];if(!r){let e=at(a,C);e&&(this.log(`[OK] Cursor hooks configured \u2192 ${e}`),v.push(e));let c=lt(a,C);c&&(this.log(`[OK] Copilot Agent hooks configured \u2192 ${c}`),v.push(c));let d=pt(a);d&&(this.log(`[OK] Antigravity workspace hooks configured \u2192 ${d}`),v.push(d))}let H=ft();H&&this.log(`[OK] Antigravity user hooks configured \u2192 ${H}`);let U=nt(C);if(U&&this.log(`[OK] Cursor user hooks configured \u2192 ${U}`),i["statusline-usage"])if(_(n))this.log("[OK] Statusline shim already installed");else{let e=Q(n);z(e),n.statusLine=m,e?this.log(`[OK] Statusline shim installed \u2192 ${m.command} (chains to: ${e})`):this.log(`[OK] Statusline shim installed \u2192 ${m.command}`),this.log(" Rate-limit rings will now update on Claude Code's statusline refresh cadence")}let{backupPath:N}=tt(y,n);N&&this.log(`[OK] Backed up previous settings \u2192 ${N}`),this.log(""),this.log("Setup complete! Next steps:"),r?(this.log(" 1. Install always-on API: rulemetric service install"),this.log(" 2. Start Claude Code in any project \u2014 all sessions tracked automatically"),this.log(""),this.log("The API service starts on login and restarts on crash.")):P?(this.log(" 1. Start Claude Code \u2014 sessions are captured by the hooks just installed"),this.log(""),this.log("No proxy means no mitmproxy / CA cert needed. The hooks capture full"),this.log("sessions on their own; re-run without --no-proxy to also capture system"),this.log("prompts for instruction-effectiveness linking.")):(this.log(" 1. Start Claude Code \u2014 sessions are captured by hooks immediately"),S?this.log(" 2. Full-depth capture is ON \u2014 the proxy is live on :"+s+"."):this.log(" 2. Enable full depth (proxy not yet live): rulemetric proxy start"),this.log(""),this.log("Hooks capture every session (lifecycle + transcript). The proxy adds the"),this.log("rendered system prompt (instruction-effectiveness linking) + cross-tool capture."),this.log(""),this.log("To capture traffic from other clients (Python, curl, etc.):"),this.log(' eval "$(rulemetric proxy env --all)"'));let j=mt(a,v);j&&this.log(j),await M(),Y("hooks_installed",{proxy:S,caTrusted:p,reason:h,installers:T,global:r},this.config.version);let b=await yt();this.log(""),b.available?this.log(`[OK] tmux found${b.version?` (v${b.version})`:""} \u2014 live send enabled`):this.log("[!!] tmux not found \u2014 live send will fall back to opening Terminal. Install with: brew install tmux")}installMitmproxy(){for(let[i,r]of[["pipx",["install","mitmproxy"]],["uv",["tool","install","mitmproxy"]],["brew",["install","mitmproxy"]]]){let a=f(i);if(a&&(this.log(` Installing via ${i}...`),l(a,[...r],{stdio:"inherit"}).status===0))return!0}return!1}isCACertTrusted(){return process.platform==="darwin"?l("security",["verify-cert","-c",o],{stdio:"pipe"}).status===0:process.platform==="linux"?u("/usr/local/share/ca-certificates/mitmproxy-ca.crt"):process.platform==="win32"?l("certutil",["-verify",o],{stdio:"pipe"}).status===0:!1}trustCACert(){return process.platform==="darwin"?l("sudo",["security","add-trusted-cert","-d","-r","trustRoot","-k","/Library/Keychains/System.keychain",o],{stdio:"inherit"}).status===0:process.platform==="linux"?l("sudo",["cp",o,"/usr/local/share/ca-certificates/mitmproxy-ca.crt"],{stdio:"inherit"}).status!==0?!1:l("sudo",["update-ca-certificates"],{stdio:"inherit"}).status===0:process.platform==="win32"?l("certutil",["-addstore","-f","Root",o],{stdio:"inherit"}).status===0:!1}};export{K as default};
@@ -1 +1 @@
1
- import{a,b,c,d,e}from"../chunk-DTMLVFNM.js";import"../chunk-TIOKY2IA.js";import"../chunk-W2MOTOQE.js";import"../chunk-3FJT6LL4.js";import"../chunk-DAGDGAPU.js";import"../chunk-6QKI756T.js";import"../chunk-RSKQRVPJ.js";import"../chunk-JEBHSV2M.js";import"../chunk-L5ORQB25.js";import"../chunk-Q76LGSIU.js";import"../chunk-WEF5LPGS.js";import"../chunk-N7TGRFVZ.js";import"../chunk-3DLVY7LD.js";import"../chunk-ZAWAPLIE.js";import"../chunk-AIEM6VXO.js";import"../chunk-BZUIMHHT.js";import"../chunk-QFVGUZDO.js";import"../chunk-ZL4MZMUK.js";import"../chunk-5RBLIVBF.js";import"../chunk-LEXVC7VH.js";import"../chunk-5W55CRBJ.js";import"../chunk-JR4CWY3J.js";import"../chunk-ZERPMWRT.js";import"../chunk-UR6GZGFK.js";import"../chunk-UF6US7VU.js";import"../chunk-YQ56FQEO.js";import"../chunk-JGYCLDYF.js";import"../chunk-ZWKKFAIX.js";import"../chunk-TDD7IOV2.js";import"../chunk-MODXEGCU.js";import"../chunk-7DFNBRLY.js";import"../chunk-4VZLIGMS.js";import"../chunk-3VYV6ZYB.js";import"../chunk-OZQV5CDT.js";import"../chunk-6UEFCMR2.js";import"../chunk-CJRYYV76.js";import"../chunk-BSJBNOJT.js";import"../chunk-L2CFSMBC.js";import"../chunk-Z72VHGV7.js";import"../chunk-7AZKPX2J.js";import"../chunk-CDY7TQFZ.js";import"../chunk-V5MJ4ICC.js";import"../chunk-WKXTY5AM.js";import"../chunk-HMMO27U4.js";import"../chunk-OPUI7CM2.js";import"../chunk-MODFTCZY.js";import"../chunk-PJ3WHQ67.js";import"../chunk-N7ODX6PO.js";import"../chunk-H2ORWIEY.js";import"../chunk-O6C2UGBY.js";import"../chunk-ZLLER3HP.js";export{b as CAPABILITIES,a as HANDLERS,c as availableCapabilities,d as pollForWork,e as runAgentLoop};
1
+ import{a,b,c,d,e}from"../chunk-VXIRJ7YY.js";import"../chunk-TIOKY2IA.js";import"../chunk-LEXVC7VH.js";import"../chunk-W2MOTOQE.js";import"../chunk-3FJT6LL4.js";import"../chunk-DAGDGAPU.js";import"../chunk-6QKI756T.js";import"../chunk-RSKQRVPJ.js";import"../chunk-JEBHSV2M.js";import"../chunk-UF6US7VU.js";import"../chunk-L5ORQB25.js";import"../chunk-E3MQVVH6.js";import"../chunk-WEF5LPGS.js";import"../chunk-N7TGRFVZ.js";import"../chunk-3DLVY7LD.js";import"../chunk-ZAWAPLIE.js";import"../chunk-AIEM6VXO.js";import"../chunk-BZUIMHHT.js";import"../chunk-QFVGUZDO.js";import"../chunk-ZL4MZMUK.js";import"../chunk-5RBLIVBF.js";import"../chunk-5W55CRBJ.js";import"../chunk-JR4CWY3J.js";import"../chunk-ZERPMWRT.js";import"../chunk-UR6GZGFK.js";import"../chunk-ZWKKFAIX.js";import"../chunk-YQ56FQEO.js";import"../chunk-JGYCLDYF.js";import"../chunk-TDD7IOV2.js";import"../chunk-MODXEGCU.js";import"../chunk-7DFNBRLY.js";import"../chunk-4VZLIGMS.js";import"../chunk-3VYV6ZYB.js";import"../chunk-OZQV5CDT.js";import"../chunk-6UEFCMR2.js";import"../chunk-CJRYYV76.js";import"../chunk-BSJBNOJT.js";import"../chunk-L2CFSMBC.js";import"../chunk-Z72VHGV7.js";import"../chunk-7AZKPX2J.js";import"../chunk-CDY7TQFZ.js";import"../chunk-V5MJ4ICC.js";import"../chunk-WKXTY5AM.js";import"../chunk-HMMO27U4.js";import"../chunk-OPUI7CM2.js";import"../chunk-MODFTCZY.js";import"../chunk-PJ3WHQ67.js";import"../chunk-N7ODX6PO.js";import"../chunk-H2ORWIEY.js";import"../chunk-O6C2UGBY.js";import"../chunk-ZLLER3HP.js";export{b as CAPABILITIES,a as HANDLERS,c as availableCapabilities,d as pollForWork,e as runAgentLoop};
@@ -0,0 +1 @@
1
+ import{a,b,c,d,e}from"../chunk-VX2GJCQ4.js";import"../chunk-ZLLER3HP.js";export{a as CAPTURE_CONFIG_FILENAME,e as createCodexScopeWatcher,d as describeCodexCaptureScope,c as isPathInScope,b as loadCodexCaptureScope};
@@ -1 +1 @@
1
- import{a,b}from"../../chunk-Q76LGSIU.js";import"../../chunk-MODFTCZY.js";import"../../chunk-PJ3WHQ67.js";import"../../chunk-N7ODX6PO.js";import"../../chunk-H2ORWIEY.js";import"../../chunk-O6C2UGBY.js";import"../../chunk-ZLLER3HP.js";export{b as distillMemories,a as selectNewMemories};
1
+ import{a,b}from"../../chunk-E3MQVVH6.js";import"../../chunk-MODFTCZY.js";import"../../chunk-PJ3WHQ67.js";import"../../chunk-N7ODX6PO.js";import"../../chunk-H2ORWIEY.js";import"../../chunk-O6C2UGBY.js";import"../../chunk-ZLLER3HP.js";export{b as distillMemories,a as selectNewMemories};
@@ -1 +1 @@
1
- import{a as l}from"../chunk-OVZZUI33.js";import{a as g}from"../chunk-L5ORQB25.js";import"../chunk-N7TGRFVZ.js";import"../chunk-3DLVY7LD.js";import"../chunk-ZL4MZMUK.js";import{a as u}from"../chunk-UF6US7VU.js";import"../chunk-TDD7IOV2.js";import"../chunk-MODXEGCU.js";import{a as i}from"../chunk-GY5IJ4RS.js";import"../chunk-MODFTCZY.js";import"../chunk-PJ3WHQ67.js";import"../chunk-N7ODX6PO.js";import"../chunk-H2ORWIEY.js";import"../chunk-O6C2UGBY.js";import{a as o}from"../chunk-ZLLER3HP.js";var c={cron_check_changelog:o(async(r,e)=>{let s=await l({logger:{info:o(t=>e.logger.info(t),"info"),warn:o(t=>e.logger.warn(t),"warn")}});e.logger.info(`[cron_check_changelog] result: created=${s.createdCount} skipped=${s.skippedCount}`)},"cron_check_changelog"),cron_suggest_instructions:o((r,e)=>g(r,e),"cron_suggest_instructions"),cron_refresh_skills:o((r,e)=>u(r,e),"cron_refresh_skills")},p=i.map(r=>{let e=c[r.name];if(!e)throw new Error(`manual-tasks: no handler registered for "${r.name}"`);return{...r,handler:e}});async function m(r){let e=p.find(n=>n.name===r);if(!e)return{name:r,durationMs:0,ok:!1,error:`Unknown task: ${r}`,logs:[]};let s=[],t={logger:{info:o(n=>s.push({level:"info",message:n}),"info"),warn:o(n=>s.push({level:"warn",message:n}),"warn"),error:o(n=>s.push({level:"error",message:n}),"error")},job:{attempts:0}},a=Date.now();try{return await e.handler({},t),{name:r,durationMs:Date.now()-a,ok:!0,logs:s}}catch(n){return{name:r,durationMs:Date.now()-a,ok:!1,error:n instanceof Error?n.message:String(n),logs:s}}}o(m,"runManualTask");export{p as MANUAL_TASKS,m as runManualTask};
1
+ import{a as l}from"../chunk-OVZZUI33.js";import{a as u}from"../chunk-UF6US7VU.js";import{a as g}from"../chunk-L5ORQB25.js";import"../chunk-N7TGRFVZ.js";import"../chunk-3DLVY7LD.js";import"../chunk-ZL4MZMUK.js";import"../chunk-TDD7IOV2.js";import"../chunk-MODXEGCU.js";import{a as i}from"../chunk-GY5IJ4RS.js";import"../chunk-MODFTCZY.js";import"../chunk-PJ3WHQ67.js";import"../chunk-N7ODX6PO.js";import"../chunk-H2ORWIEY.js";import"../chunk-O6C2UGBY.js";import{a as o}from"../chunk-ZLLER3HP.js";var c={cron_check_changelog:o(async(r,e)=>{let s=await l({logger:{info:o(t=>e.logger.info(t),"info"),warn:o(t=>e.logger.warn(t),"warn")}});e.logger.info(`[cron_check_changelog] result: created=${s.createdCount} skipped=${s.skippedCount}`)},"cron_check_changelog"),cron_suggest_instructions:o((r,e)=>g(r,e),"cron_suggest_instructions"),cron_refresh_skills:o((r,e)=>u(r,e),"cron_refresh_skills")},p=i.map(r=>{let e=c[r.name];if(!e)throw new Error(`manual-tasks: no handler registered for "${r.name}"`);return{...r,handler:e}});async function m(r){let e=p.find(n=>n.name===r);if(!e)return{name:r,durationMs:0,ok:!1,error:`Unknown task: ${r}`,logs:[]};let s=[],t={logger:{info:o(n=>s.push({level:"info",message:n}),"info"),warn:o(n=>s.push({level:"warn",message:n}),"warn"),error:o(n=>s.push({level:"error",message:n}),"error")},job:{attempts:0}},a=Date.now();try{return await e.handler({},t),{name:r,durationMs:Date.now()-a,ok:!0,logs:s}}catch(n){return{name:r,durationMs:Date.now()-a,ok:!1,error:n instanceof Error?n.message:String(n),logs:s}}}o(m,"runManualTask");export{p as MANUAL_TASKS,m as runManualTask};
@@ -0,0 +1 @@
1
+ import{a,b}from"../chunk-UGIUIZ2V.js";import"../chunk-ZLLER3HP.js";export{b as describeUntrackedRepoArtifacts,a as untrackedRepoArtifacts};
@@ -1 +1 @@
1
- import{a,b,c}from"../chunk-47HDPK73.js";import"../chunk-ZLLER3HP.js";export{a as TranscriptSync,b as createCodexTranscriptProvider,c as startTranscriptSync};
1
+ import{a,b,c}from"../chunk-3HSEVXMQ.js";import"../chunk-VX2GJCQ4.js";import"../chunk-ZLLER3HP.js";export{a as TranscriptSync,b as createCodexTranscriptProvider,c as startTranscriptSync};
@@ -4334,5 +4334,5 @@
4334
4334
  ]
4335
4335
  }
4336
4336
  },
4337
- "version": "0.7.32"
4337
+ "version": "0.7.34"
4338
4338
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulemetric/cli",
3
- "version": "0.7.32",
3
+ "version": "0.7.34",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -74,9 +74,9 @@
74
74
  "postgres": "^3.4.0",
75
75
  "react": "^19.0.0",
76
76
  "zod": "^3.24.0",
77
- "@rulemetric/hooks": "0.7.32",
78
- "@rulemetric/proxy": "0.7.32",
79
- "@rulemetric/skills-registry": "0.7.32"
77
+ "@rulemetric/hooks": "0.7.34",
78
+ "@rulemetric/proxy": "0.7.34",
79
+ "@rulemetric/skills-registry": "0.7.34"
80
80
  },
81
81
  "//deps": "Worker (`evals agent`) runtime deps: the @opentelemetry/* set, @sentry/node, and postgres are STATIC top-level imports loaded at worker startup (via bundled @rulemetric/{telemetry,db}) — they MUST be runtime deps or a clean `npm i -g` crashes with ModuleLoadError. `better-sqlite3` is optionalDependencies (below): it's a native module, lazy `require()`'d inside a try/catch (packages/session providers cursor/opencode) so a missing binary degrades gracefully — making it a hard dep would break `npm i -g` on platforms without prebuilt binaries. `resend` stays a devDep: lazy `await import('resend')` guarded by RESEND_API_KEY, which end-user workers never set (provider falls back to noop). Classification verified via import-graph scan of dist/commands/evals/agent.js + per-importer static/lazy check.",
82
82
  "optionalDependencies": {
@@ -100,8 +100,8 @@
100
100
  "@rulemetric/effectiveness": "0.0.1",
101
101
  "@rulemetric/email": "0.0.1",
102
102
  "@rulemetric/session": "0.2.1",
103
- "@rulemetric/typescript-config": "0.0.0",
104
- "@rulemetric/telemetry": "0.0.1"
103
+ "@rulemetric/telemetry": "0.0.1",
104
+ "@rulemetric/typescript-config": "0.0.0"
105
105
  },
106
106
  "scripts": {
107
107
  "build": "node scripts/build.mjs",
@@ -1,2 +0,0 @@
1
- import{a as o}from"./chunk-ZLLER3HP.js";import{createHash as T}from"node:crypto";import{mkdir as w,open as M,readFile as h,readdir as b,rename as x,stat as P,writeFile as C}from"node:fs/promises";import{homedir as _}from"node:os";import{basename as A,dirname as z,join as l}from"node:path";var F=3e4,$=5,E=8*1024*1024;function f(i){return T("sha256").update(`${i.externalSessionId}:${Math.round(i.mtimeMs)}:${i.size}`).digest("hex")}o(f,"fingerprint");async function I(i){try{return{fingerprints:JSON.parse(await h(i,"utf8")).fingerprints??{}}}catch{return{fingerprints:{}}}}o(I,"readState");async function O(i,e){let s=`${i}.${process.pid}.tmp`;await w(z(i),{recursive:!0}),await C(s,JSON.stringify(e)),await x(s,i)}o(O,"persistState");var g=class{constructor(e){this.options=e}static{o(this,"TranscriptSync")}lastSeenSize=new Map;lastSyncAt=new Map;inFlight=!1;async scan(){if(this.inFlight)return{discovered:0,imported:0,skipped:0};this.inFlight=!0;try{let e=await I(this.options.statePath),s=(await Promise.all(this.options.providers.map(t=>t.discover()))).flat().sort((t,c)=>c.mtimeMs-t.mtimeMs),r=Date.now(),n=[],a=0;for(let t of s){let c=f(t),d=this.lastSeenSize.get(t.path);if(this.lastSeenSize.set(t.path,t.size),e.fingerprints[t.path]===c){a++;continue}let m=this.options.minStableAgeMs??3e3;if(m>0&&r-t.mtimeMs<m){a++;continue}let S=d!==void 0&&d===t.size,u=this.lastSyncAt.get(t.path),v=u===void 0||r-u>=(this.options.activeResyncMs??F);if(!S&&!v){a++;continue}if(n.push(t),n.length>=(this.options.maxImportsPerScan??$))break}let p=0;for(let t of n){let c=f(t);if(t.size>(this.options.maxContentBytes??E)){e.fingerprints[t.path]=c,a++,this.options.log?.(`Transcript sync skipped oversized file: ${t.path}`);continue}let d=await h(t.path,"utf8");await this.options.importTranscript({content:d,tool:t.tool}),e.fingerprints[t.path]=c,this.lastSyncAt.set(t.path,Date.now()),p++,this.options.log?.(`Transcript sync imported ${t.tool} ${A(t.path)} (${t.externalSessionId}).`)}return n.length>0&&await O(this.options.statePath,e),{discovered:s.length,imported:p,skipped:a}}finally{this.inFlight=!1}}};async function k(i){let e;try{e=await M(i,"r");let s=Buffer.alloc(64*1024),{bytesRead:r}=await e.read(s,0,s.length,0),n=s.subarray(0,r).toString("utf8").split(`
2
- `)[0];return n?JSON.parse(n):null}catch{return null}finally{await e?.close()}}o(k,"readFirstJsonLine");async function y(i,e,s){let r=await b(i,{withFileTypes:!0}).catch(()=>[]);for(let n of r){let a=l(i,n.name);if(n.isDirectory()){await y(a,e,s);continue}if(!n.isFile()||!n.name.startsWith("rollout-")||!n.name.endsWith(".jsonl"))continue;let[p,t]=await Promise.all([P(a).catch(()=>null),k(a)]);if(!p||p.mtimeMs<s||t?.type!=="session_meta")continue;let c=t.payload?.originator,d=t.payload?.id??t.payload?.session_id;!c?.toLowerCase().startsWith("codex")||!d||e.push({path:a,tool:"codex",externalSessionId:d,projectPath:t.payload?.cwd??null,mtimeMs:p.mtimeMs,size:p.size})}}o(y,"walkCodexSessions");function j(i=process.env.CODEX_HOME??l(_(),".codex"),e=14){return{name:"codex",async discover(){let s=[],r=Date.now()-Math.max(1,e)*24*60*60*1e3;return await y(l(i,"sessions"),s,r),s}}}o(j,"createCodexTranscriptProvider");function B(i,e){let s=o(async()=>{try{let n=await i.scan();n.imported>0&&e.log?.(`Transcript sync imported/updated ${n.imported} session${n.imported===1?"":"s"}.`)}catch(n){e.log?.(`Transcript sync failed: ${n instanceof Error?n.message:String(n)}`)}},"run"),r=setInterval(()=>void s(),e.intervalMs??3e4);e.signal.addEventListener("abort",()=>clearInterval(r),{once:!0}),s()}o(B,"startTranscriptSync");export{g as a,j as b,B as c};
@@ -1,24 +0,0 @@
1
- import{f as E}from"./chunk-MODFTCZY.js";import{b,e as y,f as k,h as M}from"./chunk-H2ORWIEY.js";import{a as u}from"./chunk-ZLLER3HP.js";var j=50,I=24e3,v=3,P=3,R=40,C=4e3,N=new Set(["fact","decision","correction","convention"]),x=`You are distilling DURABLE, PROJECT-SPECIFIC knowledge from an AI coding session so a future agent working on the SAME project starts smarter.
2
-
3
- Extract ONLY facts / decisions / corrections / conventions that will STILL be true and useful in FUTURE sessions \u2014 architecture facts, deliberate design decisions, corrections of a wrong approach, or project conventions.
4
-
5
- Do NOT extract:
6
- - what the user asked for in THIS session, or the step-by-step actions taken
7
- - anything specific to this one task that will not recur
8
- - anything already covered by the existing memories listed below
9
-
10
- Existing memories for this project (do NOT duplicate these):
11
- {{EXISTING}}
12
-
13
- Prefer FEWER, higher-quality memories \u2014 most sessions yield 0-2, many yield 0. A weak or redundant memory is worse than none.
14
-
15
- Each memory has: "kind" (one of: fact, decision, correction, convention), a short "title" (<= 120 chars), and a 1-3 sentence "body".
16
-
17
- Return STRICT JSON: {"memories": [{"kind": "...", "title": "...", "body": "..."}]} \u2014 use an empty array if nothing durable was learned.`;function A(e){return e.toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}u(A,"normalizeTitle");function L(e,n,f=P){let l=Array.isArray(e?.memories)?e.memories:[],g=new Set(n.map(A)),d=[];for(let m of l){if(d.length>=f)break;if(!m||typeof m!="object")continue;let o=m,r=typeof o.kind=="string"?o.kind.trim().toLowerCase():"",s=typeof o.title=="string"?o.title.trim():"",p=typeof o.body=="string"?o.body.trim():"";if(!N.has(r)||!s||!p||s.length>300||p.length>C)continue;let a=A(s);!a||g.has(a)||(g.add(a),d.push({kind:r,title:s,body:p}))}return d}u(L,"selectNewMemories");async function O(e,n){await M(`/api/sessions/${e}/enrich`,{analysis:{distilledAt:n}})}u(O,"stampDistilledViaApi");async function F(e,n={}){let f=n.llm??E,l=n.now??Date.now,g=n.getSession??(async t=>{try{return await y(`/api/sessions/${t}`)}catch(i){if(i instanceof b&&i.statusCode===404)return null;throw i}}),d=n.getTurns??((t,i)=>y(`/api/sessions/${t}/events?limit=${i}&sort=desc&eventType=user_message,assistant_response&hasContent=true`)),m=n.getExistingTitles??(async(t,i)=>(await y(`/api/memories?projectId=${t}&status=active&limit=${i}`)).map(_=>_.title)),o=n.createMemory??(async t=>{await k("/api/memories",{kind:t.kind,title:t.title,body:t.body,projectId:t.projectId,source:"distilled"})}),r=n.stamp??O,s=await g(e.sessionId);if(!s)return{skipped:"not_found"};if((s.analysis??{}).distilledAt)return{skipped:"already_distilled"};if(!s.projectId)return await r(e.sessionId,new Date(l()).toISOString()),{skipped:"no_project"};let a=s.projectId,S=(await d(e.sessionId,j)).reverse().map(t=>`${t.eventType==="user_message"?"USER":"ASSISTANT"}: ${t.content}`);if(S.length<v)return await r(e.sessionId,new Date(l()).toISOString()),{skipped:"too_short"};let c=S.join(`
18
-
19
- `);if(c.length>I){let t=c.slice(0,Math.floor(I*.45)),i=c.slice(c.length-Math.floor(I*.45));c=`${t}
20
-
21
- [... middle of session omitted ...]
22
-
23
- ${i}`}let h=await m(a,R),D=x.replace("{{EXISTING}}",h.length?h.map(t=>`- ${t}`).join(`
24
- `):"(none yet)"),T=await f(D,c,700);if(T===null)throw new Error("distill_memories: LLM backend unavailable or returned unparseable output");let w=L(T,h);for(let t of w)await o({...t,projectId:a});return await r(e.sessionId,new Date(l()).toISOString()),{created:w.length,candidates:w.length}}u(F,"distillMemories");export{L as a,F as b};