loki-mode 8.0.1 → 8.0.3

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 v8.0.1
6
+ # Loki Mode v8.0.3
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.0.1 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
472
+ **v8.0.3 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 8.0.1
1
+ 8.0.3
@@ -624,12 +624,29 @@
624
624
  } else {
625
625
  gaps = '<p class="h-sub" style="margin-top:10px;">No gaps were flagged for this run.</p>';
626
626
  }
627
+ // Why the run stopped. The generator already collects facts.termination
628
+ // facts.execution (reason / outcome / run_status) but nothing rendered it,
629
+ // so a receipt for
630
+ // a run that stopped on a gate showed a headline with no cause -- the reader
631
+ // could see the verdict and not the reason for it. Display-only: the value
632
+ // is whatever the generator recorded, never recomputed here.
633
+ var term = "";
634
+ var tReason = String(g(p, "facts.execution.reason", "") || "").trim();
635
+ var tOutcome = String(g(p, "facts.execution.outcome", "") || "").trim();
636
+ if (tReason || (tOutcome && tOutcome !== "complete")) {
637
+ term = '<div class="h-sub">Stopped: ' +
638
+ esc(tReason || tOutcome) +
639
+ (tReason && tOutcome && tOutcome !== tReason
640
+ ? " (" + esc(tOutcome) + ")" : "") +
641
+ "</div>";
642
+ }
627
643
  el.className = "honesty " + v.cls;
628
644
  el.innerHTML =
629
645
  '<span class="h-mark" aria-hidden="true">' + v.mark + "</span>" +
630
646
  '<div class="h-body">' +
631
647
  '<div class="h-headline">' + esc(headline) + "</div>" +
632
648
  '<div class="h-sub">' + v.sub + "</div>" +
649
+ term +
633
650
  gaps +
634
651
  "</div>";
635
652
  }
package/autonomy/loki CHANGED
@@ -3896,6 +3896,8 @@ WHYJSON
3896
3896
  _LOKI_WHY_LAST_ERROR="$loki_dir/state/LAST_ERROR.json" \
3897
3897
  _LOKI_WHY_UNCERTAINTY="$loki_dir/signals/UNCERTAINTY_ESCALATION" \
3898
3898
  _LOKI_WHY_CONVERGENCE="$loki_dir/council/convergence.log" \
3899
+ _LOKI_WHY_GATE="$loki_dir/signals/GATE_ESCALATION.json" \
3900
+ _LOKI_WHY_TARGET="${TARGET_DIR:-$(dirname "$loki_dir")}" \
3899
3901
  _LOKI_WHY_HANDOFFS="$loki_dir/memory/handoffs" python3 - <<'WHYTXT'
3900
3902
  import json, os, glob
3901
3903
  def load(p):
@@ -3953,6 +3955,11 @@ GUIDE = {
3953
3955
  "If no build is active it likely crashed; in durable mode (LOKI_DURABLE_STATE=1) a restart resumes, else loki start re-runs."),
3954
3956
  "exited": ("The build process exited mid-iteration (likely a crash, kill, or empty provider output).",
3955
3957
  "Read .loki/logs/ for the last error; if nothing is running it crashed -- re-run with loki start, or loki resume in durable mode."),
3958
+ # A real terminal state that had no entry, so `loki why` answered the one
3959
+ # question it exists to answer with "No diagnosis mapping for this status".
3960
+ "inconclusive_spec_contradiction": (
3961
+ "Loki could not reconcile parts of the spec, so it stopped rather than guess.",
3962
+ "The specific contradictions are in .loki/assumptions/ledger.md -- resolve them in the spec, then re-run."),
3956
3963
  }
3957
3964
  meaning, action = GUIDE.get(status, ("No diagnosis mapping for this status; see the raw fields below.",
3958
3965
  "Check loki status and .loki/logs/ for detail."))
@@ -3979,13 +3986,66 @@ if iters is not None:
3979
3986
  print(f" Iterations : {iters}")
3980
3987
  if comp.get("branch"):
3981
3988
  print(f" Branch : {comp['branch']}{comp_label}")
