loki-mode 9.26.3 → 9.27.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 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 v9.26.3
6
+ # Loki Mode v9.27.1
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -470,4 +470,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
470
470
 
471
471
  ---
472
472
 
473
- **v9.26.3 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
473
+ **v9.27.1 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 9.26.3
1
+ 9.27.1
@@ -644,6 +644,66 @@ def _collect_model(loki_dir, observed):
644
644
  return "unavailable"
645
645
 
646
646
 
647
+ def _collect_decisions(loki_dir):
648
+ """Which models actually ran, and did the configuration change mid-flight?
649
+
650
+ Reads the append-only trail at .loki/decisions/decisions.jsonl that
651
+ autonomy/lib/decision_record.py writes once per dispatch. That module
652
+ already computes the audit fact worth showing -- more than one model_id in
653
+ one project means the deciding component changed while the run was in
654
+ flight -- so this reuses summarize() rather than recomputing it.
655
+
656
+ THREE STATES, NEVER COLLAPSED, for the same reason _collect_model returns
657
+ the literal "unavailable" instead of guessing:
658
+
659
+ measured -- records exist; counts and model_changed are real
660
+ no_records -- the trail is absent for this run
661
+ unreadable -- the trail exists but could not be read
662
+
663
+ A section that renders nothing when the trail is absent reads as "no swap
664
+ happened", which is exactly the false green this receipt exists to prevent.
665
+ Absence of evidence is reported as absence of evidence.
666
+
667
+ model_changed is a DISCLOSED FACT, not a failure. run.sh documents four
668
+ legitimate reasons a model changes mid-run (opus-pin force, LOKI_MAX_TIER
669
+ clamp, mid-flight override, fable collapse). It never touches the verdict.
670
+
671
+ Corrupt lines are surfaced via unparseable_lines rather than dropped: an
672
+ audit trail that quietly discards what it cannot parse is worse than one
673
+ that admits the gap.
674
+ """
675
+ try:
676
+ from decision_record import summarize as _summarize
677
+ except ImportError:
678
+ return {"status": "unreadable", "reason": "decision_record module unavailable"}
679
+
680
+ try:
681
+ raw = _summarize(loki_dir)
682
+ except Exception as exc: # never let provenance reporting break the receipt
683
+ return {"status": "unreadable", "reason": str(exc)}
684
+
685
+ if not isinstance(raw, dict):
686
+ return {"status": "unreadable", "reason": "invalid summary"}
687
+
688
+ if raw.get("status") == "measured":
689
+ return {
690
+ "status": "measured",
691
+ "records": _to_int(raw.get("records"), 0),
692
+ "unparseable_lines": _to_int(raw.get("unparseable_lines"), 0),
693
+ "models": raw.get("models") or {},
694
+ "stages": raw.get("stages") or {},
695
+ "model_changed": bool(raw.get("model_changed")),
696
+ "temperature_changed": bool(raw.get("temperature_changed")),
697
+ # Never influences the verdict: this is provenance, not a gate.
698
+ "affects_verdict": False,
699
+ }
700
+
701
+ reason = str(raw.get("reason") or "unknown")
702
+ status = "no_records" if reason == "no_records" else "unreadable"
703
+ return {"status": status, "reason": reason,
704
+ "detail": str(raw.get("detail") or ""), "affects_verdict": False}
705
+
706
+
647
707
  def _collect_security(loki_dir):
