loki-mode 7.121.0 → 7.121.2

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,6 +15,8 @@ _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: v7.121.1**
19
+
18
20
  </div>
19
21
 
20
22
  ---
@@ -26,6 +28,7 @@ _The free, source-available autonomous coding agent by [Autonomi](https://www.au
26
28
  ## Why Loki Mode?
27
29
 
28
30
  - **Spec-driven, autonomous, with a built-in trust layer** -- Hand Loki a spec, walk away, come back to working code with tests. The full RARV-C closure loop (Reason - Act - Reflect - Verify - Close) runs until the work is actually done, not just attempted. The verified-completion evidence gate (`skills/quality-gates.md`) refuses any "done" claim on an empty git diff against the run-start commit, and blocks completion when tests run red, so "complete" means proven, not promised.
31
+ - **A checklist verifier that is honest, not brittle** -- Each completion checklist item is checked deterministically before the completion council will accept "done". The verifier speaks extended regex (`grep -E`) so real LLM-emitted patterns match instead of erroring, and it is runner-agnostic: it runs the project's own declared test command rather than assuming a fixed runner. Crucially, a check that cannot be established is reported as inconclusive (pending), never as a false pass and never as a false failure. `rc == 0` alone is not a pass; a test check goes green only on a real "N passed" signal from the runner (v7.121.x).
29
32
  - **Production quality built in** -- 8 quality gates (`skills/quality-gates.md`), blind 3-reviewer code review (`run.sh:run_code_review()`), anti-sycophancy checks
30
33
  - **Standalone verification: `loki verify`** -- Run Loki's deterministic gates (build, tests, static analysis, secret scan, dependency audit) against any branch or PR diff, including code written by other agents or humans. CI-ready exit codes (0 VERIFIED, 1 CONCERNS, 2 BLOCKED), machine-readable evidence at `.loki/verify/evidence.json`. Inconclusive evidence is never reported as VERIFIED (v7.27.0).
31
34
  - **Living spec and pre-build interrogation** -- `loki spec` locks a spec and detects drift deterministically (`spec.lock`, `drift-report.json`, and a `SPEC_DRIFT` finding in `loki verify` with CI exit codes), so you can tell when the build diverges from what was agreed. `loki grill` runs a Devil's-Advocate interrogation of the spec before you build, surfacing gaps and contradictions early (v7.28.0).
@@ -76,6 +79,9 @@ This is honesty-of-done, not a claim of perfection. The receipt proves the
76
79
  completion claim is backed by deterministic evidence and is independently
77
80
  re-checkable; it does not claim the generated code is bug-free.
78
81
 
82
+ <details>
83
+ <summary><strong>Verify a receipt yourself -- <code>loki proof</code> commands, tamper/drift checks, proven PRs (advanced)</strong></summary>
84
+
79
85
  ### Verify it yourself
80
86
 
81
87
  Receipts are written to `.loki/proofs/<run_id>/` automatically at run completion
@@ -134,6 +140,8 @@ to a GitHub check-run. It is opt-in (`LOKI_PROVEN_PR_CHECK=1`) and can never blo
134
140
  a merge on its own. To make verified-completion blocking, add it as a required
135
141
  status check in your repository's branch-protection settings.
136
142
 
143
+ </details>
144
+
137
145
  ---
138
146
 
139
147
  ## Get Started in 30 Seconds
@@ -244,7 +252,8 @@ See the [Installation Guide](docs/INSTALLATION.md) for the long form.
244
252
 
245
253
  ---
246
254
 
247
- ## Runtime Architecture
255
+ <details>
256
+ <summary><strong>Runtime architecture -- dual Bash/Bun runtime, rollback flag, migration cost (advanced)</strong></summary>
248
257
 
249
258
  Loki Mode runs a dual runtime by deliberate design: the battle-tested Bash engine is the stable core (the autonomous loop, quality gates, and completion council stay on it; it receives bug fixes and hardening), and new product surfaces are built TypeScript/Bun-first as modules that wrap the engine rather than reimplement it. An earlier plan to make v8 Bun-only has been superseded by this stable-engine approach: rewriting the verified trust layer would risk the exact guarantees this product exists to provide, for no capability gain. Bash support is not going away.
250
259
 
@@ -279,6 +288,8 @@ The next major release sunsets the Bash runtime entirely. There is no firm calen
279
288
  - [UPGRADING.md](UPGRADING.md) -- per-version upgrade and rollback guidance.
280
289
  - [ADR-001: Runtime Migration](docs/architecture/ADR-001-runtime-migration.md) -- design rationale and phase definitions.
281
290
 
291
+ </details>
292
+
282
293
  ---
283
294
 
284
295
  <details>
@@ -347,7 +358,8 @@ All formats land in the same RARV pipeline and pass the same 8 quality gates (`s
347
358
 
348
359
  ---
349
360
 
350
- ## Architecture
361
+ <details>
362
+ <summary><strong>Internal architecture -- RARV cycle, agent roles, quality gates, memory, dashboard, enterprise layer (advanced)</strong></summary>
351
363
 
352
364
  <div align="center">
353
365
  <img width="100%" alt="Loki Mode Architecture" src="https://github.com/user-attachments/assets/c9798120-9587-4847-8e8d-8f421f984dfc" />
@@ -410,6 +422,8 @@ env vars. See [Enterprise Identity Roadmap](docs/ENTERPRISE-IDENTITY-ROADMAP.md)
410
422
  </tr>
411
423
  </table>
412
424
 
425
+ </details>
426
+
413
427
  ---
414
428
 
415
429
  ## Purple Lab
@@ -425,7 +439,7 @@ The historical feature set (platform pages, Monaco IDE workspace, AI chat panel)
425
439
  | Feature | Loki Mode | bolt.new | Replit | Lovable |
426
440
  |---------|:---------:|:--------:|:------:|:-------:|
427
441
  | Self-hosted / your keys | Yes | No | No | No |
428
- | 5 AI provider failover | Yes | No | No | No |
442
+ | Multi-provider failover (4 providers) | Yes | No | No | No |
429
443
  | 8 quality gates | Yes | No | No | No |
430
444
  | Blind code review | Yes | No | No | No |
431
445
  | Enterprise auth (OIDC token + scoped RBAC) | Yes | No | Yes | No |
@@ -438,7 +452,8 @@ Loki Mode is the only platform that is fully self-hosted, source-available (BUSL
438
452
 
439
453
  ---
440
454
 
441
- ## Provider-Agnostic Runtime
455
+ <details>
456
+ <summary><strong>Provider matrix -- per-provider status, autonomous flags, parallelism, install (includes deprecated Gemini)</strong></summary>
442
457
 
443
458
  Loki's autonomy and quality loop are the product; the underlying coding CLI is swappable. Loki runs on any of the providers below so you are never locked to one vendor.
444
459
 
@@ -452,6 +467,8 @@ Loki's autonomy and quality loop are the product; the underlying coding CLI is s
452
467
 
453
468
  Status legend: "E2E-verified" means we run real spec-to-code builds on it ourselves. Claude Code is the primary, fully supported provider and the one Loki Mode is built for; it gets full features (subagents, parallelization, MCP, Task tool). "Experimental" means the wiring is in place but we have not produced an end-to-end verified build ourselves; treat as community-tested. Experimental providers run sequentially. Auto-failover switches providers when rate-limited. See [Provider Guide](skills/providers.md).
454
469
 
470
+ </details>
471
+
455
472
  ---
456
473
 
457
474
  ## CLI Reference
@@ -569,7 +586,7 @@ See [benchmarks/](benchmarks/) for methodology.
569
586
  | **Code Gen** | Full-stack apps from PRDs | Complex domain logic may need human review |
570
587
  | **Deploy** | Generates configs, Dockerfiles, CI/CD; `loki deploy` prints the exact deploy command | Does not deploy -- human runs the printed deploy command (Loki never runs a cloud CLI or git push) |
571
588
  | **Testing** | 8 automated quality gates | Test quality depends on AI assertions |
572
- | **Providers** | 5 providers with auto-failover | Non-Claude providers lack parallel agents |
589
+ | **Providers** | 4 providers with auto-failover | Non-Claude providers lack parallel agents |
573
590
  | **Dashboard** | Real-time single-machine monitoring | No multi-node clustering |
574
591
 
575
592
  > **What "autonomous" means:** The system runs RARV cycles without prompting. It does NOT access your cloud accounts, payment systems, or external services unless you provide credentials. Human oversight is expected for deployment, API keys, and critical decisions.
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 v7.121.0
6
+ # Loki Mode v7.121.2
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -408,4 +408,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
408
408
 
409
409
  ---
410
410
 
411
- **v7.121.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
411
+ **v7.121.2 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 7.121.0
1
+ 7.121.2
@@ -142,12 +142,16 @@ def run_check(check: dict, project_dir: str, timeout: int) -> dict:
142
142
  )
143
143
  combined = proc.stdout + proc.stderr
144
144
  _low = combined.lower()
145
- # Trust gate: a tests_pass check REQUIRES that at least one test
146
- # actually ran. A runner can exit 0 with nothing executed
147
- # (jest --passWithNoTests, a mismatched vitest filter, a no-op
148
- # `echo` test script) -> that would be a FAKE-GREEN (a required
149
- # verification passing with zero tests). Detect the no-test
150
- # signals across runners and refuse to pass on an empty run.
145
+ # Trust gate: a tests_pass check REQUIRES POSITIVE PROOF that at
146
+ # least one test actually ran and passed. rc==0 alone is NOT
147
+ # proof: a no-op test script (`echo done`, `exit 0`, `true`, `:`)
148
+ # exits 0 having run ZERO tests -> passing on rc==0 would be a
149
+ # FAKE-GREEN (a required verification green with nothing tested).
150
+ # So we require a runner's "N passed" signal. Absence of that
151
+ # signal on rc==0 is INCONCLUSIVE (None -> pending), never True
152
+ # (fake-green) and never False (that would fake-RED an exotic
153
+ # passing runner). rc!=0 with real failures -> honest False. This
154
+ # is the same inconclusive-never-false moat as grep_codebase.
151
155
  no_tests = (
152
156
  "no tests found" in _low # jest
153
157
  or "no test files found" in _low # vitest
@@ -155,6 +159,24 @@ def run_check(check: dict, project_dir: str, timeout: int) -> dict:
155
159
  or "no tests to run" in _low
156
160
  or proc.returncode == 5 # pytest: none collected
157
161
  )
162
+ # Positive "tests ran and passed" signals across common runners.
163
+ # jest: "Tests: 3 passed" vitest: "Tests 26 passed (26)"
164
+ # pytest:"3 passed in 0.05s" mocha: "3 passing"
165
+ # node:test: "# pass 3" tap: "pass 3"
166
+ # The count MUST be >=1 (`[1-9]\d*`, never a literal 0): a
167
+ # runner can print "0 passed" / "0 passing" (mocha over an
168
+ # empty describe, all-.skip, node:test "# pass 0") on rc=0 with
169
+ # ZERO tests run -- matching a bare `\d+` would re-open the
170
+ # fake-green. A zero count falls through to the inconclusive
171
+ # else -> None (pending), correctly never green.
172
+ _ran_and_passed = bool(
173
+ re.search(r'tests?:\s*[1-9]\d*\s+passed', _low) # jest
174
+ or re.search(r'tests?\s+[1-9]\d*\s+passed', _low) # vitest
175
+ or re.search(r'\b[1-9]\d*\s+passed\b', _low) # pytest/vitest
176
+ or re.search(r'\b[1-9]\d*\s+passing\b', _low) # mocha
177
+ or re.search(r'#\s*pass\s+[1-9]\d*', _low) # node:test
178
+ or re.search(r'\bpass\s+[1-9]\d*\b', _low) # tap
179
+ )
158
180
  if no_tests:
159
181
  result["passed"] = False
160
182
  result["output"] = (
@@ -162,13 +184,26 @@ def run_check(check: dict, project_dir: str, timeout: int) -> dict:
162
184
  "(a tests_pass check must run at least one test). "
163
185
  "Output: "
164
186
  ) + combined[:400]
165
- else:
166
- # rc==0 with tests run -> True; ran and FAILED -> False
167
- # (blocks). "Ran and failed" is an honest False; only a
168
- # runner that could not RUN (timeout / not found, below)
169
- # is inconclusive None -- same moat as grep_codebase.
170
- result["passed"] = proc.returncode == 0
187
+ elif proc.returncode != 0:
188
+ # Ran and FAILED -> honest False (blocks). A non-zero exit
189
+ # from a real runner is a genuine negative, not inconclusive.
190
+ result["passed"] = False
191
+ result["output"] = combined[:500]
192
+ elif _ran_and_passed:
193
+ # rc==0 WITH positive "N passed" proof -> True.
194
+ result["passed"] = True
171
195
  result["output"] = combined[:500]
196
+ else:
197
+ # rc==0 but NO positive "tests ran" signal (a no-op script,
198
+ # or a runner whose output we do not recognise). We cannot
199
+ # prove tests ran -> INCONCLUSIVE (None -> pending), never a
200
+ # fake-green True and never a fake-RED False.
201
+ result["passed"] = None
202
+ result["output"] = (
203
+ "Test command exited 0 but produced no recognisable "
204
+ "'N passed' signal -- cannot confirm any test ran "
205
+ "(inconclusive, not a pass). Output: "
206
+ ) + combined[:350]
172
207
  except subprocess.TimeoutExpired:
173
208
  result["passed"] = None # timeout = pending (couldn't run)
174
209
  result["output"] = f"Timed out after {timeout}s"
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "7.121.0"
10
+ __version__ = "7.121.2"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -2,7 +2,7 @@
2
2
 
3
3
  The flagship product of [Autonomi](https://www.autonomi.dev/). Loki Mode is a spec-driven autonomous builder with a built-in trust layer that takes any spec to a deployed product and verifies completion with evidence (quality gates plus a completion council), not just a "done" claim. Complete installation instructions for all platforms and use cases.
4
4
 
5
- **Version:** v7.121.0
5
+ **Version:** v7.121.2
6
6
 
7
7
  ---
8
8
 
@@ -107,8 +107,9 @@ shim auto-routes read-only commands to the Bun runtime when `bun` is on `PATH`
107
107
  and falls back to the bash CLI otherwise (the core autonomous engine is the
108
108
  same on both routes).
109
109
 
110
- **Prerequisites:** Node.js 18+. Bun 1.3+ optional but recommended for the
111
- faster routed commands and forward-compat with v8.0.0.
110
+ **Prerequisites:** Node.js 20+ (Node 22 LTS recommended; it is the primary
111
+ tested/CI target). Bun 1.3+ optional but recommended for the faster routed
112
+ commands and forward-compat with v8.0.0.
112
113
 
113
114
  **What it does:**
114
115
  - Installs the `loki` CLI binary to your PATH (`bin/loki` shim)
@@ -395,7 +396,7 @@ provider works inside the container. Provide auth with your Anthropic API key:
395
396
  # Run Loki Mode in Docker (Claude provider, API-key auth)
396
397
  docker run --rm -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
397
398
  -v $(pwd):/workspace -w /workspace \
398
- asklokesh/loki-mode:7.93.0 start ./my-spec.md
399
+ asklokesh/loki-mode:7.121.2 start ./my-spec.md
399
400
  ```
400
401
 
401
402
  ##### docker compose + .env (no host install)
@@ -1008,7 +1009,8 @@ After installation:
1008
1009
 
1009
1010
  ## Release Operations
1010
1011
 
1011
- ### Token Rotation Runbook
1012
+ <details>
1013
+ <summary>Token Rotation Runbook (maintainers only)</summary>
1012
1014
 
1013
1015
  Follow this runbook when a release workflow fails to publish to npm.
1014
1016
 
@@ -1036,6 +1038,8 @@ A 404 on PUT means the registry rejected the credential, not that the package is
1036
1038
 
1037
1039
  **Note on `publish-ts-sdk`:** This job publishes `sdk/typescript` to npm and uses the same `secrets.NPM_TOKEN` as `publish-npm` (see `.github/workflows/release.yml`). Rotating `NPM_TOKEN` fixes both jobs. The new Automation token must have publish access to both the `loki-mode` package and the TypeScript SDK package.
1038
1040
 
1041
+ </details>
1042
+
1039
1043
  ---
1040
1044
 
1041
1045
  ## Need Help?
@@ -28,7 +28,7 @@ ln -sf ~/.claude/skills/loki-mode/autonomy/loki /usr/local/bin/loki
28
28
 
29
29
  ## Docker
30
30
 
31
- **Status:** Image exists on Docker Hub. Tags: `latest`, version-specific (e.g., `5.52.0`).
31
+ **Status:** Image exists on Docker Hub. Tags: `latest`, version-specific (e.g., `7.121.1`).
32
32
 
33
33
  ```bash
34
34
  docker pull asklokesh/loki-mode:latest
@@ -64,7 +64,7 @@ jobs:
64
64
  runs-on: ubuntu-latest
65
65
  steps:
66
66
  - uses: actions/checkout@v4
67
- - uses: asklokesh/loki-mode@v5
67
+ - uses: asklokesh/loki-mode@v7.121.1
68
68
  with:
69
69
  github_token: ${{ secrets.GITHUB_TOKEN }}
70
70
  mode: review
@@ -76,7 +76,7 @@ jobs:
76
76
  ```
77
77
 
78
78
  **Prerequisites:**
79
- - API key for your provider (set as repository secret): `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, or `GOOGLE_API_KEY`
79
+ - API key for your provider (set as repository secret): `ANTHROPIC_API_KEY` (Claude, recommended) or `OPENAI_API_KEY` (Codex). `GOOGLE_API_KEY` was for the Gemini provider, which is DEPRECATED as of v7.5.18.
80
80
  - The action auto-installs `loki-mode` and `@anthropic-ai/claude-code`
81
81
 
82
82
  **Action Inputs:**
@@ -84,7 +84,7 @@ jobs:
84
84
  | Input | Default | Description |
85
85
  |-------|---------|-------------|
86
86
  | `mode` | `review` | `review`, `fix`, or `test` |
87
- | `provider` | `claude` | `claude`, `codex`, or `gemini` |
87
+ | `provider` | `claude` | `claude` (recommended), `codex`, `cline`, or `aider`. `gemini` is DEPRECATED (v7.5.18). |
88
88
  | `budget_limit` | `5.00` | Max cost in USD |
89
89
  | `max_iterations` | `3` | Max RARV cycles |
90
90
  | `github_token` | (required) | GitHub token for PR comments |
@@ -108,22 +108,19 @@ jobs:
108
108
 
109
109
  ```bash
110
110
  # Download and extract to skills directory
111
- curl -sL https://github.com/asklokesh/loki-mode/archive/refs/tags/v5.52.0.tar.gz | tar xz
112
- mv loki-mode-5.52.0 ~/.claude/skills/loki-mode
111
+ curl -sL https://github.com/asklokesh/loki-mode/archive/refs/tags/v7.121.1.tar.gz | tar xz
112
+ mv loki-mode-7.121.1 ~/.claude/skills/loki-mode
113
113
  ```
114
114
 
115
115
  **Best for:** Offline or air-gapped environments, pinned version deployments.
116
116
 
117
117
  ---
118
118
 
119
- ## VS Code Extension
119
+ ## VS Code Extension (Deprecated)
120
120
 
121
- **Status:** Available on VS Code Marketplace.
122
-
123
- Search for "Loki Mode" in VS Code Extensions, or:
124
-
125
- ```bash
126
- code --install-extension asklokesh.loki-mode
127
- ```
128
-
129
- **Best for:** VS Code users who want dashboard integration within their editor.
121
+ > **DEPRECATED as of v7.2.0.** The Loki Mode VS Code extension is no longer
122
+ > maintained and is no longer published to the VS Code Marketplace. Use the
123
+ > dashboard instead (`loki dashboard start`, then open http://localhost:57374).
124
+ > The `vscode-extension/` source remains in the repository for contributors who
125
+ > want to build it locally. See
126
+ > [INSTALLATION.md](INSTALLATION.md#vs-code-extension-deprecated) for details.
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var q8=Object.defineProperty;var J8=($)=>$;function V8($,Q){this[$]=J8.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)q8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:V8.bind(Q,Z)})};var P=($,Q)=>()=>($&&(Q=$($=0)),Q);var J$=import.meta.require;var m1={};b(m1,{lokiDir:()=>j,homeLokiDir:()=>T$,findRepoRootForVersion:()=>$1,REPO_ROOT:()=>h});import{resolve as t,dirname as e$}from"path";import{fileURLToPath as W8}from"url";import{existsSync as N$}from"fs";import{homedir as H8}from"os";function G8(){let $=v1;for(let Q=0;Q<6;Q++){if(N$(t($,"VERSION"))&&N$(t($,"autonomy/run.sh")))return $;let Z=e$($);if(Z===$)break;$=Z}return t(v1,"..","..","..")}function $1($){let Q=$;for(let Z=0;Z<6;Z++){if(N$(t(Q,"VERSION"))&&N$(t(Q,"autonomy/run.sh")))return Q;let z=e$(Q);if(z===Q)break;Q=z}return t($,"..","..","..")}function j(){return process.env.LOKI_DIR??t(process.cwd(),".loki")}function T$(){return t(H8(),".loki")}var v1,h;var C=P(()=>{v1=e$(W8(import.meta.url));h=G8()});import{readFileSync as U8}from"fs";import{resolve as Y8,dirname as B8}from"path";import{fileURLToPath as M8}from"url";function S$(){if(Q$!==null)return Q$;let $="7.121.0";if(typeof $==="string"&&$.length>0)return Q$=$,Q$;try{let Q=B8(M8(import.meta.url)),Z=$1(Q);Q$=U8(Y8(Z,"VERSION"),"utf-8").trim()}catch{Q$="unknown"}return Q$}var Q$=null;var Q1=P(()=>{C()});var p1={};b(p1,{runOrThrow:()=>S8,run:()=>R,readStreamCapped:()=>c1,commandVersion:()=>C8,commandExists:()=>u,ShellError:()=>Z1,MAX_STDOUT_BYTES:()=>u1});async function c1($,Q=u1){let Z=$.getReader(),z=new TextDecoder,X="",q=0;try{while(q<Q){let{done:K,value:W}=await Z.read();if(K)break;if(!W)continue;if(q+=W.byteLength,q>Q){let V=W.byteLength-(q-Q);X+=z.decode(W.subarray(0,V),{stream:!0});break}X+=z.decode(W,{stream:!0})}X+=z.decode()}finally{try{await Z.cancel()}catch{}Z.releaseLock()}return X}async function R($,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[q,K,W]=await Promise.all([c1(Z.stdout),new Response(Z.stderr).text(),Z.exited]);return{stdout:q,stderr:K,exitCode:W}}finally{if(z)clearTimeout(z);if(X)clearTimeout(X)}}async function S8($,Q={}){let Z=await R($,Q);if(Z.exitCode!==0)throw new Z1(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function u($){let Q=D8($),Z=await R(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function D8($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function C8($,Q="--version"){if(!await u($))return null;let z=await R([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var u1=16777216,Z1;var o=P(()=>{Z1=class Z1 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 r($){return b8?"":$}var b8,M,S,_,tZ,L,k,y,J;var p=P(()=>{b8=(process.env.NO_COLOR??"").length>0;M=r("\x1B[0;31m"),S=r("\x1B[0;32m"),_=r("\x1B[1;33m"),tZ=r("\x1B[0;34m"),L=r("\x1B[0;36m"),k=r("\x1B[1m"),y=r("\x1B[2m"),J=r("\x1B[0m")});import{existsSync as l8}from"fs";async function Z$(){if(A$!==void 0)return A$;let $="/opt/homebrew/bin/python3.12";if(l8($))return A$=$,$;let Q=await u("python3.12");if(Q)return A$=Q,Q;let Z=await u("python3");return A$=Z,Z}async function z$($,Q={}){let Z=await Z$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return R([Z,"-c",$],Q)}var A$;var V$=P(()=>{o()});var V0={};b(V0,{runStatus:()=>U3});import{existsSync as v,readFileSync as H$,readdirSync as $0,statSync as Q0}from"fs";import{resolve as D,basename as z3}from"path";import{homedir as X3}from"os";function Z0($){let Q=Math.trunc($);if(Q>=1e6)return`${(Math.trunc(Q/1e6*10)/10).toFixed(1)}M`;if(Q>=1000)return`${(Math.trunc(Q/1000*10)/10).toFixed(1)}K`;return String(Q)}function z0($,Q,Z){if(Q===0)return null;let z=Math.trunc($*100/Q),X=Math.trunc($*C$/Q);if(X>C$)X=C$;let q=C$-X,K=S;if(z>=80)K=M;else if(z>=50)K=_;let W="=".repeat(Math.max(0,X))+" ".repeat(Math.max(0,q)),V=Z0($),H=Z0(Q);return` ${k}${Z}${J} ${K}[${W}]${J} ${z}% (${V} / ${H})`}async function q3(){if(await u("jq"))return!0;return process.stdout.write(`${M}Error: jq is required but not installed.${J}
2
+ var q8=Object.defineProperty;var J8=($)=>$;function V8($,Q){this[$]=J8.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)q8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:V8.bind(Q,Z)})};var P=($,Q)=>()=>($&&(Q=$($=0)),Q);var J$=import.meta.require;var m1={};b(m1,{lokiDir:()=>j,homeLokiDir:()=>T$,findRepoRootForVersion:()=>$1,REPO_ROOT:()=>h});import{resolve as t,dirname as e$}from"path";import{fileURLToPath as W8}from"url";import{existsSync as N$}from"fs";import{homedir as H8}from"os";function G8(){let $=v1;for(let Q=0;Q<6;Q++){if(N$(t($,"VERSION"))&&N$(t($,"autonomy/run.sh")))return $;let Z=e$($);if(Z===$)break;$=Z}return t(v1,"..","..","..")}function $1($){let Q=$;for(let Z=0;Z<6;Z++){if(N$(t(Q,"VERSION"))&&N$(t(Q,"autonomy/run.sh")))return Q;let z=e$(Q);if(z===Q)break;Q=z}return t($,"..","..","..")}function j(){return process.env.LOKI_DIR??t(process.cwd(),".loki")}function T$(){return t(H8(),".loki")}var v1,h;var C=P(()=>{v1=e$(W8(import.meta.url));h=G8()});import{readFileSync as U8}from"fs";import{resolve as Y8,dirname as B8}from"path";import{fileURLToPath as M8}from"url";function S$(){if(Q$!==null)return Q$;let $="7.121.2";if(typeof $==="string"&&$.length>0)return Q$=$,Q$;try{let Q=B8(M8(import.meta.url)),Z=$1(Q);Q$=U8(Y8(Z,"VERSION"),"utf-8").trim()}catch{Q$="unknown"}return Q$}var Q$=null;var Q1=P(()=>{C()});var p1={};b(p1,{runOrThrow:()=>S8,run:()=>R,readStreamCapped:()=>c1,commandVersion:()=>C8,commandExists:()=>u,ShellError:()=>Z1,MAX_STDOUT_BYTES:()=>u1});async function c1($,Q=u1){let Z=$.getReader(),z=new TextDecoder,X="",q=0;try{while(q<Q){let{done:K,value:W}=await Z.read();if(K)break;if(!W)continue;if(q+=W.byteLength,q>Q){let V=W.byteLength-(q-Q);X+=z.decode(W.subarray(0,V),{stream:!0});break}X+=z.decode(W,{stream:!0})}X+=z.decode()}finally{try{await Z.cancel()}catch{}Z.releaseLock()}return X}async function R($,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[q,K,W]=await Promise.all([c1(Z.stdout),new Response(Z.stderr).text(),Z.exited]);return{stdout:q,stderr:K,exitCode:W}}finally{if(z)clearTimeout(z);if(X)clearTimeout(X)}}async function S8($,Q={}){let Z=await R($,Q);if(Z.exitCode!==0)throw new Z1(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function u($){let Q=D8($),Z=await R(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function D8($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function C8($,Q="--version"){if(!await u($))return null;let z=await R([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var u1=16777216,Z1;var o=P(()=>{Z1=class Z1 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 r($){return b8?"":$}var b8,M,S,_,tZ,L,k,y,J;var p=P(()=>{b8=(process.env.NO_COLOR??"").length>0;M=r("\x1B[0;31m"),S=r("\x1B[0;32m"),_=r("\x1B[1;33m"),tZ=r("\x1B[0;34m"),L=r("\x1B[0;36m"),k=r("\x1B[1m"),y=r("\x1B[2m"),J=r("\x1B[0m")});import{existsSync as l8}from"fs";async function Z$(){if(A$!==void 0)return A$;let $="/opt/homebrew/bin/python3.12";if(l8($))return A$=$,$;let Q=await u("python3.12");if(Q)return A$=Q,Q;let Z=await u("python3");return A$=Z,Z}async function z$($,Q={}){let Z=await Z$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return R([Z,"-c",$],Q)}var A$;var V$=P(()=>{o()});var V0={};b(V0,{runStatus:()=>U3});import{existsSync as v,readFileSync as H$,readdirSync as $0,statSync as Q0}from"fs";import{resolve as D,basename as z3}from"path";import{homedir as X3}from"os";function Z0($){let Q=Math.trunc($);if(Q>=1e6)return`${(Math.trunc(Q/1e6*10)/10).toFixed(1)}M`;if(Q>=1000)return`${(Math.trunc(Q/1000*10)/10).toFixed(1)}K`;return String(Q)}function z0($,Q,Z){if(Q===0)return null;let z=Math.trunc($*100/Q),X=Math.trunc($*C$/Q);if(X>C$)X=C$;let q=C$-X,K=S;if(z>=80)K=M;else if(z>=50)K=_;let W="=".repeat(Math.max(0,X))+" ".repeat(Math.max(0,q)),V=Z0($),H=Z0(Q);return` ${k}${Z}${J} ${K}[${W}]${J} ${z}% (${V} / ${H})`}async function q3(){if(await u("jq"))return!0;return process.stdout.write(`${M}Error: jq is required but not installed.${J}
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)
@@ -814,4 +814,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
814
814
  `),2}default:return process.stderr.write(`Unknown command: ${Q}
815
815
  `),process.stderr.write(K8),2}}e1();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var SZ=await NZ(Bun.argv.slice(2));process.exit(SZ);
816
816
 
817
- //# debugId=3745511B1A35D37D64756E2164756E21
817
+ //# debugId=1A0B5967C64F275B64756E2164756E21
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '7.121.0'
60
+ __version__ = '7.121.2'
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.121.0",
4
+ "version": "7.121.2",
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.121.0",
5
+ "version": "7.121.2",
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",
@@ -3,7 +3,7 @@
3
3
  # Run: docker compose -f docker-compose.purple-lab.yml up
4
4
 
5
5
  # --- Stage 1: Build frontend ---
6
- FROM node:20-alpine AS frontend-build
6
+ FROM node:22-alpine AS frontend-build
7
7
 
8
8
  WORKDIR /build
9
9
  COPY package.json package-lock.json* ./