loki-mode 7.47.0 → 7.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/PRIVACY.md CHANGED
@@ -6,14 +6,21 @@ match the code, the code is the bug; please open an issue.
6
6
 
7
7
  ## Summary
8
8
 
9
- - Loki Mode collects anonymous diagnostics to help find and fix bugs.
10
- - It NEVER collects your code, prompts, PRDs, file paths, environment values,
11
- API keys, repository names, emails, or IP addresses.
12
- - In this version (crash reporting Phase 0), NOTHING is sent automatically.
13
- Crash reports are written to a local directory only, so you can inspect
14
- exactly what a future version would send.
15
- - You can opt out at any time with a single switch. The same switch also
16
- disables the existing anonymous usage telemetry described below.
9
+ - Anonymous diagnostics are OPT-IN and OFF by default. A default install sends
10
+ no telemetry or diagnostics of any kind. This covers a default `npm install`,
11
+ the CLI (session and command events), the dashboard, and the welcome page
12
+ form. Air-gapped, GDPR, and FedRAMP deployments are safe out of the box: an
13
+ untouched install sends us no telemetry or diagnostics. (This statement scopes
14
+ to telemetry and diagnostics; provider CLIs you configure, such as Claude or
15
+ Codex, make their own network calls under your own credentials and are
16
+ governed by their vendors.)
17
+ - When you DO opt in, Loki Mode collects anonymous diagnostics to help find and
18
+ fix bugs. It NEVER collects your code, prompts, PRDs, file paths, environment
19
+ values, API keys, repository names, emails, or IP addresses.
20
+ - Crash reporting (Phase 0) is local-only with zero network egress regardless,
21
+ and is also gated by opt-in, so a default install writes nothing at all.
22
+ - You opt in with a single switch (`loki telemetry on` or `LOKI_TELEMETRY=on`)
23
+ and can opt back out at any time. Opt-out always wins over opt-in.
17
24
 
18
25
  ## Two collection paths exist
19
26
 
@@ -39,17 +46,35 @@ Phase 0 behavior:
39
46
  GitHub issue URL so you can submit it manually if you choose. Loki Mode does
40
47
  not submit anything for you in this version.
41
48
 
42
- ### 2. Usage telemetry (existing, anonymous)
49
+ ### 2. Usage telemetry (anonymous, opt-in)
43
50
 
44
- Loki Mode already ships anonymous usage telemetry via PostHog. This predates the
45
- crash-reporting feature and is disclosed here for completeness.
51
+ Loki Mode can send anonymous usage telemetry via PostHog, but ONLY after you opt
52
+ in. By default it is OFF and nothing is sent.
46
53
 
47
- - Events: `session_start`, `session_end`, and an install-time event.
48
- - These are anonymous and gated by the same opt-out described below.
49
- - They never carry your code, prompts, paths, keys, or repository names.
54
+ - Endpoint: `https://us.i.posthog.com/capture/` (override with
55
+ `LOKI_TELEMETRY_ENDPOINT`). The PostHog project key is a public ingest key.
56
+ - Events: `install` (on `npm install`), `session_start`, `session_end`,
57
+ `cli_command`, and `dashboard_start`.
58
+ - Exact payload (every event): `os` (uname system), `arch` (CPU arch),
59
+ `version` (Loki Mode version), `channel` (npm / docker / homebrew / skill /
60
+ source), and a random per-machine `distinct_id` (a uuid4 stored in
61
+ `~/.loki-telemetry-id`, never an email or name). The `install` event also adds
62
+ `node_version` and `providers_installed` (which provider CLIs were detected,
63
+ e.g. "claude,codex"). Some events add a small free-of-PII property such as the
64
+ command name. No code, prompts, paths, keys, repo names, emails, or IPs.
50
65
 
51
- This document and the first-run notice describe BOTH paths. The opt-out is
52
- unified: one switch disables crash reporting AND usage telemetry together.
66
+ ### 3. Welcome page form (anonymous, explicit submit, opt-in)
67
+
68
+ The `loki welcome` page (`assets/welcome/welcome.html`) shows an optional form.
69
+ It NEVER sends anything on page load and is rendered inert unless you have opted
70
+ in. If you have opted in AND you choose to fill in and submit the form, it sends
71
+ these additional self-reported fields to PostHog: your role, company size, and
72
+ the tools you use, plus the same anonymous `distinct_id`. It still never sends
73
+ your name, email, or IP. In headless / Docker / CI environments there is no
74
+ browser, so this path never runs.
75
+
76
+ This document and the first-run notice describe ALL paths. The model is unified:
77
+ one opt-in enables them and one opt-out (which always wins) disables them.
53
78
 
54
79
  ## What is collected (the whitelist)
55
80
 
@@ -90,17 +115,46 @@ prompts, briefs, and diffs can never reach the payload even if a redaction rule
90
115
  were to miss something. Secrets are additionally scrubbed by the shared redactor
91
116
  before whitelisting.
92
117
 
93
- ## How to opt out
118
+ ## How to opt in (and opt back out)
119
+
120
+ Collection is OFF by default. To turn it on, use ANY one of:
94
121
 
95
- Any one of the following disables BOTH crash reporting and usage telemetry:
122
+ - Run `loki telemetry on` (persists `TELEMETRY_ENABLED=true` to `~/.loki/config`)
123
+ - Set the environment variable `LOKI_TELEMETRY=on` (exact word `on`,
124
+ case-insensitive; values like `1` or `true` do NOT count as consent)
125
+
126
+ To opt back out at any time, use ANY one of the following. Opt-out always wins
127
+ over opt-in, so setting one of these guarantees nothing is collected or sent:
96
128
 
97
- - Set the environment variable `LOKI_TELEMETRY=off`
98
129
  - Run `loki telemetry off`
130
+ - Set `LOKI_TELEMETRY=off`
99
131
  - Set `DO_NOT_TRACK=1` (the cross-tool community convention)
100
132
  - Set `LOKI_TELEMETRY_DISABLED=true`
101
133
 
102
- To re-enable later, run `loki telemetry on` or unset the variables. Once you opt
103
- out, the first-run notice is never shown again.
134
+ ### Precedence (exact)
135
+
136
+ 1. If any opt-out flag is set, collection is OFF (hard kill, always wins).
137
+ 2. Else if any opt-in flag is set, collection is ON.
138
+ 3. Otherwise (the default), collection is OFF.
139
+
140
+ ### Air-gapped and enterprise deployments
141
+
142
+ Because collection is opt-in, a default install in an air-gapped, GDPR, or
143
+ FedRAMP environment sends us no telemetry or diagnostics: there is nothing to
144
+ turn off because there is nothing on. To make opting in impossible by accident
145
+ across a fleet, bake `LOKI_TELEMETRY_DISABLED=true` (or `DO_NOT_TRACK=1`) into
146
+ your base image or CI environment; opt-out always wins regardless of any later
147
+ opt-in.
148
+
149
+ This same gate covers ALL paths: the `npm install` event, CLI session and
150
+ command events, the dashboard event, the welcome form, and local crash capture.
151
+
152
+ ### OpenTelemetry (separate, self-hosted)
153
+
154
+ `loki telemetry enable [endpoint]` and `LOKI_OTEL_ENDPOINT` configure optional
155
+ OpenTelemetry tracing to an endpoint YOU run. There is no default endpoint, so
156
+ this never egresses to us; it is opt-in by definition and points only where you
157
+ tell it to.
104
158
 
105
159
  ## Where reports are stored locally
106
160
 
@@ -129,12 +183,16 @@ that choice plainly so you can decide whether to opt out.
129
183
 
130
184
  ## Compliance posture
131
185
 
186
+ - Opt-in by default: nothing is collected or sent unless the user explicitly
187
+ opts in. A default install (including air-gapped) sends us no telemetry or
188
+ diagnostics.
132
189
  - Anonymous by design: no PII is in the whitelist; emails and IP addresses are
133
- denied outright.
190
+ denied outright. The welcome form's role / company-size / tools fields are
191
+ self-reported and anonymous (no name, email, or IP).
134
192
  - Disclosed: this document plus a first-run notice describe collection before
135
193
  any egress occurs.
136
- - Opt-out is persistent and friction-free (see above) and applies to both
137
- collection paths.
194
+ - Opt-out is persistent, friction-free, and ALWAYS wins over opt-in. It applies
195
+ to every collection path.
138
196
  - The project id is non-reversible (one-way hash).
139
197
  - Deletion: you can delete local reports yourself by removing files under