648
708
  """Read .loki/quality/security-findings.json (the secure-by-default gate).
649
709
 
@@ -1237,6 +1297,9 @@ def _build_proof(args, loki_dir, target_dir, repo_root):
1237
1297
  termination = _collect_termination(loki_dir, args.session_exit_code)
1238
1298
  tests = _collect_tests(loki_dir)
1239
1299
  security = _collect_security(loki_dir)
1300
+ # Model provenance: which models actually ran, and did that change
1301
+ # mid-flight. Provenance only -- affects_verdict is False.
1302
+ decisions = _collect_decisions(loki_dir)
1240
1303
  functional = _collect_functional(loki_dir) # FV-2 record-half: descriptive only
1241
1304
  healthcheck = _collect_healthcheck(loki_dir) # Evidence Receipt record-half
1242
1305
  evidence_gate = _collect_evidence_gate(loki_dir)
@@ -1429,6 +1492,7 @@ def _build_proof(args, loki_dir, target_dir, repo_root):
1429
1492
  "diffs": diffs,
1430
1493
  "council": council,
1431
1494
  "quality_gates": quality_gates,
1495
+ "decisions": decisions,
1432
1496
  "cost": cost,
1433
1497
  "deployment": deployment,
1434
1498
  # Typed compatibility mirror for consumers that do not traverse facts.
@@ -1815,6 +1879,41 @@ def _render_fallback_html(proof):
1815
1879
  "who produced them, so this receipt trusts its generator. "
1816
1880
  "To sign future receipts, set LOKI_PROOF_GPG_KEY to a gpg "
1817
1881
  "key id (see docs/SIGNED-RECEIPTS.md).</p>")
1882
+
1883
+ # Model provenance, mirrored here for the same reason the signature row is:
1884
+ # this fallback "must not be quieter about provenance than the page it
1885
+ # stands in for". Three states, never collapsed -- an absent trail is
1886
+ # reported as absent, not rendered as silence that reads like "no change".
1887
+ dec = proof.get("decisions") or {}
1888
+ dstatus = dec.get("status")
1889
+ if dstatus == "measured":
1890
+ models = dec.get("models") or {}
1891
+ listing = ", ".join(
1892
+ "%s (%s)" % (esc(k2), esc(v2)) for k2, v2 in sorted(models.items())
1893
+ ) or "none recorded"
1894
+ if dec.get("model_changed"):
1895
+ change = ("The model CHANGED during this run. That is disclosed, not a "
1896
+ "fault -- a tier clamp, an operator override or a mid-flight "
1897
+ "failover all cause it legitimately.")
1898
+ else:
1899
+ change = "One model ran for the whole project; no mid-flight change."
1900
+ bad = _to_int(dec.get("unparseable_lines"), 0)
1901
+ badnote = ""
1902
+ if bad > 0:
1903
+ badnote = (" %d trail line(s) could not be parsed and are counted "
1904
+ "rather than dropped, so the list may be incomplete." % bad)
1905
+ rows.append("<p>Models dispatched (append-only decision trail): %s. %s%s "
1906
+ "Provenance only; never changes the verdict.</p>"
1907
+ % (listing, change, badnote))
1908
+ elif dstatus == "no_records":
1909
+ rows.append("<p>Models dispatched: no decision trail was recorded for this "
1910
+ "run, so per-iteration model attribution cannot be confirmed. "
1911
+ "Absence of the trail is reported as absence of evidence, not "
1912
+ "as proof that nothing changed.</p>")
1913
+ elif dstatus:
1914
+ rows.append("<p>Models dispatched: the decision trail could not be read "
1915
+ "(%s), so per-iteration model attribution is unavailable. "
1916
+ "Reported rather than omitted.</p>" % esc(dec.get("reason", "unknown")))
1818
1917
  red = proof.get("redaction", {})
1819
1918
  rows.append("<p>Redaction applied: %s (%s redactions, rules v%s)</p>" % (
1820
1919
  esc(red.get("applied")), esc(red.get("redactions_count")),
@@ -1268,10 +1268,60 @@
1268
1268
  var signNote = signed
1269
1269
  ? '<p class="hash-note">This receipt carries a detached GPG signature over the same canonical bytes that were hashed. A verifier holding the signer public key can confirm its provenance offline with <code>loki proof verify</code>.</p>'
1270
1270
  : '<p class="hash-note">This receipt is UNSIGNED, so it trusts the generator that produced it (<code>loki proof verify</code> reports <code>generator_trusted: true</code>). To sign future receipts, set <code>LOKI_PROOF_GPG_KEY</code> to a GPG key id before the run.</p>';
1271
+ // Model provenance from the append-only decision trail
1272
+ // (.loki/decisions/decisions.jsonl, written once per dispatch).
1273
+ //
1274
+ // The "Model" row above is the run-level model. It cannot answer the
1275
+ // question a regulated buyer actually asks: did the deciding component
1276
+ // change while the run was in flight? Two model ids in one project is
1277
+ // exactly that case, and the trail proves it either way.
1278
+ //
1279
+ // THREE STATES, NEVER COLLAPSED. A section that renders nothing when the
1280
+ // trail is absent reads as "no swap happened" -- the false green this
1281
+ // receipt exists to prevent. An older receipt predating this field has no
1282
+ // "decisions" key at all and is left silent rather than described, since a
1283
+ // receipt that never recorded the trail cannot honestly report on it.
1284
+ var dec = g(p, "decisions", null);
1285
+ var decNote = "";
1286
+ if (dec && g(dec, "status", "")) {
1287
+ var dstatus = g(dec, "status", "");
1288
+ if (dstatus === "measured") {
1289
+ var models = g(dec, "models", {}) || {};
1290
+ var names = [];
1291
+ for (var mk in models) {
1292
+ if (Object.prototype.hasOwnProperty.call(models, mk)) {
1293
+ names.push(esc(mk) + " (" + num(models[mk]) + ")");
1294
+ }
1295
+ }
1296
+ var changed = !!g(dec, "model_changed", false);
1297
+ var bad = num(g(dec, "unparseable_lines", 0));
1298
+ decNote = '<p class="hash-note">Models dispatched, from the run\u2019s own ' +
1299
+ "append-only decision trail: " + (names.length ? names.join(", ") : "none recorded") +
1300
+ ". " +
1301
+ (changed
1302
+ ? "The model CHANGED during this run. That is disclosed, not a fault -- a tier clamp, an " +
1303
+ "operator override or a mid-flight failover all cause it legitimately -- but it is the " +
1304
+ "thing you would otherwise have to take on trust."
1305
+ : "One model ran for the whole project; the trail shows no mid-flight change.") +
1306
+ (bad > 0
1307
+ ? " " + bad + " line" + (bad === 1 ? "" : "s") + " of the trail could not be parsed and " +
1308
+ "are counted here rather than dropped, so this list may be incomplete."
1309
+ : "") +
1310
+ " This is provenance and never changes the verdict.</p>";
1311
+ } else if (dstatus === "no_records") {
1312
+ decNote = '<p class="hash-note">No decision trail was recorded for this run, so which ' +
1313
+ "model served each iteration cannot be confirmed from this receipt. Absence of the trail " +
1314
+ "is reported as absence of evidence, not as proof that nothing changed.</p>";
1315
+ } else {
1316
+ decNote = '<p class="hash-note">The decision trail for this run could not be read (' +
1317
+ esc(g(dec, "reason", "unknown")) + "), so per-iteration model attribution is unavailable. " +
1318
+ "Reported rather than omitted.</p>";
1319
+ }
1320
+ }
1271
1321
  card.innerHTML = '<div class="kv">' + kv + "</div>" +
1272
1322
  (hash ? '<p class="hash-note">The integrity hash is a ' + esc(algo) +
1273
1323
  " digest of this proof. It proves the artifact has not been altered after it was emitted. It does not, by itself, prove who or what produced the work.</p>" : "") +
1274
- signNote;
1324
+ signNote + decNote;
1275
1325
  }
1276
1326
 
1277
1327
  function renderCta(p) {
package/autonomy/loki CHANGED
@@ -2148,6 +2148,26 @@ cmd_start() {
2148
2148
  echo " loki start --yes # Skip confirmation prompt"
2149
2149
  echo " LOKI_PRD_FILE=./prd.md loki start # PRD via env var"
2150
2150
  echo ""
2151
+ # Exit codes, stated where a CI author actually looks. The full
2152
+ # contract lived only in docs/exit-codes.md, so a script author
2153
+ # read this help, saw nothing, and built the coarse gate -- the
2154
+ # tiered contract might as well not have shipped.
2155
+ echo "Exit codes:"
2156
+ echo " 0 The run completed, or a human stopped it"
2157
+ echo " nonzero Something went wrong (no finer signal by default)"
2158
+ echo ""
2159
+ echo " With LOKI_DURABLE_STATE=1 the platform contract applies, which"
2160
+ echo " distinguishes 'retrying cannot help' from 'the process died':"
2161
+ echo " 0 Completed, or a human stopped it -> do not retry"
2162
+ echo " 20 Deterministic terminal failure (gate failed, max"
2163
+ echo " iterations or retries, budget or wall-clock cap,"
2164
+ echo " policy block, contradictory spec) -> do not retry;"
2165
+ echo " the same inputs fail the same way"
2166
+ echo " other nonzero Crash (SIGKILL, eviction, node loss) -> retry;"
2167
+ echo " the run resumes from the durable volume"
2168
+ echo " Both the bash and Bun runners implement this identically."
2169
+ echo " Full table, including loki verify and loki ci: docs/exit-codes.md"
2170
+ echo ""
2151
2171
  echo "Note: 'loki run' is a deprecated alias for 'loki start' with an issue ref."
2152
2172
  exit 0
2153
2173
  ;;
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "9.26.3"
10
+ __version__ = "9.27.1"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -24,32 +24,32 @@ one strengthens a claim a buyer can verify without trusting us.
24
24
 
25
25
  ---
26
26
 
27
- ## 1. Surface the evidence the receipt already holds
28
-
29
- **Status: the computation exists; the presentation does not.**
30
-
31
- `autonomy/lib/proof-generator.py` already computes, per run:
32
-
33
- - the exogenous-vs-advisory gate split (`:291-307`) -- which gates are
34
- agent-independent and which are model-authored, fail-closed on unknown gates
35
- - files changed and a stat-level diff hash (`:910-917`)
36
- - per-run efficiency cost, shared with the benchmark adapters so both compute it
37
- identically (`:39`, `:93-94`)
38
-
39
- None of it reaches the top of the receipt. A reader has to know it is in there.
40
-
41
- This is the highest-value item because the research validated it from the
42
- outside: Factory users complain about false-green runs and untrustworthy model
43
- attribution. Those complaints describe exactly the fields Loki already has and
44
- does not show.
45
-
46
- **Do:** promote to the receipt header -- gate counts split exogenous/advisory,
47
- files changed, tokens and turns, dispatched model per iteration, per-run cost.
48
- No new measurement, no new dependency.
49
-
50
- **Verify:** a receipt from a real run displays all six without opening the JSON.
51
-
52
- ---
27
+ ## 1. Model provenance on the receipt -- SHIPPED v9.27.0
28
+
29
+ **Corrected.** An earlier draft of this item claimed the exogenous/advisory gate
30
+ split, files-changed and cost "do not reach the top of the receipt". That was
31
+ wrong, and reading the renderers disproved it: `proof-template.html:1165-1192`
32
+ already renders the split with the strongest honesty framing in the repo
33
+ ("a model that is confidently wrong scores itself green here"), plus
34
+ disabled-gate disclosure, cost, tokens, wall clock and files changed.
35
+
36
+ Five of the six fields were already shipped. **One was genuinely missing:**
37
+ per-iteration model attribution. `autonomy/lib/decision_record.py` writes an
38
+ append-only trail to `.loki/decisions/decisions.jsonl` once per dispatch, and
39
+ already computes the audit fact worth showing -- `model_changed`, meaning more
40
+ than one model id served one project. The proof generator read that file
41
+ **zero times**.
42
+
43
+ That is the question a regulated buyer actually asks, and the one Factory users
44
+ complain about: did the deciding component change mid-run without anyone saying
45
+ so? The run-level "Model" row cannot answer it.
46
+
47
+ **Shipped in v9.27.0** in both renderers, with three states never collapsed --
48
+ `measured`, `no_records`, `unreadable`. An absent trail is reported as absent
49
+ rather than rendered as silence that reads like "no swap happened", corrupt
50
+ trail lines are counted rather than dropped, and `affects_verdict` is `false`:
51
+ a mid-flight change is a disclosed fact (tier clamp, operator override,
52
+ failover all cause it legitimately), never a fault and never a verdict input.
53
53
 
54
54
  ## 2. Make the machine contract discoverable
55
55
 
@@ -66,8 +66,16 @@ A CI author reads `--help`, sees "0 on success, nonzero on failure", and builds
66
66
  the coarse gate. Factory's `droid exec` advertises its exit codes in its own help
67
67
  output; ours are a doc you have to already know exists.
68
68
 
69
- **Do:** surface the durable contract in `loki start --help` and `loki verify
70
- --help`, with a one-line pointer to `docs/exit-codes.md`.
69
+ **Shipped in v9.27.1** for `loki start --help`: the two-tier contract, code 20,
70
+ its retry semantics, and a pointer to `docs/exit-codes.md`. A drift assertion
71
+ fails if the code stated in the help stops matching the code in the doc, since
72
+ two documents disagreeing about a value a Kubernetes Job is configured on is
73
+ worse than one.
74
+
75
+ `loki verify --help` needed no change -- it already carried an `EXIT CODES`
76
+ section. Adding a second one (which I briefly did) would have created exactly
77
+ the duplicated-and-drifting help this item exists to prevent; a test now asserts
78
+ there is exactly one.
71
79
 
72
80
  **Note:** the research framed this as "Loki has no headless one-shot contract".
73
81
  That framing was wrong -- the contract exists. The defect is discoverability,
@@ -97,17 +105,22 @@ of the time is worse than no number.
97
105
 
98
106
  ## 4. Close the config-diagnostic gap for the remaining format
99
107
 
100
- **Status: shipped for JSON and .env in v9.26.1-9.26.2; YAML is parser-dependent.**
108
+ **Status: shipped for JSON, `.env`, and YAML. Narrow residual gap only.**
109
+
110
+ `loki config validate` reports unknown keys in JSON and `.env` (v9.26.1) and in
111
+ YAML via pyyaml with a `yq` fallback (v9.26.2). `yq` is preinstalled on the
112
+ GitHub ubuntu-24.04 runner, and the fallback was verified against a stand-in
113
+ honouring both invocation shapes the real `yq` is called with, so CI and any
114
+ Linux host with either parser get full detection.
101
115
 
102
- `loki config validate` now reports unknown keys in JSON and `.env`. YAML
103
- detection requires pyyaml or `yq`; with neither installed it degrades quietly
104
- (correct -- a missing parser must not invent a verdict) but silently provides no
105
- detection.
116
+ The residual gap is narrow: a host with **neither** pyyaml nor `yq` (a stock
117
+ macOS dev machine) gets no YAML detection. It degrades quietly, which is correct
118
+ -- a missing parser must never invent a verdict -- but silently.
106
119
 
107
- **Do:** either vendor a minimal YAML key-path scanner (the file already has
108
- `loki_yaml_fallback_extract` for the read path, so the precedent exists), or
109
- state the dependency in `loki config validate --help` so the gap is visible
110
- rather than silent.
120
+ **Do:** state the dependency in `loki config validate --help` so the gap is
121
+ visible rather than silent. Vendoring a YAML scanner is not worth it for one
122
+ host shape that already has a documented fallback available via `brew install
123
+ yq`.
111
124
 
112
125
  ---
113
126
 
@@ -160,10 +173,16 @@ before every push. v9.26.0 failed the same way on repo-wide ShellCheck.
160
173
  CLAUDE.md already states the rule ("a check that guards the shipped artifact must
161
174
  run in the FAST tier"). The rule is not enforced.
162
175
 
163
- **Do:** when a change touches a file, run the deferred suites that grep that file
164
- before pushing. This session did it by hand (16 suites for `verify.sh`, then
165
- repo-wide ShellCheck) and it caught the failure locally instead of one 25-minute
166
- CI cycle at a time. Make it a script rather than a habit.
176
+ **Shipped in v9.26.3** as `scripts/guard-changed.sh`: runs the suites that
177
+ reference the files in your diff, plus ShellCheck on the changed shell files.
178
+ Measured 8s for a one-file change, ~135s worst case, against 26m50s for the FULL
179
+ tier. Verified it would have blocked the v9.26.0 push locally.
180
+
181
+ **Remaining:** it is necessary, not sufficient. It cannot catch a failure that
182
+ depends on CI differing from your machine -- which is exactly how v9.26.2 failed,
183
+ on a suite this script selects and runs. The follow-on work is making
184
+ environment-conditional assertions name their condition (`command -v yq`) rather
185
+ than assume the author's host. That is a review habit, not a script.
167
186
 
168
187
  ---
169
188
 
@@ -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:** v9.26.3
5
+ **Version:** v9.27.1
6
6
 
7
7
  ---
8
8
 
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var St=Object.create;var{getPrototypeOf:yt,defineProperty:jG,getOwnPropertyNames:bt}=Object;var ft=Object.prototype.hasOwnProperty;function _t($){return this[$]}var vt,ht,gt=($,X,Q)=>{var z=$!=null&&typeof $==="object";if(z){var Z=X?vt??=new WeakMap:ht??=new WeakMap,K=Z.get($);if(K)return K}Q=$!=null?St(yt($)):{};let J=X||!$||!$.__esModule?jG(Q,"default",{value:$,enumerable:!0}):Q;for(let q of bt($))if(!ft.call(J,q))jG(J,q,{get:_t.bind($,q),enumerable:!0});if(z)Z.set($,J);return J};var zq=($,X)=>()=>(X||$((X={exports:{}}).exports,X),X.exports);var mt=($)=>$;function ut($,X){this[$]=mt.bind(null,X)}var B1=($,X)=>{for(var Q in X)jG($,Q,{get:X[Q],enumerable:!0,configurable:!0,set:ut.bind(X,Q)})};var s=($,X)=>()=>($&&(X=$($=0)),X);var w5=import.meta.require;var YR={};B1(YR,{lokiDir:()=>h0,homeLokiDir:()=>HQ,findRepoRootForVersion:()=>AG,REPO_ROOT:()=>L1});import{resolve as h2,dirname as LG}from"path";import{fileURLToPath as dt}from"url";import{existsSync as Zq}from"fs";import{homedir as pt}from"os";function ct(){let $=VR;for(let X=0;X<6;X++){if(Zq(h2($,"VERSION"))&&Zq(h2($,"autonomy/run.sh")))return $;let Q=LG($);if(Q===$)break;$=Q}return h2(VR,"..","..","..")}function AG($){let X=$;for(let Q=0;Q<6;Q++){if(Zq(h2(X,"VERSION"))&&Zq(h2(X,"autonomy/run.sh")))return X;let z=LG(X);if(z===X)break;X=z}return h2($,"..","..","..")}function h0(){return process.env.LOKI_DIR??h2(process.cwd(),".loki")}function HQ(){return h2(pt(),".loki")}var VR,L1;var k1=s(()=>{VR=LG(dt(import.meta.url));L1=ct()});import{readFileSync as lt}from"fs";import{resolve as it,dirname as at}from"path";import{fileURLToPath as ot}from"url";function j9(){if(n3!==null)return n3;let $="9.26.3";if(typeof $==="string"&&$.length>0)return n3=$,n3;try{let X=at(ot(import.meta.url)),Q=AG(X);n3=lt(it(Q,"VERSION"),"utf-8").trim()}catch{n3="unknown"}return n3}var n3=null;var Kq=s(()=>{k1()});var GR={};B1(GR,{runOrThrow:()=>Ue,run:()=>$1,readStreamCapped:()=>Jq,commandVersion:()=>We,commandExists:()=>g5,ShellError:()=>CG,MAX_STDOUT_BYTES:()=>WR});async function Jq($,X=WR){let Q=$.getReader(),z=new TextDecoder,Z="",K=0;try{while(K<X){let{done:J,value:q}=await Q.read();if(J)break;if(!q)continue;if(K+=q.byteLength,K>X){let V=q.byteLength-(K-X);Z+=z.decode(q.subarray(0,V),{stream:!0});break}Z+=z.decode(q,{stream:!0})}Z+=z.decode()}finally{try{await Q.cancel()}catch{}Q.releaseLock()}return Z}async function $1($,X={}){let Q=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:X.env?{...process.env,...X.env}:process.env,cwd:X.cwd}),z,Z;if(X.timeoutMs&&X.timeoutMs>0)z=setTimeout(()=>{try{Q.kill("SIGTERM")}catch{}Z=setTimeout(()=>{try{Q.kill("SIGKILL")}catch{}},2000)},X.timeoutMs);try{let[K,J,q]=await Promise.all([Jq(Q.stdout),new Response(Q.stderr).text(),Q.exited]);return{stdout:K,stderr:J,exitCode:q}}finally{if(z)clearTimeout(z);if(Z)clearTimeout(Z)}}async function Ue($,X={}){let Q=await $1($,X);if(Q.exitCode!==0)throw new CG(`command failed (${Q.exitCode}): ${$.join(" ")}`,Q.exitCode,Q.stdout,Q.stderr);return Q}async function g5($){let X=He($),Q=await $1(["sh","-c",`command -v ${X}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function He($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function We($,X="--version"){if(!await g5($))return null;let z=await $1([$,X],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var WR=16777216,CG;var y8=s(()=>{CG=class CG extends Error{message;exitCode;stdout;stderr;constructor($,X,Q,z){super($);this.message=$;this.exitCode=X;this.stdout=Q;this.stderr=z;this.name="ShellError"}}});function g2($){return Ge?"":$}var Ge,p0,$5,q1,L61,A1,f1,m5,r;var t7=s(()=>{Ge=(process.env.NO_COLOR??"").length>0;p0=g2("\x1B[0;31m"),$5=g2("\x1B[0;32m"),q1=g2("\x1B[1;33m"),L61=g2("\x1B[0;34m"),A1=g2("\x1B[0;36m"),f1=g2("\x1B[1m"),m5=g2("\x1B[2m"),r=g2("\x1B[0m")});import{existsSync as De}from"fs";async function Z2(){if(GQ!==void 0)return GQ;let $="/opt/homebrew/bin/python3.12";if(De($))return GQ=$,$;let X=await g5("python3.12");if(X)return GQ=X,X;let Q=await g5("python3");return GQ=Q,Q}async function I4($,X={}){let Q=await Z2();if(!Q)return{stdout:"",stderr:"python3 not found",exitCode:127};return $1([Q,"-c",$],X)}var GQ;var m2=s(()=>{y8()});var yR={};B1(yR,{runStatus:()=>oe});import{existsSync as u5,readFileSync as A9,readdirSync as RR,statSync as IR}from"fs";import{resolve as A5,basename as ge}from"path";import{homedir as me}from"os";function wR($){let X=Math.trunc($);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 PR($,X,Q){if(X===0)return null;let z=Math.trunc($*100/X),Z=Math.trunc($*Vq/X);if(Z>Vq)Z=Vq;let K=Vq-Z,J=$5;if(z>=80)J=p0;else if(z>=50)J=q1;let q="=".repeat(Math.max(0,Z))+" ".repeat(Math.max(0,K)),V=wR($),Y=wR(X);return` ${f1}${Q}${r} ${J}[${q}]${r} ${z}% (${V} / ${Y})`}async function de(){if(await g5("jq"))return!0;return process.stdout.write(`${p0}Error: jq is required but not installed.${r}
2
+ var St=Object.create;var{getPrototypeOf:yt,defineProperty:jG,getOwnPropertyNames:bt}=Object;var ft=Object.prototype.hasOwnProperty;function _t($){return this[$]}var vt,ht,gt=($,X,Q)=>{var z=$!=null&&typeof $==="object";if(z){var Z=X?vt??=new WeakMap:ht??=new WeakMap,K=Z.get($);if(K)return K}Q=$!=null?St(yt($)):{};let J=X||!$||!$.__esModule?jG(Q,"default",{value:$,enumerable:!0}):Q;for(let q of bt($))if(!ft.call(J,q))jG(J,q,{get:_t.bind($,q),enumerable:!0});if(z)Z.set($,J);return J};var zq=($,X)=>()=>(X||$((X={exports:{}}).exports,X),X.exports);var mt=($)=>$;function ut($,X){this[$]=mt.bind(null,X)}var B1=($,X)=>{for(var Q in X)jG($,Q,{get:X[Q],enumerable:!0,configurable:!0,set:ut.bind(X,Q)})};var s=($,X)=>()=>($&&(X=$($=0)),X);var w5=import.meta.require;var YR={};B1(YR,{lokiDir:()=>h0,homeLokiDir:()=>HQ,findRepoRootForVersion:()=>AG,REPO_ROOT:()=>L1});import{resolve as h2,dirname as LG}from"path";import{fileURLToPath as dt}from"url";import{existsSync as Zq}from"fs";import{homedir as pt}from"os";function ct(){let $=VR;for(let X=0;X<6;X++){if(Zq(h2($,"VERSION"))&&Zq(h2($,"autonomy/run.sh")))return $;let Q=LG($);if(Q===$)break;$=Q}return h2(VR,"..","..","..")}function AG($){let X=$;for(let Q=0;Q<6;Q++){if(Zq(h2(X,"VERSION"))&&Zq(h2(X,"autonomy/run.sh")))return X;let z=LG(X);if(z===X)break;X=z}return h2($,"..","..","..")}function h0(){return process.env.LOKI_DIR??h2(process.cwd(),".loki")}function HQ(){return h2(pt(),".loki")}var VR,L1;var k1=s(()=>{VR=LG(dt(import.meta.url));L1=ct()});import{readFileSync as lt}from"fs";import{resolve as it,dirname as at}from"path";import{fileURLToPath as ot}from"url";function j9(){if(n3!==null)return n3;let $="9.27.1";if(typeof $==="string"&&$.length>0)return n3=$,n3;try{let X=at(ot(import.meta.url)),Q=AG(X);n3=lt(it(Q,"VERSION"),"utf-8").trim()}catch{n3="unknown"}return n3}var n3=null;var Kq=s(()=>{k1()});var GR={};B1(GR,{runOrThrow:()=>Ue,run:()=>$1,readStreamCapped:()=>Jq,commandVersion:()=>We,commandExists:()=>g5,ShellError:()=>CG,MAX_STDOUT_BYTES:()=>WR});async function Jq($,X=WR){let Q=$.getReader(),z=new TextDecoder,Z="",K=0;try{while(K<X){let{done:J,value:q}=await Q.read();if(J)break;if(!q)continue;if(K+=q.byteLength,K>X){let V=q.byteLength-(K-X);Z+=z.decode(q.subarray(0,V),{stream:!0});break}Z+=z.decode(q,{stream:!0})}Z+=z.decode()}finally{try{await Q.cancel()}catch{}Q.releaseLock()}return Z}async function $1($,X={}){let Q=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:X.env?{...process.env,...X.env}:process.env,cwd:X.cwd}),z,Z;if(X.timeoutMs&&X.timeoutMs>0)z=setTimeout(()=>{try{Q.kill("SIGTERM")}catch{}Z=setTimeout(()=>{try{Q.kill("SIGKILL")}catch{}},2000)},X.timeoutMs);try{let[K,J,q]=await Promise.all([Jq(Q.stdout),new Response(Q.stderr).text(),Q.exited]);return{stdout:K,stderr:J,exitCode:q}}finally{if(z)clearTimeout(z);if(Z)clearTimeout(Z)}}async function Ue($,X={}){let Q=await $1($,X);if(Q.exitCode!==0)throw new CG(`command failed (${Q.exitCode}): ${$.join(" ")}`,Q.exitCode,Q.stdout,Q.stderr);return Q}async function g5($){let X=He($),Q=await $1(["sh","-c",`command -v ${X}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function He($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function We($,X="--version"){if(!await g5($))return null;let z=await $1([$,X],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var WR=16777216,CG;var y8=s(()=>{CG=class CG extends Error{message;exitCode;stdout;stderr;constructor($,X,Q,z){super($);this.message=$;this.exitCode=X;this.stdout=Q;this.stderr=z;this.name="ShellError"}}});function g2($){return Ge?"":$}var Ge,p0,$5,q1,L61,A1,f1,m5,r;var t7=s(()=>{Ge=(process.env.NO_COLOR??"").length>0;p0=g2("\x1B[0;31m"),$5=g2("\x1B[0;32m"),q1=g2("\x1B[1;33m"),L61=g2("\x1B[0;34m"),A1=g2("\x1B[0;36m"),f1=g2("\x1B[1m"),m5=g2("\x1B[2m"),r=g2("\x1B[0m")});import{existsSync as De}from"fs";async function Z2(){if(GQ!==void 0)return GQ;let $="/opt/homebrew/bin/python3.12";if(De($))return GQ=$,$;let X=await g5("python3.12");if(X)return GQ=X,X;let Q=await g5("python3");return GQ=Q,Q}async function I4($,X={}){let Q=await Z2();if(!Q)return{stdout:"",stderr:"python3 not found",exitCode:127};return $1([Q,"-c",$],X)}var GQ;var m2=s(()=>{y8()});var yR={};B1(yR,{runStatus:()=>oe});import{existsSync as u5,readFileSync as A9,readdirSync as RR,statSync as IR}from"fs";import{resolve as A5,basename as ge}from"path";import{homedir as me}from"os";function wR($){let X=Math.trunc($);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 PR($,X,Q){if(X===0)return null;let z=Math.trunc($*100/X),Z=Math.trunc($*Vq/X);if(Z>Vq)Z=Vq;let K=Vq-Z,J=$5;if(z>=80)J=p0;else if(z>=50)J=q1;let q="=".repeat(Math.max(0,Z))+" ".repeat(Math.max(0,K)),V=wR($),Y=wR(X);return` ${f1}${Q}${r} ${J}[${q}]${r} ${z}% (${V} / ${Y})`}async function de(){if(await g5("jq"))return!0;return process.stdout.write(`${p0}Error: jq is required but not installed.${r}
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)
@@ -1334,4 +1334,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
1334
1334
  `),2}case"start":{let{runStart:z}=await Promise.resolve().then(() => (Et(),Pt));return z(Q)}default:return process.stderr.write(`Unknown command: ${X}
1335
1335
  `),process.stderr.write(xt),2}}DR();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var X61=await $61(Bun.argv.slice(2));process.exit(X61);
1336
1336
 
1337
- //# debugId=9A71B209805E0B772494C9C8FABE742E
1337
+ //# debugId=C762269CBE59DB093F45DC7B1E7F30AF
package/mcp/__init__.py CHANGED
@@ -75,4 +75,4 @@ try:
75
75
  except ImportError:
76
76
  __all__ = ['mcp']
77
77
 
78
- __version__ = '9.26.3'
78
+ __version__ = '9.27.1'
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": "9.26.3",
4
+ "version": "9.27.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, opencode).",
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": "9.26.3",
5
+ "version": "9.27.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",