3982
- if comp.get("files_changed") is not None:
3983
- print(f" Changes : {comp.get('files_changed',0)} files (+{comp.get('insertions',0)}/-{comp.get('deletions',0)}){comp_label}")
3989
+ _fc = comp.get("files_changed")
3990
+ if _fc is not None:
3991
+ if _fc == 0:
3992
+ # A recorded 0 is NOT trustworthy on its own: a greenfield run whose
3993
+ # baseline never resolved records 0 while having committed real work.
3994
+ # Saying "0 files" flatly told users nothing was built on a run that
3995
+ # produced 9 files and 1300 insertions. Re-derive from git, and only
3996
+ # claim zero when git agrees.
3997
+ _real = None
3998
+ try:
3999
+ import subprocess as _sp
4000
+ _tgt = os.environ.get("_LOKI_WHY_TARGET") or "."
4001
+ _base = (comp.get("start_sha") or "").strip()
4002
+ if not _base or _base == "HEAD":
4003
+ _base = _sp.run(["git", "-C", _tgt, "hash-object", "-t", "tree", os.devnull],
4004
+ capture_output=True, text=True, timeout=10).stdout.strip()
4005
+ if _base:
4006
+ _o = _sp.run(["git", "-C", _tgt, "diff", "--shortstat", f"{_base}..HEAD",
4007
+ "--", ".", ":(exclude).loki/"],
4008
+ capture_output=True, text=True, timeout=15).stdout.strip()
4009
+ if _o:
4010
+ _real = _o
4011
+ except Exception:
4012
+ _real = None
4013
+ if _real:
4014
+ print(f" Changes : {_real} (re-derived; the run record said 0)")
4015
+ else:
4016
+ print(f" Changes : none recorded (the run did not resolve a baseline)")
4017
+ else:
4018
+ print(f" Changes : {_fc} files (+{comp.get('insertions',0)}/-{comp.get('deletions',0)}){comp_label}")
3984
4019
  if comp.get("pr_url"):
3985
4020
  print(f" PR : {comp['pr_url']}{comp_label}")
4021
+
4022
+ # Name the gate that actually stopped the run. Without this the user is told to
4023
+ # "resume", which the run's own pause notice warns will stop at the same gate.
4024
+ _gate = load(os.environ.get("_LOKI_WHY_GATE", "")) or {}
4025
+ _gname = str(_gate.get("gate", "") or "").strip()
4026
+ if _gname:
4027
+ _gc, _gt = _gate.get("count"), _gate.get("threshold")
4028
+ _detail = f"{_gname} (failed {_gc} times, threshold {_gt})" if isinstance(_gc, int) and isinstance(_gt, int) else _gname
4029
+ print(f" Blocked by : {_detail}")
4030
+
3986
4031
  print()
3987
4032
  print(f" What happened: {meaning}")
3988
- print(f" What to do : {action}")
4033
+ if _gname:
4034
+ print(f" What to do : Read the {_gname} findings and fix what they name, then resume.")
4035
+ print(f" Resuming unchanged will stop at the same gate again.")
4036
+ _art = str(_gate.get("latest_artifact", "") or "")
4037
+ if _art:
4038
+ # Make the pointer relative to the workspace. The recorded value is an
4039
+ # absolute path from the machine that ran the build -- on the preserved
4040
+ # run it names a /var/folders temp dir that no longer exists, so the one
4041
+ # actionable pointer in the whole notice led nowhere. Anchor on
4042
+ # ".loki/" so it works even when the run happened elsewhere.
4043
+ _i = _art.find(".loki/")
4044
+ if _i != -1:
4045
+ _art = _art[_i:]
4046
+ print(f" Findings: {_art}")
4047
+ else:
4048
+ print(f" What to do : {action}")
3989
4049
 
3990
4050
  # RUN-25 iter 22b/iter 23 (Wave D #6): when a run is PAUSED / live and actually
3991
4051
  # STUCK, name the REAL stall reason instead of the generic "resume". The runner
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "8.0.1"
10
+ __version__ = "8.0.3"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -679,14 +679,37 @@ async def _push_loki_state_loop() -> None:
679
679
  pass
680
680
 
681
681
  status_str = raw.get("mode", "autonomous")
