loki-mode 8.24.0 → 8.26.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/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/loki +17 -3
- package/autonomy/run.sh +72 -5
- 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.26.0
|
|
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.26.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
8.
|
|
1
|
+
8.26.0
|
package/autonomy/loki
CHANGED
|
@@ -30083,13 +30083,27 @@ try:
|
|
|
30083
30083
|
except Exception as exc:
|
|
30084
30084
|
sys.stderr.write(f'[ERROR] magic.core.debate not available: {exc}\n')
|
|
30085
30085
|
sys.exit(2)
|
|
30086
|
-
|
|
30086
|
+
import json, os
|
|
30087
|
+
# run_debate takes component_path, not react_path/wc_path -- passing those
|
|
30088
|
+
# raised TypeError on EVERY invocation, and the caller's '|| true' swallowed it
|
|
30089
|
+
# so Gate 12 reported PASS on total failure. Pick whichever generated artifact
|
|
30090
|
+
# exists; the debate critiques code against the spec, so with neither present it
|
|
30091
|
+
# still runs on the spec alone.
|
|
30092
|
+
_react = '.loki/magic/generated/react/${name}.tsx'
|
|
30093
|
+
_wc = '.loki/magic/generated/webcomponent/${name}.js'
|
|
30094
|
+
_component = _react if os.path.exists(_react) else (_wc if os.path.exists(_wc) else '')
|
|
30095
|
+
_result = run_debate(
|
|
30087
30096
|
name='$name',
|
|
30088
30097
|
spec_path='$spec_path',
|
|
30098
|
+
component_path=_component,
|
|
30089
30099
|
rounds=$rounds,
|
|
30090
|
-
react_path='.loki/magic/generated/react/${name}.tsx',
|
|
30091
|
-
wc_path='.loki/magic/generated/webcomponent/${name}.js',
|
|
30092
30100
|
)
|
|
30101
|
+
# The result must reach stdout: the gate greps it for a blocking severity, and
|
|
30102
|
+
# a returned-but-never-printed dict is invisible to every caller.
|
|
30103
|
+
print(json.dumps(_result, indent=2, default=str))
|
|
30104
|
+
if _result.get('error'):
|
|
30105
|
+
sys.stderr.write('[ERROR] debate error: %s\n' % _result['error'])
|
|
30106
|
+
sys.exit(1)
|
|
30093
30107
|
" || {
|
|
30094
30108
|
log_error "Debate failed"
|
|
30095
30109
|
return 1
|
package/autonomy/run.sh
CHANGED
|
@@ -12014,15 +12014,82 @@ run_magic_debate_gate() {
|
|
|
12014
12014
|
local latest_name
|
|
12015
12015
|
latest_name=$(basename "$latest_spec" .md)
|
|
12016
12016
|
|
|
12017
|
+
# NOT guarded on "a generated artifact exists". That guard was written and
|
|
12018
|
+
# then removed after measuring: the `magic update` call above GENERATES the
|
|
12019
|
+
# component (verified -- a bare spec directory gains a 2689-byte
|
|
12020
|
+
# generated/react/<name>.tsx), so by this point the artifact is present and
|
|
12021
|
+
# its code does reach the personas. A guard here would be dead code resting
|
|
12022
|
+
# on a false premise.
|
|
12023
|
+
#
|
|
12024
|
+
# The BLOCK observed while fixing this ("CODE TO REVIEW is still empty") came
|
|
12025
|
+
# from debating a deliberately one-line stub spec, which is a legitimate
|
|
12026
|
+
# verdict on genuinely thin input, not a spurious process block.
|
|
12017
12027
|
log_info "Magic Modules: running debate on '$latest_name'"
|
|
12018
|
-
local debate_out
|
|
12028
|
+
local debate_out debate_rc
|
|
12019
12029
|
debate_out=$(cd "$TARGET_DIR" && PYTHONPATH="$PROJECT_DIR" LOKI_PROVIDER="${PROVIDER_NAME:-claude}" \
|
|
12020
|
-
timeout 300 "$PROJECT_DIR/autonomy/loki" magic debate "$latest_name" --rounds 2 2>&1
|
|
12030
|
+
timeout 300 "$PROJECT_DIR/autonomy/loki" magic debate "$latest_name" --rounds 2 2>&1) \
|
|
12031
|
+
&& debate_rc=0 || debate_rc=$?
|
|
12021
12032
|
|
|
12022
|
-
#
|
|
12033
|
+
# A debate that could not RUN is not a debate that found nothing. The old
|
|
12034
|
+
# code ended this pipeline in '|| true' and then grepped for a blocking
|
|
12035
|
+
# severity, so a crash produced no match and the gate reported PASS -- which
|
|
12036
|
+
# is how a TypeError in the CLI call left Gate 12 silently fail-open.
|
|
12037
|
+
#
|
|
12038
|
+
# But "could not run" splits in two, and the halves need opposite handling:
|
|
12039
|
+
#
|
|
12040
|
+
# ENVIRONMENT the provider CLI is absent, timed out, or exited non-zero.
|
|
12041
|
+
# Common and not the project's fault. Blocking here would
|
|
12042
|
+
# stop every run without working provider credentials over an
|
|
12043
|
+
# advisory gate, so this DEGRADES: warn, record, return 0.
|
|
12044
|
+
# WIRING the debate itself is broken (import error, bad arguments).
|
|
12045
|
+
# Nobody's build is judged and nobody is told, which is the
|
|
12046
|
+
# defect being fixed. This must be LOUD.
|
|
12047
|
+
#
|
|
12048
|
+
# Fail-safe direction is deliberate: an unrecognised failure degrades rather
|
|
12049
|
+
# than blocks, so a new provider error shape can never wedge every build.
|
|
12050
|
+
if [ "$debate_rc" -ne 0 ]; then
|
|
12051
|
+
case "$debate_out" in
|
|
12052
|
+
*"not available"*|*TypeError*|*SyntaxError*|*ImportError*|*"unexpected keyword"*)
|
|
12053
|
+
log_error "Magic Modules Gate 12 is BROKEN for '$latest_name' (rc=$debate_rc): the debate could not execute, so no component is being judged."
|
|
12054
|
+
printf '%s\n' "$debate_out" | tail -5 >&2
|
|
12055
|
+
return 1
|
|
12056
|
+
;;
|
|
12057
|
+
esac
|
|
12058
|
+
if [ "$debate_rc" -eq 124 ]; then
|
|
12059
|
+
log_warn "Magic Modules Gate 12: debate timed out after 300s for '$latest_name'; treating as not-judged, not as PASS"
|
|
12060
|
+
else
|
|
12061
|
+
log_warn "Magic Modules Gate 12: debate could not run (rc=$debate_rc, provider/environment) for '$latest_name'; treating as not-judged, not as PASS"
|
|
12062
|
+
fi
|
|
12063
|
+
printf '%s\n' "$debate_out" | tail -3 >&2
|
|
12064
|
+
return 0
|
|
12065
|
+
fi
|
|
12066
|
+
|
|
12067
|
+
# Parse debate outcome; block if any persona set severity=block.
|
|
12068
|
+
#
|
|
12069
|
+
# ADVISORY BY DEFAULT (LOKI_GATE_MAGIC_DEBATE_BLOCKING=true to enforce).
|
|
12070
|
+
# This gate was fail-open from v6.77.0 until the TypeError above was fixed,
|
|
12071
|
+
# so its blocking path had NEVER run against a real project. Measuring it
|
|
12072
|
+
# before enabling it showed why that matters: on a deliberately thorough
|
|
12073
|
+
# spec -- explicit KB budgets, a named device class, zero-JS server
|
|
12074
|
+
# component, stated contrast ratio -- THREE of four personas still returned
|
|
12075
|
+
# "block". Two independent specs, two blocks.
|
|
12076
|
+
#
|
|
12077
|
+
# A single "block" from any one persona ANDs four strict reviewers together,
|
|
12078
|
+
# so the gate approves only when all four are simultaneously satisfied. That
|
|
12079
|
+
# is a threshold almost nothing clears, and flipping it on would turn a gate
|
|
12080
|
+
# that never blocked into one that blocks nearly every build -- a worse
|
|
12081
|
+
# regression than the silent fail-open being fixed here.
|
|
12082
|
+
#
|
|
12083
|
+
# The finding is still surfaced and still recorded; it just does not stop
|
|
12084
|
+
# the run until the threshold is tuned against real projects. Making a
|
|
12085
|
+
# never-exercised gate enforcing is a separate, measured decision.
|
|
12023
12086
|
if echo "$debate_out" | grep -qi '"severity"[[:space:]]*:[[:space:]]*"block"'; then
|
|
12024
|
-
|
|
12025
|
-
|
|
12087
|
+
if [ "${LOKI_GATE_MAGIC_DEBATE_BLOCKING:-false}" = "true" ]; then
|
|
12088
|
+
log_warn "Magic Modules Gate 12: debate returned BLOCK severity for '$latest_name'"
|
|
12089
|
+
return 1
|
|
12090
|
+
fi
|
|
12091
|
+
log_warn "Magic Modules Gate 12: debate returned BLOCK severity for '$latest_name' (advisory; set LOKI_GATE_MAGIC_DEBATE_BLOCKING=true to enforce)"
|
|
12092
|
+
return 0
|
|
12026
12093
|
fi
|
|
12027
12094
|
|
|
12028
12095
|
log_info "Magic Modules Gate 12: PASS"
|
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.26.0";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=5ABB78E68257C81D64756E2164756E21
|
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.26.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.
|
|
5
|
+
"version": "8.26.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",
|