loki-mode 8.17.2 → 8.18.1
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 +2 -2
- package/VERSION +1 -1
- package/autonomy/lib/own-render.py +62 -3
- package/dashboard/__init__.py +1 -1
- package/loki-ts/dist/loki.js +2 -2
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
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.
|
|
6
|
+
# Loki Mode v8.18.1
|
|
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.
|
|
472
|
+
**v8.18.1 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
8.
|
|
1
|
+
8.18.1
|
|
@@ -138,9 +138,21 @@ def _is_ready(proof):
|
|
|
138
138
|
"""Deterministic green gate. True ONLY when:
|
|
139
139
|
- honesty.headline == "VERIFIED", AND
|
|
140
140
|
- facts.tests.status in (passed, verified), AND
|
|
141
|
-
- the build actually ran (facts.build.ran true / status not 'not_run')
|
|
141
|
+
- the build actually ran (facts.build.ran true / status not 'not_run'), AND
|
|
142
|
+
- no trust gate was disabled for the run.
|
|
142
143
|
|
|
143
144
|
Any one missing -> not ready. This is the line that must never overclaim.
|
|
145
|
+
|
|
146
|
+
The disabled-gate condition is the newest and the one this page needs most.
|
|
147
|
+
This renderer exists for a NON-TECHNICAL owner: the reader least equipped to
|
|
148
|
+
notice that code review and security never ran. Every other surface can show
|
|
149
|
+
a caveat beside a green badge and trust the reader to weigh it. Here the
|
|
150
|
+
badge IS the message, so a run with its trust gates switched off must not
|
|
151
|
+
reach green at all.
|
|
152
|
+
|
|
153
|
+
Only gates that bear on correctness count. Turning off performance testing
|
|
154
|
+
or competitor research does not make a build unverified, and treating it as
|
|
155
|
+
such would push honest runs to amber and teach owners to ignore the colour.
|
|
144
156
|
"""
|
|
145
157
|
if not isinstance(proof, dict):
|
|
146
158
|
return False
|
|
@@ -154,7 +166,18 @@ def _is_ready(proof):
|
|
|
154
166
|
build = facts.get("build") or {}
|
|
155
167
|
build_ran = bool(build.get("ran")) or \
|
|
156
168
|
str(build.get("status") or "").strip().lower() not in ("not_run", "", "none")
|
|
157
|
-
|
|
169
|
+
if not build_ran:
|
|
170
|
+
return False
|
|
171
|
+
|
|
172
|
+
# A trust gate switched off means the work was not fully checked, whatever
|
|
173
|
+
# the headline says. Recorded by the proof generator since v8.17.0.
|
|
174
|
+
gates = proof.get("quality_gates") or {}
|
|
175
|
+
disabled = gates.get("disabled_phases")
|
|
176
|
+
disabled = disabled if isinstance(disabled, list) else []
|
|
177
|
+
trust_gates = {"code_review", "security", "unit_tests", "e2e_tests"}
|
|
178
|
+
if any(str(name).strip().lower() in trust_gates for name in disabled):
|
|
179
|
+
return False
|
|
180
|
+
return True
|
|
158
181
|
|
|
159
182
|
|
|
160
183
|
# ---------------------------------------------------------------------------
|
|
@@ -296,6 +319,24 @@ def _section_is_it_working(proof, run_id=None):
|
|
|
296
319
|
lines.append("This means Loki is NOT telling you it is ready to ship. Here is "
|
|
297
320
|
"what was not verified:")
|
|
298
321
|
lines.append("")
|
|
322
|
+
# Gates the operator switched off. This is the FIRST gap listed, because a
|
|
323
|
+
# run blocked only by disabled gates has headline VERIFIED and an EMPTY
|
|
324
|
+
# degraded list -- so without this the owner was told "no specific gaps were
|
|
325
|
+
# itemized" when the reason was precise and already known.
|
|
326
|
+
#
|
|
327
|
+
# It also resolves a contradiction the owner could see on the page: the
|
|
328
|
+
# verdict line reads VERIFIED while the section refuses to call it ready.
|
|
329
|
+
# Naming the skipped gates is what makes those two statements consistent.
|
|
330
|
+
_qg = (proof or {}).get("quality_gates") or {}
|
|
331
|
+
_off = _qg.get("disabled_phases")
|
|
332
|
+
_off = [str(x).strip() for x in _off] if isinstance(_off, list) else []
|
|
333
|
+
_trust_off = sorted(n for n in _off
|
|
334
|
+
if n.lower() in ("code_review", "security",
|
|
335
|
+
"unit_tests", "e2e_tests"))
|
|
336
|
+
if _trust_off:
|
|
337
|
+
lines.append("- These checks were switched OFF for this run, so they "
|
|
338
|
+
"never ran: %s" % ", ".join(_trust_off))
|
|
339
|
+
|
|
299
340
|
if isinstance(degraded, list) and degraded:
|
|
300
341
|
for d in degraded:
|
|
301
342
|
if isinstance(d, dict):
|
|
@@ -307,7 +348,7 @@ def _section_is_it_working(proof, run_id=None):
|
|
|
307
348
|
lines.append("- %s%s" % (item, tail))
|
|
308
349
|
else:
|
|
309
350
|
lines.append("- %s" % str(d))
|
|
310
|
-
|
|
351
|
+
elif not _trust_off:
|
|
311
352
|
lines.append("- (no specific gaps were itemized, but the verdict above is "
|
|
312
353
|
"not a clean pass)")
|
|
313
354
|
lines.append("")
|
|
@@ -457,6 +498,24 @@ def _section_still_to_do(proof, completion):
|
|
|
457
498
|
# Anything not verified (degraded items) is also a to-do.
|
|
458
499
|
honesty = (proof or {}).get("honesty") or {}
|
|
459
500
|
degraded = honesty.get("degraded") or []
|
|
501
|
+
# Gates the operator switched off. This is the FIRST gap listed, because a
|
|
502
|
+
# run blocked only by disabled gates has headline VERIFIED and an EMPTY
|
|
503
|
+
# degraded list -- so without this the owner was told "no specific gaps were
|
|
504
|
+
# itemized" when the reason was precise and already known.
|
|
505
|
+
#
|
|
506
|
+
# It also resolves a contradiction the owner could see on the page: the
|
|
507
|
+
# verdict line reads VERIFIED while the section refuses to call it ready.
|
|
508
|
+
# Naming the skipped gates is what makes those two statements consistent.
|
|
509
|
+
_qg = (proof or {}).get("quality_gates") or {}
|
|
510
|
+
_off = _qg.get("disabled_phases")
|
|
511
|
+
_off = [str(x).strip() for x in _off] if isinstance(_off, list) else []
|
|
512
|
+
_trust_off = sorted(n for n in _off
|
|
513
|
+
if n.lower() in ("code_review", "security",
|
|
514
|
+
"unit_tests", "e2e_tests"))
|
|
515
|
+
if _trust_off:
|
|
516
|
+
lines.append("- These checks were switched OFF for this run, so they "
|
|
517
|
+
"never ran: %s" % ", ".join(_trust_off))
|
|
518
|
+
|
|
460
519
|
if isinstance(degraded, list) and degraded:
|
|
461
520
|
for d in degraded:
|
|
462
521
|
if isinstance(d, dict):
|
package/dashboard/__init__.py
CHANGED
package/loki-ts/dist/loki.js
CHANGED
|
@@ -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.
|
|
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.18.1";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=
|
|
1225
|
+
//# debugId=4A4305ACD35A512D64756E2164756E21
|
package/mcp/__init__.py
CHANGED
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.
|
|
4
|
+
"version": "8.18.1",
|
|
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.
|
|
5
|
+
"version": "8.18.1",
|
|
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",
|