682
- if not _pid_alive:
683
- status_str = "stopped"
684
- elif status_str == "paused":
685
- status_str = "paused"
686
- elif status_str in ("stopped", ""):
687
- status_str = "stopped"
688
- else:
689
- status_str = "running"
682
+ # Control files are the AUTHORITY, and they are checked
683
+ # first. dashboard-state.json's "mode" is written by the
684
+ # engine and goes stale the moment a run pauses: on a
685
+ # real paused run it still read "autonomous", so this
686
+ # stream reported "running" while /api/status -- which
687
+ # reads .loki/PAUSE -- reported "paused" for the SAME
688
+ # run. Two live surfaces disagreeing about whether a
689
+ # build is running is worse than either being wrong
690
+ # alone, because the user cannot tell which to believe.
691
+ try:
692
+ _ctl = loki_dir
693
+ if (_ctl / "STOP").exists():
694
+ status_str = "stopped"
695
+ elif (_ctl / "PAUSE").exists():
696
+ status_str = "paused"
697
+ elif not _pid_alive:
698
+ status_str = "stopped"
699
+ elif status_str == "paused":
700
+ status_str = "paused"
701
+ elif status_str in ("stopped", ""):
702
+ status_str = "stopped"
703
+ else:
704
+ status_str = "running"
705
+ except (OSError, TypeError, NameError):
706
+ # Never let a control-file read break the stream.
707
+ if not _pid_alive:
708
+ status_str = "stopped"
709
+ elif status_str in ("stopped", ""):
710
+ status_str = "stopped"
711
+ elif status_str != "paused":
712
+ status_str = "running"
690
713
 
