loki-mode 8.3.3 → 8.4.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/README.md CHANGED
@@ -15,7 +15,7 @@ _The free, source-available autonomous coding agent by [Autonomi](https://www.au
15
15
 
16
16
  [Website](https://www.autonomi.dev/) | [Documentation](wiki/Home.md) | [Installation](docs/INSTALLATION.md) | [Changelog](CHANGELOG.md) | [Purple Lab -- deprecated v7.44.0](#purple-lab)
17
17
 
18
- **Current release: v8.3.3**
18
+ **Current release: v8.4.0**
19
19
 
20
20
  </div>
21
21
 
@@ -63,10 +63,15 @@ does not execute functions"
63
63
  ([continue#5696](https://github.com/continuedev/continue/issues/5696)). None of
64
64
  those seven publishes a machine-checkable completion artifact.
65
65
 
66
- We have not audited the closed-source products (Cursor, Devin, Replit Agent)
67
- feature by feature, so treat this as "unclaimed as far as we can verify" rather
68
- than a proven first. The receipt stands on its own either way: run the tour and
69
- check the hash.
66
+ We measured every named competitor that ships a local CLI -- opencode 1.18.9,
67
+ aider 0.86.2, codex-cli 0.146.0, Claude Code 2.1.220, cursor-agent -- and none
68
+ exposes a command that verifies the agent's own output. Rerun it yourself with
69
+ `bash tests/test-competitor-verify-surface.sh`.
70
+
71
+ That is a measurement of the CLI surface, not of whole products: a web UI or an
72
+ API could expose something `--help` does not, and Devin and Replit Agent ship no
73
+ local CLI so they are not covered. The receipt stands on its own either way:
74
+ run the tour and check the hash.
70
75
 
71
76
  **Evaluating this against something else?** [docs/EVALUATING.md](docs/EVALUATING.md)
72
77
  puts a runnable command next to every claim we make, and states plainly what we
package/VERSION CHANGED
@@ -1 +1 @@
1
- 8.3.3
1
+ 8.4.0
package/autonomy/run.sh CHANGED
@@ -3971,6 +3971,30 @@ except Exception:
3971
3971
  print('')" "$_app_state_file" 2>/dev/null)"
3972
3972
  fi
3973
3973
 
3974
+ # Time to first preview: how long until the user could SEE something running.
3975
+ #
3976
+ # WRITTEN BUT NEVER READ, until now. app-runner.sh:177 has recorded
3977
+ # seconds_to_first_preview since v8, write-once and atomically, and NOTHING
3978
+ # consumed it -- not this summary, not the receipt, not the dashboard. A
3979
+ # measurement nobody surfaces cannot influence anything, so the number that
3980
+ # research identifies as the delight peak of this whole category (a working
3981
+ # preview at roughly four minutes) was invisible to the person who waited for
3982
+ # it and to us.
3983
+ #
3984
+ # Read-only and best-effort by design: absent file means the app never
3985
+ # previewed (or the run predates the writer), and we render nothing rather
3986
+ # than a zero. A fabricated "0s to preview" would be worse than silence.
3987
+ local first_preview_s=""
3988
+ local _fp_file="$loki_dir/app-runner/first-preview.json"
3989
+ if [ -f "$_fp_file" ]; then
3990
+ first_preview_s="$(python3 -c "import json,sys
3991
+ try:
3992
+ v=json.load(open(sys.argv[1])).get('seconds_to_first_preview')
3993
+ print(int(v) if isinstance(v,(int,float)) and v>=0 else '')
3994
+ except Exception:
3995
+ print('')" "$_fp_file" 2>/dev/null)"
3996
+ fi
3997
+
3974
3998
  # Branch + diff stats vs the run-start SHA (best-effort; non-git or empty
3975
3999
  # baseline yields empty values, which we render as "unknown"/"0").
3976
4000
  local start_sha="${_LOKI_RUN_START_SHA:-}"
@@ -4100,6 +4124,11 @@ except Exception:
4100
4124
  fi
4101
4125
  printf '%-14s %s\n' "Branch:" "$branch"
4102
4126
  printf '%-14s %s\n' "Files:" "$files_changed (+$insertions / -$deletions)"
4127
+ # Only rendered when the app actually previewed. Silence beats a
4128
+ # fabricated zero for a run where nothing ever came up.
4129
+ if [ -n "$first_preview_s" ]; then
4130
+ printf '%-14s %ss\n' "First preview:" "$first_preview_s"
4131
+ fi
4103
4132
  printf '%-14s %s\n' "Finished:" "$ts"
4104
4133
  if [ -n "$delegate_branch" ]; then
4105
4134
  printf '%-14s %s\n' "Delegate:" "$delegate_branch"
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "8.3.3"
10
+ __version__ = "8.4.0"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -114,6 +114,48 @@ check you run at the end and a check something else can call as a dependency.
114
114
 
115
115
  ---
116
116
 
117
+ ## 6. Head to head, on this machine, in ten seconds
118
+
119
+ The one competitive comparison we will publish, because you can rerun it:
120
+
121
+ ```bash
122
+ bash tests/test-competitor-verify-surface.sh
123
+ ```
124
+
125
+ It runs `--help` on whatever agent CLIs are installed and checks whether any of
126
+ them exposes a command that verifies the agent's OWN output.
127
+
128
+ Measured 2026-07-30 against every one of the six named competitors that ships
129
+ a local CLI:
130
+
131
+ | CLI | Verifies its own output |
132
+ | --- | --- |
133
+ | opencode 1.18.9 | no |
134
+ | aider 0.86.2 | no |
135
+ | codex-cli 0.146.0 | no |
136
+ | Claude Code 2.1.220 | no |
137
+ | cursor-agent 2026.05.24 | no |
138
+ | loki-mode 8.3.3 | yes (`loki proof verify`) |
139
+
140
+ Devin and Replit Agent ship no local CLI, so they are not covered and no claim
141
+ is made about them.
142
+
143
+ **The detail that makes this honest.** aider matches eight times on
144
+ verify/proof/attest terms. All eight are `--verify-ssl` (TLS certificate
145
+ validation) and `--git-commit-verify` (git pre-commit hooks). Neither verifies
146
+ the agent's output. A count-based comparison would have scored aider 8 against
147
+ our 2 and concluded the opposite of the truth, so the test reads every hit
148
+ instead of counting them.
149
+
150
+ **Scope.** This inspects the CLI surface of locally installed tools. Devin and
151
+ Replit Agent ship no local CLI and are not covered. Absence from a `--help`
152
+ listing is also not proof of absence from a product: a web UI or an API could
153
+ expose something the CLI does not. If a competitor does ship output
154
+ verification, this test is how we would find out, and the claim above would be
155
+ corrected rather than defended.
156
+
157
+ ---
158
+
117
159
  ## What we do not have
118
160
 
119
161
  Stating this plainly, because you will find it out anyway and it is cheaper for
@@ -124,11 +166,14 @@ both of us if you find it here.
124
166
  - **No independent third-party benchmark placement.** The SWE-bench Verified
125
167
  leaderboard is months stale and every entry on it is self-reported, ours would
126
168
  be too.
127
- - **No audit of the closed-source products.** We have verified that seven open
128
- harnesses (OpenHands, Cline, Aider, SWE-agent, Roo-Code, OpenCode, Continue)
129
- publish no machine-checkable completion artifact. Cursor, Devin, Replit Agent
130
- and Claude Code we have **not** audited feature by feature, so treat the
131
- receipt as "unclaimed as far as we can verify", not as a proven first.
169
+ - **Only the CLI surface is audited, not whole products.** Section 6 measures
170
+ every named competitor that ships a local CLI (opencode, aider, codex, Claude
171
+ Code, cursor-agent) and none exposes output verification. That is a real
172
+ measurement you can rerun, but it is a measurement of `--help`, not of a
173
+ product: a web UI or an API could expose something the CLI does not. Devin and
174
+ Replit Agent ship no local CLI and are not covered at all. Treat this as
175
+ "unclaimed on every surface we can reach", which is stronger than the earlier
176
+ "unclaimed as far as we can verify" and still short of a proven first.
132
177
  - **Generation is not air-gapped.** See section 2.
133
178
 
134
179
  ---
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var S_=Object.create;var{getPrototypeOf:y_,defineProperty:oK,getOwnPropertyNames:b_}=Object;var __=Object.prototype.hasOwnProperty;function f_(Z){return this[Z]}var h_,v_,g_=(Z,X,Q)=>{var Y=Z!=null&&typeof Z==="object";if(Y){var J=X?h_??=new WeakMap:v_??=new WeakMap,z=J.get(Z);if(z)return z}Q=Z!=null?S_(y_(Z)):{};let K=X||!Z||!Z.__esModule?oK(Q,"default",{value:Z,enumerable:!0}):Q;for(let $ of b_(Z))if(!__.call(K,$))oK(K,$,{get:f_.bind(Z,$),enumerable:!0});if(Y)J.set(Z,K);return K};var VQ=(Z,X)=>()=>(X||Z((X={exports:{}}).exports,X),X.exports);var m_=(Z)=>Z;function u_(Z,X){this[Z]=m_.bind(null,X)}var c0=(Z,X)=>{for(var Q in X)oK(Z,Q,{get:X[Q],enumerable:!0,configurable:!0,set:u_.bind(X,Q)})};var p=(Z,X)=>()=>(Z&&(X=Z(Z=0)),X);var t0=import.meta.require;var IO={};c0(IO,{lokiDir:()=>j0,homeLokiDir:()=>I4,findRepoRootForVersion:()=>tK,REPO_ROOT:()=>Q8});import{resolve as s7,dirname as rK}from"path";import{fileURLToPath as p_}from"url";import{existsSync as qQ}from"fs";import{homedir as d_}from"os";function c_(){let Z=EO;for(let X=0;X<6;X++){if(qQ(s7(Z,"VERSION"))&&qQ(s7(Z,"autonomy/run.sh")))return Z;let Q=rK(Z);if(Q===Z)break;Z=Q}return s7(EO,"..","..","..")}function tK(Z){let X=Z;for(let Q=0;Q<6;Q++){if(qQ(s7(X,"VERSION"))&&qQ(s7(X,"autonomy/run.sh")))return X;let Y=rK(X);if(Y===X)break;X=Y}return s7(Z,"..","..","..")}function j0(){return process.env.LOKI_DIR??s7(process.cwd(),".loki")}function I4(){return s7(d_(),".loki")}var EO,Q8;var G8=p(()=>{EO=rK(p_(import.meta.url));Q8=c_()});import{readFileSync as l_}from"fs";import{resolve as i_,dirname as a_}from"path";import{fileURLToPath as s_}from"url";function _3(){if(f5!==null)return f5;let Z="8.3.3";if(typeof Z==="string"&&Z.length>0)return f5=Z,f5;try{let X=a_(s_(import.meta.url)),Q=tK(X);f5=l_(i_(Q,"VERSION"),"utf-8").trim()}catch{f5="unknown"}return f5}var f5=null;var GQ=p(()=>{G8()});var xO={};c0(xO,{runOrThrow:()=>qf,run:()=>E0,readStreamCapped:()=>HQ,commandVersion:()=>Hf,commandExists:()=>X9,ShellError:()=>eK,MAX_STDOUT_BYTES:()=>kO});async function HQ(Z,X=kO){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([HQ(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 qf(Z,X={}){let Q=await E0(Z,X);if(Q.exitCode!==0)throw new eK(`command failed (${Q.exitCode}): ${Z.join(" ")}`,Q.exitCode,Q.stdout,Q.stderr);return Q}async function X9(Z){let X=Gf(Z),Q=await E0(["sh","-c",`command -v ${X}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function Gf(Z){if(!/^[A-Za-z0-9._/-]+$/.test(Z))throw Error(`refused to shell-escape suspect token: ${Z}`);return Z}async function Hf(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 kO=16777216,eK;var k9=p(()=>{eK=class eK 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 n7(Z){return Uf?"":Z}var Uf,M0,k8,l0,dW0,a0,H8,Q9,g;var S6=p(()=>{Uf=(process.env.NO_COLOR??"").length>0;M0=n7("\x1B[0;31m"),k8=n7("\x1B[0;32m"),l0=n7("\x1B[1;33m"),dW0=n7("\x1B[0;34m"),a0=n7("\x1B[0;36m"),H8=n7("\x1B[1m"),Q9=n7("\x1B[2m"),g=n7("\x1B[0m")});import{existsSync as Ff}from"fs";async function F7(){if(R4!==void 0)return R4;let Z="/opt/homebrew/bin/python3.12";if(Ff(Z))return R4=Z,Z;let X=await X9("python3.12");if(X)return R4=X,X;let Q=await X9("python3");return R4=Q,Q}async function D7(Z,X={}){let Q=await F7();if(!Q)return{stdout:"",stderr:"python3 not found",exitCode:127};return E0([Q,"-c",Z],X)}var R4;var o7=p(()=>{k9()});var rO={};c0(rO,{runStatus:()=>tf});import{existsSync as Y9,readFileSync as h3,readdirSync as dO,statSync as cO}from"fs";import{resolve as h8,basename as pf}from"path";import{homedir as df}from"os";function lO(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 iO(Z,X,Q){if(X===0)return null;let Y=Math.trunc(Z*100/X),J=Math.trunc(Z*BQ/X);if(J>BQ)J=BQ;let z=BQ-J,K=k8;if(Y>=80)K=M0;else if(Y>=50)K=l0;let $="=".repeat(Math.max(0,J))+" ".repeat(Math.max(0,z)),W=lO(Z),V=lO(X);return` ${H8}${Q}${g} ${K}[${$}]${g} ${Y}% (${W} / ${V})`}async function lf(){if(await X9("jq"))return!0;return process.stdout.write(`${M0}Error: jq is required but not installed.${g}
2
+ var S_=Object.create;var{getPrototypeOf:y_,defineProperty:oK,getOwnPropertyNames:b_}=Object;var __=Object.prototype.hasOwnProperty;function f_(Z){return this[Z]}var h_,v_,g_=(Z,X,Q)=>{var Y=Z!=null&&typeof Z==="object";if(Y){var J=X?h_??=new WeakMap:v_??=new WeakMap,z=J.get(Z);if(z)return z}Q=Z!=null?S_(y_(Z)):{};let K=X||!Z||!Z.__esModule?oK(Q,"default",{value:Z,enumerable:!0}):Q;for(let $ of b_(Z))if(!__.call(K,$))oK(K,$,{get:f_.bind(Z,$),enumerable:!0});if(Y)J.set(Z,K);return K};var VQ=(Z,X)=>()=>(X||Z((X={exports:{}}).exports,X),X.exports);var m_=(Z)=>Z;function u_(Z,X){this[Z]=m_.bind(null,X)}var c0=(Z,X)=>{for(var Q in X)oK(Z,Q,{get:X[Q],enumerable:!0,configurable:!0,set:u_.bind(X,Q)})};var p=(Z,X)=>()=>(Z&&(X=Z(Z=0)),X);var t0=import.meta.require;var IO={};c0(IO,{lokiDir:()=>j0,homeLokiDir:()=>I4,findRepoRootForVersion:()=>tK,REPO_ROOT:()=>Q8});import{resolve as s7,dirname as rK}from"path";import{fileURLToPath as p_}from"url";import{existsSync as qQ}from"fs";import{homedir as d_}from"os";function c_(){let Z=EO;for(let X=0;X<6;X++){if(qQ(s7(Z,"VERSION"))&&qQ(s7(Z,"autonomy/run.sh")))return Z;let Q=rK(Z);if(Q===Z)break;Z=Q}return s7(EO,"..","..","..")}function tK(Z){let X=Z;for(let Q=0;Q<6;Q++){if(qQ(s7(X,"VERSION"))&&qQ(s7(X,"autonomy/run.sh")))return X;let Y=rK(X);if(Y===X)break;X=Y}return s7(Z,"..","..","..")}function j0(){return process.env.LOKI_DIR??s7(process.cwd(),".loki")}function I4(){return s7(d_(),".loki")}var EO,Q8;var G8=p(()=>{EO=rK(p_(import.meta.url));Q8=c_()});import{readFileSync as l_}from"fs";import{resolve as i_,dirname as a_}from"path";import{fileURLToPath as s_}from"url";function _3(){if(f5!==null)return f5;let Z="8.4.0";if(typeof Z==="string"&&Z.length>0)return f5=Z,f5;try{let X=a_(s_(import.meta.url)),Q=tK(X);f5=l_(i_(Q,"VERSION"),"utf-8").trim()}catch{f5="unknown"}return f5}var f5=null;var GQ=p(()=>{G8()});var xO={};c0(xO,{runOrThrow:()=>qf,run:()=>E0,readStreamCapped:()=>HQ,commandVersion:()=>Hf,commandExists:()=>X9,ShellError:()=>eK,MAX_STDOUT_BYTES:()=>kO});async function HQ(Z,X=kO){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([HQ(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 qf(Z,X={}){let Q=await E0(Z,X);if(Q.exitCode!==0)throw new eK(`command failed (${Q.exitCode}): ${Z.join(" ")}`,Q.exitCode,Q.stdout,Q.stderr);return Q}async function X9(Z){let X=Gf(Z),Q=await E0(["sh","-c",`command -v ${X}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function Gf(Z){if(!/^[A-Za-z0-9._/-]+$/.test(Z))throw Error(`refused to shell-escape suspect token: ${Z}`);return Z}async function Hf(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 kO=16777216,eK;var k9=p(()=>{eK=class eK 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 n7(Z){return Uf?"":Z}var Uf,M0,k8,l0,dW0,a0,H8,Q9,g;var S6=p(()=>{Uf=(process.env.NO_COLOR??"").length>0;M0=n7("\x1B[0;31m"),k8=n7("\x1B[0;32m"),l0=n7("\x1B[1;33m"),dW0=n7("\x1B[0;34m"),a0=n7("\x1B[0;36m"),H8=n7("\x1B[1m"),Q9=n7("\x1B[2m"),g=n7("\x1B[0m")});import{existsSync as Ff}from"fs";async function F7(){if(R4!==void 0)return R4;let Z="/opt/homebrew/bin/python3.12";if(Ff(Z))return R4=Z,Z;let X=await X9("python3.12");if(X)return R4=X,X;let Q=await X9("python3");return R4=Q,Q}async function D7(Z,X={}){let Q=await F7();if(!Q)return{stdout:"",stderr:"python3 not found",exitCode:127};return E0([Q,"-c",Z],X)}var R4;var o7=p(()=>{k9()});var rO={};c0(rO,{runStatus:()=>tf});import{existsSync as Y9,readFileSync as h3,readdirSync as dO,statSync as cO}from"fs";import{resolve as h8,basename as pf}from"path";import{homedir as df}from"os";function lO(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 iO(Z,X,Q){if(X===0)return null;let Y=Math.trunc(Z*100/X),J=Math.trunc(Z*BQ/X);if(J>BQ)J=BQ;let z=BQ-J,K=k8;if(Y>=80)K=M0;else if(Y>=50)K=l0;let $="=".repeat(Math.max(0,J))+" ".repeat(Math.max(0,z)),W=lO(Z),V=lO(X);return` ${H8}${Q}${g} ${K}[${$}]${g} ${Y}% (${W} / ${V})`}async function lf(){if(await X9("jq"))return!0;return process.stdout.write(`${M0}Error: jq is required but not installed.${g}
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)
@@ -1206,4 +1206,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
1206
1206
  `),2}case"start":{let{runStart:Y}=await Promise.resolve().then(() => (R_(),P_));return Y(Q)}default:return process.stderr.write(`Unknown command: ${X}
1207
1207
  `),process.stderr.write(k_),2}}uO();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var DW0=await FW0(Bun.argv.slice(2));process.exit(DW0);
1208
1208
 
1209
- //# debugId=0387E93FA065D70464756E2164756E21
1209
+ //# debugId=8285CFE544C8109D64756E2164756E21
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.3.3",
4
+ "version": "8.4.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": "8.3.3",
5
+ "version": "8.4.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",