loki-mode 7.27.0 → 7.28.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/README.md +3 -2
- package/SKILL.md +11 -2
- package/VERSION +1 -1
- package/autonomy/completion-council.sh +285 -6
- package/autonomy/context-tracker.py +32 -7
- package/autonomy/grill.sh +339 -0
- package/autonomy/loki +49 -0
- package/autonomy/prd-checklist.sh +248 -14
- package/autonomy/run.sh +170 -27
- package/autonomy/spec.sh +646 -0
- package/autonomy/verify.sh +55 -0
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/loki-ts/dist/loki.js +2 -2
- package/mcp/__init__.py +1 -1
- package/package.json +2 -1
- package/skills/quality-gates.md +46 -0
package/autonomy/verify.sh
CHANGED
|
@@ -971,6 +971,55 @@ OUTPUT:
|
|
|
971
971
|
EOF
|
|
972
972
|
}
|
|
973
973
|
|
|
974
|
+
# ---------------------------------------------------------------------------
|
|
975
|
+
# Living-spec drift gate (integration with autonomy/spec.sh).
|
|
976
|
+
#
|
|
977
|
+
# When .loki/spec/spec.lock exists, source the spec module and run its
|
|
978
|
+
# verify hook, which emits at most one SPEC_DRIFT finding (Medium -> CONCERNS)
|
|
979
|
+
# in the verify finding TSV shape. Records a gate row either way so the
|
|
980
|
+
# evidence document shows the spec was checked. Fully graceful: no lock, no
|
|
981
|
+
# module, or a hook error all degrade to a skipped gate and never abort verify.
|
|
982
|
+
# ---------------------------------------------------------------------------
|
|
983
|
+
verify_spec_drift_gate() {
|
|
984
|
+
local tree="${1:-.}"
|
|
985
|
+
local spec_dir="$tree/.loki/spec"
|
|
986
|
+
# Normalize a leading "./" so the lock-path probe is clean.
|
|
987
|
+
spec_dir="${spec_dir#./}"
|
|
988
|
+
local lock_file="$spec_dir/spec.lock"
|
|
989
|
+
|
|
990
|
+
if [ ! -f "$lock_file" ]; then
|
|
991
|
+
_verify_add_gate "spec_drift" "skipped" "loki-spec" "no spec lock (.loki/spec/spec.lock); run 'loki spec lock' to enable" "true"
|
|
992
|
+
return 0
|
|
993
|
+
fi
|
|
994
|
+
|
|
995
|
+
local spec_mod
|
|
996
|
+
spec_mod="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/spec.sh"
|
|
997
|
+
if [ ! -f "$spec_mod" ]; then
|
|
998
|
+
_verify_add_gate "spec_drift" "inconclusive" "loki-spec" "spec lock present but spec module not found" "true"
|
|
999
|
+
return 0
|
|
1000
|
+
fi
|
|
1001
|
+
|
|
1002
|
+
# Source the spec module in a guarded subshell-free way: it defines
|
|
1003
|
+
# spec_verify_hook which prints zero or one TSV finding line on stdout.
|
|
1004
|
+
# shellcheck source=/dev/null
|
|
1005
|
+
if ! source "$spec_mod" 2>/dev/null; then
|
|
1006
|
+
_verify_add_gate "spec_drift" "inconclusive" "loki-spec" "could not load spec module" "true"
|
|
1007
|
+
return 0
|
|
1008
|
+
fi
|
|
1009
|
+
|
|
1010
|
+
local finding
|
|
1011
|
+
finding="$(spec_verify_hook "$spec_dir" 2>/dev/null || true)"
|
|
1012
|
+
|
|
1013
|
+
if [ -n "$finding" ]; then
|
|
1014
|
+
# Append the SPEC_DRIFT finding(s) verbatim (already TSV-shaped).
|
|
1015
|
+
printf '%s\n' "$finding" >>"$_VERIFY_FINDINGS_FILE"
|
|
1016
|
+
_verify_add_gate "spec_drift" "fail" "loki-spec" "spec has drifted from its lock (see SPEC_DRIFT finding)" "true"
|
|
1017
|
+
else
|
|
1018
|
+
_verify_add_gate "spec_drift" "pass" "loki-spec" "spec is in sync with its lock" "true"
|
|
1019
|
+
fi
|
|
1020
|
+
return 0
|
|
1021
|
+
}
|
|
1022
|
+
|
|
974
1023
|
# ---------------------------------------------------------------------------
|
|
975
1024
|
# Entry point
|
|
976
1025
|
# ---------------------------------------------------------------------------
|
|
@@ -1052,6 +1101,12 @@ verify_main() {
|
|
|
1052
1101
|
verify_gate_dependency_audit "$tree"
|
|
1053
1102
|
fi
|
|
1054
1103
|
|
|
1104
|
+
# Living-spec integration: when a spec lock exists, fold a SPEC_DRIFT
|
|
1105
|
+
# finding into the verdict. Graceful no-op when there is no lock or the
|
|
1106
|
+
# spec machinery is unavailable -- verify must never fail to complete
|
|
1107
|
+
# because the optional spec module is missing.
|
|
1108
|
+
verify_spec_drift_gate "$tree"
|
|
1109
|
+
|
|
1055
1110
|
verify_compute_verdict "$block_on"
|
|
1056
1111
|
|
|
1057
1112
|
completed_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
package/dashboard/__init__.py
CHANGED
package/docs/INSTALLATION.md
CHANGED
|
@@ -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.
|
|
5
|
+
**Version:** v7.28.1
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
package/loki-ts/dist/loki.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var f8=Object.defineProperty;var u8=($)=>$;function c8($,Q){this[$]=u8.bind(null,Q)}var g=($,Q)=>{for(var Z in Q)f8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:c8.bind(Q,Z)})};var k=($,Q)=>()=>($&&(Q=$($=0)),Q);var X1=import.meta.require;var F$={};g(F$,{lokiDir:()=>P,homeLokiDir:()=>o1,findRepoRootForVersion:()=>d1,REPO_ROOT:()=>f});import{resolve as n,dirname as l1}from"path";import{fileURLToPath as p8}from"url";import{existsSync as L1}from"fs";import{homedir as l8}from"os";function d8(){let $=j$;for(let Q=0;Q<6;Q++){if(L1(n($,"VERSION"))&&L1(n($,"autonomy/run.sh")))return $;let Z=l1($);if(Z===$)break;$=Z}return n(j$,"..","..","..")}function d1($){let Q=$;for(let Z=0;Z<6;Z++){if(L1(n(Q,"VERSION"))&&L1(n(Q,"autonomy/run.sh")))return Q;let z=l1(Q);if(z===Q)break;Q=z}return n($,"..","..","..")}function P(){return process.env.LOKI_DIR??n(process.cwd(),".loki")}function o1(){return n(l8(),".loki")}var j$,f;var y=k(()=>{j$=l1(p8(import.meta.url));f=d8()});import{readFileSync as o8}from"fs";import{resolve as n8,dirname as a8}from"path";import{fileURLToPath as s8}from"url";function k1(){if($1!==null)return $1;let $="7.
|
|
2
|
+
var f8=Object.defineProperty;var u8=($)=>$;function c8($,Q){this[$]=u8.bind(null,Q)}var g=($,Q)=>{for(var Z in Q)f8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:c8.bind(Q,Z)})};var k=($,Q)=>()=>($&&(Q=$($=0)),Q);var X1=import.meta.require;var F$={};g(F$,{lokiDir:()=>P,homeLokiDir:()=>o1,findRepoRootForVersion:()=>d1,REPO_ROOT:()=>f});import{resolve as n,dirname as l1}from"path";import{fileURLToPath as p8}from"url";import{existsSync as L1}from"fs";import{homedir as l8}from"os";function d8(){let $=j$;for(let Q=0;Q<6;Q++){if(L1(n($,"VERSION"))&&L1(n($,"autonomy/run.sh")))return $;let Z=l1($);if(Z===$)break;$=Z}return n(j$,"..","..","..")}function d1($){let Q=$;for(let Z=0;Z<6;Z++){if(L1(n(Q,"VERSION"))&&L1(n(Q,"autonomy/run.sh")))return Q;let z=l1(Q);if(z===Q)break;Q=z}return n($,"..","..","..")}function P(){return process.env.LOKI_DIR??n(process.cwd(),".loki")}function o1(){return n(l8(),".loki")}var j$,f;var y=k(()=>{j$=l1(p8(import.meta.url));f=d8()});import{readFileSync as o8}from"fs";import{resolve as n8,dirname as a8}from"path";import{fileURLToPath as s8}from"url";function k1(){if($1!==null)return $1;let $="7.28.1";if(typeof $==="string"&&$.length>0)return $1=$,$1;try{let Q=a8(s8(import.meta.url)),Z=d1(Q);$1=o8(n8(Z,"VERSION"),"utf-8").trim()}catch{$1="unknown"}return $1}var $1=null;var n1=k(()=>{y()});var E$={};g(E$,{runOrThrow:()=>t8,run:()=>j,commandVersion:()=>i8,commandExists:()=>v,ShellError:()=>a1});async function j($,Q={}){let Z=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),z,K;if(Q.timeoutMs&&Q.timeoutMs>0)z=setTimeout(()=>{try{Z.kill("SIGTERM")}catch{}K=setTimeout(()=>{try{Z.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[H,X,q]=await Promise.all([new Response(Z.stdout).text(),new Response(Z.stderr).text(),Z.exited]);return{stdout:H,stderr:X,exitCode:q}}finally{if(z)clearTimeout(z);if(K)clearTimeout(K)}}async function t8($,Q={}){let Z=await j($,Q);if(Z.exitCode!==0)throw new a1(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function v($){let Q=r8($),Z=await j(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function r8($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function i8($,Q="--version"){if(!await v($))return null;let z=await j([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var a1;var d=k(()=>{a1=class a1 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 a($){return e8?"":$}var e8,T,N,_,KZ,A,R,h,J;var c=k(()=>{e8=(process.env.NO_COLOR??"").length>0;T=a("\x1B[0;31m"),N=a("\x1B[0;32m"),_=a("\x1B[1;33m"),KZ=a("\x1B[0;34m"),A=a("\x1B[0;36m"),R=a("\x1B[1m"),h=a("\x1B[2m"),J=a("\x1B[0m")});import{existsSync as U7}from"fs";async function Q1(){if(B1!==void 0)return B1;let $="/opt/homebrew/bin/python3.12";if(U7($))return B1=$,$;let Q=await v("python3.12");if(Q)return B1=Q,Q;let Z=await v("python3");return B1=Z,Z}async function Z1($,Q={}){let Z=await Q1();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return j([Z,"-c",$],Q)}var B1;var H1=k(()=>{d()});var d$={};g(d$,{runStatus:()=>N7});import{existsSync as b,readFileSync as q1,readdirSync as v$,statSync as f$}from"fs";import{resolve as D,basename as P7}from"path";import{homedir as L7}from"os";async function j7(){if(await v("jq"))return!0;return process.stdout.write(`${T}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)
|
|
@@ -787,4 +787,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
|
|
|
787
787
|
`),2}default:return process.stderr.write(`Unknown command: ${Q}
|
|
788
788
|
`),process.stderr.write(v8),2}}g$();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var l3=await p3(Bun.argv.slice(2));process.exit(l3);
|
|
789
789
|
|
|
790
|
-
//# debugId=
|
|
790
|
+
//# debugId=07839D2F23465AFE64756E2164756E21
|
package/mcp/__init__.py
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loki-mode",
|
|
3
|
-
"
|
|
3
|
+
"mcpName": "io.github.asklokesh/loki-mode",
|
|
4
|
+
"version": "7.28.1",
|
|
4
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 11 quality gates. Provider-agnostic (Claude Code, OpenAI Codex, Cline, Aider).",
|
|
5
6
|
"keywords": [
|
|
6
7
|
"agent",
|
package/skills/quality-gates.md
CHANGED
|
@@ -158,6 +158,18 @@ pass, not PRD-semantic correctness (the council vote is the semantic check).
|
|
|
158
158
|
The common false-block is a project that was ALREADY red before the run; the
|
|
159
159
|
one-step opt-out is the escape hatch.
|
|
160
160
|
|
|
161
|
+
**Inconclusive-baseline disclosure (v7.28.0):** when the gate cannot establish a
|
|
162
|
+
diff baseline (reason `no_git_repo` or `no_run_start_sha`) it still passes
|
|
163
|
+
through (it never blocks a non-git project), but completion is no longer
|
|
164
|
+
independently verified. Instead of passing silently, the gate writes
|
|
165
|
+
`.loki/state/evidence-inconclusive.json` (recording the reason, iteration, and
|
|
166
|
+
timestamp) and emits an `evidence_inconclusive` trust event. The run summary in
|
|
167
|
+
`.loki/COMPLETION.txt` then carries one honest line:
|
|
168
|
+
`Evidence gate: inconclusive (<reason>) - completion not independently
|
|
169
|
+
verified`. The record is removed automatically on any later run that resolves a
|
|
170
|
+
conclusive baseline. This is a diff-baseline-only disclosure: red tests still
|
|
171
|
+
block completion independently, regardless of the inconclusive state.
|
|
172
|
+
|
|
161
173
|
**Override-judge knobs (v7.5.4+):**
|
|
162
174
|
|
|
163
175
|
```bash
|
|
@@ -220,6 +232,40 @@ crash via the primitive's `finally` cleanup.
|
|
|
220
232
|
|
|
221
233
|
---
|
|
222
234
|
|
|
235
|
+
## Held-out spec evals (v7.28.0, default-on when reserved)
|
|
236
|
+
|
|
237
|
+
Anti-reward-hacking for the checklist. Before the first verification,
|
|
238
|
+
`checklist_select_heldout` (`autonomy/prd-checklist.sh`) deterministically
|
|
239
|
+
reserves a slice of checklist items as held-out:
|
|
240
|
+
`count = clamp(round(0.25 * N), 1, 5)` for checklists with `N >= 4` items
|
|
241
|
+
(smaller checklists reserve nothing). Selection is reproducible, not random:
|
|
242
|
+
items are ranked by `sha256(id)` and the first `count` are taken, then written
|
|
243
|
+
once to `.loki/checklist/held-out.json` (idempotent: never reselected once
|
|
244
|
+
chosen).
|
|
245
|
+
|
|
246
|
+
Held-out item IDs are EXCLUDED from everything the build loop sees: the checklist
|
|
247
|
+
summary, the visible counts, and the per-iteration checklist gate all omit them,
|
|
248
|
+
so the build agent cannot tune to those specific acceptance checks. The
|
|
249
|
+
completion council evaluates them only at the ship gate via
|
|
250
|
+
`council_heldout_gate` (`autonomy/completion-council.sh`): a held-out item whose
|
|
251
|
+
status is `failing` (and not waived) blocks completion exactly like any other
|
|
252
|
+
critical failure. Each evaluation records a `heldout_eval` trust event with the
|
|
253
|
+
verdict and pass/fail counts (no event is emitted when nothing is reserved).
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
LOKI_HELDOUT_GATE=0 # opt out: the held-out gate never blocks completion.
|
|
257
|
+
# Default is on (1), and the gate is inert anyway when
|
|
258
|
+
# no held-out items were reserved (N < 4).
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Honest limit: this protects against the PROMPT FEED, not against filesystem
|
|
262
|
+
access. The reservation lives on disk at `.loki/checklist/held-out.json`; an
|
|
263
|
+
adversarial agent with read access to the working tree can open that file and
|
|
264
|
+
learn which items were held out. The guarantee is that held-out items are kept
|
|
265
|
+
out of the build loop's own prompt context, not that they are sandboxed.
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
223
269
|
## Uncertainty-gated escalation (v7.19.2, default-on)
|
|
224
270
|
|
|
225
271
|
When Loki is likely stuck or thrashing, it escalates proactively to the human
|