691
714
  payload = {
692
715
  "status": status_str,
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var k_=Object.create;var{getPrototypeOf:x_,defineProperty:oK,getOwnPropertyNames:S_}=Object;var y_=Object.prototype.hasOwnProperty;function b_(Z){return this[Z]}var __,f_,h_=(Z,X,Q)=>{var Y=Z!=null&&typeof Z==="object";if(Y){var J=X?__??=new WeakMap:f_??=new WeakMap,z=J.get(Z);if(z)return z}Q=Z!=null?k_(x_(Z)):{};let K=X||!Z||!Z.__esModule?oK(Q,"default",{value:Z,enumerable:!0}):Q;for(let $ of S_(Z))if(!y_.call(K,$))oK(K,$,{get:b_.bind(Z,$),enumerable:!0});if(Y)J.set(Z,K);return K};var VQ=(Z,X)=>()=>(X||Z((X={exports:{}}).exports,X),X.exports);var v_=(Z)=>Z;function g_(Z,X){this[Z]=v_.bind(null,X)}var c0=(Z,X)=>{for(var Q in X)oK(Z,Q,{get:X[Q],enumerable:!0,configurable:!0,set:g_.bind(X,Q)})};var p=(Z,X)=>()=>(Z&&(X=Z(Z=0)),X);var t0=import.meta.require;var EO={};c0(EO,{lokiDir:()=>j0,homeLokiDir:()=>I4,findRepoRootForVersion:()=>tK,REPO_ROOT:()=>Q8});import{resolve as s7,dirname as rK}from"path";import{fileURLToPath as m_}from"url";import{existsSync as qQ}from"fs";import{homedir as u_}from"os";function p_(){let Z=DO;for(let X=0;X<6;X++){if(qQ(s7(Z,"VERSION"))&&qQ(s7(Z,"autonomy/run.sh")))return Z;let Q=rK(Z);if(Q===Z)break;Z=Q}return s7(DO,"..","..","..")}function tK(Z){let X=Z;for(let Q=0;Q<6;Q++){if(qQ(s7(X,"VERSION"))&&qQ(s7(X,"autonomy/run.sh")))return X;let Y=rK(X);if(Y===X)break;X=Y}return s7(Z,"..","..","..")}function j0(){return process.env.LOKI_DIR??s7(process.cwd(),".loki")}function I4(){return s7(u_(),".loki")}var DO,Q8;var G8=p(()=>{DO=rK(m_(import.meta.url));Q8=p_()});import{readFileSync as d_}from"fs";import{resolve as c_,dirname as l_}from"path";import{fileURLToPath as i_}from"url";function _3(){if(f5!==null)return f5;let Z="8.0.1";if(typeof Z==="string"&&Z.length>0)return f5=Z,f5;try{let X=l_(i_(import.meta.url)),Q=tK(X);f5=d_(c_(Q,"VERSION"),"utf-8").trim()}catch{f5="unknown"}return f5}var f5=null;var GQ=p(()=>{G8()});var kO={};c0(kO,{runOrThrow:()=>Wf,run:()=>D0,readStreamCapped:()=>HQ,commandVersion:()=>qf,commandExists:()=>H9,ShellError:()=>eK,MAX_STDOUT_BYTES:()=>RO});async function HQ(Z,X=RO){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 D0(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([HQ(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 Wf(Z,X={}){let Q=await D0(Z,X);if(Q.exitCode!==0)throw new eK(`command failed (${Q.exitCode}): ${Z.join(" ")}`,Q.exitCode,Q.stdout,Q.stderr);return Q}async function H9(Z){let X=Vf(Z),Q=await D0(["sh","-c",`command -v ${X}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function Vf(Z){if(!/^[A-Za-z0-9._/-]+$/.test(Z))throw Error(`refused to shell-escape suspect token: ${Z}`);return Z}async function qf(Z,X="--version"){if(!await H9(Z))return null;let Y=await D0([Z,X],{timeoutMs:5000});if(Y.exitCode!==0)return null;return((Y.stdout||Y.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var RO=16777216,eK;var k9=p(()=>{eK=class eK 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 n7(Z){return Gf?"":Z}var Gf,M0,k8,l0,gW0,a0,H8,X9,g;var S6=p(()=>{Gf=(process.env.NO_COLOR??"").length>0;M0=n7("\x1B[0;31m"),k8=n7("\x1B[0;32m"),l0=n7("\x1B[1;33m"),gW0=n7("\x1B[0;34m"),a0=n7("\x1B[0;36m"),H8=n7("\x1B[1m"),X9=n7("\x1B[2m"),g=n7("\x1B[0m")});import{existsSync as wf}from"fs";async function F7(){if(R4!==void 0)return R4;let Z="/opt/homebrew/bin/python3.12";if(wf(Z))return R4=Z,Z;let X=await H9("python3.12");if(X)return R4=X,X;let Q=await H9("python3");return R4=Q,Q}async function D7(Z,X={}){let Q=await F7();if(!Q)return{stdout:"",stderr:"python3 not found",exitCode:127};return D0([Q,"-c",Z],X)}var R4;var o7=p(()=>{k9()});var oO={};c0(oO,{runStatus:()=>of});import{existsSync as Q9,readFileSync as h3,readdirSync as pO,statSync as dO}from"fs";import{resolve as h8,basename as mf}from"path";import{homedir as uf}from"os";function cO(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 lO(Z,X,Q){if(X===0)return null;let Y=Math.trunc(Z*100/X),J=Math.trunc(Z*BQ/X);if(J>BQ)J=BQ;let z=BQ-J,K=k8;if(Y>=80)K=M0;else if(Y>=50)K=l0;let $="=".repeat(Math.max(0,J))+" ".repeat(Math.max(0,z)),W=cO(Z),V=cO(X);return` ${H8}${Q}${g} ${K}[${$}]${g} ${Y}% (${W} / ${V})`}async function df(){if(await H9("jq"))return!0;return process.stdout.write(`${M0}Error: jq is required but not installed.${g}
2
+ var k_=Object.create;var{getPrototypeOf:x_,defineProperty:oK,getOwnPropertyNames:S_}=Object;var y_=Object.prototype.hasOwnProperty;function b_(Z){return this[Z]}var __,f_,h_=(Z,X,Q)=>{var Y=Z!=null&&typeof Z==="object";if(Y){var J=X?__??=new WeakMap:f_??=new WeakMap,z=J.get(Z);if(z)return z}Q=Z!=null?k_(x_(Z)):{};let K=X||!Z||!Z.__esModule?oK(Q,"default",{value:Z,enumerable:!0}):Q;for(let $ of S_(Z))if(!y_.call(K,$))oK(K,$,{get:b_.bind(Z,$),enumerable:!0});if(Y)J.set(Z,K);return K};var VQ=(Z,X)=>()=>(X||Z((X={exports:{}}).exports,X),X.exports);var v_=(Z)=>Z;function g_(Z,X){this[Z]=v_.bind(null,X)}var c0=(Z,X)=>{for(var Q in X)oK(Z,Q,{get:X[Q],enumerable:!0,configurable:!0,set:g_.bind(X,Q)})};var p=(Z,X)=>()=>(Z&&(X=Z(Z=0)),X);var t0=import.meta.require;var EO={};c0(EO,{lokiDir:()=>j0,homeLokiDir:()=>I4,findRepoRootForVersion:()=>tK,REPO_ROOT:()=>Q8});import{resolve as s7,dirname as rK}from"path";import{fileURLToPath as m_}from"url";import{existsSync as qQ}from"fs";import{homedir as u_}from"os";function p_(){let Z=DO;for(let X=0;X<6;X++){if(qQ(s7(Z,"VERSION"))&&qQ(s7(Z,"autonomy/run.sh")))return Z;let Q=rK(Z);if(Q===Z)break;Z=Q}return s7(DO,"..","..","..")}function tK(Z){let X=Z;for(let Q=0;Q<6;Q++){if(qQ(s7(X,"VERSION"))&&qQ(s7(X,"autonomy/run.sh")))return X;let Y=rK(X);if(Y===X)break;X=Y}return s7(Z,"..","..","..")}function j0(){return process.env.LOKI_DIR??s7(process.cwd(),".loki")}function I4(){return s7(u_(),".loki")}var DO,Q8;var G8=p(()=>{DO=rK(m_(import.meta.url));Q8=p_()});import{readFileSync as d_}from"fs";import{resolve as c_,dirname as l_}from"path";import{fileURLToPath as i_}from"url";function _3(){if(f5!==null)return f5;let Z="8.0.3";if(typeof Z==="string"&&Z.length>0)return f5=Z,f5;try{let X=l_(i_(import.meta.url)),Q=tK(X);f5=d_(c_(Q,"VERSION"),"utf-8").trim()}catch{f5="unknown"}return f5}var f5=null;var GQ=p(()=>{G8()});var kO={};c0(kO,{runOrThrow:()=>Wf,run:()=>D0,readStreamCapped:()=>HQ,commandVersion:()=>qf,commandExists:()=>H9,ShellError:()=>eK,MAX_STDOUT_BYTES:()=>RO});async function HQ(Z,X=RO){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 D0(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([HQ(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 Wf(Z,X={}){let Q=await D0(Z,X);if(Q.exitCode!==0)throw new eK(`command failed (${Q.exitCode}): ${Z.join(" ")}`,Q.exitCode,Q.stdout,Q.stderr);return Q}async function H9(Z){let X=Vf(Z),Q=await D0(["sh","-c",`command -v ${X}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function Vf(Z){if(!/^[A-Za-z0-9._/-]+$/.test(Z))throw Error(`refused to shell-escape suspect token: ${Z}`);return Z}async function qf(Z,X="--version"){if(!await H9(Z))return null;let Y=await D0([Z,X],{timeoutMs:5000});if(Y.exitCode!==0)return null;return((Y.stdout||Y.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var RO=16777216,eK;var k9=p(()=>{eK=class eK 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 n7(Z){return Gf?"":Z}var Gf,M0,k8,l0,gW0,a0,H8,X9,g;var S6=p(()=>{Gf=(process.env.NO_COLOR??"").length>0;M0=n7("\x1B[0;31m"),k8=n7("\x1B[0;32m"),l0=n7("\x1B[1;33m"),gW0=n7("\x1B[0;34m"),a0=n7("\x1B[0;36m"),H8=n7("\x1B[1m"),X9=n7("\x1B[2m"),g=n7("\x1B[0m")});import{existsSync as wf}from"fs";async function F7(){if(R4!==void 0)return R4;let Z="/opt/homebrew/bin/python3.12";if(wf(Z))return R4=Z,Z;let X=await H9("python3.12");if(X)return R4=X,X;let Q=await H9("python3");return R4=Q,Q}async function D7(Z,X={}){let Q=await F7();if(!Q)return{stdout:"",stderr:"python3 not found",exitCode:127};return D0([Q,"-c",Z],X)}var R4;var o7=p(()=>{k9()});var oO={};c0(oO,{runStatus:()=>of});import{existsSync as Q9,readFileSync as h3,readdirSync as pO,statSync as dO}from"fs";import{resolve as h8,basename as mf}from"path";import{homedir as uf}from"os";function cO(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 lO(Z,X,Q){if(X===0)return null;let Y=Math.trunc(Z*100/X),J=Math.trunc(Z*BQ/X);if(J>BQ)J=BQ;let z=BQ-J,K=k8;if(Y>=80)K=M0;else if(Y>=50)K=l0;let $="=".repeat(Math.max(0,J))+" ".repeat(Math.max(0,z)),W=cO(Z),V=cO(X);return` ${H8}${Q}${g} ${K}[${$}]${g} ${Y}% (${W} / ${V})`}async function df(){if(await H9("jq"))return!0;return process.stdout.write(`${M0}Error: jq is required but not installed.${g}
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)
@@ -1203,4 +1203,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
1203
1203
  `),2}case"start":{let{runStart:Y}=await Promise.resolve().then(() => (I_(),E_));return Y(Q)}default:return process.stderr.write(`Unknown command: ${X}
1204
1204
  `),process.stderr.write(P_),2}}mO();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var TW0=await MW0(Bun.argv.slice(2));process.exit(TW0);
1205
1205
 
1206
- //# debugId=1CA655940A54189964756E2164756E21
1206
+ //# debugId=3C2284D252E647F964756E2164756E21
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '8.0.1'
60
+ __version__ = '8.0.3'
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.0.1",
4
+ "version": "8.0.3",
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.0.1",
5
+ "version": "8.0.3",
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",