140
198
  `.loki/crash/`.
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var n6=Object.defineProperty;var a6=($)=>$;function s6($,Q){this[$]=a6.bind(null,Q)}var h=($,Q)=>{for(var Z in Q)n6($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:s6.bind(Q,Z)})};var L=($,Q)=>()=>($&&(Q=$($=0)),Q);var K$=import.meta.require;var S1={};h(S1,{lokiDir:()=>P,homeLokiDir:()=>o$,findRepoRootForVersion:()=>d$,REPO_ROOT:()=>m});import{resolve as n,dirname as l$}from"path";import{fileURLToPath as t6}from"url";import{existsSync as P$}from"fs";import{homedir as r6}from"os";function i6(){let $=N1;for(let Q=0;Q<6;Q++){if(P$(n($,"VERSION"))&&P$(n($,"autonomy/run.sh")))return $;let Z=l$($);if(Z===$)break;$=Z}return n(N1,"..","..","..")}function d$($){let Q=$;for(let Z=0;Z<6;Z++){if(P$(n(Q,"VERSION"))&&P$(n(Q,"autonomy/run.sh")))return Q;let z=l$(Q);if(z===Q)break;Q=z}return n($,"..","..","..")}function P(){return process.env.LOKI_DIR??n(process.cwd(),".loki")}function o$(){return n(r6(),".loki")}var N1,m;var C=L(()=>{N1=l$(t6(import.meta.url));m=i6()});import{readFileSync as e6}from"fs";import{resolve as $Q,dirname as QQ}from"path";import{fileURLToPath as ZQ}from"url";function F$(){if($$!==null)return $$;let $="7.47.0";if(typeof $==="string"&&$.length>0)return $$=$,$$;try{let Q=QQ(ZQ(import.meta.url)),Z=d$(Q);$$=e6($Q(Z,"VERSION"),"utf-8").trim()}catch{$$="unknown"}return $$}var $$=null;var n$=L(()=>{C()});var C1={};h(C1,{runOrThrow:()=>zQ,run:()=>j,commandVersion:()=>KQ,commandExists:()=>f,ShellError:()=>a$});async function j($,Q={}){let Z=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),z,X;if(Q.timeoutMs&&Q.timeoutMs>0)z=setTimeout(()=>{try{Z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{Z.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[W,K,U]=await Promise.all([new Response(Z.stdout).text(),new Response(Z.stderr).text(),Z.exited]);return{stdout:W,stderr:K,exitCode:U}}finally{if(z)clearTimeout(z);if(X)clearTimeout(X)}}async function zQ($,Q={}){let Z=await j($,Q);if(Z.exitCode!==0)throw new a$(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function f($){let Q=XQ($),Z=await j(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function XQ($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function KQ($,Q="--version"){if(!await f($))return null;let z=await j([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var a$;var d=L(()=>{a$=class a$ extends Error{message;exitCode;stdout;stderr;constructor($,Q,Z,z){super($);this.message=$;this.exitCode=Q;this.stdout=Z;this.stderr=z;this.name="ShellError"}}});function a($){return WQ?"":$}var WQ,T,S,I,TZ,w,R,y,q;var c=L(()=>{WQ=(process.env.NO_COLOR??"").length>0;T=a("\x1B[0;31m"),S=a("\x1B[0;32m"),I=a("\x1B[1;33m"),TZ=a("\x1B[0;34m"),w=a("\x1B[0;36m"),R=a("\x1B[1m"),y=a("\x1B[2m"),q=a("\x1B[0m")});import{existsSync as TQ}from"fs";async function Q$(){if(B$!==void 0)return B$;let $="/opt/homebrew/bin/python3.12";if(TQ($))return B$=$,$;let Q=await f("python3.12");if(Q)return B$=Q,Q;let Z=await f("python3");return B$=Z,Z}async function Z$($,Q={}){let Z=await Q$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return j([Z,"-c",$],Q)}var B$;var W$=L(()=>{d()});var t1={};h(t1,{runStatus:()=>gQ});import{existsSync as v,readFileSync as U$,readdirSync as l1,statSync as d1}from"fs";import{resolve as D,basename as xQ}from"path";import{homedir as NQ}from"os";async function DQ(){if(await f("jq"))return!0;return process.stdout.write(`${T}Error: jq is required but not installed.${q}
2
+ var n6=Object.defineProperty;var a6=($)=>$;function s6($,Q){this[$]=a6.bind(null,Q)}var h=($,Q)=>{for(var Z in Q)n6($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:s6.bind(Q,Z)})};var L=($,Q)=>()=>($&&(Q=$($=0)),Q);var K$=import.meta.require;var S1={};h(S1,{lokiDir:()=>P,homeLokiDir:()=>o$,findRepoRootForVersion:()=>d$,REPO_ROOT:()=>g});import{resolve as n,dirname as l$}from"path";import{fileURLToPath as t6}from"url";import{existsSync as P$}from"fs";import{homedir as r6}from"os";function i6(){let $=N1;for(let Q=0;Q<6;Q++){if(P$(n($,"VERSION"))&&P$(n($,"autonomy/run.sh")))return $;let Z=l$($);if(Z===$)break;$=Z}return n(N1,"..","..","..")}function d$($){let Q=$;for(let Z=0;Z<6;Z++){if(P$(n(Q,"VERSION"))&&P$(n(Q,"autonomy/run.sh")))return Q;let z=l$(Q);if(z===Q)break;Q=z}return n($,"..","..","..")}function P(){return process.env.LOKI_DIR??n(process.cwd(),".loki")}function o$(){return n(r6(),".loki")}var N1,g;var C=L(()=>{N1=l$(t6(import.meta.url));g=i6()});import{readFileSync as e6}from"fs";import{resolve as $Q,dirname as QQ}from"path";import{fileURLToPath as ZQ}from"url";function F$(){if($$!==null)return $$;let $="7.48.0";if(typeof $==="string"&&$.length>0)return $$=$,$$;try{let Q=QQ(ZQ(import.meta.url)),Z=d$(Q);$$=e6($Q(Z,"VERSION"),"utf-8").trim()}catch{$$="unknown"}return $$}var $$=null;var n$=L(()=>{C()});var C1={};h(C1,{runOrThrow:()=>zQ,run:()=>j,commandVersion:()=>KQ,commandExists:()=>f,ShellError:()=>a$});async function j($,Q={}){let Z=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),z,X;if(Q.timeoutMs&&Q.timeoutMs>0)z=setTimeout(()=>{try{Z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{Z.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[W,K,U]=await Promise.all([new Response(Z.stdout).text(),new Response(Z.stderr).text(),Z.exited]);return{stdout:W,stderr:K,exitCode:U}}finally{if(z)clearTimeout(z);if(X)clearTimeout(X)}}async function zQ($,Q={}){let Z=await j($,Q);if(Z.exitCode!==0)throw new a$(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function f($){let Q=XQ($),Z=await j(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function XQ($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function KQ($,Q="--version"){if(!await f($))return null;let z=await j([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var a$;var d=L(()=>{a$=class a$ extends Error{message;exitCode;stdout;stderr;constructor($,Q,Z,z){super($);this.message=$;this.exitCode=Q;this.stdout=Z;this.stderr=z;this.name="ShellError"}}});function a($){return WQ?"":$}var WQ,T,S,I,TZ,w,R,y,q;var c=L(()=>{WQ=(process.env.NO_COLOR??"").length>0;T=a("\x1B[0;31m"),S=a("\x1B[0;32m"),I=a("\x1B[1;33m"),TZ=a("\x1B[0;34m"),w=a("\x1B[0;36m"),R=a("\x1B[1m"),y=a("\x1B[2m"),q=a("\x1B[0m")});import{existsSync as TQ}from"fs";async function Q$(){if(B$!==void 0)return B$;let $="/opt/homebrew/bin/python3.12";if(TQ($))return B$=$,$;let Q=await f("python3.12");if(Q)return B$=Q,Q;let Z=await f("python3");return B$=Z,Z}async function Z$($,Q={}){let Z=await Q$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return j([Z,"-c",$],Q)}var B$;var W$=L(()=>{d()});var t1={};h(t1,{runStatus:()=>mQ});import{existsSync as v,readFileSync as U$,readdirSync as l1,statSync as d1}from"fs";import{resolve as D,basename as xQ}from"path";import{homedir as NQ}from"os";async function DQ(){if(await f("jq"))return!0;return process.stdout.write(`${T}Error: jq is required but not installed.${q}
3
3
  `),process.stdout.write(`Install with:
4
4
  `),process.stdout.write(` brew install jq (macOS)
5
5
  `),process.stdout.write(` apt install jq (Debian/Ubuntu)
@@ -50,8 +50,8 @@ ${w}Phase 1 artifacts:${q}
50
50
  `)}}if(K){let V=s1(z);if(V&&Array.isArray(V.learnings)&&V.learnings.length>0){let H=new Map;for(let Y of V.learnings){let G=String(Y.trigger??"unknown");H.set(G,(H.get(G)??0)+1)}let J=[...H.entries()].sort((Y,G)=>G[1]-Y[1]).slice(0,3).map(([Y,G])=>`${G} ${Y}`).join(", ");process.stdout.write(` Learnings: ${V.learnings.length} total (${J})
51
51
  `)}}if(U){let V=0,H="";try{let Y=(await import("fs")).readdirSync(X).filter((G)=>G.endsWith(".md"));if(V=Y.length,Y.length>0)Y.sort(),H=Y[Y.length-1]??""}catch{}if(V>0)process.stdout.write(` Escalations: ${V} handoff doc${V===1?"":"s"} (latest: ${H})
52
52
  `)}}function yQ($){if(!v($))return[];try{return K$("fs").readdirSync($).filter((z)=>/^findings-\d+\.json$/.test(z)).sort((z,X)=>{let W=Number.parseInt(z.replace(/[^0-9]/g,""),10)||0,K=Number.parseInt(X.replace(/[^0-9]/g,""),10)||0;return W-K}).map((z)=>D($,z))}catch{return[]}}function s1($){try{let Q=K$("fs");return JSON.parse(Q.readFileSync($,"utf-8"))}catch{return null}}async function vQ(){let $=await Q$();if(!$)return process.stderr.write(`{"error": "Failed to generate JSON status. Ensure python3 is available."}
53
- `),1;let Q=m,Z=P(),z=process.env.LOKI_DASHBOARD_PORT||"57374",X=process.env.LOKI_PROVIDER||"claude",W=await j([$,"-c",SQ,Q,Z,z,X],{timeoutMs:30000});if(W.exitCode!==0)return process.stderr.write(`{"error": "Failed to generate JSON status. Ensure python3 is available."}
54
- `),1;return process.stdout.write(W.stdout),0}async function gQ($){let Q=[...$];while(Q.length>0){let Z=Q[0];if(Z==="--json")return vQ();if(Z==="--help"||Z==="-h")return process.stdout.write(`Usage: loki status [--json]
53
+ `),1;let Q=g,Z=P(),z=process.env.LOKI_DASHBOARD_PORT||"57374",X=process.env.LOKI_PROVIDER||"claude",W=await j([$,"-c",SQ,Q,Z,z,X],{timeoutMs:30000});if(W.exitCode!==0)return process.stderr.write(`{"error": "Failed to generate JSON status. Ensure python3 is available."}
54
+ `),1;return process.stdout.write(W.stdout),0}async function mQ($){let Q=[...$];while(Q.length>0){let Z=Q[0];if(Z==="--json")return vQ();if(Z==="--help"||Z==="-h")return process.stdout.write(`Usage: loki status [--json]
55
55
  `),0;return process.stdout.write(`${T}Unknown flag: ${Z}${q}
56
56
  `),process.stdout.write(`Usage: loki status [--json]
57
57
  `),1}return bQ()}var SQ=`
@@ -328,8 +328,8 @@ if os.path.isfile(gate_count_file):
328
328
  result['phase1'] = phase1
329
329
 
330
330
  print(json.dumps(result, indent=2))
331
- `;var r1=L(()=>{d();W$();c();C()});var e1={};h(e1,{emitDeprecatedAlias:()=>r$,deprecatedAliasShouldSuppress:()=>i1});function i1($){let Q=$[0];if(Q!==void 0&&fQ.has(Q))return!0;for(let Z of $)if(mQ.has(Z))return!0;return!1}function r$($,Q,Z){if(i1(Z))return;process.stderr.write(`note: 'loki ${$}' is now 'loki ${Q}'. The old form still works.
332
- `)}var mQ,fQ;var i$=L(()=>{mQ=new Set(["--json","-q","--quiet"]),fQ=new Set(["json","csv","timeline"])});var z0={};h(z0,{runStats:()=>oQ,computeStats:()=>Z0});import{readdirSync as $0,readFileSync as uQ,statSync as Q0}from"fs";import{join as s}from"path";function V$($){try{if(!Q0($).isFile())return null;return JSON.parse(uQ($,"utf-8"))}catch{return null}}function Q1($){try{return Q0($).isDirectory()}catch{return!1}}function cQ($){if(!Q1($))return[];try{let Q=$0($).filter((Z)=>Z.startsWith("iteration-")&&Z.endsWith(".json"));return Q.sort(),Q.map((Z)=>s($,Z))}catch{return[]}}function H$($){return Math.trunc($).toLocaleString("en-US")}function e$($){let Q=Math.trunc($);if(Q<60)return`${Q}s`;let Z=Math.trunc(Q/3600),z=Math.trunc(Q%3600/60),X=Q%60;if(Z>0)return`${Z}h ${String(z).padStart(2,"0")}m`;return`${z}m ${String(X).padStart(2,"0")}s`}function t($,Q=0){let Z=Math.pow(10,Q);return Math.round($*Z)/Z}function J$($,Q){return $.toFixed(Q)}function $1($,Q){return $.length>=Q?$:$+" ".repeat(Q-$.length)}function pQ($){let Q="N/A",Z=0,z=V$(s($,"state","orchestrator.json"));if(z&&typeof z==="object"){if(typeof z.currentPhase==="string")Q=z.currentPhase;if(typeof z.currentIteration==="number")Z=z.currentIteration}let X=s($,"metrics","efficiency"),W=cQ(X),K=[];for(let F of W){let E=V$(F);if(E&&typeof E==="object")K.push(E)}if(K.length>0)Z=Math.max(Z,K.length);let U=K.reduce((F,E)=>F+(E.input_tokens??0),0),V=K.reduce((F,E)=>F+(E.output_tokens??0),0),H=U+V,J=K.reduce((F,E)=>F+(E.cost_usd??0),0),Y=K.reduce((F,E)=>F+(E.duration_seconds??0),0),G=0,B=0,O=V$(s($,"metrics","budget.json"));if(O&&typeof O==="object"){if(typeof O.budget_limit==="number")G=O.budget_limit;if(typeof O.budget_used==="number")B=O.budget_used}let M=0,x=0,N=V$(s($,"state","quality-gates.json"));if(N&&typeof N==="object"){if(Array.isArray(N)){for(let F of N)if(x+=1,F===!0)M+=1;else if(F&&typeof F==="object"){let E=F;if(E.passed===!0||E.status==="passed")M+=1}}else for(let F of Object.values(N))if(typeof F==="boolean"){if(x+=1,F)M+=1}else if(F&&typeof F==="object"){x+=1;let E=F;if(E.passed===!0||E.status==="passed")M+=1}}let b={},_=V$(s($,"quality","gate-failure-count.json"));if(_&&typeof _==="object"&&!Array.isArray(_)){let F={};for(let[E,l]of Object.entries(_))if(typeof l==="number")F[E]=l;b=F}let k=0,u=0,E1=0,p$=s($,"quality");if(Q1(p$)){let F=[];try{F=$0(p$)}catch{F=[]}for(let E of F){if(!E.endsWith(".json")||E==="gate-failure-count.json")continue;let l=V$(s(p$,E));if(!l||typeof l!=="object")continue;if(!(("verdict"in l)||("approved"in l)||("reviewers"in l)))continue;k+=1;let x1=(l.verdict??"").toString().toLowerCase();if(l.approved===!0||["approved","approve","pass"].includes(x1))u+=1;else if(["revision","revise","changes_requested","reject"].includes(x1))E1+=1}}return{phase:Q,iterationCount:Z,iterations:K,totalInput:U,totalOutput:V,totalTokens:H,totalCost:J,totalDuration:Y,budgetLimit:G,budgetUsed:B,gatesPassed:M,gatesTotal:x,gateFailures:b,reviewsTotal:k,reviewsApproved:u,reviewsRevision:E1}}function lQ($,Q){let Z=$.iterationCount,z={session:{iterations:Z,duration_seconds:$.totalDuration,phase:$.phase},tokens:{input:$.totalInput,output:$.totalOutput,total:$.totalTokens,cost_usd:t($.totalCost,2)},quality:{gates_passed:$.gatesPassed,gates_total:$.gatesTotal,reviews_total:$.reviewsTotal,reviews_approved:$.reviewsApproved,reviews_revision:$.reviewsRevision,gate_failures:$.gateFailures},efficiency:{avg_tokens_per_iteration:Z>0?t($.totalTokens/Z,0):0,avg_cost_per_iteration:Z>0?t($.totalCost/Z,2):0,avg_duration_per_iteration:Z>0?t($.totalDuration/Z,1):0},budget:{used:t($.budgetUsed,2),limit:$.budgetLimit,percent:$.budgetLimit>0?t($.budgetUsed/$.budgetLimit*100,1):0}};if(Q)z.iterations=$.iterations.map((K,U)=>({number:U+1,input_tokens:K.input_tokens??0,output_tokens:K.output_tokens??0,cost_usd:t(K.cost_usd??0,2),duration_seconds:K.duration_seconds??0}));let X=JSON.stringify(z,null,2);function W(K,U){if(!U)return;let V=new RegExp(`("${K}": )(-?\\d+)(,?)$`,"m");X=X.replace(V,(H,J,Y,G)=>`${J}${Y}.0${G}`)}if(W("avg_duration_per_iteration",Z>0&&Number.isInteger(z.efficiency.avg_duration_per_iteration)),W("percent",$.budgetLimit>0&&Number.isInteger(z.budget.percent)),W("cost_usd",Z>0&&Number.isInteger(z.tokens.cost_usd)),Q)X=X.replace(/("cost_usd": )(-?\d+)(,?)$/gm,(K,U,V,H)=>`${U}${V}.0${H}`);return X}function dQ($,Q){let Z=[];if(Z.push("Loki Mode Session Statistics"),Z.push("============================"),Z.push(""),Z.push("Session"),Z.push(` Iterations completed: ${$.iterationCount}`),Z.push(` Duration: ${e$($.totalDuration)}`),Z.push(` Current phase: ${$.phase}`),Z.push(""),Z.push("Token Usage"),$.iterations.length>0)Z.push(` Input tokens: ${H$($.totalInput)}`),Z.push(` Output tokens: ${H$($.totalOutput)}`),Z.push(` Total tokens: ${H$($.totalTokens)}`),Z.push(` Estimated cost: $${J$($.totalCost,2)}`);else Z.push(" N/A (no iteration metrics found)");if(Z.push(""),Z.push("Quality Gates"),$.gatesTotal>0){let z=Math.round($.gatesPassed/$.gatesTotal*100);Z.push(` Gates passed: ${$.gatesPassed}/${$.gatesTotal} (${z}%)`)}else Z.push(" Gates passed: N/A");if($.reviewsTotal>0){let z=[];if($.reviewsApproved>0)z.push(`${$.reviewsApproved} approved`);if($.reviewsRevision>0)z.push(`${$.reviewsRevision} revision requested`);let X=z.length>0?z.join(", "):"N/A";Z.push(` Code reviews: ${$.reviewsTotal} (${X})`)}if(Object.keys($.gateFailures).length>0){let z=Object.entries($.gateFailures).filter(([,X])=>X>0).map(([X,W])=>`${X} (${W})`);if(z.length>0)Z.push(` Gate failures: ${z.join(", ")}`)}if(Z.push(""),Z.push("Efficiency"),$.iterationCount>0&&$.iterations.length>0){let z=Math.round($.totalTokens/$.iterationCount),X=$.totalCost/$.iterationCount,W=$.totalDuration/$.iterationCount;Z.push(` Avg tokens/iteration: ${H$(z)}`),Z.push(` Avg cost/iteration: $${J$(X,2)}`),Z.push(` Avg duration/iteration: ${e$(W)}`)}else Z.push(" N/A (no iteration metrics found)");if(Z.push(""),Z.push("Budget"),$.budgetLimit>0){let z=t($.budgetUsed/$.budgetLimit*100,1),X=Number.isInteger(z)?`${z}.0`:`${z}`;Z.push(` Used: $${J$($.budgetUsed,2)} / $${J$($.budgetLimit,2)} (${X}%)`)}else if($.budgetUsed>0)Z.push(` Used: $${J$($.budgetUsed,2)} (no limit set)`);else Z.push(" N/A");if(Q&&$.iterations.length>0)Z.push(""),Z.push("Per-Iteration Breakdown"),$.iterations.forEach((z,X)=>{let W=X+1,K=$1(H$(z.input_tokens??0),10),U=$1(H$(z.output_tokens??0),10),V=z.cost_usd??0,H=e$(z.duration_seconds??0),J=$1(`${W}`,3);Z.push(` #${J} input: ${K} output: ${U} cost: $${J$(V,2)} time: ${H}`)});return Z.join(`
331
+ `;var r1=L(()=>{d();W$();c();C()});var e1={};h(e1,{emitDeprecatedAlias:()=>r$,deprecatedAliasShouldSuppress:()=>i1});function i1($){let Q=$[0];if(Q!==void 0&&fQ.has(Q))return!0;for(let Z of $)if(gQ.has(Z))return!0;return!1}function r$($,Q,Z){if(i1(Z))return;process.stderr.write(`note: 'loki ${$}' is now 'loki ${Q}'. The old form still works.
332
+ `)}var gQ,fQ;var i$=L(()=>{gQ=new Set(["--json","-q","--quiet"]),fQ=new Set(["json","csv","timeline"])});var z0={};h(z0,{runStats:()=>oQ,computeStats:()=>Z0});import{readdirSync as $0,readFileSync as uQ,statSync as Q0}from"fs";import{join as s}from"path";function V$($){try{if(!Q0($).isFile())return null;return JSON.parse(uQ($,"utf-8"))}catch{return null}}function Q1($){try{return Q0($).isDirectory()}catch{return!1}}function cQ($){if(!Q1($))return[];try{let Q=$0($).filter((Z)=>Z.startsWith("iteration-")&&Z.endsWith(".json"));return Q.sort(),Q.map((Z)=>s($,Z))}catch{return[]}}function H$($){return Math.trunc($).toLocaleString("en-US")}function e$($){let Q=Math.trunc($);if(Q<60)return`${Q}s`;let Z=Math.trunc(Q/3600),z=Math.trunc(Q%3600/60),X=Q%60;if(Z>0)return`${Z}h ${String(z).padStart(2,"0")}m`;return`${z}m ${String(X).padStart(2,"0")}s`}function t($,Q=0){let Z=Math.pow(10,Q);return Math.round($*Z)/Z}function J$($,Q){return $.toFixed(Q)}function $1($,Q){return $.length>=Q?$:$+" ".repeat(Q-$.length)}function pQ($){let Q="N/A",Z=0,z=V$(s($,"state","orchestrator.json"));if(z&&typeof z==="object"){if(typeof z.currentPhase==="string")Q=z.currentPhase;if(typeof z.currentIteration==="number")Z=z.currentIteration}let X=s($,"metrics","efficiency"),W=cQ(X),K=[];for(let F of W){let E=V$(F);if(E&&typeof E==="object")K.push(E)}if(K.length>0)Z=Math.max(Z,K.length);let U=K.reduce((F,E)=>F+(E.input_tokens??0),0),V=K.reduce((F,E)=>F+(E.output_tokens??0),0),H=U+V,J=K.reduce((F,E)=>F+(E.cost_usd??0),0),Y=K.reduce((F,E)=>F+(E.duration_seconds??0),0),G=0,B=0,O=V$(s($,"metrics","budget.json"));if(O&&typeof O==="object"){if(typeof O.budget_limit==="number")G=O.budget_limit;if(typeof O.budget_used==="number")B=O.budget_used}let M=0,x=0,N=V$(s($,"state","quality-gates.json"));if(N&&typeof N==="object"){if(Array.isArray(N)){for(let F of N)if(x+=1,F===!0)M+=1;else if(F&&typeof F==="object"){let E=F;if(E.passed===!0||E.status==="passed")M+=1}}else for(let F of Object.values(N))if(typeof F==="boolean"){if(x+=1,F)M+=1}else if(F&&typeof F==="object"){x+=1;let E=F;if(E.passed===!0||E.status==="passed")M+=1}}let b={},_=V$(s($,"quality","gate-failure-count.json"));if(_&&typeof _==="object"&&!Array.isArray(_)){let F={};for(let[E,l]of Object.entries(_))if(typeof l==="number")F[E]=l;b=F}let k=0,u=0,E1=0,p$=s($,"quality");if(Q1(p$)){let F=[];try{F=$0(p$)}catch{F=[]}for(let E of F){if(!E.endsWith(".json")||E==="gate-failure-count.json")continue;let l=V$(s(p$,E));if(!l||typeof l!=="object")continue;if(!(("verdict"in l)||("approved"in l)||("reviewers"in l)))continue;k+=1;let x1=(l.verdict??"").toString().toLowerCase();if(l.approved===!0||["approved","approve","pass"].includes(x1))u+=1;else if(["revision","revise","changes_requested","reject"].includes(x1))E1+=1}}return{phase:Q,iterationCount:Z,iterations:K,totalInput:U,totalOutput:V,totalTokens:H,totalCost:J,totalDuration:Y,budgetLimit:G,budgetUsed:B,gatesPassed:M,gatesTotal:x,gateFailures:b,reviewsTotal:k,reviewsApproved:u,reviewsRevision:E1}}function lQ($,Q){let Z=$.iterationCount,z={session:{iterations:Z,duration_seconds:$.totalDuration,phase:$.phase},tokens:{input:$.totalInput,output:$.totalOutput,total:$.totalTokens,cost_usd:t($.totalCost,2)},quality:{gates_passed:$.gatesPassed,gates_total:$.gatesTotal,reviews_total:$.reviewsTotal,reviews_approved:$.reviewsApproved,reviews_revision:$.reviewsRevision,gate_failures:$.gateFailures},efficiency:{avg_tokens_per_iteration:Z>0?t($.totalTokens/Z,0):0,avg_cost_per_iteration:Z>0?t($.totalCost/Z,2):0,avg_duration_per_iteration:Z>0?t($.totalDuration/Z,1):0},budget:{used:t($.budgetUsed,2),limit:$.budgetLimit,percent:$.budgetLimit>0?t($.budgetUsed/$.budgetLimit*100,1):0}};if(Q)z.iterations=$.iterations.map((K,U)=>({number:U+1,input_tokens:K.input_tokens??0,output_tokens:K.output_tokens??0,cost_usd:t(K.cost_usd??0,2),duration_seconds:K.duration_seconds??0}));let X=JSON.stringify(z,null,2);function W(K,U){if(!U)return;let V=new RegExp(`("${K}": )(-?\\d+)(,?)$`,"m");X=X.replace(V,(H,J,Y,G)=>`${J}${Y}.0${G}`)}if(W("avg_duration_per_iteration",Z>0&&Number.isInteger(z.efficiency.avg_duration_per_iteration)),W("percent",$.budgetLimit>0&&Number.isInteger(z.budget.percent)),W("cost_usd",Z>0&&Number.isInteger(z.tokens.cost_usd)),Q)X=X.replace(/("cost_usd": )(-?\d+)(,?)$/gm,(K,U,V,H)=>`${U}${V}.0${H}`);return X}function dQ($,Q){let Z=[];if(Z.push("Loki Mode Session Statistics"),Z.push("============================"),Z.push(""),Z.push("Session"),Z.push(` Iterations completed: ${$.iterationCount}`),Z.push(` Duration: ${e$($.totalDuration)}`),Z.push(` Current phase: ${$.phase}`),Z.push(""),Z.push("Token Usage"),$.iterations.length>0)Z.push(` Input tokens: ${H$($.totalInput)}`),Z.push(` Output tokens: ${H$($.totalOutput)}`),Z.push(` Total tokens: ${H$($.totalTokens)}`),Z.push(` Estimated cost: $${J$($.totalCost,2)}`);else Z.push(" N/A (no iteration metrics found)");if(Z.push(""),Z.push("Quality Gates"),$.gatesTotal>0){let z=Math.round($.gatesPassed/$.gatesTotal*100);Z.push(` Gates passed: ${$.gatesPassed}/${$.gatesTotal} (${z}%)`)}else Z.push(" Gates passed: N/A");if($.reviewsTotal>0){let z=[];if($.reviewsApproved>0)z.push(`${$.reviewsApproved} approved`);if($.reviewsRevision>0)z.push(`${$.reviewsRevision} revision requested`);let X=z.length>0?z.join(", "):"N/A";Z.push(` Code reviews: ${$.reviewsTotal} (${X})`)}if(Object.keys($.gateFailures).length>0){let z=Object.entries($.gateFailures).filter(([,X])=>X>0).map(([X,W])=>`${X} (${W})`);if(z.length>0)Z.push(` Gate failures: ${z.join(", ")}`)}if(Z.push(""),Z.push("Efficiency"),$.iterationCount>0&&$.iterations.length>0){let z=Math.round($.totalTokens/$.iterationCount),X=$.totalCost/$.iterationCount,W=$.totalDuration/$.iterationCount;Z.push(` Avg tokens/iteration: ${H$(z)}`),Z.push(` Avg cost/iteration: $${J$(X,2)}`),Z.push(` Avg duration/iteration: ${e$(W)}`)}else Z.push(" N/A (no iteration metrics found)");if(Z.push(""),Z.push("Budget"),$.budgetLimit>0){let z=t($.budgetUsed/$.budgetLimit*100,1),X=Number.isInteger(z)?`${z}.0`:`${z}`;Z.push(` Used: $${J$($.budgetUsed,2)} / $${J$($.budgetLimit,2)} (${X}%)`)}else if($.budgetUsed>0)Z.push(` Used: $${J$($.budgetUsed,2)} (no limit set)`);else Z.push(" N/A");if(Q&&$.iterations.length>0)Z.push(""),Z.push("Per-Iteration Breakdown"),$.iterations.forEach((z,X)=>{let W=X+1,K=$1(H$(z.input_tokens??0),10),U=$1(H$(z.output_tokens??0),10),V=z.cost_usd??0,H=e$(z.duration_seconds??0),J=$1(`${W}`,3);Z.push(` #${J} input: ${K} output: ${U} cost: $${J$(V,2)} time: ${H}`)});return Z.join(`
333
333
  `)}function Z0($){let Q=!1,Z=!1;for(let K of $)if(K==="--json")Q=!0;else if(K==="--efficiency")Z=!0;let z=P();if(!Q1(z)){if(Q)return{exitCode:0,stdout:'{"error": "No active session"}'};return{exitCode:0,stdout:`${I}No active session found.${q}
334
334
  Start a session with: loki start <prd>`}}let X=pQ(z);return{exitCode:0,stdout:Q?lQ(X,Z):dQ(X,Z)}}async function oQ($){let{emitDeprecatedAlias:Q}=await Promise.resolve().then(() => (i$(),e1));Q("stats","report session",$);let Z=Z0($);return console.log(Z.stdout),Z.exitCode}var X0=L(()=>{C();c()});var B0={};h(B0,{runDoctor:()=>W3,pythonImportOk:()=>K1,httpReachable:()=>z1,checkTool:()=>V0,checkSkills:()=>H0,checkDisk:()=>X1,buildDoctorJson:()=>G0,_setPythonImportOkForTest:()=>eQ});import{existsSync as K0,lstatSync as nQ,readlinkSync as aQ,statfsSync as sQ}from"fs";import{spawnSync as tQ}from"child_process";import{homedir as W0}from"os";import{resolve as Z1}from"path";function iQ($){let Q=$.match(rQ);return Q?Q[1]:null}async function q0($){try{let Q=await j([$,"--version"],{timeoutMs:5000}),Z=(Q.stdout||Q.stderr||"").trim();return iQ(Z)}catch{return null}}function U0($,Q){let Z=$.split(".").map((X)=>parseInt(X,10)),z=Q.split(".").map((X)=>parseInt(X,10));while(Z.length<2)Z.push(0);while(z.length<2)z.push(0);for(let X=0;X<2;X++){let W=Z[X]??0,K=z[X]??0;if(Number.isNaN(W)||Number.isNaN(K))return 0;if(W!==K)return W-K}return 0}async function V0($,Q,Z,z=null){let X=await f(Q),W=X!==null,K=W?await q0(Q):null,U="pass";if(!W)U=Z==="required"?"fail":"warn";else if(z&&K){if(U0(K,z)<0)U=Z==="required"?"fail":"warn"}return{name:$,command:Q,found:W,version:K,required:Z,min_version:z,status:U,path:X}}function X1(){let $=null;try{let Z=sQ(W0()),z=Number(Z.bavail)*Number(Z.bsize);$=Math.round(z/1073741824*10)/10}catch{$=null}let Q="pass";if($!==null){if($<1)Q="fail";else if($<5)Q="warn"}return{available_gb:$,status:Q}}async function z1($,Q=2000){try{return(await fetch($,{signal:AbortSignal.timeout(Q)})).ok}catch{return!1}}async function K1($,Q=!1){let Z=`import ${$}`,z=Q?30000:5000;if(!Q)return(await Z$(Z,{timeoutMs:z})).exitCode===0;let X=await Q$();if(!X)return!1;return(await j([X,"-c",Z],{timeoutMs:z})).exitCode===0}function eQ($){N$.fn=$??K1}function H0(){let $=W0();return $3.map(({name:Q,dir:Z})=>{let z=Z1($,Z),X=z,W=Z1(z,"SKILL.md");if(K0(W))return{name:Q,path:X,status:"pass",detail:""};try{if(nQ(z).isSymbolicLink()){let U="unknown";try{U=aQ(z)}catch{}return{name:Q,path:X,status:"fail",detail:`(broken symlink -> ${U})`}}}catch{}return{name:Q,path:X,status:"warn",detail:"(not found - run 'loki setup-skill')"}})}async function J0(){return Promise.all(Q3.map(async($)=>{return{...await V0($.jsonName,$.cmd,$.required,$.min??null),displayName:$.displayName}}))}async function Z3(){let Q=await f("sentrux")!==null,Z=Q?await q0("sentrux"):null;return{found:Q,version:Z,status:Q?"pass":"warn",required:"optional"}}async function z3(){let{openSync:$,statSync:Q,readSync:Z,closeSync:z,existsSync:X}=await import("fs"),{join:W}=await import("path"),K=65536,U=process.env.LOKI_DIR??".loki",V=W(U,"memory",".errors.log"),H=[],J=!1;try{if(X(V)){J=!0;let Y=Q(V).size,G=Math.max(0,Y-65536),B=Y-G,O=Buffer.alloc(B),M=$(V,"r");try{Z(M,O,0,B,G)}finally{z(M)}let N=O.toString("utf-8").split(`
335
335
  `);if(G>0&&N.length>0)N=N.slice(1);N=N.map((b)=>b.trim()).filter((b)=>b.length>0),H=N.slice(-5)}}catch{H=[]}return{errors_log_path:J?V:null,recent_errors:H,recent_error_count:H.length,status:H.length===0?"pass":"warn"}}async function G0(){let Q=(await J0()).map(({displayName:V,...H})=>H),Z=X1(),z=await Z3(),X=await z3(),W=0,K=0,U=0;for(let V of Q)if(V.status==="pass")W++;else if(V.status==="fail")K++;else U++;if(Z.status==="pass")W++;else if(Z.status==="fail")K++;else U++;return{loki_mode_version:F$(),checks:Q,disk:Z,sentrux:z,memory:X,summary:{passed:W,failed:K,warnings:U,ok:K===0}}}function A($){switch($){case"pass":return`${S}PASS${q}`;case"fail":return`${T}FAIL${q}`;case"warn":return`${I}WARN${q}`}}function E$($){let Q=$.version?` (v${$.version})`:"",Z=$.displayName;if(!$.found){let z=$.required==="required"?"not found":$.required==="recommended"?"not found (recommended)":"not found (optional)";return` ${A($.status)} ${Z} - ${z}`}if($.min_version&&$.version&&U0($.version,$.min_version)<0){let z=$.required==="required"?"requires":"recommended";return` ${A($.status)} ${Z}${Q} - ${z} >= ${$.min_version}`}return` ${A($.status)} ${Z}${Q}`}function x$($,Q){if(Q==="pass")$.pass++;else if(Q==="fail")$.fail++;else $.warn++}function X3(){process.stdout.write(`${R}loki doctor${q} - Check system prerequisites
@@ -352,7 +352,7 @@ Start a session with: loki start <prd>`}}let X=pQ(z);return{exitCode:0,stdout:Q?
352
352
  `);let z=["claude","codex","cline","aider"],X=!1;for(let _ of z){let k=Z.get(_);if(process.stdout.write(E$(k)+`
353
353
  `),x$($,k.status),k.found)X=!0}if(!X){if(process.stdout.write(` ${A("fail")} No AI provider CLI installed -- at least one is required
354
354
  `),process.stdout.write(` ${I}Install: npm install -g @anthropic-ai/claude-code${q}
355
- `),$.fail++,process.stdout.isTTY){let _=Z1(m,"autonomy/provider-offer.sh");if(K0(_))tQ("bash",[_,"report"],{stdio:"inherit"})}}process.stdout.write(`
355
+ `),$.fail++,process.stdout.isTTY){let _=Z1(g,"autonomy/provider-offer.sh");if(K0(_))tQ("bash",[_,"report"],{stdio:"inherit"})}}process.stdout.write(`
356
356
  `),process.stdout.write(`${w}API Keys:${q}
357
357
  `);let W=Z.get("claude")?.found??!1,K=Z.get("codex")?.found??!1,U=process.env;if(U.ANTHROPIC_API_KEY)process.stdout.write(` ${A("pass")} ANTHROPIC_API_KEY is set
358
358
  `),$.pass++;else if(W)process.stdout.write(` ${y} -- ${q} ANTHROPIC_API_KEY not set (Claude CLI uses its own login)
@@ -439,7 +439,7 @@ iteration success rate.
439
439
  This is the Phase K MVP -- read-only derivation. Per-iteration
440
440
  emission, dashboard panel, and the loki-bench harness are deferred
441
441
  follow-ups (see project_v7_5_18_arc_status.md).
442
- `;var q1=L(()=>{j0();C();i$()});var k0={};h(k0,{delegateToBash:()=>R3});import{resolve as j3}from"path";async function R3($){let Q=j3(m,"autonomy","loki"),Z=Bun.spawn({cmd:[Q,...$],stdin:"inherit",stdout:"inherit",stderr:"inherit",env:{...process.env,LOKI_LEGACY_BASH:"1"}}),z=setTimeout(()=>{try{Z.kill("SIGKILL")}catch{}},k3);try{return await Z.exited}finally{clearTimeout(z)}}var k3=3600000;var R0=L(()=>{C()});import{existsSync as E3,mkdirSync as x3,readdirSync as N3,readFileSync as S3,statSync as D3,writeFileSync as C3}from"fs";import{join as T$}from"path";function U1($){return $&&typeof $==="object"?$:{}}function z$($){return Math.round($*1e4)/1e4}function g3($){let Q=String($??"").trim().toUpperCase();if(!Q)return null;for(let Z of x0)if(Q.startsWith(Z))return!0;return!1}function m3($){let Q=g3($.final_verdict);if(Q!==null)return Q?1:0;let Z=$.reviewers;if(Array.isArray(Z)&&Z.length>0){let z=0,X=0;for(let W of Z){if(!W||typeof W!=="object")continue;X+=1;let K=String(W.vote??"").trim().toUpperCase();if(x0.some((U)=>K.startsWith(U)))z+=1}if(X>0)return z===X?1:0}return null}function f3($){let Q=Number($.total),Z=Number($.passed);if(!Number.isFinite(Q)||!Number.isFinite(Z))return null;if(Q<=0)return null;return Math.max(0,Math.min(1,Z/Q))}function u3($){let Q;if($&&typeof $==="object")Q=$.count;else Q=$;let Z=Number(Q);if(!Number.isFinite(Z)||Z<0)return null;return Z}function c3($){let Q=U1($.council);for(let Z of[Q.interventions,$.interventions]){let z=Number(Z);if(Number.isFinite(z)&&z>=0)return z}return null}function p3($){let Q=T$($,"proofs"),Z=[];if(!E3(Q))return Z;let z;try{z=N3(Q).sort()}catch{return Z}for(let X of z){let W=T$(Q,X);try{if(!D3(W).isDirectory())continue}catch{continue}let K=null;try{K=JSON.parse(S3(T$(W,"proof.json"),"utf8"))}catch{continue}if(!K||typeof K!=="object")continue;Z.push({run_id:String(K.run_id??X),generated_at:typeof K.generated_at==="string"?K.generated_at:null,council_pass_rate:m3(U1(K.council)),gate_pass_rate:f3(U1(K.quality_gates)),iterations:u3(K.iterations),interventions:c3(K)})}return Z.sort((X,W)=>{let K=X.generated_at===null?1:0,U=W.generated_at===null?1:0;if(K!==U)return K-U;return(X.generated_at??"").localeCompare(W.generated_at??"")}),Z}function E0($){return $.reduce((Q,Z)=>Q+Z,0)/$.length}function l3($,Q){let Z=h3[$],z=y3[$],X=v3[$],W=Q.filter((M)=>M!==null),K=W.length;if(K===0)return{axis:$,label:X,available:!1,higher_is_better:Z,note:"no runs recorded this metric"};if(K<2)return{axis:$,label:X,available:!0,higher_is_better:Z,data_points:K,latest:z$(W[K-1]),direction:"flat",improving:null,delta:0,earlier_mean:z$(W[0]),later_mean:z$(W[K-1]),insufficient:!0,note:"not enough history yet (need 2+ runs with this metric)"};let U=Math.floor(K/2),V=W.slice(0,U),H=W.slice(K-U),J=E0(V),Y=E0(H),G=Y-J,B;if(Math.abs(G)<=z)B="flat";else if(G>0)B="up";else B="down";let O;if(B==="flat")O=null;else O=B==="up"===Z;return{axis:$,label:X,available:!0,higher_is_better:Z,data_points:K,latest:z$(W[K-1]),direction:B,improving:O,delta:z$(G),earlier_mean:z$(J),later_mean:z$(Y),insufficient:!1}}function N0($){let Q=p3($),Z=Q.map((V)=>({run_id:V.run_id,generated_at:V.generated_at,council_pass_rate:V.council_pass_rate,gate_pass_rate:V.gate_pass_rate,iterations:V.iterations,interventions:V.interventions})),z={};for(let V of C$)z[V]=l3(V,Q.map((H)=>H[V]));let X=Q.length<2,W=C$.filter((V)=>z[V].available&&z[V].improving===!0),K=C$.filter((V)=>z[V].available&&z[V].improving===!1),U=[];if(X)U.push(`not enough history yet: ${Q.length} run(s) recorded, need 2+ to show a trend`);if(!z.interventions.available)U.push("intervention trend unavailable: no per-run intervention count in proof.json yet (axis lights up automatically once recorded)");return{schema_version:b3,generated_at:new Date().toISOString(),loki_dir:$,runs_count:Q.length,insufficient:X,axes:z,improving_count:W.length,regressing_count:K.length,improving_axes:W,regressing_axes:K,series:Z,notes:U}}function S0($){return JSON.stringify($,null,2)}function D0($,Q){let Z=T$($,"metrics"),z=T$(Z,"trust-trajectory.json");try{return x3(Z,{recursive:!0}),C3(z,JSON.stringify(Q,null,2)),z}catch{return null}}function d3($){if($==="up")return"up";if($==="down")return"down";return"flat"}function o3($){let Q=$.label??$.axis;if(!$.available)return` ${(Q+":").padEnd(26)} no data`;let Z;if($.insufficient)Z="(need 2+ runs)";else if($.improving===!0)Z="improving";else if($.improving===!1)Z="regressing";else Z="stable";let z=$.higher_is_better?"higher better":"lower better",X=$.latest??"n/a";return` ${(Q+":").padEnd(26)} ${d3($.direction).padEnd(5)} latest=${String(X).padEnd(7)} ${Z.padEnd(11)} [${z}]`}function C0($){let Q=[];if(Q.push(`Loki Mode Trust Trajectory (snapshot at ${$.generated_at})`),Q.push(`Source: ${$.loki_dir}`),Q.push(`Runs analyzed: ${$.runs_count}`),Q.push(""),$.insufficient){if(Q.push("Not enough history yet."),Q.push("Trust trajectory needs 2+ recorded runs to show a direction."),Q.push("Each `loki start` run writes a proof-of-run; come back after the next run."),$.notes.length>0){Q.push(""),Q.push("Notes");for(let X of $.notes)Q.push(` - ${X}`)}return Q.join(`
442
+ `;var q1=L(()=>{j0();C();i$()});var k0={};h(k0,{delegateToBash:()=>R3});import{resolve as j3}from"path";async function R3($){let Q=j3(g,"autonomy","loki"),Z=Bun.spawn({cmd:[Q,...$],stdin:"inherit",stdout:"inherit",stderr:"inherit",env:{...process.env,LOKI_LEGACY_BASH:"1"}}),z=setTimeout(()=>{try{Z.kill("SIGKILL")}catch{}},k3);try{return await Z.exited}finally{clearTimeout(z)}}var k3=3600000;var R0=L(()=>{C()});import{existsSync as E3,mkdirSync as x3,readdirSync as N3,readFileSync as S3,statSync as D3,writeFileSync as C3}from"fs";import{join as T$}from"path";function U1($){return $&&typeof $==="object"?$:{}}function z$($){return Math.round($*1e4)/1e4}function m3($){let Q=String($??"").trim().toUpperCase();if(!Q)return null;for(let Z of x0)if(Q.startsWith(Z))return!0;return!1}function g3($){let Q=m3($.final_verdict);if(Q!==null)return Q?1:0;let Z=$.reviewers;if(Array.isArray(Z)&&Z.length>0){let z=0,X=0;for(let W of Z){if(!W||typeof W!=="object")continue;X+=1;let K=String(W.vote??"").trim().toUpperCase();if(x0.some((U)=>K.startsWith(U)))z+=1}if(X>0)return z===X?1:0}return null}function f3($){let Q=Number($.total),Z=Number($.passed);if(!Number.isFinite(Q)||!Number.isFinite(Z))return null;if(Q<=0)return null;return Math.max(0,Math.min(1,Z/Q))}function u3($){let Q;if($&&typeof $==="object")Q=$.count;else Q=$;let Z=Number(Q);if(!Number.isFinite(Z)||Z<0)return null;return Z}function c3($){let Q=U1($.council);for(let Z of[Q.interventions,$.interventions]){let z=Number(Z);if(Number.isFinite(z)&&z>=0)return z}return null}function p3($){let Q=T$($,"proofs"),Z=[];if(!E3(Q))return Z;let z;try{z=N3(Q).sort()}catch{return Z}for(let X of z){let W=T$(Q,X);try{if(!D3(W).isDirectory())continue}catch{continue}let K=null;try{K=JSON.parse(S3(T$(W,"proof.json"),"utf8"))}catch{continue}if(!K||typeof K!=="object")continue;Z.push({run_id:String(K.run_id??X),generated_at:typeof K.generated_at==="string"?K.generated_at:null,council_pass_rate:g3(U1(K.council)),gate_pass_rate:f3(U1(K.quality_gates)),iterations:u3(K.iterations),interventions:c3(K)})}return Z.sort((X,W)=>{let K=X.generated_at===null?1:0,U=W.generated_at===null?1:0;if(K!==U)return K-U;return(X.generated_at??"").localeCompare(W.generated_at??"")}),Z}function E0($){return $.reduce((Q,Z)=>Q+Z,0)/$.length}function l3($,Q){let Z=h3[$],z=y3[$],X=v3[$],W=Q.filter((M)=>M!==null),K=W.length;if(K===0)return{axis:$,label:X,available:!1,higher_is_better:Z,note:"no runs recorded this metric"};if(K<2)return{axis:$,label:X,available:!0,higher_is_better:Z,data_points:K,latest:z$(W[K-1]),direction:"flat",improving:null,delta:0,earlier_mean:z$(W[0]),later_mean:z$(W[K-1]),insufficient:!0,note:"not enough history yet (need 2+ runs with this metric)"};let U=Math.floor(K/2),V=W.slice(0,U),H=W.slice(K-U),J=E0(V),Y=E0(H),G=Y-J,B;if(Math.abs(G)<=z)B="flat";else if(G>0)B="up";else B="down";let O;if(B==="flat")O=null;else O=B==="up"===Z;return{axis:$,label:X,available:!0,higher_is_better:Z,data_points:K,latest:z$(W[K-1]),direction:B,improving:O,delta:z$(G),earlier_mean:z$(J),later_mean:z$(Y),insufficient:!1}}function N0($){let Q=p3($),Z=Q.map((V)=>({run_id:V.run_id,generated_at:V.generated_at,council_pass_rate:V.council_pass_rate,gate_pass_rate:V.gate_pass_rate,iterations:V.iterations,interventions:V.interventions})),z={};for(let V of C$)z[V]=l3(V,Q.map((H)=>H[V]));let X=Q.length<2,W=C$.filter((V)=>z[V].available&&z[V].improving===!0),K=C$.filter((V)=>z[V].available&&z[V].improving===!1),U=[];if(X)U.push(`not enough history yet: ${Q.length} run(s) recorded, need 2+ to show a trend`);if(!z.interventions.available)U.push("intervention trend unavailable: no per-run intervention count in proof.json yet (axis lights up automatically once recorded)");return{schema_version:b3,generated_at:new Date().toISOString(),loki_dir:$,runs_count:Q.length,insufficient:X,axes:z,improving_count:W.length,regressing_count:K.length,improving_axes:W,regressing_axes:K,series:Z,notes:U}}function S0($){return JSON.stringify($,null,2)}function D0($,Q){let Z=T$($,"metrics"),z=T$(Z,"trust-trajectory.json");try{return x3(Z,{recursive:!0}),C3(z,JSON.stringify(Q,null,2)),z}catch{return null}}function d3($){if($==="up")return"up";if($==="down")return"down";return"flat"}function o3($){let Q=$.label??$.axis;if(!$.available)return` ${(Q+":").padEnd(26)} no data`;let Z;if($.insufficient)Z="(need 2+ runs)";else if($.improving===!0)Z="improving";else if($.improving===!1)Z="regressing";else Z="stable";let z=$.higher_is_better?"higher better":"lower better",X=$.latest??"n/a";return` ${(Q+":").padEnd(26)} ${d3($.direction).padEnd(5)} latest=${String(X).padEnd(7)} ${Z.padEnd(11)} [${z}]`}function C0($){let Q=[];if(Q.push(`Loki Mode Trust Trajectory (snapshot at ${$.generated_at})`),Q.push(`Source: ${$.loki_dir}`),Q.push(`Runs analyzed: ${$.runs_count}`),Q.push(""),$.insufficient){if(Q.push("Not enough history yet."),Q.push("Trust trajectory needs 2+ recorded runs to show a direction."),Q.push("Each `loki start` run writes a proof-of-run; come back after the next run."),$.notes.length>0){Q.push(""),Q.push("Notes");for(let X of $.notes)Q.push(` - ${X}`)}return Q.join(`
443
443
  `)}Q.push("Is the agent earning autonomy on this repo?");for(let X of C$)if($.axes[X])Q.push(o3($.axes[X]));Q.push("");let{improving_count:Z,regressing_count:z}=$;if(Z&&!z)Q.push(`Overall: trending more trustworthy (${Z} axis improving).`);else if(z&&!Z)Q.push(`Overall: trust regressing (${z} axis regressing). Review recent runs.`);else if(Z||z)Q.push(`Overall: mixed (${Z} improving / ${z} regressing).`);else Q.push("Overall: stable.");if($.notes.length>0){Q.push(""),Q.push("Notes");for(let X of $.notes)Q.push(` - ${X}`)}return Q.join(`
444
444
  `)}var b3=1,C$,h3,y3,v3,x0;var b0=L(()=>{C$=["council_pass_rate","gate_pass_rate","iterations","interventions"],h3={council_pass_rate:!0,gate_pass_rate:!0,iterations:!1,interventions:!1},y3={council_pass_rate:0.01,gate_pass_rate:0.01,iterations:0.25,interventions:0.25},v3={council_pass_rate:"Council pass rate",gate_pass_rate:"Gate pass rate",iterations:"Iterations to completion",interventions:"Human interventions"},x0=["APPROVE","APPROVED","COMPLETE","PASS","PASSED"]});var h0={};h(h0,{runTrust:()=>a3});function a3($){let Q=!1;for(let X of $){if(X==="--help"||X==="-h"||X==="help")return process.stdout.write(n3),0;if(X==="--json"){Q=!0;continue}return process.stderr.write(`loki trust: unknown arg: ${X}
445
445
  Run 'loki trust --help' for usage.
@@ -461,13 +461,13 @@ Shows whether the agent is earning autonomy on THIS repo over time:
461
461
  Derived read-only from proof-of-run history in .loki/proofs/. With fewer
462
462
  than 2 recorded runs it reports "not enough history yet" rather than a
463
463
  fabricated trend. Complements 'loki kpis' (single-run snapshot).
464
- `;var y0=L(()=>{b0();C()});import{closeSync as V1,fstatSync as s3,lstatSync as t3,mkdirSync as v0,openSync as g0,readSync as r3,renameSync as i3,rmSync as m0,statSync as e3,unlinkSync as f0,writeFileSync as $8,writeSync as Q8}from"fs";import{dirname as u0}from"path";function O$($,Q){v0(u0($),{recursive:!0});let Z=`${$}.tmp.${process.pid}.${++Z8}`;$8(Z,`${JSON.stringify(Q,null,2)}
465
- `),i3(Z,$)}async function c0($,Q){let Z=b$.get($)??Promise.resolve(),z=()=>{},X=new Promise((K)=>{z=K}),W=Z.catch(()=>{}).then(()=>X);b$.set($,W);try{return await Z.catch(()=>{}),await Q()}finally{if(z(),b$.get($)===W)b$.delete($)}}function X8($){return`${$}.lock`}function K8($){if(!Number.isFinite($)||$<=0)return!1;try{return process.kill($,0),!0}catch(Q){return Q?.code==="EPERM"}}function W8($){let Q=null;try{return v0(u0($),{recursive:!0}),Q=g0($,"wx"),Q8(Q,`${process.pid}
466
- `),Q}catch(Z){if(Q!==null){try{V1(Q)}catch{}try{f0($)}catch{}}if(Z?.code==="EEXIST")return null;throw Z}}function q8($,Q){let Z;try{Z=t3($)}catch{return!0}if(Z.isSymbolicLink())try{return f0($),!0}catch{return!1}let z;try{z=g0($,"r")}catch{return!0}try{let X=s3(z);if(Date.now()-X.mtimeMs<Q)return!1;let K=NaN;try{let U=Buffer.alloc(64),V=r3(z,U,0,64,0);K=Number.parseInt(U.subarray(0,V).toString("utf-8").trim(),10)}catch{}if(Number.isFinite(K)&&K8(K))return!1;try{if(e3($).mtimeMs>X.mtimeMs)return!1}catch{return!0}try{m0($,{force:!0})}catch{}return!0}finally{try{V1(z)}catch{}}}function H1($,Q,Z={}){let z=Z.timeoutMs??1e4,X=Z.pollMs??25,W=Z.staleMs??30000,K=X8($),U=Date.now()+z,V=null,H=0,J=new Int32Array(new SharedArrayBuffer(4));while(V===null){if(V=W8(K),V!==null)break;if(Date.now()>U)throw Error(`withFileLockSync: timed out after ${z}ms acquiring ${K}`);if(q8(K,W))continue;let Y=Math.min(X*2**Math.min(H,4),z8);H+=1,Atomics.wait(J,0,0,Y)}try{return Q()}finally{try{V1(V)}catch{}try{m0(K,{force:!0})}catch{}}}var Z8=0,b$,z8=50;var h$=L(()=>{b$=new Map});import{existsSync as X$,mkdirSync as G$,copyFileSync as o0,readFileSync as B1,readdirSync as U8,statSync as V8,writeFileSync as H8,renameSync as n0,appendFileSync as a0,rmSync as J8}from"fs";import{join as g,dirname as y$}from"path";function B8($){let Q=l0.then($,$);return l0=Q.catch((Z)=>{console.warn("[checkpoint] serialized op rejected:",Z);return}),Q}function o($){return g($,"state","checkpoints")}function s0($){return g(o($),"index.jsonl")}async function Y8($){let Q=await j(["git","rev-parse","HEAD"],{cwd:$,timeoutMs:5000});if(Q.exitCode!==0)return"no-git";return Q.stdout.trim()||"no-git"}async function M8($){let Q=await j(["git","branch","--show-current"],{cwd:$,timeoutMs:5000});if(Q.exitCode!==0)return"unknown";return Q.stdout.trim()||"unknown"}async function T8($){let Q=await j(["git","diff","--quiet"],{cwd:$,timeoutMs:5000}),Z=await j(["git","diff","--cached","--quiet"],{cwd:$,timeoutMs:5000}),z=Q.exitCode===1,X=Z.exitCode===1;return z||X}function O8($){let Q=g($,"state","orchestrator.json");if(!X$(Q))return"unknown";try{let z=JSON.parse(B1(Q,"utf-8")).currentPhase;return typeof z==="string"&&z.length>0?z:"unknown"}catch{return"unknown"}}function w8($,Q){for(let Z of A8){let z=g($,Z);if(!X$(z))continue;let X=g(Q,Z);G$(y$(X),{recursive:!0});try{o0(z,X)}catch{}}}function r0($,Q){G$(y$($),{recursive:!0});let Z=`${$}.tmp.${process.pid}.${++t0}`;H8(Z,Q),n0(Z,$)}function _8($){return JSON.stringify($,null,2)}function i0($){return`{${[`"id": ${JSON.stringify($.id)}`,`"ts": ${JSON.stringify($.ts)}`,`"iter": ${JSON.stringify($.iter)}`,`"task": ${JSON.stringify($.task)}`,`"sha": ${JSON.stringify($.sha)}`].join(", ")}}`}async function I8($){return B8(()=>L8($))}async function L8($){let Q=$.lokiDirOverride??P(),Z=process.cwd(),z=o(Q);if(G$(z,{recursive:!0}),!$.forceCreate){if(!await T8(Z))return{created:!1,reason:"no uncommitted changes"}}let X=await Y8(Z),W=await M8(Z),K=$.iteration??Number.parseInt(process.env.ITERATION_COUNT??"0",10),U=$.epochOverride??Math.floor(Date.now()/1000),V=`cp-${K}-${U}`,H=g(z,V);G$(H,{recursive:!0}),w8(Q,H);let J=new Date().toISOString().replace(/\.\d{3}Z$/,"Z"),Y=($.taskDescription??"task completed").slice(0,G8),G=$.provider??process.env.PROVIDER_NAME??"claude",B={id:V,timestamp:J,iteration:K,task_id:$.taskId??"unknown",task_description:Y,git_sha:X,git_branch:W,provider:G,phase:O8(Q)};r0(g(H,"metadata.json"),_8(B));let O={id:B.id,ts:B.timestamp,iter:B.iteration,task:B.task_description,sha:B.git_sha},M=s0(Q);return H1(M,()=>{a0(M,`${i0(O)}
467
- `)}),P8(Q),{created:!0,id:V,metadata:B,dir:H}}function Y1($){let Q=o($);if(!X$(Q))return[];return U8(Q).filter((Z)=>Z.startsWith("cp-")).filter((Z)=>{try{return V8(g(Q,Z)).isDirectory()}catch{return!1}})}function M1($){return[...$].sort((Q,Z)=>{let z=d0(Q),X=d0(Z);return z-X})}function d0($){let Q=$.split("-");if(Q.length<3)return 0;let Z=Q[Q.length-1],z=Number.parseInt(Z??"0",10);return Number.isFinite(z)?z:0}function P8($){let Q=Y1($);if(Q.length<=p0)return;let Z=M1(Q),z=Z.slice(0,Z.length-p0);for(let X of z)try{J8(g(o($),X),{recursive:!0,force:!0})}catch{}F8($)}function F8($){let Q=M1(Y1($)),Z=[];for(let W of Q){let K=g(o($),W,"metadata.json"),U=g(o($),W);if(!X$(K)){J1($,U,"missing_field","metadata.json");continue}try{let V=JSON.parse(B1(K,"utf-8")),H=$6(V,K);if(!H.ok){J1($,U,H.reason,H.field);continue}let J=H.value;Z.push(i0({id:J.id,ts:J.timestamp,iter:J.iteration,task:J.task_description??"",sha:J.git_sha}))}catch{J1($,U,"invalid_type","metadata.json")}}let z=s0($),X=Z.length>0?`${Z.join(`
464
+ `;var y0=L(()=>{b0();C()});import{closeSync as V1,fstatSync as s3,lstatSync as t3,mkdirSync as v0,openSync as m0,readSync as r3,renameSync as i3,rmSync as g0,statSync as e3,unlinkSync as f0,writeFileSync as $8,writeSync as Q8}from"fs";import{dirname as u0}from"path";function O$($,Q){v0(u0($),{recursive:!0});let Z=`${$}.tmp.${process.pid}.${++Z8}`;$8(Z,`${JSON.stringify(Q,null,2)}
465
+ `),i3(Z,$)}async function c0($,Q){let Z=b$.get($)??Promise.resolve(),z=()=>{},X=new Promise((K)=>{z=K}),W=Z.catch(()=>{}).then(()=>X);b$.set($,W);try{return await Z.catch(()=>{}),await Q()}finally{if(z(),b$.get($)===W)b$.delete($)}}function X8($){return`${$}.lock`}function K8($){if(!Number.isFinite($)||$<=0)return!1;try{return process.kill($,0),!0}catch(Q){return Q?.code==="EPERM"}}function W8($){let Q=null;try{return v0(u0($),{recursive:!0}),Q=m0($,"wx"),Q8(Q,`${process.pid}
466
+ `),Q}catch(Z){if(Q!==null){try{V1(Q)}catch{}try{f0($)}catch{}}if(Z?.code==="EEXIST")return null;throw Z}}function q8($,Q){let Z;try{Z=t3($)}catch{return!0}if(Z.isSymbolicLink())try{return f0($),!0}catch{return!1}let z;try{z=m0($,"r")}catch{return!0}try{let X=s3(z);if(Date.now()-X.mtimeMs<Q)return!1;let K=NaN;try{let U=Buffer.alloc(64),V=r3(z,U,0,64,0);K=Number.parseInt(U.subarray(0,V).toString("utf-8").trim(),10)}catch{}if(Number.isFinite(K)&&K8(K))return!1;try{if(e3($).mtimeMs>X.mtimeMs)return!1}catch{return!0}try{g0($,{force:!0})}catch{}return!0}finally{try{V1(z)}catch{}}}function H1($,Q,Z={}){let z=Z.timeoutMs??1e4,X=Z.pollMs??25,W=Z.staleMs??30000,K=X8($),U=Date.now()+z,V=null,H=0,J=new Int32Array(new SharedArrayBuffer(4));while(V===null){if(V=W8(K),V!==null)break;if(Date.now()>U)throw Error(`withFileLockSync: timed out after ${z}ms acquiring ${K}`);if(q8(K,W))continue;let Y=Math.min(X*2**Math.min(H,4),z8);H+=1,Atomics.wait(J,0,0,Y)}try{return Q()}finally{try{V1(V)}catch{}try{g0(K,{force:!0})}catch{}}}var Z8=0,b$,z8=50;var h$=L(()=>{b$=new Map});import{existsSync as X$,mkdirSync as G$,copyFileSync as o0,readFileSync as B1,readdirSync as U8,statSync as V8,writeFileSync as H8,renameSync as n0,appendFileSync as a0,rmSync as J8}from"fs";import{join as m,dirname as y$}from"path";function B8($){let Q=l0.then($,$);return l0=Q.catch((Z)=>{console.warn("[checkpoint] serialized op rejected:",Z);return}),Q}function o($){return m($,"state","checkpoints")}function s0($){return m(o($),"index.jsonl")}async function Y8($){let Q=await j(["git","rev-parse","HEAD"],{cwd:$,timeoutMs:5000});if(Q.exitCode!==0)return"no-git";return Q.stdout.trim()||"no-git"}async function M8($){let Q=await j(["git","branch","--show-current"],{cwd:$,timeoutMs:5000});if(Q.exitCode!==0)return"unknown";return Q.stdout.trim()||"unknown"}async function T8($){let Q=await j(["git","diff","--quiet"],{cwd:$,timeoutMs:5000}),Z=await j(["git","diff","--cached","--quiet"],{cwd:$,timeoutMs:5000}),z=Q.exitCode===1,X=Z.exitCode===1;return z||X}function O8($){let Q=m($,"state","orchestrator.json");if(!X$(Q))return"unknown";try{let z=JSON.parse(B1(Q,"utf-8")).currentPhase;return typeof z==="string"&&z.length>0?z:"unknown"}catch{return"unknown"}}function w8($,Q){for(let Z of A8){let z=m($,Z);if(!X$(z))continue;let X=m(Q,Z);G$(y$(X),{recursive:!0});try{o0(z,X)}catch{}}}function r0($,Q){G$(y$($),{recursive:!0});let Z=`${$}.tmp.${process.pid}.${++t0}`;H8(Z,Q),n0(Z,$)}function _8($){return JSON.stringify($,null,2)}function i0($){return`{${[`"id": ${JSON.stringify($.id)}`,`"ts": ${JSON.stringify($.ts)}`,`"iter": ${JSON.stringify($.iter)}`,`"task": ${JSON.stringify($.task)}`,`"sha": ${JSON.stringify($.sha)}`].join(", ")}}`}async function I8($){return B8(()=>L8($))}async function L8($){let Q=$.lokiDirOverride??P(),Z=process.cwd(),z=o(Q);if(G$(z,{recursive:!0}),!$.forceCreate){if(!await T8(Z))return{created:!1,reason:"no uncommitted changes"}}let X=await Y8(Z),W=await M8(Z),K=$.iteration??Number.parseInt(process.env.ITERATION_COUNT??"0",10),U=$.epochOverride??Math.floor(Date.now()/1000),V=`cp-${K}-${U}`,H=m(z,V);G$(H,{recursive:!0}),w8(Q,H);let J=new Date().toISOString().replace(/\.\d{3}Z$/,"Z"),Y=($.taskDescription??"task completed").slice(0,G8),G=$.provider??process.env.PROVIDER_NAME??"claude",B={id:V,timestamp:J,iteration:K,task_id:$.taskId??"unknown",task_description:Y,git_sha:X,git_branch:W,provider:G,phase:O8(Q)};r0(m(H,"metadata.json"),_8(B));let O={id:B.id,ts:B.timestamp,iter:B.iteration,task:B.task_description,sha:B.git_sha},M=s0(Q);return H1(M,()=>{a0(M,`${i0(O)}
467
+ `)}),P8(Q),{created:!0,id:V,metadata:B,dir:H}}function Y1($){let Q=o($);if(!X$(Q))return[];return U8(Q).filter((Z)=>Z.startsWith("cp-")).filter((Z)=>{try{return V8(m(Q,Z)).isDirectory()}catch{return!1}})}function M1($){return[...$].sort((Q,Z)=>{let z=d0(Q),X=d0(Z);return z-X})}function d0($){let Q=$.split("-");if(Q.length<3)return 0;let Z=Q[Q.length-1],z=Number.parseInt(Z??"0",10);return Number.isFinite(z)?z:0}function P8($){let Q=Y1($);if(Q.length<=p0)return;let Z=M1(Q),z=Z.slice(0,Z.length-p0);for(let X of z)try{J8(m(o($),X),{recursive:!0,force:!0})}catch{}F8($)}function F8($){let Q=M1(Y1($)),Z=[];for(let W of Q){let K=m(o($),W,"metadata.json"),U=m(o($),W);if(!X$(K)){J1($,U,"missing_field","metadata.json");continue}try{let V=JSON.parse(B1(K,"utf-8")),H=$6(V,K);if(!H.ok){J1($,U,H.reason,H.field);continue}let J=H.value;Z.push(i0({id:J.id,ts:J.timestamp,iter:J.iteration,task:J.task_description??"",sha:J.git_sha}))}catch{J1($,U,"invalid_type","metadata.json")}}let z=s0($),X=Z.length>0?`${Z.join(`
468
468
  `)}
469
- `:"";r0(z,X)}function J1($,Q,Z,z){let X=g($,"events.jsonl"),W={timestamp:new Date().toISOString(),type:"checkpoint.metadata.dropped",checkpoint_dir:Q,reason:Z,field:z};try{G$(y$(X),{recursive:!0}),H1(X,()=>{a0(X,`${JSON.stringify(W)}
470
- `)})}catch{}}function T1($){let Q=$??P(),Z=M1(Y1(Q)),z=[];for(let X of Z){let W=e0(Q,X);if(W)z.push(W)}return z}function e0($,Q){let Z=g(o($),Q,"metadata.json");if(!X$(Z))return null;try{let z=JSON.parse(B1(Z,"utf-8"));return j8(z,Z)}catch{return null}}function j8($,Q){let Z=$6($,Q);return Z.ok?Z.value:null}function $6($,Q){if($===null||typeof $!=="object")return console.warn(`[checkpoint] invalid metadata at ${Q}: not an object`),{ok:!1,reason:"invalid_type",field:"<root>"};let Z=$,z=["id","timestamp","task_id","task_description","git_sha","git_branch","provider","phase"];for(let X of z){if(!(X in Z))return console.warn(`[checkpoint] invalid metadata at ${Q}: field "${X}" missing`),{ok:!1,reason:"missing_field",field:X};if(typeof Z[X]!=="string")return console.warn(`[checkpoint] invalid metadata at ${Q}: field "${X}" not a string`),{ok:!1,reason:"invalid_type",field:X}}if(!Object.prototype.hasOwnProperty.call(Z,"iteration"))return console.warn(`[checkpoint] invalid metadata at ${Q}: field "iteration" missing`),{ok:!1,reason:"missing_field",field:"iteration"};if(typeof Z.iteration!=="number"||!Number.isFinite(Z.iteration))return console.warn(`[checkpoint] invalid metadata at ${Q}: field "iteration" not a finite number`),{ok:!1,reason:"invalid_type",field:"iteration"};for(let X of R8){let W=Z[X];if(k8.test(W))return console.warn(`[checkpoint] invalid metadata at ${Q}: field "${X}" contains control characters`),{ok:!1,reason:"control_chars",field:X}}return{ok:!0,value:{id:Z.id,timestamp:Z.timestamp,iteration:Z.iteration,task_id:Z.task_id,task_description:Z.task_description,git_sha:Z.git_sha,git_branch:Z.git_branch,provider:Z.provider,phase:Z.phase}}}function O1($,Q){if(!E8.test($))throw new Q6($);let Z=Q??P(),z=g(o(Z),$);if(!X$(z))throw new G1($);let X=e0(Z,$);if(!X)throw new G1($);return X}function Z6($,Q){let Z=O1($,Q),z=Q??P(),X=g(o(z),$),W=[];for(let K of x8){let U=g(X,K);if(!X$(U))continue;W.push({from:U,to:g(z,K)})}return{id:$,metadata:Z,restore:W}}function N8($){let Q=[],Z=0;for(let z of $.restore)try{G$(y$(z.to),{recursive:!0});let X=`${z.to}.tmp.${process.pid}.${++t0}`;o0(z.from,X),n0(X,z.to),Z+=1}catch(X){Q.push(`${z.from} -> ${z.to}: ${X.message}`)}return{restored:Z,errors:Q}}async function z6($,Q,Z=!1){let z=null;try{let W=await I8({taskDescription:`pre-rollback snapshot (before restoring ${$.id})`,taskId:"rollback",forceCreate:!0,lokiDirOverride:Q});if(W.created)z=W.id}catch(W){let K=W instanceof Error?W.message:String(W);if(!Z)throw Error("pre-rollback snapshot failed ("+K+"); aborting rollback to preserve current state. Re-run with force to roll back anyway without a safety snapshot.");console.warn("[checkpoint] pre-rollback snapshot failed; proceeding due to force:",K)}let X=N8($);return{preRollbackSnapshotId:z,restored:X.restored,errors:X.errors}}var p0=50,G8=200,l0,A8,t0=0,k8,R8,E8,G1,Q6,x8;var X6=L(()=>{C();d();h$();l0=Promise.resolve();A8=["state/orchestrator.json","autonomy-state.json","queue/pending.json","queue/completed.json","queue/in-progress.json","queue/current-task.json","CONTINUITY.md"];k8=/[\x00-\x08\x0a-\x1f\x7f-\x9f]/,R8=["id","task_id","git_sha","git_branch","provider","phase"];E8=/^[a-zA-Z0-9_-]+$/;G1=class G1 extends Error{id;constructor($){super(`Checkpoint not found: ${$}`);this.id=$;this.name="CheckpointNotFoundError"}};Q6=class Q6 extends Error{id;constructor($){super(`Invalid checkpoint ID: must be alphanumeric, hyphens, underscores only (got: ${$})`);this.id=$;this.name="InvalidCheckpointIdError"}};x8=["state/orchestrator.json","queue/pending.json","queue/completed.json","queue/in-progress.json","queue/current-task.json","CONTINUITY.md"]});var q6={};h(q6,{runRollback:()=>S8});async function S8($){let Q=$[0],Z=$.slice(1);if(Q===void 0||Q==="help"||Q==="--help"||Q==="-h")return process.stdout.write(K6),Q===void 0?1:0;switch(Q){case"list":{let z=[...T1()].reverse();if(z.length===0)return process.stdout.write(`${I}No checkpoints found.${q}
469
+ `:"";r0(z,X)}function J1($,Q,Z,z){let X=m($,"events.jsonl"),W={timestamp:new Date().toISOString(),type:"checkpoint.metadata.dropped",checkpoint_dir:Q,reason:Z,field:z};try{G$(y$(X),{recursive:!0}),H1(X,()=>{a0(X,`${JSON.stringify(W)}
470
+ `)})}catch{}}function T1($){let Q=$??P(),Z=M1(Y1(Q)),z=[];for(let X of Z){let W=e0(Q,X);if(W)z.push(W)}return z}function e0($,Q){let Z=m(o($),Q,"metadata.json");if(!X$(Z))return null;try{let z=JSON.parse(B1(Z,"utf-8"));return j8(z,Z)}catch{return null}}function j8($,Q){let Z=$6($,Q);return Z.ok?Z.value:null}function $6($,Q){if($===null||typeof $!=="object")return console.warn(`[checkpoint] invalid metadata at ${Q}: not an object`),{ok:!1,reason:"invalid_type",field:"<root>"};let Z=$,z=["id","timestamp","task_id","task_description","git_sha","git_branch","provider","phase"];for(let X of z){if(!(X in Z))return console.warn(`[checkpoint] invalid metadata at ${Q}: field "${X}" missing`),{ok:!1,reason:"missing_field",field:X};if(typeof Z[X]!=="string")return console.warn(`[checkpoint] invalid metadata at ${Q}: field "${X}" not a string`),{ok:!1,reason:"invalid_type",field:X}}if(!Object.prototype.hasOwnProperty.call(Z,"iteration"))return console.warn(`[checkpoint] invalid metadata at ${Q}: field "iteration" missing`),{ok:!1,reason:"missing_field",field:"iteration"};if(typeof Z.iteration!=="number"||!Number.isFinite(Z.iteration))return console.warn(`[checkpoint] invalid metadata at ${Q}: field "iteration" not a finite number`),{ok:!1,reason:"invalid_type",field:"iteration"};for(let X of R8){let W=Z[X];if(k8.test(W))return console.warn(`[checkpoint] invalid metadata at ${Q}: field "${X}" contains control characters`),{ok:!1,reason:"control_chars",field:X}}return{ok:!0,value:{id:Z.id,timestamp:Z.timestamp,iteration:Z.iteration,task_id:Z.task_id,task_description:Z.task_description,git_sha:Z.git_sha,git_branch:Z.git_branch,provider:Z.provider,phase:Z.phase}}}function O1($,Q){if(!E8.test($))throw new Q6($);let Z=Q??P(),z=m(o(Z),$);if(!X$(z))throw new G1($);let X=e0(Z,$);if(!X)throw new G1($);return X}function Z6($,Q){let Z=O1($,Q),z=Q??P(),X=m(o(z),$),W=[];for(let K of x8){let U=m(X,K);if(!X$(U))continue;W.push({from:U,to:m(z,K)})}return{id:$,metadata:Z,restore:W}}function N8($){let Q=[],Z=0;for(let z of $.restore)try{G$(y$(z.to),{recursive:!0});let X=`${z.to}.tmp.${process.pid}.${++t0}`;o0(z.from,X),n0(X,z.to),Z+=1}catch(X){Q.push(`${z.from} -> ${z.to}: ${X.message}`)}return{restored:Z,errors:Q}}async function z6($,Q,Z=!1){let z=null;try{let W=await I8({taskDescription:`pre-rollback snapshot (before restoring ${$.id})`,taskId:"rollback",forceCreate:!0,lokiDirOverride:Q});if(W.created)z=W.id}catch(W){let K=W instanceof Error?W.message:String(W);if(!Z)throw Error("pre-rollback snapshot failed ("+K+"); aborting rollback to preserve current state. Re-run with force to roll back anyway without a safety snapshot.");console.warn("[checkpoint] pre-rollback snapshot failed; proceeding due to force:",K)}let X=N8($);return{preRollbackSnapshotId:z,restored:X.restored,errors:X.errors}}var p0=50,G8=200,l0,A8,t0=0,k8,R8,E8,G1,Q6,x8;var X6=L(()=>{C();d();h$();l0=Promise.resolve();A8=["state/orchestrator.json","autonomy-state.json","queue/pending.json","queue/completed.json","queue/in-progress.json","queue/current-task.json","CONTINUITY.md"];k8=/[\x00-\x08\x0a-\x1f\x7f-\x9f]/,R8=["id","task_id","git_sha","git_branch","provider","phase"];E8=/^[a-zA-Z0-9_-]+$/;G1=class G1 extends Error{id;constructor($){super(`Checkpoint not found: ${$}`);this.id=$;this.name="CheckpointNotFoundError"}};Q6=class Q6 extends Error{id;constructor($){super(`Invalid checkpoint ID: must be alphanumeric, hyphens, underscores only (got: ${$})`);this.id=$;this.name="InvalidCheckpointIdError"}};x8=["state/orchestrator.json","queue/pending.json","queue/completed.json","queue/in-progress.json","queue/current-task.json","CONTINUITY.md"]});var q6={};h(q6,{runRollback:()=>S8});async function S8($){let Q=$[0],Z=$.slice(1);if(Q===void 0||Q==="help"||Q==="--help"||Q==="-h")return process.stdout.write(K6),Q===void 0?1:0;switch(Q){case"list":{let z=[...T1()].reverse();if(z.length===0)return process.stdout.write(`${I}No checkpoints found.${q}
471
471
  `),0;process.stdout.write(`${R}Checkpoints${q} (${z.length}, newest first):
472
472
  `);for(let X of z)process.stdout.write(` ${w}${X.id}${q} iter=${X.iteration} ${X.git_branch||"(no branch)"}@${(X.git_sha||"").slice(0,7)} ${X.timestamp}
473
473
  `);return 0}case"show":{let z=Z[0];if(!z)return process.stderr.write(`${T}Missing checkpoint id.${q} Use \`loki rollback list\`.
@@ -505,7 +505,7 @@ to the checkpoint's snapshot (if one was anchored at checkpoint time):
505
505
  git stash apply refs/loki/cp/<id>
506
506
 
507
507
  Re-run \`loki start\` to resume from the restored state.
508
- `;var U6=L(()=>{X6();c()});function D8(){return process.env.LOKI_TIER||"oss"}function V6($){let Q=D8();if(Q==="oss")return{allowed:!0,notes:[]};if(!process.env.LOKI_LICENSE_KEY)return{allowed:!1,notes:[`${I}LOKI_TIER='${Q}' requested but no LOKI_LICENSE_KEY set.${q}`,`Hosted/enterprise license verification is not available yet (capability: ${$}).`,"OSS users: leave LOKI_TIER unset (or 'oss') -- everything stays free."]};return{allowed:!0,notes:[`${I}LOKI_LICENSE_KEY set but the verification backend is not available yet (R9 seam).${q}`]}}var H6=L(()=>{c()});var G6={};h(G6,{runProof:()=>n8});import{existsSync as A$,readdirSync as C8,readFileSync as J6,mkdtempSync as b8,copyFileSync as h8,rmSync as y8}from"fs";import{join as i}from"path";import{tmpdir as v8}from"os";import{createInterface as g8}from"readline";import{readFile as m8}from"fs/promises";function e($){return $&&typeof $==="object"?$:{}}function p($){return $===void 0||$===null?"-":String($)}function w$(){return i(P(),"proofs")}function A1($){let Q=i(w$(),$,"proof.json");if(!A$(Q))return null;try{return JSON.parse(J6(Q,"utf8"))}catch{return{}}}function r($,Q){return $.length>=Q?$:$+" ".repeat(Q-$.length)}function u8(){let $=w$();if(!A$($))return process.stdout.write(`${I}No proofs found.${q} Run 'loki start' to generate one.
508
+ `;var U6=L(()=>{X6();c()});function D8(){return process.env.LOKI_TIER||"oss"}function V6($){let Q=D8();if(Q==="oss")return{allowed:!0,notes:[]};if(!process.env.LOKI_LICENSE_KEY)return{allowed:!1,notes:[`${I}LOKI_TIER='${Q}' requested but no LOKI_LICENSE_KEY set.${q}`,`Hosted/enterprise license verification is not available yet (capability: ${$}).`,"OSS users: leave LOKI_TIER unset (or 'oss') -- everything stays free."]};return{allowed:!0,notes:[`${I}LOKI_LICENSE_KEY set but the verification backend is not available yet (R9 seam).${q}`]}}var H6=L(()=>{c()});var G6={};h(G6,{runProof:()=>n8});import{existsSync as A$,readdirSync as C8,readFileSync as J6,mkdtempSync as b8,copyFileSync as h8,rmSync as y8}from"fs";import{join as i}from"path";import{tmpdir as v8}from"os";import{createInterface as m8}from"readline";import{readFile as g8}from"fs/promises";function e($){return $&&typeof $==="object"?$:{}}function p($){return $===void 0||$===null?"-":String($)}function w$(){return i(P(),"proofs")}function A1($){let Q=i(w$(),$,"proof.json");if(!A$(Q))return null;try{return JSON.parse(J6(Q,"utf8"))}catch{return{}}}function r($,Q){return $.length>=Q?$:$+" ".repeat(Q-$.length)}function u8(){let $=w$();if(!A$($))return process.stdout.write(`${I}No proofs found.${q} Run 'loki start' to generate one.
509
509
  `),0;let Q=[];try{Q=C8($,{withFileTypes:!0}).filter((z)=>z.isDirectory()).map((z)=>z.name).sort()}catch{Q=[]}let Z=[];for(let z of Q){let X=i($,z,"proof.json");if(!A$(X))continue;let W={};try{W=JSON.parse(J6(X,"utf8"))}catch{W={}}let K=p(W.run_id),U=p(W.generated_at),V=p(e(W.council).final_verdict),H=p(e(W.cost).usd),J=p(e(W.files_changed).count);Z.push(`${r(K,26)} ${r(U,20)} ${r(V,10)} ${r(H,9)} ${J}`)}if(Z.length===0)return process.stdout.write(`${I}No proofs found.${q} Run 'loki start' to generate one.
510
510
  `),0;process.stdout.write(`${r("RUN_ID",26)} ${r("GENERATED_AT",20)} ${r("VERDICT",10)} ${r("COST_USD",9)} FILES
511
511
  `);for(let z of Z)process.stdout.write(`${z}
@@ -520,7 +520,7 @@ Re-run \`loki start\` to resume from the restored state.
520
520
  `);for(let Z of["open","xdg-open","start"])try{if((await j([Z,Q],{timeoutMs:5000})).exitCode===0)return 0}catch{}return process.stdout.write(`
521
521
  Could not detect browser opener.
522
522
  `),process.stdout.write(`Please open in browser: ${Q}
523
- `),0}function l8($){return new Promise((Q)=>{let Z=g8({input:process.stdin,output:process.stdout});Z.question($,(z)=>{Z.close();let X=z.trim().toLowerCase();Q(X==="y"||X==="yes")})})}async function d8($,Q,Z){let z=V6("hosted_publish");for(let G of z.notes)process.stderr.write(`${G}
523
+ `),0}function l8($){return new Promise((Q)=>{let Z=m8({input:process.stdin,output:process.stdout});Z.question($,(z)=>{Z.close();let X=z.trim().toLowerCase();Q(X==="y"||X==="yes")})})}async function d8($,Q,Z){let z=V6("hosted_publish");for(let G of z.notes)process.stderr.write(`${G}
524
524
  `);let X=process.env.LOKI_HOSTED_ENDPOINT||"";if(!X)return process.stderr.write(`${I}Hosted publishing backend not available.${q}
525
525
  `),process.stderr.write(`There is no official Loki hosted service yet (R9 ships the seam, not a live backend).
526
526
  `),process.stderr.write(`To publish to your own hosted endpoint, set LOKI_HOSTED_ENDPOINT to its URL.
@@ -531,7 +531,7 @@ Could not detect browser opener.
531
531
  `),process.stdout.write(` endpoint: ${X}
532
532
  `),process.stdout.write(` payload: ${Q} (already redacted by the generator)
533
533
 
534
- `);let K;try{K=await m8(Q)}catch{return process.stderr.write(`${T}Could not read proof page: ${Q}${q}
534
+ `);let K;try{K=await g8(Q)}catch{return process.stderr.write(`${T}Could not read proof page: ${Q}${q}
535
535
  `),1}let U={"Content-Type":"text/html","X-Loki-Proof-Id":$},V=process.env.LOKI_LICENSE_KEY||"";if(V)U.Authorization=`Bearer ${V}`;let H;try{H=await fetch(X,{method:"POST",headers:U,body:new Uint8Array(K)})}catch(G){return process.stderr.write(`${T}Failed to reach hosted endpoint: ${String(G.message||G)}${q}
536
536
  `),process.stderr.write(`Check LOKI_HOSTED_ENDPOINT or publish to a gist: loki proof share ${$}
537
537
  `),1}let J=await H.text();if(!H.ok){if(process.stderr.write(`${T}Hosted endpoint returned HTTP ${H.status}.${q}
@@ -586,12 +586,12 @@ Options for 'share':
586
586
  --hosted Publish to LOKI_HOSTED_ENDPOINT (open-core seam; no official backend yet)
587
587
 
588
588
  Proofs are generated automatically at run completion (LOKI_PROOF=0 to opt out).
589
- `});var w6={};h(w6,{runCrash:()=>Z7});import{existsSync as M6,readdirSync as a8,readFileSync as s8}from"fs";import{join as T6}from"path";function _$($){return $===void 0||$===null?"-":String($)}function v$($,Q){return $.length>=Q?$:$+" ".repeat(Q-$.length)}function O6(){return T6(P(),"crash")}function w1(){let $=O6();if(!M6($))return[];try{return a8($,{withFileTypes:!0}).filter((Q)=>Q.isFile()&&Q.name.endsWith(".json")).map((Q)=>Q.name.slice(0,-5)).sort()}catch{return[]}}function r8($){if($.length===0)return!1;if($.includes("/")||$.includes("\\"))return!1;if($.includes(".."))return!1;return!0}function g$($){if(!r8($))return null;let Q=T6(O6(),`${$}.json`);if(!M6(Q))return null;try{return JSON.parse(s8(Q,"utf8"))}catch{return{}}}function i8(){let $=w1();if($.length===0)return process.stdout.write(`${I}No crash reports found.${q} Nothing has been captured in .loki/crash/.
589
+ `});var w6={};h(w6,{runCrash:()=>Z7});import{existsSync as M6,readdirSync as a8,readFileSync as s8}from"fs";import{join as T6}from"path";function _$($){return $===void 0||$===null?"-":String($)}function v$($,Q){return $.length>=Q?$:$+" ".repeat(Q-$.length)}function O6(){return T6(P(),"crash")}function w1(){let $=O6();if(!M6($))return[];try{return a8($,{withFileTypes:!0}).filter((Q)=>Q.isFile()&&Q.name.endsWith(".json")).map((Q)=>Q.name.slice(0,-5)).sort()}catch{return[]}}function r8($){if($.length===0)return!1;if($.includes("/")||$.includes("\\"))return!1;if($.includes(".."))return!1;return!0}function m$($){if(!r8($))return null;let Q=T6(O6(),`${$}.json`);if(!M6(Q))return null;try{return JSON.parse(s8(Q,"utf8"))}catch{return{}}}function i8(){let $=w1();if($.length===0)return process.stdout.write(`${I}No crash reports found.${q} Nothing has been captured in .loki/crash/.
590
590
  `),0;process.stdout.write(`${v$("ID",40)} ${v$("CAPTURED_AT",22)} ERROR_CLASS
591
- `);for(let Q of $){let Z=g$(Q)??{},z=_$(Z.fingerprint),X=_$(Z.captured_at),W=_$(Z.error_class),K=z!=="-"?z:Q;process.stdout.write(`${v$(K,40)} ${v$(X,22)} ${W}
591
+ `);for(let Q of $){let Z=m$(Q)??{},z=_$(Z.fingerprint),X=_$(Z.captured_at),W=_$(Z.error_class),K=z!=="-"?z:Q;process.stdout.write(`${v$(K,40)} ${v$(X,22)} ${W}
592
592
  `)}return process.stdout.write(`
593
593
  ${$.length} report(s). Run 'loki crash show <id>' to inspect, 'loki crash submit' to get a prefilled GitHub issue URL.
594
- `),0}function A6($){let Q=g$($);if(Q!==null)return{id:$,report:Q};for(let Z of w1()){let z=g$(Z);if(z&&String(z.fingerprint??"")===$)return{id:Z,report:z}}return null}function e8($){if(!$)return process.stderr.write(`${T}Missing crash id.${q} Use 'loki crash' to list reports.
594
+ `),0}function A6($){let Q=m$($);if(Q!==null)return{id:$,report:Q};for(let Z of w1()){let z=m$(Z);if(z&&String(z.fingerprint??"")===$)return{id:Z,report:z}}return null}function e8($){if(!$)return process.stderr.write(`${T}Missing crash id.${q} Use 'loki crash' to list reports.
595
595
  `),2;let Q=A6($);if(Q===null)return process.stderr.write(`${T}Crash report not found: ${$}${q}
596
596
  `),process.stderr.write(`Use 'loki crash' to see available reports.
597
597
  `),1;return process.stdout.write(`${JSON.stringify(Q.report,null,2)}
@@ -599,7 +599,7 @@ ${$.length} report(s). Run 'loki crash show <id>' to inspect, 'loki crash submit
599
599
  `),U=new URLSearchParams({title:X,body:K});return`${t8}?${U.toString()}`}function Q7($){let Q;if($){if(Q=A6($),Q===null)return process.stderr.write(`${T}Crash report not found: ${$}${q}
600
600
  `),process.stderr.write(`Use 'loki crash' to see available reports.
601
601
  `),1}else{let Z=w1();if(Z.length===0)return process.stdout.write(`${I}No crash reports found.${q} Nothing to submit.
602
- `),0;let z=Z[Z.length-1],X=g$(z)??{};Q={id:z,report:X}}return process.stdout.write(`${R}Scrubbed payload (this is the ENTIRE report):${q}
602
+ `),0;let z=Z[Z.length-1],X=m$(z)??{};Q={id:z,report:X}}return process.stdout.write(`${R}Scrubbed payload (this is the ENTIRE report):${q}
603
603
  `),process.stdout.write(`${JSON.stringify(Q.report,null,2)}
604
604
 
605
605
  `),process.stdout.write(`${I}Nothing is sent automatically in this version.${q} Loki Mode never transmits crash data on its own.
@@ -629,7 +629,7 @@ Sections: architecture, modules, data-flow
629
629
  `),1;if(Q){if(!X7.has(Q))return process.stderr.write(`${T}No such section: ${Q} (try: architecture, modules, data-flow)${q}
630
630
  `),1;let X=I1(Z,`${Q}.md`);if(!_1(X))return process.stderr.write(`${T}Section not generated: ${Q}${q}
631
631
  `),1;return process.stdout.write(I6(X,"utf8")),0}let z=I1(Z,"index.md");if(!_1(z))return process.stderr.write(`${T}Wiki index not found. Run 'loki wiki generate'.${q}
632
- `),1;return process.stdout.write(I6(z,"utf8")),0}async function P6($,Q){let Z=z7(m,"autonomy","loki"),z=3600000,X=Bun.spawn({cmd:[Z,"wiki",$,...Q],stdin:"inherit",stdout:"inherit",stderr:"inherit",env:{...process.env,LOKI_LEGACY_BASH:"1"}}),W=setTimeout(()=>{try{X.kill("SIGKILL")}catch{}},3600000);try{return await X.exited}finally{clearTimeout(W)}}async function q7($){let Q=$[0],Z=$.slice(1);switch(Q){case void 0:case"help":case"--help":case"-h":return process.stdout.write(L6),0;case"show":return W7(Z);case"generate":return P6("generate",Z);case"ask":return P6("ask",Z);default:return process.stderr.write(`${T}Unknown wiki command: ${Q}${q}
632
+ `),1;return process.stdout.write(I6(z,"utf8")),0}async function P6($,Q){let Z=z7(g,"autonomy","loki"),z=3600000,X=Bun.spawn({cmd:[Z,"wiki",$,...Q],stdin:"inherit",stdout:"inherit",stderr:"inherit",env:{...process.env,LOKI_LEGACY_BASH:"1"}}),W=setTimeout(()=>{try{X.kill("SIGKILL")}catch{}},3600000);try{return await X.exited}finally{clearTimeout(W)}}async function q7($){let Q=$[0],Z=$.slice(1);switch(Q){case void 0:case"help":case"--help":case"-h":return process.stdout.write(L6),0;case"show":return W7(Z);case"generate":return P6("generate",Z);case"ask":return P6("ask",Z);default:return process.stderr.write(`${T}Unknown wiki command: ${Q}${q}
633
633
  `),process.stdout.write(L6),1}}var L6,X7;var j6=L(()=>{C();c();L6=`${R}loki wiki${q} - Auto-generated, cited codebase wiki + Q&A
634
634
 
635
635
  Usage: loki wiki <command> [options]
@@ -646,8 +646,8 @@ Examples:
646
646
  loki wiki generate
647
647
  loki wiki show architecture
648
648
  loki wiki ask "how does the cli dispatch commands"
649
- `,X7=new Set(["architecture","modules","data-flow"])});var P1={};h(P1,{renderFindingsForPrompt:()=>G7,loadPreviousFindings:()=>L1,findLatestReviewDir:()=>N6,_parseReviewerOutputForTests:()=>B7});import{existsSync as R6,readFileSync as k6,readdirSync as E6,statSync as U7}from"fs";import{join as m$}from"path";function J7($){let Q=$.toLowerCase();if(Q==="critical")return"Critical";if(Q==="high")return"High";if(Q==="medium")return"Medium";return"Low"}function x6($,Q,Z,z){let X=[],W=$.split(/\r?\n/);for(let K of W){let U=K.trim();if(U.length===0)continue;let V=U.replace(/^[-*]\s*/,""),H=V7.exec(V);if(!H||!H[1]||!H[2])continue;let J=J7(H[1]),Y=H[2].trim(),G=H7.exec(Y),B=G&&G[1]?G[1]:null,O=G&&G[2]?Number.parseInt(G[2],10):null;X.push({reviewId:Z,iteration:z,reviewer:Q,severity:J,description:Y,file:B,line:Number.isFinite(O)?O:null,raw:U})}return X}function N6($,Q){let Z=m$($,"quality","reviews");if(!R6(Z))return null;let z;try{z=E6(Z)}catch{return null}let X=Q===void 0?z.filter((U)=>U.startsWith("review-")):z.filter((U)=>U.endsWith(`-${Q}`)&&U.startsWith("review-"));if(X.length===0)return null;X.sort();let W=X[X.length-1];if(!W)return null;let K=m$(Z,W);try{if(!U7(K).isDirectory())return null}catch{return null}return K}function L1($,Q){let Z=N6($,Q);if(Z===null)return{reviewDir:null,reviewId:null,iteration:null,findings:[]};let z=null,X=null,W=m$(Z,"aggregate.json");if(R6(W))try{let H=k6(W,"utf-8"),J=JSON.parse(H);if(typeof J.review_id==="string")z=J.review_id;if(typeof J.iteration==="number")X=J.iteration}catch{}let K;try{K=E6(Z)}catch{return{reviewDir:Z,reviewId:z,iteration:X,findings:[]}}let U=new Set(["diff.txt","files.txt","anti-sycophancy.txt"]),V=[];for(let H of K){if(!H.endsWith(".txt"))continue;if(U.has(H))continue;if(H.endsWith("-prompt.txt"))continue;let J=H.replace(/\.txt$/,""),Y;try{Y=k6(m$(Z,H),"utf-8")}catch{continue}V.push(...x6(Y,J,z??"",X??-1))}return{reviewDir:Z,reviewId:z,iteration:X,findings:V}}function G7($){if($.length===0)return"";let Q=["Critical","High","Medium","Low"],Z=new Map;for(let X of Q)Z.set(X,[]);for(let X of $){let W=Z.get(X.severity);if(W)W.push(X)}let z=[];z.push("PREVIOUS REVIEWER FINDINGS (must address each, or supply counter-evidence in .loki/state/counter-evidence-<iter>.json):");for(let X of Q){let W=Z.get(X)??[];if(W.length===0)continue;z.push(` [${X}] (${W.length}):`);for(let K of W){let U=K.file?` (${K.file}${K.line!==null?":"+K.line:""})`:"";z.push(` - ${K.description}${U} -- via ${K.reviewer}`)}}return z.join(`
650
- `)}function B7($,Q,Z="review-test",z=0){return x6($,Q,Z,z)}var V7,H7;var f$=L(()=>{V7=/\[(Critical|High|Medium|Low)\]\s*(.+)/i,H7=/([\w.\-/]+\.[a-zA-Z]+):(\d+)/});import{existsSync as Y7}from"fs";import{join as M7}from"path";async function S6($,Q){let Z=M7($,"memory");if(!Y7(Z))return{stored:!1,reason:"memory dir not initialized"};let z=Math.max(0,Math.floor(Q.durationSeconds??0)),X={_LOKI_PROJECT_DIR:m,_LOKI_TARGET_DIR:process.cwd(),_LOKI_TASK_ID:Q.taskId,_LOKI_OUTCOME:Q.outcome,_LOKI_PHASE:Q.phase,_LOKI_GOAL:Q.goal,_LOKI_DURATION:String(z),_LOKI_LOKI_DIR:$},K=await Z$(`
649
+ `,X7=new Set(["architecture","modules","data-flow"])});var P1={};h(P1,{renderFindingsForPrompt:()=>G7,loadPreviousFindings:()=>L1,findLatestReviewDir:()=>N6,_parseReviewerOutputForTests:()=>B7});import{existsSync as R6,readFileSync as k6,readdirSync as E6,statSync as U7}from"fs";import{join as g$}from"path";function J7($){let Q=$.toLowerCase();if(Q==="critical")return"Critical";if(Q==="high")return"High";if(Q==="medium")return"Medium";return"Low"}function x6($,Q,Z,z){let X=[],W=$.split(/\r?\n/);for(let K of W){let U=K.trim();if(U.length===0)continue;let V=U.replace(/^[-*]\s*/,""),H=V7.exec(V);if(!H||!H[1]||!H[2])continue;let J=J7(H[1]),Y=H[2].trim(),G=H7.exec(Y),B=G&&G[1]?G[1]:null,O=G&&G[2]?Number.parseInt(G[2],10):null;X.push({reviewId:Z,iteration:z,reviewer:Q,severity:J,description:Y,file:B,line:Number.isFinite(O)?O:null,raw:U})}return X}function N6($,Q){let Z=g$($,"quality","reviews");if(!R6(Z))return null;let z;try{z=E6(Z)}catch{return null}let X=Q===void 0?z.filter((U)=>U.startsWith("review-")):z.filter((U)=>U.endsWith(`-${Q}`)&&U.startsWith("review-"));if(X.length===0)return null;X.sort();let W=X[X.length-1];if(!W)return null;let K=g$(Z,W);try{if(!U7(K).isDirectory())return null}catch{return null}return K}function L1($,Q){let Z=N6($,Q);if(Z===null)return{reviewDir:null,reviewId:null,iteration:null,findings:[]};let z=null,X=null,W=g$(Z,"aggregate.json");if(R6(W))try{let H=k6(W,"utf-8"),J=JSON.parse(H);if(typeof J.review_id==="string")z=J.review_id;if(typeof J.iteration==="number")X=J.iteration}catch{}let K;try{K=E6(Z)}catch{return{reviewDir:Z,reviewId:z,iteration:X,findings:[]}}let U=new Set(["diff.txt","files.txt","anti-sycophancy.txt"]),V=[];for(let H of K){if(!H.endsWith(".txt"))continue;if(U.has(H))continue;if(H.endsWith("-prompt.txt"))continue;let J=H.replace(/\.txt$/,""),Y;try{Y=k6(g$(Z,H),"utf-8")}catch{continue}V.push(...x6(Y,J,z??"",X??-1))}return{reviewDir:Z,reviewId:z,iteration:X,findings:V}}function G7($){if($.length===0)return"";let Q=["Critical","High","Medium","Low"],Z=new Map;for(let X of Q)Z.set(X,[]);for(let X of $){let W=Z.get(X.severity);if(W)W.push(X)}let z=[];z.push("PREVIOUS REVIEWER FINDINGS (must address each, or supply counter-evidence in .loki/state/counter-evidence-<iter>.json):");for(let X of Q){let W=Z.get(X)??[];if(W.length===0)continue;z.push(` [${X}] (${W.length}):`);for(let K of W){let U=K.file?` (${K.file}${K.line!==null?":"+K.line:""})`:"";z.push(` - ${K.description}${U} -- via ${K.reviewer}`)}}return z.join(`
650
+ `)}function B7($,Q,Z="review-test",z=0){return x6($,Q,Z,z)}var V7,H7;var f$=L(()=>{V7=/\[(Critical|High|Medium|Low)\]\s*(.+)/i,H7=/([\w.\-/]+\.[a-zA-Z]+):(\d+)/});import{existsSync as Y7}from"fs";import{join as M7}from"path";async function S6($,Q){let Z=M7($,"memory");if(!Y7(Z))return{stored:!1,reason:"memory dir not initialized"};let z=Math.max(0,Math.floor(Q.durationSeconds??0)),X={_LOKI_PROJECT_DIR:g,_LOKI_TARGET_DIR:process.cwd(),_LOKI_TASK_ID:Q.taskId,_LOKI_OUTCOME:Q.outcome,_LOKI_PHASE:Q.phase,_LOKI_GOAL:Q.goal,_LOKI_DURATION:String(z),_LOKI_LOKI_DIR:$},K=await Z$(`
651
651
  import os, sys
652
652
  project = os.environ.get('_LOKI_PROJECT_DIR', '')
653
653
  loki = os.environ.get('_LOKI_LOKI_DIR', '.loki')
@@ -675,14 +675,14 @@ try:
675
675
  except Exception as e:
676
676
  print('ERR:' + str(e))
677
677
  `,{env:X,timeoutMs:15000});if(K.exitCode===127)return{stored:!1,reason:"python3 not found"};let U=K.stdout.trim();if(U==="OK")return{stored:!0,reason:"stored"};if(U.startsWith("ERR:"))return{stored:!1,reason:U.replace(/^ERR:/,"")};return{stored:!1,reason:K.stderr.trim()||"unknown"}}var D6=L(()=>{W$();C()});var y6={};h(y6,{loadLearnings:()=>F1,appendLearning:()=>I$,appendFromGateFailure:()=>P7});import{existsSync as T7,readFileSync as O7}from"fs";import{join as C6}from"path";import{createHash as A7}from"crypto";function b6($){return C6($,w7)}function _7($){if($===null||typeof $!=="object")return!1;let Q=$;return typeof Q.id==="string"&&typeof Q.timestamp==="string"&&typeof Q.iteration==="number"&&typeof Q.trigger==="string"&&typeof Q.rootCause==="string"&&typeof Q.fix==="string"&&typeof Q.preventInFuture==="string"&&typeof Q.evidence==="object"&&Q.evidence!==null}function h6($){if(!T7($))return{version:1,learnings:[]};try{let Q=O7($,"utf-8"),Z=JSON.parse(Q);if(Z.version===1&&Array.isArray(Z.learnings))return{version:1,learnings:Z.learnings.filter(_7)}}catch{}return{version:1,learnings:[]}}function I7($,Q){return A7("sha256").update(`${$}\x00${Q}`).digest("hex").slice(0,16)}async function I$($,Q,Z={}){let z=I7(Q.trigger,Q.rootCause),X=new Date().toISOString(),W={id:z,timestamp:X,...Q},K=b6($);if(await c0(K,()=>{let V=h6(K),H=V.learnings.findIndex((J)=>J.id===z);if(H>=0){let J=V.learnings[H];V.learnings[H]={...J,timestamp:X,iteration:W.iteration}}else V.learnings.push(W);O$(K,V)}),Z.episodeBridge!==null&&(Z.episodeBridge!==void 0||process.env.LOKI_AUTO_LEARNINGS_EPISODE==="1")){let V=Z.episodeBridge??S6,H=Z.bridgeFailureLog??L7;try{let J=await V($,{taskId:`learning-${z}`,outcome:"failure",phase:"VERIFY",goal:`${Q.trigger}: ${Q.rootCause}`});if(J&&!J.stored){if(!new Set(["memory dir not initialized","stub"]).has(J.reason))H(`episode_bridge skipped: ${J.reason}`)}}catch(J){H(`episode_bridge threw: ${J.message}`)}}return W}function L7($){process.stderr.write(`[learnings_writer] ${$}
678
- `)}async function P7($,Q,Z,z={}){let X=`[${Z.severity}] ${Z.description}`;return I$($,{iteration:Q,trigger:"gate_failure",rootCause:X,fix:"pending: dev agent must address in next iteration or supply counter-evidence",preventInFuture:"if this finding recurs, lower its severity threshold or add a regression test",evidence:{reviewId:Z.reviewId,file:Z.file??void 0,line:Z.line??void 0,severity:Z.severity,reviewer:Z.reviewer}},z)}function F1($){return h6(b6($))}var w7;var u$=L(()=>{h$();D6();w7=C6("state","relevant-learnings.json")});var g6={};h(g6,{runOverrideCouncil:()=>x7,recordOverrideOutcome:()=>N7,loadCounterEvidence:()=>E7,canonicalFindingId:()=>j1,DEFAULT_OVERRIDE_JUDGES:()=>v6});import{existsSync as F7,readFileSync as j7}from"fs";import{join as k7}from"path";function E7($,Q){let Z=k7($,"state",`counter-evidence-${Q}.json`);if(!F7(Z))return null;try{let z=j7(Z,"utf-8"),X=JSON.parse(z);if(typeof X.iteration!=="number")return null;let W=Array.isArray(X.evidence)?X.evidence:[],K=[];for(let U of W){if(typeof U!=="object"||U===null)continue;let V=U;if(typeof V.findingId!=="string")continue;if(typeof V.claim!=="string")continue;let H=V.proofType;if(typeof H!=="string"||!R7.has(H))continue;let J=H,Y=Array.isArray(V.artifacts)?V.artifacts:[];K.push({findingId:V.findingId,claim:V.claim,proofType:J,artifacts:Y.filter((G)=>typeof G==="string")})}return{iteration:X.iteration,evidence:K}}catch{return null}}async function x7($,Q,Z,z={}){let X=z.judges??v6,W=new Set,K=new Set,U={},V=new Map;for(let H of Q.evidence)V.set(H.findingId,H);for(let H of $){let J=j1(H),Y=V.get(J);if(!Y){K.add(J);continue}let G=await Promise.all(X.map((O)=>Z({finding:H,evidence:Y,judge:O})));if(U[J]=G,G.filter((O)=>O.verdict==="APPROVE_OVERRIDE").length>=2)W.add(J);else K.add(J)}return{approvedFindingIds:W,rejectedFindingIds:K,votes:U}}function j1($){let Q=$.raw.slice(0,80).replace(/\s+/g," ").trim();return`${$.reviewer}::${Q}`}async function N7($,Q,Z,z,X={}){let W={episodeBridge:X.episodeBridge===void 0?null:X.episodeBridge};for(let K of z){let U=j1(K);if(Z.approvedFindingIds.has(U))await I$($,{iteration:Q,trigger:"override_approved",rootCause:`[${K.severity}] ${K.description}`,fix:"override council approved counter-evidence; finding lifted",preventInFuture:"if this reviewer/file pair recurs, narrow the reviewer's selector OR add a baseline doc",evidence:{findingId:U,reviewId:K.reviewId,file:K.file??void 0,line:K.line??void 0,severity:K.severity,reviewer:K.reviewer}},W);else if(Z.rejectedFindingIds.has(U))await I$($,{iteration:Q,trigger:"override_rejected",rootCause:`[${K.severity}] ${K.description}`,fix:"override council rejected -- dev agent must fix the finding",preventInFuture:"address this finding in the next iteration",evidence:{findingId:U,reviewId:K.reviewId,file:K.file??void 0,line:K.line??void 0,severity:K.severity,reviewer:K.reviewer}},W)}}var R7,v6;var m6=L(()=>{u$();R7=new Set(["file-exists","test-passes","grep-miss","reviewer-misread","duplicate-code-path","out-of-scope"]);v6=["judge-primary","judge-secondary","judge-tertiary"]});var c6={};h(c6,{writeEscalationHandoff:()=>c7,renderHandoff:()=>f6,readLatestHandoff:()=>p7});import{existsSync as S7,mkdirSync as D7,readdirSync as C7,readFileSync as b7,renameSync as h7,writeFileSync as y7}from"fs";import{dirname as v7,join as c$}from"path";function g7(){return new Date().toISOString()}function m7($){let Q=$.file?` (${$.file}${$.line!==null?":"+$.line:""})`:"";return` - [${$.severity}] ${$.description}${Q} -- ${$.reviewer}`}function f7($){let Q=$.evidence,Z=Q.file?` ${Q.file}${Q.line!==void 0?":"+Q.line:""}`:"";return` - **${$.trigger}** (iter ${$.iteration})${Z}: ${$.rootCause}`}function f6($,Q,Z){let z=[];if(z.push(`# Loki escalation handoff -- ${g7()}`),z.push(""),z.push(`Gate **${$.gateName}** has failed ${$.consecutiveFailures} consecutive times at iteration ${$.iteration}.`),z.push(""),z.push(`Reason: ${$.detail}`),z.push(""),Q.length>0){z.push(`## Outstanding findings (${Q.length})`),z.push("");for(let X of Q)z.push(m7(X));z.push("")}else z.push("## Outstanding findings"),z.push(""),z.push("(no per-finding records captured -- gate failed without populating reviewer outputs)"),z.push("");if(Z.length>0){z.push(`## Recent learnings (${Math.min(Z.length,10)})`),z.push("");for(let X of Z.slice(-10))z.push(f7(X));z.push("")}return z.push("## What the human must decide"),z.push(""),z.push("- Approve override? Write `.loki/state/counter-evidence-<iter>.json` with one entry per finding to dispute, then `rm .loki/PAUSE` to resume."),z.push("- Disable a gate? Set `LOKI_GATE_<NAME>=false` in env (see skills/quality-gates.md)."),z.push("- Tweak escalation? Set `LOKI_GATE_PAUSE_LIMIT` or `LOKI_GATE_ESCALATE_LIMIT`."),z.push("- Roll back? Switch to `LOKI_LEGACY_BASH=1` and re-run; the bash route does not consult this handoff doc."),z.push(""),z.push("To resume: address the findings (or supply counter-evidence) and `rm .loki/PAUSE`."),z.join(`
678
+ `)}async function P7($,Q,Z,z={}){let X=`[${Z.severity}] ${Z.description}`;return I$($,{iteration:Q,trigger:"gate_failure",rootCause:X,fix:"pending: dev agent must address in next iteration or supply counter-evidence",preventInFuture:"if this finding recurs, lower its severity threshold or add a regression test",evidence:{reviewId:Z.reviewId,file:Z.file??void 0,line:Z.line??void 0,severity:Z.severity,reviewer:Z.reviewer}},z)}function F1($){return h6(b6($))}var w7;var u$=L(()=>{h$();D6();w7=C6("state","relevant-learnings.json")});var m6={};h(m6,{runOverrideCouncil:()=>x7,recordOverrideOutcome:()=>N7,loadCounterEvidence:()=>E7,canonicalFindingId:()=>j1,DEFAULT_OVERRIDE_JUDGES:()=>v6});import{existsSync as F7,readFileSync as j7}from"fs";import{join as k7}from"path";function E7($,Q){let Z=k7($,"state",`counter-evidence-${Q}.json`);if(!F7(Z))return null;try{let z=j7(Z,"utf-8"),X=JSON.parse(z);if(typeof X.iteration!=="number")return null;let W=Array.isArray(X.evidence)?X.evidence:[],K=[];for(let U of W){if(typeof U!=="object"||U===null)continue;let V=U;if(typeof V.findingId!=="string")continue;if(typeof V.claim!=="string")continue;let H=V.proofType;if(typeof H!=="string"||!R7.has(H))continue;let J=H,Y=Array.isArray(V.artifacts)?V.artifacts:[];K.push({findingId:V.findingId,claim:V.claim,proofType:J,artifacts:Y.filter((G)=>typeof G==="string")})}return{iteration:X.iteration,evidence:K}}catch{return null}}async function x7($,Q,Z,z={}){let X=z.judges??v6,W=new Set,K=new Set,U={},V=new Map;for(let H of Q.evidence)V.set(H.findingId,H);for(let H of $){let J=j1(H),Y=V.get(J);if(!Y){K.add(J);continue}let G=await Promise.all(X.map((O)=>Z({finding:H,evidence:Y,judge:O})));if(U[J]=G,G.filter((O)=>O.verdict==="APPROVE_OVERRIDE").length>=2)W.add(J);else K.add(J)}return{approvedFindingIds:W,rejectedFindingIds:K,votes:U}}function j1($){let Q=$.raw.slice(0,80).replace(/\s+/g," ").trim();return`${$.reviewer}::${Q}`}async function N7($,Q,Z,z,X={}){let W={episodeBridge:X.episodeBridge===void 0?null:X.episodeBridge};for(let K of z){let U=j1(K);if(Z.approvedFindingIds.has(U))await I$($,{iteration:Q,trigger:"override_approved",rootCause:`[${K.severity}] ${K.description}`,fix:"override council approved counter-evidence; finding lifted",preventInFuture:"if this reviewer/file pair recurs, narrow the reviewer's selector OR add a baseline doc",evidence:{findingId:U,reviewId:K.reviewId,file:K.file??void 0,line:K.line??void 0,severity:K.severity,reviewer:K.reviewer}},W);else if(Z.rejectedFindingIds.has(U))await I$($,{iteration:Q,trigger:"override_rejected",rootCause:`[${K.severity}] ${K.description}`,fix:"override council rejected -- dev agent must fix the finding",preventInFuture:"address this finding in the next iteration",evidence:{findingId:U,reviewId:K.reviewId,file:K.file??void 0,line:K.line??void 0,severity:K.severity,reviewer:K.reviewer}},W)}}var R7,v6;var g6=L(()=>{u$();R7=new Set(["file-exists","test-passes","grep-miss","reviewer-misread","duplicate-code-path","out-of-scope"]);v6=["judge-primary","judge-secondary","judge-tertiary"]});var c6={};h(c6,{writeEscalationHandoff:()=>c7,renderHandoff:()=>f6,readLatestHandoff:()=>p7});import{existsSync as S7,mkdirSync as D7,readdirSync as C7,readFileSync as b7,renameSync as h7,writeFileSync as y7}from"fs";import{dirname as v7,join as c$}from"path";function m7(){return new Date().toISOString()}function g7($){let Q=$.file?` (${$.file}${$.line!==null?":"+$.line:""})`:"";return` - [${$.severity}] ${$.description}${Q} -- ${$.reviewer}`}function f7($){let Q=$.evidence,Z=Q.file?` ${Q.file}${Q.line!==void 0?":"+Q.line:""}`:"";return` - **${$.trigger}** (iter ${$.iteration})${Z}: ${$.rootCause}`}function f6($,Q,Z){let z=[];if(z.push(`# Loki escalation handoff -- ${m7()}`),z.push(""),z.push(`Gate **${$.gateName}** has failed ${$.consecutiveFailures} consecutive times at iteration ${$.iteration}.`),z.push(""),z.push(`Reason: ${$.detail}`),z.push(""),Q.length>0){z.push(`## Outstanding findings (${Q.length})`),z.push("");for(let X of Q)z.push(g7(X));z.push("")}else z.push("## Outstanding findings"),z.push(""),z.push("(no per-finding records captured -- gate failed without populating reviewer outputs)"),z.push("");if(Z.length>0){z.push(`## Recent learnings (${Math.min(Z.length,10)})`),z.push("");for(let X of Z.slice(-10))z.push(f7(X));z.push("")}return z.push("## What the human must decide"),z.push(""),z.push("- Approve override? Write `.loki/state/counter-evidence-<iter>.json` with one entry per finding to dispute, then `rm .loki/PAUSE` to resume."),z.push("- Disable a gate? Set `LOKI_GATE_<NAME>=false` in env (see skills/quality-gates.md)."),z.push("- Tweak escalation? Set `LOKI_GATE_PAUSE_LIMIT` or `LOKI_GATE_ESCALATE_LIMIT`."),z.push("- Roll back? Switch to `LOKI_LEGACY_BASH=1` and re-run; the bash route does not consult this handoff doc."),z.push(""),z.push("To resume: address the findings (or supply counter-evidence) and `rm .loki/PAUSE`."),z.join(`
679
679
  `)}function u7($,Q){D7(v7($),{recursive:!0});let Z=`${$}.tmp.${process.pid}.${++u6}`;y7(Z,Q),h7(Z,$)}function c7($,Q,Z={}){let z=Z.findings??L1($,Q.iteration).findings,X=Z.learnings??F1($).learnings,W=f6(Q,z,X),K=(Z.now?.()??new Date).toISOString().replace(/[-:.]/g,""),U=c$($,"escalations"),V=++u6,H=c$(U,`handoff-${K}-${process.pid}-${V}-${Q.gateName}.md`);return u7(H,W),{path:H,bytes:W.length}}function p7($){let Q=c$($,"escalations");if(!S7(Q))return null;let Z;try{Z=C7(Q).filter((W)=>W.endsWith(".md"))}catch{return null}if(Z.length===0)return null;Z.sort();let z=Z[Z.length-1];if(!z)return null;let X=c$(Q,z);try{return{path:X,body:b7(X,"utf-8")}}catch{return null}}var u6=0;var p6=L(()=>{f$();u$()});var l6={};h(l6,{runInternalPhase1Hooks:()=>s7,_resolveForTests:()=>a7,_internalPhase1HooksHelp:()=>e7});import{existsSync as l7,mkdirSync as d7,readdirSync as o7,statSync as n7}from"fs";import{join as L$,resolve as a7}from"path";async function s7($){let[Q,...Z]=$;switch(Q){case void 0:case"help":case"--help":case"-h":return process.stdout.write(k1),Q===void 0?1:0;case"reflect":return t7(Z);case"override":return r7(Z);case"handoff":return i7(Z);default:return process.stderr.write(`Unknown subcommand: ${Q}
680
680
  `),process.stderr.write(k1),2}}async function t7($){let Q=R1($[0]);if(Q===null)return process.stderr.write(`reflect: missing or invalid <iter>
681
681
  `),2;let Z=P();try{let X=(await Promise.resolve().then(() => (f$(),P1))).loadPreviousFindings(Z,Q);if(X.findings.length===0)return process.stdout.write(`reflect: no findings for iter ${Q} (nothing to do)
682
682
  `),0;let W=L$(Z,"state");d7(W,{recursive:!0}),O$(L$(W,`findings-${Q}.json`),{review_id:X.reviewId,iteration:Q,findings:X.findings});let K=await Promise.resolve().then(() => (u$(),y6)),U=0;if(process.env.LOKI_AUTO_LEARNINGS!=="0"){for(let V of X.findings)if(V.severity==="Critical"||V.severity==="High")await K.appendFromGateFailure(Z,Q,V,{episodeBridge:null}),U+=1}return process.stdout.write(`reflect: persisted ${X.findings.length} findings + ${U} learnings (iter ${Q})
683
683
  `),0}catch(z){return process.stderr.write(`reflect: ${z.message}
684
684
  `),1}}async function r7($){let Q=R1($[0]);if(Q===null)return process.stderr.write(`override: missing or invalid <iter>
685
- `),2;let Z=P();try{let z=await Promise.resolve().then(() => (m6(),g6)),X=z.loadCounterEvidence(Z,Q);if(X===null||X.evidence.length===0)return process.stdout.write(`override: no counter-evidence for iter ${Q} (skip)
685
+ `),2;let Z=P();try{let z=await Promise.resolve().then(() => (g6(),m6)),X=z.loadCounterEvidence(Z,Q);if(X===null||X.evidence.length===0)return process.stdout.write(`override: no counter-evidence for iter ${Q} (skip)
686
686
  `),0;let K=(await Promise.resolve().then(() => (f$(),P1))).loadPreviousFindings(Z,Q),U=K.findings.filter((M)=>M.severity==="Critical"||M.severity==="High");if(U.length===0)return process.stdout.write(`override: no blocking findings for iter ${Q} (skip)
687
687
  `),0;let V=new Set(["duplicate-code-path","file-exists","test-passes","grep-miss","out-of-scope"]),H=async(M)=>{let x=V.has(M.evidence.proofType);return{judge:M.judge,verdict:x?"APPROVE_OVERRIDE":"REJECT_OVERRIDE",reasoning:x?`[stub] proofType=${M.evidence.proofType} trusted`:`[stub] proofType=${M.evidence.proofType} requires manual review`}},J=await z.runOverrideCouncil(U,X,H);await z.recordOverrideOutcome(Z,Q,J,U);let Y=L$(Z,"quality","reviews");if(l7(Y))try{let M=o7(Y).filter((N)=>N.startsWith("review-")).sort(),x=M[M.length-1];if(x&&n7(L$(Y,x)).isDirectory())O$(L$(Y,x,`override-${Q}.json`),{review_id:K.reviewId,iteration:Q,approved_finding_ids:Array.from(J.approvedFindingIds),rejected_finding_ids:Array.from(J.rejectedFindingIds),votes:J.votes})}catch{}let G=J.approvedFindingIds.size,B=J.rejectedFindingIds.size;if(B===0&&G>0)process.stdout.write(`override: LIFTED -- ${G} approved, ${B} rejected
688
688
  `);else process.stdout.write(`override: BLOCKED -- ${G} approved, ${B} rejected
@@ -754,11 +754,11 @@ except ImportError:
754
754
  print('Error: memory.layers module not found')
755
755
  except Exception as e:
756
756
  print(f'Error: {e}')
757
- `.trim(),X=await Z$(z,{cwd:m});return process.stdout.write(X.stdout),0}let Q=q$(P(),"memory","index.json");if(!y1(Q))return process.stdout.write(`No index found
757
+ `.trim(),X=await Z$(z,{cwd:g});return process.stdout.write(X.stdout),0}let Q=q$(P(),"memory","index.json");if(!y1(Q))return process.stdout.write(`No index found
758
758
  `),0;let Z=await Z$(`import json, sys; sys.stdout.write(json.dumps(json.load(open(${JSON.stringify(Q)})), indent=4) + "\\n")`);if(Z.exitCode!==0)return process.stdout.write(`No index found
759
- `),0;return process.stdout.write(Z.stdout),0}async function v1($){switch($[0]??"list"){case"list":case"ls":return wQ();case"index":return _Q($[1]==="rebuild");default:{let Z=q$(m,"autonomy","loki"),z=3600000,X=Bun.spawn({cmd:[Z,"memory",...$],stdin:"inherit",stdout:"inherit",stderr:"inherit",env:{...process.env,LOKI_LEGACY_BASH:"1"}}),W=setTimeout(()=>{try{X.kill("SIGKILL")}catch{}},3600000);try{return await X.exited}finally{clearTimeout(W)}}}}C();W$();d();import{resolve as IQ,join as LQ}from"path";import{existsSync as t$,readFileSync as PQ}from"fs";import{homedir as FQ}from"os";import{spawnSync as u1}from"child_process";var c1=3000;function jQ(){if((process.env.LOKI_TELEMETRY??"").toLowerCase()==="off")return!1;if(process.env.LOKI_TELEMETRY_DISABLED==="true")return!1;if(process.env.DO_NOT_TRACK==="1")return!1;try{let $=LQ(FQ(),".loki","config");if(t$($)){let Q=PQ($,"utf8");for(let Z of Q.split(`
760
- `))if(Z.replace(/\r$/,"")==="TELEMETRY_DISABLED=true")return!1}}catch{}return!0}var j$=!1;function kQ(){return IQ(m,"autonomy","lib","crash_capture.py")}function RQ($,Q){let Z=[$,"--error-class",Q.errorClass,"--message",Q.message];if(Q.stack!==void 0)Z.push("--stack",Q.stack);if(Q.rarvPhase!==void 0)Z.push("--rarv-phase",Q.rarvPhase);if(Q.exitCode!==void 0)Z.push("--exit-code",String(Q.exitCode));if(Q.frictionKind!==void 0)Z.push("--friction-kind",Q.frictionKind);return Z.push("--target-dir",Q.targetDir??process.cwd()),Z}function EQ(){if(t$("/opt/homebrew/bin/python3.12"))return"/opt/homebrew/bin/python3.12";for(let Q of["python3.12","python3"])try{let Z=u1("sh",["-c",`command -v ${Q}`],{timeout:c1,encoding:"utf8"});if(Z.status===0){let z=(Z.stdout||"").trim();if(z)return z}}catch{}return null}function g1($){try{if(!jQ())return;let Q=kQ();if(!t$(Q))return;let Z=EQ();if(!Z)return;let z=RQ(Q,$);u1(Z,z,{timeout:c1,stdio:"ignore"})}catch{}}function m1($,Q){if($ instanceof Error){let z={errorClass:$.name&&$.name.length>0?$.name:Q,message:$.message};if($.stack)z.stack=$.stack;return z}return{errorClass:Q,message:String($)}}var f1=!1;function p1(){if(f1)return;f1=!0,process.on("uncaughtException",($)=>{if(!j$){j$=!0;let Q=m1($,"UncaughtException");g1({errorClass:Q.errorClass,message:Q.message,...Q.stack!==void 0?{stack:Q.stack}:{},exitCode:1})}try{process.stderr.write(`${$&&$.stack||String($)}
761
- `)}catch{}process.exit(1)}),process.on("unhandledRejection",($)=>{if(!j$){j$=!0;let Q=m1($,"UnhandledRejection");g1({errorClass:Q.errorClass,message:Q.message,...Q.stack!==void 0?{stack:Q.stack}:{},exitCode:1})}try{let Q=$ instanceof Error?$.stack||$.message:String($);process.stderr.write(`Unhandled promise rejection: ${Q}
759
+ `),0;return process.stdout.write(Z.stdout),0}async function v1($){switch($[0]??"list"){case"list":case"ls":return wQ();case"index":return _Q($[1]==="rebuild");default:{let Z=q$(g,"autonomy","loki"),z=3600000,X=Bun.spawn({cmd:[Z,"memory",...$],stdin:"inherit",stdout:"inherit",stderr:"inherit",env:{...process.env,LOKI_LEGACY_BASH:"1"}}),W=setTimeout(()=>{try{X.kill("SIGKILL")}catch{}},3600000);try{return await X.exited}finally{clearTimeout(W)}}}}C();W$();d();import{resolve as IQ,join as LQ}from"path";import{existsSync as t$,readFileSync as PQ}from"fs";import{homedir as FQ}from"os";import{spawnSync as u1}from"child_process";var c1=3000;function jQ(){let $=(process.env.LOKI_TELEMETRY??"").toLowerCase();if($==="off")return!1;if(process.env.LOKI_TELEMETRY_DISABLED==="true")return!1;if(process.env.DO_NOT_TRACK==="1")return!1;let Q=!1,Z=!1;try{let z=LQ(FQ(),".loki","config");if(t$(z)){let X=PQ(z,"utf8");for(let W of X.split(`
760
+ `)){let K=W.replace(/\r$/,"");if(K==="TELEMETRY_DISABLED=true")Q=!0;if(K==="TELEMETRY_ENABLED=true")Z=!0}}}catch{}if(Q)return!1;if($==="on"||Z)return!0;return!1}var j$=!1;function kQ(){return IQ(g,"autonomy","lib","crash_capture.py")}function RQ($,Q){let Z=[$,"--error-class",Q.errorClass,"--message",Q.message];if(Q.stack!==void 0)Z.push("--stack",Q.stack);if(Q.rarvPhase!==void 0)Z.push("--rarv-phase",Q.rarvPhase);if(Q.exitCode!==void 0)Z.push("--exit-code",String(Q.exitCode));if(Q.frictionKind!==void 0)Z.push("--friction-kind",Q.frictionKind);return Z.push("--target-dir",Q.targetDir??process.cwd()),Z}function EQ(){if(t$("/opt/homebrew/bin/python3.12"))return"/opt/homebrew/bin/python3.12";for(let Q of["python3.12","python3"])try{let Z=u1("sh",["-c",`command -v ${Q}`],{timeout:c1,encoding:"utf8"});if(Z.status===0){let z=(Z.stdout||"").trim();if(z)return z}}catch{}return null}function m1($){try{if(!jQ())return;let Q=kQ();if(!t$(Q))return;let Z=EQ();if(!Z)return;let z=RQ(Q,$);u1(Z,z,{timeout:c1,stdio:"ignore"})}catch{}}function g1($,Q){if($ instanceof Error){let z={errorClass:$.name&&$.name.length>0?$.name:Q,message:$.message};if($.stack)z.stack=$.stack;return z}return{errorClass:Q,message:String($)}}var f1=!1;function p1(){if(f1)return;f1=!0,process.on("uncaughtException",($)=>{if(!j$){j$=!0;let Q=g1($,"UncaughtException");m1({errorClass:Q.errorClass,message:Q.message,...Q.stack!==void 0?{stack:Q.stack}:{},exitCode:1})}try{process.stderr.write(`${$&&$.stack||String($)}
761
+ `)}catch{}process.exit(1)}),process.on("unhandledRejection",($)=>{if(!j$){j$=!0;let Q=g1($,"UnhandledRejection");m1({errorClass:Q.errorClass,message:Q.message,...Q.stack!==void 0?{stack:Q.stack}:{},exitCode:1})}try{let Q=$ instanceof Error?$.stack||$.message:String($);process.stderr.write(`Unhandled promise rejection: ${Q}
762
762
  `)}catch{}process.exit(1)})}var o6=`Loki Mode (TypeScript port, Phase 2 of bash->Bun migration)
763
763
 
764
764
  Usage: loki <command> [args...]
@@ -789,4 +789,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
789
789
  `),2}default:return process.stderr.write(`Unknown command: ${Q}
790
790
  `),process.stderr.write(o6),2}}p1();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var ZZ=await QZ(Bun.argv.slice(2));process.exit(ZZ);
791
791
 
792
- //# debugId=3DD935606FD979EE64756E2164756E21
792
+ //# debugId=98FDC89DEB1F99C464756E2164756E21
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '7.47.0'
60
+ __version__ = '7.48.0'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "loki-mode",
3
3
  "mcpName": "io.github.asklokesh/loki-mode",
4
- "version": "7.47.0",
4
+ "version": "7.48.0",
5
5
  "description": "Loki Mode by Autonomi. Autonomous spec-to-product system: takes a PRD, GitHub issue, OpenAPI/JSON/YAML, or one-line brief to a deployed app via the RARV-C closure loop with 8 quality gates. Provider-agnostic (Claude Code, OpenAI Codex, Cline, Aider).",
6
6
  "keywords": [
7
7
  "agent",
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "loki-mode",
4
4
  "displayName": "Loki Mode",
5
- "version": "7.47.0",
5
+ "version": "7.48.0",
6
6
  "description": "Autonomous spec-to-product build system with a built-in trust layer (RARV-C closure loop, 8 quality gates, completion council). Ships Loki's spec-hardening, drift-detection, and deterministic PR verification commands plus the Loki MCP server.",
7
7
  "author": {
8
8
  "name": "Autonomi",