loki-mode 8.19.3 → 8.19.5

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/SKILL.md CHANGED
@@ -3,7 +3,7 @@ name: loki-mode
3
3
  description: Autonomous spec-driven build system with a built-in trust layer. It does not call work done until it is verified (RARV-C closure loop, 8 quality gates, completion council, verified-completion evidence gate). Triggers on "Loki Mode". Takes a spec (PRD, GitHub issue, OpenAPI doc, etc.) to deployed product with minimal human intervention. Provider-agnostic. Requires --dangerously-skip-permissions flag.
4
4
  ---
5
5
 
6
- # Loki Mode v8.19.3
6
+ # Loki Mode v8.19.5
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -469,4 +469,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
469
469
 
470
470
  ---
471
471
 
472
- **v8.19.3 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
472
+ **v8.19.5 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 8.19.3
1
+ 8.19.5
@@ -653,6 +653,16 @@ def _collect_security(loki_dir):
653
653
  {ran, total, active, waived, high_active, status, findings:[{rule,severity}]}.
654
654
  status: not_run (no scan) | clean (ran, no active findings) | findings
655
655
  (ran, active findings present).
656
+
657
+ PARTIAL record-half. An active HIGH finding IS read by _compute_degraded and
658
+ becomes a gap. An UNRUN scan is not: absence of a scan is deliberately not a
659
+ security gap (tests/test_proof_generator.py::test_no_security_file_is_not_a_gap),
660
+ so a receipt can read VERIFIED with an empty gap list while no scan ever ran.
661
+ That matches how `functional` and `healthcheck` behave, and like them,
662
+ changing it is the founder-gated trust decision rather than an inference to
663
+ make here. Stated explicitly because those two say so in their own
664
+ docstrings and this one did not, leaving the behaviour to be inferred from
665
+ silence -- which is the exact failure the honesty ledger exists to prevent.
656
666
  """
657
667
  out = {
658
668
  "ran": False, "total": 0, "active": 0, "waived": 0,
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "8.19.3"
10
+ __version__ = "8.19.5"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -0,0 +1,145 @@
1
+ # Founder Decisions: trust semantics (2026-08-01)
2
+
3
+ Two questions this session surfaced that I deliberately did not answer, because
4
+ answering them changes what the word "Verified" means on a receipt. Both are
5
+ recorded with the measurement that raised them, so the decision can be made on
6
+ evidence rather than on my summary of it.
7
+
8
+ Everything else from this session shipped. These are the only open items.
9
+
10
+ ## Why these are yours and not mine
11
+
12
+ `autonomy/lib/proof-generator.py` already records the rule, in the note on the
13
+ functional-verification fact:
14
+
15
+ > Making functional-satisfaction gate the green headline is a trust-semantics
16
+ > product decision (council + founder), the second half of FV-2. Recording it
17
+ > first lets the signal be seen and validated safely.
18
+
19
+ Record first, gate later. Both items below follow that pattern: the fact is now
20
+ recorded and visible on every surface, and the verdict is unchanged pending your
21
+ call.
22
+
23
+ ---
24
+
25
+ ## Decision 1: should a disabled trust gate demote "Verified"?
26
+
27
+ **Status today:** the gate is recorded and shown everywhere. The headline is
28
+ unchanged.
29
+
30
+ A run can disable any phase by environment variable. When code review or
31
+ security is switched off, the proof now records it (`quality_gates.disabled_phases`),
32
+ the honesty ledger carries a per-gate entry, both receipts name it, and the
33
+ plain-English owner page refuses to show a green "ready" badge.
34
+
35
+ What it does **not** do is change `honesty.headline`. A receipt with code review
36
+ disabled can still read `VERIFIED`.
37
+
38
+ **The case for demoting it:** "Verified" on a receipt with two correctness gates
39
+ switched off is defensible only if it means "verified as far as it was checked".
40
+ A reader who has not read the gaps list will not make that distinction.
41
+
42
+ **The case against:** the headline is re-derived by `proof-verify` to detect
43
+ tampering. Changing what feeds it changes what an honest proof looks like, and
44
+ older signed receipts would re-derive differently. There is also a real risk of
45
+ teaching users to ignore the headline if it moves for reasons they consider
46
+ routine.
47
+
48
+ **If you decide to demote:** the change is in `_compute_headline`, and the
49
+ disabled-gate entries must then be included in the verifier's re-derivation
50
+ rather than filtered out (`_recorded_degraded_raw`). Both halves must move
51
+ together or every honest receipt is reported as forged, which is exactly the bug
52
+ v8.19.2 fixed.
53
+
54
+ ---
55
+
56
+ ## Decision 2: is an unrun security scan a gap?
57
+
58
+ **Status today:** deliberately not a gap. There is a test asserting it.
59
+
60
+ Measured on a real generated proof, with a clean build and passing tests:
61
+
62
+ ```
63
+ headline : VERIFIED
64
+ gaps listed : []
65
+ security fact : {'ran': False, 'status': 'not_run', 'high_active': 0}
66
+ ```
67
+
68
+ So a receipt reads `VERIFIED` with an empty gaps list while the security scan
69
+ never ran at all.
70
+
71
+ Tests and build are both flagged as gaps when they do not run. Security is
72
+ flagged only when it **ran and found** an active HIGH finding. The asymmetry is
73
+ intentional: `tests/test_proof_generator.py::test_no_security_file_is_not_a_gap`
74
+ states it outright, "Absence of a scan is not a security gap (the gate did not
75
+ run)".
76
+
77
+ I prototyped changing it and reverted, because overriding a documented decision
78
+ with a test guarding it is not a call I should make silently.
79
+
80
+ **The case for treating it as a gap:** the honesty ledger exists so "a reader
81
+ sees exactly what was NOT verified rather than inferring it from silence"
82
+ (`_compute_degraded`'s own docstring). An unrun security scan is precisely that,
83
+ and it is the one omission a reader is least likely to notice on their own.
84
+
85
+ **The case against:** most projects have no security scanner configured, so
86
+ every receipt would carry a security gap that means nothing about that
87
+ project's quality. A gap list that is never empty is a gap list nobody reads,
88
+ and that costs more than the omission.
89
+
90
+ **A middle option:** treat it as a gap only when the project has a scanner
91
+ configured and it did not run. That distinguishes "not applicable" from
92
+ "skipped", which is the distinction the current binary answer cannot express.
93
+
94
+ ---
95
+
96
+ ## Context found after writing the above: it is three tiers, not two
97
+
98
+ Sweeping every fact the proof records showed the exclusion is a deliberate,
99
+ consistent pattern rather than an oversight in one place. Three facts are
100
+ recorded and never enter the honesty ledger on a clean run:
101
+
102
+ | Fact | In the ledger? | Documented as record-half? |
103
+ |---|---|---|
104
+ | `functional` | never | yes, explicitly (FV-2) |
105
+ | `healthcheck` | never | yes, explicitly |
106
+ | `security` | only on an active HIGH finding | no note, but behaves the same when unrun |
107
+
108
+ `functional` and `healthcheck` both carry a note in their collector saying they
109
+ are "NOT read by `_compute_headline` / `_compute_degraded`" and that gating on
110
+ them is founder-gated. `security` carries no such note, yet an unrun scan is
111
+ excluded exactly as they are.
112
+
113
+ That changes decision 2 from "is this an oversight?" to "should the third tier
114
+ join the first two, or move the other way?". It also means answering it in
115
+ isolation would leave the receipt inconsistent: if an unrun security scan
116
+ becomes a gap, a reader will reasonably ask why an unrun healthcheck is not.
117
+
118
+ The cheapest correct step, if you want one, is to give `security` the same
119
+ explicit record-half note the other two have, so the current behaviour is
120
+ documented where a reader will find it rather than inferred from its absence.
121
+
122
+ ---
123
+
124
+ ## What I would recommend, briefly
125
+
126
+ Revised after the three-tier finding above, which I had not made when I first
127
+ wrote this section.
128
+
129
+ Decision 2 should not be taken in isolation. Answering it for security alone
130
+ makes the receipt internally inconsistent, because a reader who sees an unrun
131
+ security scan flagged will reasonably ask why an unrun healthcheck is not. The
132
+ real question is what the whole record-half tier should do, and that is one
133
+ decision, not three.
134
+
135
+ Decision 1 is the larger change and depends on how you want "Verified" to read
136
+ to someone who did not run the build. It is worth deciding deliberately rather
137
+ than inheriting, and it is independent of decision 2.
138
+
139
+ Neither is blocking. Both facts are already visible to anyone who reads the
140
+ receipt.
141
+
142
+ The one change I made without asking is documentation only: `security` now
143
+ carries the same explicit record-half note that `functional` and `healthcheck`
144
+ have. That records the existing behaviour where a reader will find it. It
145
+ changes no verdict and decides nothing.
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var h_=Object.create;var{getPrototypeOf:v_,defineProperty:rK,getOwnPropertyNames:g_}=Object;var m_=Object.prototype.hasOwnProperty;function u_(Z){return this[Z]}var p_,d_,c_=(Z,X,Q)=>{var Y=Z!=null&&typeof Z==="object";if(Y){var J=X?p_??=new WeakMap:d_??=new WeakMap,z=J.get(Z);if(z)return z}Q=Z!=null?h_(v_(Z)):{};let K=X||!Z||!Z.__esModule?rK(Q,"default",{value:Z,enumerable:!0}):Q;for(let $ of g_(Z))if(!m_.call(K,$))rK(K,$,{get:u_.bind(Z,$),enumerable:!0});if(Y)J.set(Z,K);return K};var qQ=(Z,X)=>()=>(X||Z((X={exports:{}}).exports,X),X.exports);var l_=(Z)=>Z;function i_(Z,X){this[Z]=l_.bind(null,X)}var l0=(Z,X)=>{for(var Q in X)rK(Z,Q,{get:X[Q],enumerable:!0,configurable:!0,set:i_.bind(X,Q)})};var p=(Z,X)=>()=>(Z&&(X=Z(Z=0)),X);var e0=import.meta.require;var kO={};l0(kO,{lokiDir:()=>j0,homeLokiDir:()=>P4,findRepoRootForVersion:()=>eK,REPO_ROOT:()=>r0});import{resolve as n7,dirname as tK}from"path";import{fileURLToPath as a_}from"url";import{existsSync as GQ}from"fs";import{homedir as s_}from"os";function n_(){let Z=RO;for(let X=0;X<6;X++){if(GQ(n7(Z,"VERSION"))&&GQ(n7(Z,"autonomy/run.sh")))return Z;let Q=tK(Z);if(Q===Z)break;Z=Q}return n7(RO,"..","..","..")}function eK(Z){let X=Z;for(let Q=0;Q<6;Q++){if(GQ(n7(X,"VERSION"))&&GQ(n7(X,"autonomy/run.sh")))return X;let Y=tK(X);if(Y===X)break;X=Y}return n7(Z,"..","..","..")}function j0(){return process.env.LOKI_DIR??n7(process.cwd(),".loki")}function P4(){return n7(s_(),".loki")}var RO,r0;var G8=p(()=>{RO=tK(a_(import.meta.url));r0=n_()});import{readFileSync as o_}from"fs";import{resolve as r_,dirname as t_}from"path";import{fileURLToPath as e_}from"url";function f3(){if(h5!==null)return h5;let Z="8.19.3";if(typeof Z==="string"&&Z.length>0)return h5=Z,h5;try{let X=t_(e_(import.meta.url)),Q=eK(X);h5=o_(r_(Q,"VERSION"),"utf-8").trim()}catch{h5="unknown"}return h5}var h5=null;var HQ=p(()=>{G8()});var bO={};l0(bO,{runOrThrow:()=>Of,run:()=>E0,readStreamCapped:()=>UQ,commandVersion:()=>Af,commandExists:()=>X9,ShellError:()=>Z$,MAX_STDOUT_BYTES:()=>yO});async function UQ(Z,X=yO){let Q=Z.getReader(),Y=new TextDecoder,J="",z=0;try{while(z<X){let{done:K,value:$}=await Q.read();if(K)break;if(!$)continue;if(z+=$.byteLength,z>X){let W=$.byteLength-(z-X);J+=Y.decode($.subarray(0,W),{stream:!0});break}J+=Y.decode($,{stream:!0})}J+=Y.decode()}finally{try{await Q.cancel()}catch{}Q.releaseLock()}return J}async function E0(Z,X={}){let Q=Bun.spawn({cmd:[...Z],stdout:"pipe",stderr:"pipe",env:X.env?{...process.env,...X.env}:process.env,cwd:X.cwd}),Y,J;if(X.timeoutMs&&X.timeoutMs>0)Y=setTimeout(()=>{try{Q.kill("SIGTERM")}catch{}J=setTimeout(()=>{try{Q.kill("SIGKILL")}catch{}},2000)},X.timeoutMs);try{let[z,K,$]=await Promise.all([UQ(Q.stdout),new Response(Q.stderr).text(),Q.exited]);return{stdout:z,stderr:K,exitCode:$}}finally{if(Y)clearTimeout(Y);if(J)clearTimeout(J)}}async function Of(Z,X={}){let Q=await E0(Z,X);if(Q.exitCode!==0)throw new Z$(`command failed (${Q.exitCode}): ${Z.join(" ")}`,Q.exitCode,Q.stdout,Q.stderr);return Q}async function X9(Z){let X=Lf(Z),Q=await E0(["sh","-c",`command -v ${X}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function Lf(Z){if(!/^[A-Za-z0-9._/-]+$/.test(Z))throw Error(`refused to shell-escape suspect token: ${Z}`);return Z}async function Af(Z,X="--version"){if(!await X9(Z))return null;let Y=await E0([Z,X],{timeoutMs:5000});if(Y.exitCode!==0)return null;return((Y.stdout||Y.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var yO=16777216,Z$;var x9=p(()=>{Z$=class Z$ extends Error{message;exitCode;stdout;stderr;constructor(Z,X,Q,Y){super(Z);this.message=Z;this.exitCode=X;this.stdout=Q;this.stderr=Y;this.name="ShellError"}}});function o7(Z){return jf?"":Z}var jf,L0,k8,p0,ZV0,i0,H8,Q9,v;var S6=p(()=>{jf=(process.env.NO_COLOR??"").length>0;L0=o7("\x1B[0;31m"),k8=o7("\x1B[0;32m"),p0=o7("\x1B[1;33m"),ZV0=o7("\x1B[0;34m"),i0=o7("\x1B[0;36m"),H8=o7("\x1B[1m"),Q9=o7("\x1B[2m"),v=o7("\x1B[0m")});import{existsSync as xf}from"fs";async function E7(){if(k4!==void 0)return k4;let Z="/opt/homebrew/bin/python3.12";if(xf(Z))return k4=Z,Z;let X=await X9("python3.12");if(X)return k4=X,X;let Q=await X9("python3");return k4=Q,Q}async function Y7(Z,X={}){let Q=await E7();if(!Q)return{stdout:"",stderr:"python3 not found",exitCode:127};return E0([Q,"-c",Z],X)}var k4;var r7=p(()=>{x9()});var ZL={};l0(ZL,{runStatus:()=>Yh});import{existsSync as Y9,readFileSync as v3,readdirSync as iO,statSync as aO}from"fs";import{resolve as h8,basename as sf}from"path";import{homedir as nf}from"os";function sO(Z){let X=Math.trunc(Z);if(X>=1e6)return`${(Math.trunc(X/1e6*10)/10).toFixed(1)}M`;if(X>=1000)return`${(Math.trunc(X/1000*10)/10).toFixed(1)}K`;return String(X)}function nO(Z,X,Q){if(X===0)return null;let Y=Math.trunc(Z*100/X),J=Math.trunc(Z*NQ/X);if(J>NQ)J=NQ;let z=NQ-J,K=k8;if(Y>=80)K=L0;else if(Y>=50)K=p0;let $="=".repeat(Math.max(0,J))+" ".repeat(Math.max(0,z)),W=sO(Z),V=sO(X);return` ${H8}${Q}${v} ${K}[${$}]${v} ${Y}% (${W} / ${V})`}async function rf(){if(await X9("jq"))return!0;return process.stdout.write(`${L0}Error: jq is required but not installed.${v}
2
+ var h_=Object.create;var{getPrototypeOf:v_,defineProperty:rK,getOwnPropertyNames:g_}=Object;var m_=Object.prototype.hasOwnProperty;function u_(Z){return this[Z]}var p_,d_,c_=(Z,X,Q)=>{var Y=Z!=null&&typeof Z==="object";if(Y){var J=X?p_??=new WeakMap:d_??=new WeakMap,z=J.get(Z);if(z)return z}Q=Z!=null?h_(v_(Z)):{};let K=X||!Z||!Z.__esModule?rK(Q,"default",{value:Z,enumerable:!0}):Q;for(let $ of g_(Z))if(!m_.call(K,$))rK(K,$,{get:u_.bind(Z,$),enumerable:!0});if(Y)J.set(Z,K);return K};var qQ=(Z,X)=>()=>(X||Z((X={exports:{}}).exports,X),X.exports);var l_=(Z)=>Z;function i_(Z,X){this[Z]=l_.bind(null,X)}var l0=(Z,X)=>{for(var Q in X)rK(Z,Q,{get:X[Q],enumerable:!0,configurable:!0,set:i_.bind(X,Q)})};var p=(Z,X)=>()=>(Z&&(X=Z(Z=0)),X);var e0=import.meta.require;var kO={};l0(kO,{lokiDir:()=>j0,homeLokiDir:()=>P4,findRepoRootForVersion:()=>eK,REPO_ROOT:()=>r0});import{resolve as n7,dirname as tK}from"path";import{fileURLToPath as a_}from"url";import{existsSync as GQ}from"fs";import{homedir as s_}from"os";function n_(){let Z=RO;for(let X=0;X<6;X++){if(GQ(n7(Z,"VERSION"))&&GQ(n7(Z,"autonomy/run.sh")))return Z;let Q=tK(Z);if(Q===Z)break;Z=Q}return n7(RO,"..","..","..")}function eK(Z){let X=Z;for(let Q=0;Q<6;Q++){if(GQ(n7(X,"VERSION"))&&GQ(n7(X,"autonomy/run.sh")))return X;let Y=tK(X);if(Y===X)break;X=Y}return n7(Z,"..","..","..")}function j0(){return process.env.LOKI_DIR??n7(process.cwd(),".loki")}function P4(){return n7(s_(),".loki")}var RO,r0;var G8=p(()=>{RO=tK(a_(import.meta.url));r0=n_()});import{readFileSync as o_}from"fs";import{resolve as r_,dirname as t_}from"path";import{fileURLToPath as e_}from"url";function f3(){if(h5!==null)return h5;let Z="8.19.5";if(typeof Z==="string"&&Z.length>0)return h5=Z,h5;try{let X=t_(e_(import.meta.url)),Q=eK(X);h5=o_(r_(Q,"VERSION"),"utf-8").trim()}catch{h5="unknown"}return h5}var h5=null;var HQ=p(()=>{G8()});var bO={};l0(bO,{runOrThrow:()=>Of,run:()=>E0,readStreamCapped:()=>UQ,commandVersion:()=>Af,commandExists:()=>X9,ShellError:()=>Z$,MAX_STDOUT_BYTES:()=>yO});async function UQ(Z,X=yO){let Q=Z.getReader(),Y=new TextDecoder,J="",z=0;try{while(z<X){let{done:K,value:$}=await Q.read();if(K)break;if(!$)continue;if(z+=$.byteLength,z>X){let W=$.byteLength-(z-X);J+=Y.decode($.subarray(0,W),{stream:!0});break}J+=Y.decode($,{stream:!0})}J+=Y.decode()}finally{try{await Q.cancel()}catch{}Q.releaseLock()}return J}async function E0(Z,X={}){let Q=Bun.spawn({cmd:[...Z],stdout:"pipe",stderr:"pipe",env:X.env?{...process.env,...X.env}:process.env,cwd:X.cwd}),Y,J;if(X.timeoutMs&&X.timeoutMs>0)Y=setTimeout(()=>{try{Q.kill("SIGTERM")}catch{}J=setTimeout(()=>{try{Q.kill("SIGKILL")}catch{}},2000)},X.timeoutMs);try{let[z,K,$]=await Promise.all([UQ(Q.stdout),new Response(Q.stderr).text(),Q.exited]);return{stdout:z,stderr:K,exitCode:$}}finally{if(Y)clearTimeout(Y);if(J)clearTimeout(J)}}async function Of(Z,X={}){let Q=await E0(Z,X);if(Q.exitCode!==0)throw new Z$(`command failed (${Q.exitCode}): ${Z.join(" ")}`,Q.exitCode,Q.stdout,Q.stderr);return Q}async function X9(Z){let X=Lf(Z),Q=await E0(["sh","-c",`command -v ${X}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function Lf(Z){if(!/^[A-Za-z0-9._/-]+$/.test(Z))throw Error(`refused to shell-escape suspect token: ${Z}`);return Z}async function Af(Z,X="--version"){if(!await X9(Z))return null;let Y=await E0([Z,X],{timeoutMs:5000});if(Y.exitCode!==0)return null;return((Y.stdout||Y.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var yO=16777216,Z$;var x9=p(()=>{Z$=class Z$ extends Error{message;exitCode;stdout;stderr;constructor(Z,X,Q,Y){super(Z);this.message=Z;this.exitCode=X;this.stdout=Q;this.stderr=Y;this.name="ShellError"}}});function o7(Z){return jf?"":Z}var jf,L0,k8,p0,ZV0,i0,H8,Q9,v;var S6=p(()=>{jf=(process.env.NO_COLOR??"").length>0;L0=o7("\x1B[0;31m"),k8=o7("\x1B[0;32m"),p0=o7("\x1B[1;33m"),ZV0=o7("\x1B[0;34m"),i0=o7("\x1B[0;36m"),H8=o7("\x1B[1m"),Q9=o7("\x1B[2m"),v=o7("\x1B[0m")});import{existsSync as xf}from"fs";async function E7(){if(k4!==void 0)return k4;let Z="/opt/homebrew/bin/python3.12";if(xf(Z))return k4=Z,Z;let X=await X9("python3.12");if(X)return k4=X,X;let Q=await X9("python3");return k4=Q,Q}async function Y7(Z,X={}){let Q=await E7();if(!Q)return{stdout:"",stderr:"python3 not found",exitCode:127};return E0([Q,"-c",Z],X)}var k4;var r7=p(()=>{x9()});var ZL={};l0(ZL,{runStatus:()=>Yh});import{existsSync as Y9,readFileSync as v3,readdirSync as iO,statSync as aO}from"fs";import{resolve as h8,basename as sf}from"path";import{homedir as nf}from"os";function sO(Z){let X=Math.trunc(Z);if(X>=1e6)return`${(Math.trunc(X/1e6*10)/10).toFixed(1)}M`;if(X>=1000)return`${(Math.trunc(X/1000*10)/10).toFixed(1)}K`;return String(X)}function nO(Z,X,Q){if(X===0)return null;let Y=Math.trunc(Z*100/X),J=Math.trunc(Z*NQ/X);if(J>NQ)J=NQ;let z=NQ-J,K=k8;if(Y>=80)K=L0;else if(Y>=50)K=p0;let $="=".repeat(Math.max(0,J))+" ".repeat(Math.max(0,z)),W=sO(Z),V=sO(X);return` ${H8}${Q}${v} ${K}[${$}]${v} ${Y}% (${W} / ${V})`}async function rf(){if(await X9("jq"))return!0;return process.stdout.write(`${L0}Error: jq is required but not installed.${v}
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)
@@ -1222,4 +1222,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
1222
1222
  `),2}case"start":{let{runStart:Y}=await Promise.resolve().then(() => (b_(),y_));return Y(Q)}default:return process.stderr.write(`Unknown command: ${X}
1223
1223
  `),process.stderr.write(__),2}}cO();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var fW0=await _W0(Bun.argv.slice(2));process.exit(fW0);
1224
1224
 
1225
- //# debugId=6E3B5489B4A4A6BA64756E2164756E21
1225
+ //# debugId=358DEA81D936CB2E64756E2164756E21
package/mcp/__init__.py CHANGED
@@ -75,4 +75,4 @@ try:
75
75
  except ImportError:
76
76
  __all__ = ['mcp']
77
77
 
78
- __version__ = '8.19.3'
78
+ __version__ = '8.19.5'
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": "8.19.3",
4
+ "version": "8.19.5",
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": "8.19.3",
5
+ "version": "8.19.5",
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",