loki-mode 7.108.0 → 7.109.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 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 v7.108.0
6
+ # Loki Mode v7.109.0
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -408,4 +408,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
408
408
 
409
409
  ---
410
410
 
411
- **v7.108.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
411
+ **v7.109.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 7.108.0
1
+ 7.109.0
package/autonomy/run.sh CHANGED
@@ -12200,8 +12200,11 @@ parse_claude_reset_time() {
12200
12200
  local current_min=$(date +%M)
12201
12201
  local current_sec=$(date +%S)
12202
12202
 
12203
- # Calculate seconds until reset
12204
- local current_secs=$((current_hour * 3600 + current_min * 60 + current_sec))
12203
+ # Calculate seconds until reset. Force base-10 (10#) on the zero-padded
12204
+ # date values: 08/09 are invalid octal, so bare arithmetic aborts ("value
12205
+ # too great for base") during those clock windows and silently discards the
12206
+ # real rate-limit reset wait, falling back to a too-short generic backoff.
12207
+ local current_secs=$((10#$current_hour * 3600 + 10#$current_min * 60 + 10#$current_sec))
12205
12208
  local reset_secs=$((hour * 3600))
12206
12209
 
12207
12210
  local wait_secs=$((reset_secs - current_secs))
@@ -12634,8 +12637,14 @@ except Exception:
12634
12637
  # Return the payload on stdout
12635
12638
  printf '%s\n' "$payload"
12636
12639
 
12637
- # Consume the signal (next iteration would otherwise re-trigger)
12640
+ # Consume the signal (next iteration would otherwise re-trigger).
12641
+ # Also remove the fallback if it coexists: TASK_COMPLETION_CLAIMED and
12642
+ # COMPLETION_REQUESTED are both valid, non-exclusive completion mechanisms, so
12643
+ # a belt-and-suspenders agent can leave both present. Removing only the active
12644
+ # one orphans the other, which then reads as a phantom claim on a later
12645
+ # iteration and forces every-iteration council evaluation. Consume both.
12638
12646
  rm -f "$signal_file" 2>/dev/null
12647
+ rm -f "$fallback_file" 2>/dev/null
12639
12648
  return 0
12640
12649
  }
12641
12650
 
@@ -830,14 +830,22 @@ PYEOF
830
830
  if grep -qE '"(express|fastify|koa|hapi|@hapi/hapi|next|nuxt|@nestjs/core|@nestjs/platform-express|http-server|connect|restify|polka|@sveltejs/kit|vite)"[[:space:]]*:' "$dir/package.json" 2>/dev/null; then
831
831
  _node_http_signal="dep"
832
832
  fi
833
- # (b) a listen()/createServer()/serve() call in a shallow scan of JS/TS
834
- # sources (bounded: node_modules/.git excluded). Uses grep -q (a
835
- # boolean, no pipe) so this is safe under set -o pipefail (a piped
836
- # `| head` would SIGPIPE grep and, under pipefail, drop the result).
833
+ # (b) a STRONG server-creation call in a shallow scan of JS/TS sources.
834
+ # "Strong" means a module-qualified server constructor (http/https/
835
+ # http2/net .createServer, or Bun.serve/Deno.serve) -- NOT a bare
836
+ # `.listen(` which is common in tests (`http.createServer().listen(0)`)
837
+ # and unrelated code. Test/example/build dirs are excluded so an
838
+ # incidental server in a test never marks a CLI as bootable-HTTP.
839
+ # Uses grep -q (a boolean, no pipe) so this is safe under
840
+ # set -o pipefail (a piped `| head` would SIGPIPE grep and, under
841
+ # pipefail, drop the result).
837
842
  if [ -z "$_node_http_signal" ]; then
838
- if grep -rqE '\.listen\(|createServer\(|http2?\.createServer|Bun\.serve\(|Deno\.serve\(' \
843
+ if grep -rqE 'https?\.createServer|http2\.createServer|net\.createServer|Bun\.serve\(|Deno\.serve\(' \
839
844
  "$dir" --include='*.js' --include='*.ts' --include='*.mjs' --include='*.cjs' \
840
- --exclude-dir=node_modules --exclude-dir=.git 2>/dev/null; then
845
+ --exclude-dir=node_modules --exclude-dir=.git \
846
+ --exclude-dir=test --exclude-dir=tests --exclude-dir=__tests__ \
847
+ --exclude-dir=examples --exclude-dir=example \
848
+ --exclude-dir=dist --exclude-dir=build --exclude-dir=spec 2>/dev/null; then
841
849
  _node_http_signal="src"
842
850
  fi
843
851
  fi
@@ -964,6 +972,11 @@ verify_gate_runtime() {
964
972
  local deadline=$(( $(date +%s) + boot_timeout ))
965
973
  local have_curl="false"
966
974
  command -v curl >/dev/null 2>&1 && have_curl="true"
975
+ # The port we actually probe. Starts at the guessed default; if the boot log
976
+ # announces a different bound port (e.g. Vite on 5173, which ignores PORT and
977
+ # the default map cannot know), we re-point the probe THERE. scraped_port is
978
+ # stashed so teardown can also reclaim it (fixes the different-port leak).
979
+ local probe_port="$port" scraped_port=""
967
980
 
968
981
  while [ "$(date +%s)" -lt "$deadline" ]; do
969
982
  # If the app process already exited, stop polling (it failed to stay up).
@@ -971,6 +984,15 @@ verify_gate_runtime() {
971
984
  # Give one last probe in case it forked a daemon and exited.
972
985
  :
973
986
  fi
987
+ # Scrape the actually-bound port from the boot log (progressively filled).
988
+ # Only re-point when it differs from the current probe target.
989
+ if [ -z "$scraped_port" ]; then
990
+ scraped_port="$(_verify_runtime_scrape_port "$boot_log" 2>/dev/null || true)"
991
+ if [ -n "$scraped_port" ] && [ "$scraped_port" != "$probe_port" ]; then
992
+ probe_port="$scraped_port"
993
+ url="http://127.0.0.1:${probe_port}${health_path}"
994
+ fi
995
+ fi
974
996
  if [ "$have_curl" = "true" ]; then
975
997
  http_status="$(curl -s -o /dev/null -w '%{http_code}' --max-time 3 "$url" 2>/dev/null || echo "")"
976
998
  if [ -n "$http_status" ] && [ "$http_status" != "000" ]; then
@@ -980,7 +1002,7 @@ verify_gate_runtime() {
980
1002
  else
981
1003
  # Portable fallback: raw TCP GET via bash /dev/tcp (best effort).
982
1004
  local resp=""
983
- resp="$(_verify_runtime_raw_probe "$port" "$health_path" 2>/dev/null || true)"
1005
+ resp="$(_verify_runtime_raw_probe "$probe_port" "$health_path" 2>/dev/null || true)"
984
1006
  if [ -n "$resp" ]; then
985
1007
  http_status="$resp"
986
1008
  answered="true"
@@ -1007,7 +1029,10 @@ verify_gate_runtime() {
1007
1029
  fi
1008
1030
 
1009
1031
  # Teardown: kill the launcher and any child it spawned. Best effort; bounded.
1010
- _verify_runtime_teardown "$app_pid" "$port"
1032
+ # Reclaim BOTH the detected port and the actually-bound port (scraped from the
1033
+ # boot log) so a server that daemonized onto a different port than we guessed
1034
+ # does not leak. scraped_port may be empty (no banner) -- teardown handles it.
1035
+ _verify_runtime_teardown "$app_pid" "$port" "$scraped_port"
1011
1036
 
1012
1037
  # Interpret the result.
1013
1038
  if [ "$answered" != "true" ]; then
@@ -1073,6 +1098,37 @@ _verify_runtime_raw_probe() {
1073
1098
  printf '%s' "$line" | grep -oE 'HTTP/[0-9.]+ [0-9]{3}' | grep -oE '[0-9]{3}$' || return 1
1074
1099
  }
1075
1100
 
1101
+ # Scrape the actually-bound localhost port from a dev server's boot log. Dev
1102
+ # servers (Vite, SvelteKit, Nuxt, Next, CRA, ...) print a "Local:" / "listening
1103
+ # on" banner with their real port, which is frequently NOT the guessed default
1104
+ # (bare Vite binds 5173 and ignores PORT). We parse ONLY a localhost/127.0.0.1
1105
+ # bind announcement so we never lock onto an unrelated outbound URL the app might
1106
+ # have logged (which some other live process could answer -> false green). ANSI
1107
+ # color codes are stripped first (dev banners are colorized). Echoes the first
1108
+ # matched port, or nothing. Bounded: reads only the given log file, no network.
1109
+ _verify_runtime_scrape_port() {
1110
+ local log="$1"
1111
+ [ -f "$log" ] || return 1
1112
+ # Strip ANSI escapes first (dev banners are colorized). Then, line by line,
1113
+ # match a localhost/127.0.0.1 bind announcement and extract the PORT that
1114
+ # sits at the end of the match (the number after the final colon / after
1115
+ # "port"), never an IP octet. Emit the first port found.
1116
+ local line p
1117
+ while IFS= read -r line; do
1118
+ # A URL like http://localhost:5173 or http://127.0.0.1:5173/ .
1119
+ p="$(printf '%s' "$line" | grep -oiE 'https?://(localhost|127\.0\.0\.1):[0-9]{2,5}' | grep -oE ':[0-9]{2,5}' | grep -oE '[0-9]{2,5}' | head -1)"
1120
+ # Or a "listening on [127.0.0.1:]5173" / "listening on port 5173" phrase.
1121
+ if [ -z "$p" ]; then
1122
+ p="$(printf '%s' "$line" | grep -oiE 'listening on( port)?[[:space:]:]*([0-9.]+:)?[0-9]{2,5}' | grep -oE '[0-9]{2,5}$' | head -1)"
1123
+ fi
1124
+ if [ -n "$p" ]; then
1125
+ printf '%s' "$p"
1126
+ return 0
1127
+ fi
1128
+ done < <(sed -E $'s/\033\\[[0-9;?]*[A-Za-z]//g' "$log" 2>/dev/null)
1129
+ return 1
1130
+ }
1131
+
1076
1132
  # Optional screenshot via the playwright inline smoke script (reused contract:
1077
1133
  # url screenshot results timeout). Bounded by the same timeout wrapper. Returns
1078
1134
  # 0 if the screenshot file was produced, nonzero otherwise. Never fatal.
@@ -1110,30 +1166,56 @@ SMOKE_SCRIPT
1110
1166
  # Teardown: terminate the boot launcher and any process still holding the port.
1111
1167
  # Bounded and best effort; never blocks the gate.
1112
1168
  _verify_runtime_teardown() {
1113
- local app_pid="$1" port="$2"
1169
+ local app_pid="$1" port="$2" scraped_port="${3:-}"
1114
1170
  if [ -n "$app_pid" ]; then
1115
- # Kill the process group of the launcher when possible, else the PID.
1171
+ # Best: kill the launcher's whole process GROUP so a server it spawned in
1172
+ # a subshell (e.g. `vite &`) dies too. GUARDED against self-suicide: in a
1173
+ # non-interactive script job control is off, so a background job can share
1174
+ # the verifier's OWN process group; a negative-PID kill there would kill
1175
+ # us (and our parent). Only group-kill when the child's PGID differs from
1176
+ # ours AND equals the child PID (i.e. it is a real group leader).
1177
+ local child_pgid="" self_pgid=""
1178
+ child_pgid="$(ps -o pgid= -p "$app_pid" 2>/dev/null | tr -d ' ' || true)"
1179
+ self_pgid="$(ps -o pgid= -p $$ 2>/dev/null | tr -d ' ' || true)"
1180
+ if [ -n "$child_pgid" ] && [ "$child_pgid" != "$self_pgid" ] \
1181
+ && [ "$child_pgid" = "$app_pid" ]; then
1182
+ kill -- -"$child_pgid" 2>/dev/null || true
1183
+ fi
1184
+ # Always TERM the launcher PID itself.
1116
1185
  kill "$app_pid" 2>/dev/null || true
1117
1186
  # Kill any direct children (the timeout wrapper spawns sh -c "$method").
1118
1187
  if command -v pkill >/dev/null 2>&1; then
1119
1188
  pkill -P "$app_pid" 2>/dev/null || true
1120
1189
  fi
1121
- # Give it a moment, then hard-kill.
1190
+ # Give it a moment, then hard-kill (PID, and group again when safe).
1122
1191
  local i=0
1123
1192
  while [ "$i" -lt 3 ] && kill -0 "$app_pid" 2>/dev/null; do
1124
1193
  sleep 1; i=$((i + 1))
1125
1194
  done
1195
+ if [ -n "$child_pgid" ] && [ "$child_pgid" != "$self_pgid" ] \
1196
+ && [ "$child_pgid" = "$app_pid" ]; then
1197
+ kill -9 -- -"$child_pgid" 2>/dev/null || true
1198
+ fi
1126
1199
  kill -9 "$app_pid" 2>/dev/null || true
1127
1200
  fi
1128
- # Reclaim the port from any orphan that outlived the launcher (bounded).
1201
+ # Reclaim BOTH the detected port and the actually-bound (scraped) port from
1202
+ # any orphan that outlived the launcher -- a daemonized server that bound a
1203
+ # different port than we guessed would otherwise leak. Bounded, best effort.
1129
1204
  if command -v lsof >/dev/null 2>&1; then
1130
- local holders
1131
- holders="$(lsof -ti tcp:"$port" 2>/dev/null || true)"
1132
- if [ -n "$holders" ]; then
1133
- printf '%s\n' "$holders" | while IFS= read -r pid; do
1134
- [ -n "$pid" ] && kill -9 "$pid" 2>/dev/null || true
1135
- done
1136
- fi
1205
+ # Build a unique, non-empty port list (scraped only if it differs).
1206
+ local _ports="$port"
1207
+ [ -n "$scraped_port" ] && [ "$scraped_port" != "$port" ] && _ports="$_ports $scraped_port"
1208
+ local _rp
1209
+ for _rp in $_ports; do
1210
+ [ -z "$_rp" ] && continue
1211
+ local holders
1212
+ holders="$(lsof -ti tcp:"$_rp" 2>/dev/null || true)"
1213
+ if [ -n "$holders" ]; then
1214
+ printf '%s\n' "$holders" | while IFS= read -r pid; do
1215
+ [ -n "$pid" ] && kill -9 "$pid" 2>/dev/null || true
1216
+ done
1217
+ fi
1218
+ done
1137
1219
  fi
1138
1220
  }
1139
1221
 
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "7.108.0"
10
+ __version__ = "7.109.0"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -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.108.0
5
+ **Version:** v7.109.0
6
6
 
7
7
  ---
8
8
 
@@ -120,3 +120,40 @@ Generated 2026-07-01 by the top100-backlog-enumeration ultracode workflow (7 age
120
120
  | 98 | low | S | autonomous | dashboard | Web-app docs: fill video placeholder (coming-soon text) |
121
121
  | 99 | low | M | autonomous | cli | C# provider support (roslyn analyzers + dotnet build, deferred) |
122
122
  | 100 | low | XL | founder-gated | engine | BMAD-METHOD v6 integration (net-new, needs founder planning) |
123
+
124
+ ## Gate split (what can actually close autonomously) - 2026-07-01
125
+
126
+ The /goal asks to work through all 100. The honest structural reality: only a
127
+ subset is autonomously completable. The rest need a founder decision or are
128
+ community/CI-infra work that cannot be forced from this session.
129
+
130
+ - **Autonomous (I can execute end-to-end): ~25 items** (the `gate=autonomous` rows).
131
+ - **Founder-gated (blocked on a decision only the founder can make): ~35 items.**
132
+ Licensing (#5), hosted deploy target (#6, #23, #36), enterprise RBAC/SSO (#15),
133
+ sandbox backend choice (#14, #42), spend (#49 SWE-bench, #18 live-model), npm
134
+ publish + brand (#4, #13, #61), telemetry (#62), GitHub App (#46). Prepped to
135
+ one-approval-away where possible; CANNOT close without the founder.
136
+ - **Community / CI-infra (~40 items):** flake root-causes, coverage thresholds,
137
+ platform runners (ARM64/Windows), distribution resilience. Environmental
138
+ hardening, not single-session feature work.
139
+
140
+ ### Shipped this session (accuracy-moat, from the competitive gap-analysis)
141
+
142
+ Net-new accuracy work driven from docs/research-2026-07/gap-analysis-backlog.json
143
+ (a companion list), NOT retroactive Top-100 claims:
144
+
145
+ - v7.105.0 - convergence: council evaluates on completion-claim (4.0x faster, n=3). commit b8a368a0.
146
+ - v7.106.0 - reverse-classical test provenance (tautological tests downgraded). commit 71299faa.
147
+ - v7.107.0 - loki mcp --transport http loopback bind + bearer auth (+ latent crash fix). commit f3aa523c.
148
+ - v7.108.0 - runtime boot smoke gate + annotate-before-act expectation ledger. commit 806fc374.
149
+
150
+ ### Top-100 items with a concrete shipped commit
151
+
152
+ - #40 (dashboard memory summary on JSON-backed projects) - commit 330de52d.
153
+ - #22 (3 parallel code reviewers) - already present in the engine (council path).
154
+
155
+ ### Next autonomous batch (by value, unblocked, user-visible)
156
+
157
+ Picked from the `autonomous` rows by value + real-user impact (not primitives that
158
+ ship dormant). Verify each before starting; several may overlap the already-built
159
+ private autonomi-verify TS engine (e.g. #7/#8 - check before rebuilding).
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var X8=Object.defineProperty;var K8=($)=>$;function q8($,Q){this[$]=K8.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)X8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:q8.bind(Q,Z)})};var L=($,Q)=>()=>($&&(Q=$($=0)),Q);var J$=import.meta.require;var v1={};b(v1,{lokiDir:()=>P,homeLokiDir:()=>T$,findRepoRootForVersion:()=>e$,REPO_ROOT:()=>h});import{resolve as r,dirname as i$}from"path";import{fileURLToPath as J8}from"url";import{existsSync as x$}from"fs";import{homedir as V8}from"os";function W8(){let $=y1;for(let Q=0;Q<6;Q++){if(x$(r($,"VERSION"))&&x$(r($,"autonomy/run.sh")))return $;let Z=i$($);if(Z===$)break;$=Z}return r(y1,"..","..","..")}function e$($){let Q=$;for(let Z=0;Z<6;Z++){if(x$(r(Q,"VERSION"))&&x$(r(Q,"autonomy/run.sh")))return Q;let z=i$(Q);if(z===Q)break;Q=z}return r($,"..","..","..")}function P(){return process.env.LOKI_DIR??r(process.cwd(),".loki")}function T$(){return r(V8(),".loki")}var y1,h;var C=L(()=>{y1=i$(J8(import.meta.url));h=W8()});import{readFileSync as U8}from"fs";import{resolve as H8,dirname as G8}from"path";import{fileURLToPath as B8}from"url";function N$(){if(Q$!==null)return Q$;let $="7.108.0";if(typeof $==="string"&&$.length>0)return Q$=$,Q$;try{let Q=G8(B8(import.meta.url)),Z=e$(Q);Q$=U8(H8(Z,"VERSION"),"utf-8").trim()}catch{Q$="unknown"}return Q$}var Q$=null;var $1=L(()=>{C()});var c1={};b(c1,{runOrThrow:()=>x8,run:()=>F,readStreamCapped:()=>u1,commandVersion:()=>S8,commandExists:()=>f,ShellError:()=>Q1,MAX_STDOUT_BYTES:()=>f1});async function u1($,Q=f1){let Z=$.getReader(),z=new TextDecoder,X="",q=0;try{while(q<Q){let{done:K,value:W}=await Z.read();if(K)break;if(!W)continue;if(q+=W.byteLength,q>Q){let V=W.byteLength-(q-Q);X+=z.decode(W.subarray(0,V),{stream:!0});break}X+=z.decode(W,{stream:!0})}X+=z.decode()}finally{try{await Z.cancel()}catch{}Z.releaseLock()}return X}async function F($,Q={}){let Z=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),z,X;if(Q.timeoutMs&&Q.timeoutMs>0)z=setTimeout(()=>{try{Z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{Z.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[q,K,W]=await Promise.all([u1(Z.stdout),new Response(Z.stderr).text(),Z.exited]);return{stdout:q,stderr:K,exitCode:W}}finally{if(z)clearTimeout(z);if(X)clearTimeout(X)}}async function x8($,Q={}){let Z=await F($,Q);if(Z.exitCode!==0)throw new Q1(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function f($){let Q=N8($),Z=await F(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function N8($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function S8($,Q="--version"){if(!await f($))return null;let z=await F([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var f1=16777216,Q1;var d=L(()=>{Q1=class Q1 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 t($){return D8?"":$}var D8,T,S,_,nZ,I,k,y,J;var c=L(()=>{D8=(process.env.NO_COLOR??"").length>0;T=t("\x1B[0;31m"),S=t("\x1B[0;32m"),_=t("\x1B[1;33m"),nZ=t("\x1B[0;34m"),I=t("\x1B[0;36m"),k=t("\x1B[1m"),y=t("\x1B[2m"),J=t("\x1B[0m")});import{existsSync as c8}from"fs";async function Z$(){if(A$!==void 0)return A$;let $="/opt/homebrew/bin/python3.12";if(c8($))return A$=$,$;let Q=await f("python3.12");if(Q)return A$=Q,Q;let Z=await f("python3");return A$=Z,Z}async function z$($,Q={}){let Z=await Z$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return F([Z,"-c",$],Q)}var A$;var V$=L(()=>{d()});var J0={};b(J0,{runStatus:()=>U3});import{existsSync as v,readFileSync as U$,readdirSync as e1,statSync as $0}from"fs";import{resolve as D,basename as Q3}from"path";import{homedir as Z3}from"os";function Q0($){let Q=Math.trunc($);if(Q>=1e6)return`${(Math.trunc(Q/1e6*10)/10).toFixed(1)}M`;if(Q>=1000)return`${(Math.trunc(Q/1000*10)/10).toFixed(1)}K`;return String(Q)}function Z0($,Q,Z){if(Q===0)return null;let z=Math.trunc($*100/Q),X=Math.trunc($*D$/Q);if(X>D$)X=D$;let q=D$-X,K=S;if(z>=80)K=T;else if(z>=50)K=_;let W="=".repeat(Math.max(0,X))+" ".repeat(Math.max(0,q)),V=Q0($),U=Q0(Q);return` ${k}${Z}${J} ${K}[${W}]${J} ${z}% (${V} / ${U})`}async function X3(){if(await f("jq"))return!0;return process.stdout.write(`${T}Error: jq is required but not installed.${J}
2
+ var X8=Object.defineProperty;var K8=($)=>$;function q8($,Q){this[$]=K8.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)X8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:q8.bind(Q,Z)})};var L=($,Q)=>()=>($&&(Q=$($=0)),Q);var J$=import.meta.require;var v1={};b(v1,{lokiDir:()=>P,homeLokiDir:()=>T$,findRepoRootForVersion:()=>e$,REPO_ROOT:()=>h});import{resolve as r,dirname as i$}from"path";import{fileURLToPath as J8}from"url";import{existsSync as x$}from"fs";import{homedir as V8}from"os";function W8(){let $=y1;for(let Q=0;Q<6;Q++){if(x$(r($,"VERSION"))&&x$(r($,"autonomy/run.sh")))return $;let Z=i$($);if(Z===$)break;$=Z}return r(y1,"..","..","..")}function e$($){let Q=$;for(let Z=0;Z<6;Z++){if(x$(r(Q,"VERSION"))&&x$(r(Q,"autonomy/run.sh")))return Q;let z=i$(Q);if(z===Q)break;Q=z}return r($,"..","..","..")}function P(){return process.env.LOKI_DIR??r(process.cwd(),".loki")}function T$(){return r(V8(),".loki")}var y1,h;var C=L(()=>{y1=i$(J8(import.meta.url));h=W8()});import{readFileSync as U8}from"fs";import{resolve as H8,dirname as G8}from"path";import{fileURLToPath as B8}from"url";function N$(){if(Q$!==null)return Q$;let $="7.109.0";if(typeof $==="string"&&$.length>0)return Q$=$,Q$;try{let Q=G8(B8(import.meta.url)),Z=e$(Q);Q$=U8(H8(Z,"VERSION"),"utf-8").trim()}catch{Q$="unknown"}return Q$}var Q$=null;var $1=L(()=>{C()});var c1={};b(c1,{runOrThrow:()=>x8,run:()=>F,readStreamCapped:()=>u1,commandVersion:()=>S8,commandExists:()=>f,ShellError:()=>Q1,MAX_STDOUT_BYTES:()=>f1});async function u1($,Q=f1){let Z=$.getReader(),z=new TextDecoder,X="",q=0;try{while(q<Q){let{done:K,value:W}=await Z.read();if(K)break;if(!W)continue;if(q+=W.byteLength,q>Q){let V=W.byteLength-(q-Q);X+=z.decode(W.subarray(0,V),{stream:!0});break}X+=z.decode(W,{stream:!0})}X+=z.decode()}finally{try{await Z.cancel()}catch{}Z.releaseLock()}return X}async function F($,Q={}){let Z=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),z,X;if(Q.timeoutMs&&Q.timeoutMs>0)z=setTimeout(()=>{try{Z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{Z.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[q,K,W]=await Promise.all([u1(Z.stdout),new Response(Z.stderr).text(),Z.exited]);return{stdout:q,stderr:K,exitCode:W}}finally{if(z)clearTimeout(z);if(X)clearTimeout(X)}}async function x8($,Q={}){let Z=await F($,Q);if(Z.exitCode!==0)throw new Q1(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function f($){let Q=N8($),Z=await F(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function N8($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function S8($,Q="--version"){if(!await f($))return null;let z=await F([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var f1=16777216,Q1;var d=L(()=>{Q1=class Q1 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 t($){return D8?"":$}var D8,T,S,_,nZ,I,k,y,J;var c=L(()=>{D8=(process.env.NO_COLOR??"").length>0;T=t("\x1B[0;31m"),S=t("\x1B[0;32m"),_=t("\x1B[1;33m"),nZ=t("\x1B[0;34m"),I=t("\x1B[0;36m"),k=t("\x1B[1m"),y=t("\x1B[2m"),J=t("\x1B[0m")});import{existsSync as c8}from"fs";async function Z$(){if(A$!==void 0)return A$;let $="/opt/homebrew/bin/python3.12";if(c8($))return A$=$,$;let Q=await f("python3.12");if(Q)return A$=Q,Q;let Z=await f("python3");return A$=Z,Z}async function z$($,Q={}){let Z=await Z$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return F([Z,"-c",$],Q)}var A$;var V$=L(()=>{d()});var J0={};b(J0,{runStatus:()=>U3});import{existsSync as v,readFileSync as U$,readdirSync as e1,statSync as $0}from"fs";import{resolve as D,basename as Q3}from"path";import{homedir as Z3}from"os";function Q0($){let Q=Math.trunc($);if(Q>=1e6)return`${(Math.trunc(Q/1e6*10)/10).toFixed(1)}M`;if(Q>=1000)return`${(Math.trunc(Q/1000*10)/10).toFixed(1)}K`;return String(Q)}function Z0($,Q,Z){if(Q===0)return null;let z=Math.trunc($*100/Q),X=Math.trunc($*D$/Q);if(X>D$)X=D$;let q=D$-X,K=S;if(z>=80)K=T;else if(z>=50)K=_;let W="=".repeat(Math.max(0,X))+" ".repeat(Math.max(0,q)),V=Q0($),U=Q0(Q);return` ${k}${Z}${J} ${K}[${W}]${J} ${z}% (${V} / ${U})`}async function X3(){if(await f("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)
@@ -805,4 +805,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
805
805
  `),2}default:return process.stderr.write(`Unknown command: ${Q}
806
806
  `),process.stderr.write(z8),2}}i1();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var EZ=await RZ(Bun.argv.slice(2));process.exit(EZ);
807
807
 
808
- //# debugId=45CBA837EB346E2A64756E2164756E21
808
+ //# debugId=68DF9A47D3B818A364756E2164756E21
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '7.108.0'
60
+ __version__ = '7.109.0'
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": "7.108.0",
4
+ "version": "7.109.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": "7.108.0",
5
+ "version": "7.109.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",