loki-mode 7.129.2 → 7.129.4

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.129.2
6
+ # Loki Mode v7.129.4
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.129.2 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
411
+ **v7.129.4 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 7.129.2
1
+ 7.129.4
package/autonomy/run.sh CHANGED
@@ -10316,10 +10316,24 @@ auto_generate_docs_if_needed() {
10316
10316
 
10317
10317
  local project_dir="${TARGET_DIR:-.}"
10318
10318
  local manifest="$project_dir/.loki/docs/docs-manifest.json"
10319
+ # A timed-out attempt never writes the manifest (the provider call was killed
10320
+ # mid-write), so "manifest absent" stayed true forever and this re-fired the
10321
+ # full ~300s generation EVERY iteration with no progress (observed: ~20 min
10322
+ # burned across 4 iterations on one build, zero gate benefit -- doc_coverage
10323
+ # already passes on the partial docs a timed-out attempt leaves behind, since
10324
+ # it scores README/API.md presence, not the manifest). This marker records a
10325
+ # timeout attempt separately from the manifest so a killed run is remembered
10326
+ # and not retried every iteration; a genuine completed manifest still drives
10327
+ # the normal staleness (>10 commits behind) regen path unchanged.
10328
+ local timeout_marker="$project_dir/.loki/docs/.last-attempt-timed-out"
10319
10329
  local needs_gen=false
10320
10330
 
10321
10331
  if [ ! -f "$manifest" ]; then
10322
- needs_gen=true
10332
+ if [ -f "$timeout_marker" ]; then
10333
+ needs_gen=false
10334
+ else
10335
+ needs_gen=true
10336
+ fi
10323
10337
  else
10324
10338
  # Regenerate only when the existing docs are substantially stale.
10325
10339
  local doc_sha
@@ -10329,6 +10343,9 @@ auto_generate_docs_if_needed() {
10329
10343
  behind=$(git -C "$project_dir" rev-list --count "$doc_sha..HEAD" 2>/dev/null || echo "0")
10330
10344
  [ "$behind" -gt 10 ] && needs_gen=true
10331
10345
  fi
10346
+ # A fresh manifest supersedes any earlier timeout; allow future staleness
10347
+ # regen to fire normally instead of being permanently suppressed.
10348
+ [ "$needs_gen" = "true" ] && rm -f "$timeout_marker"
10332
10349
  fi
10333
10350
 
10334
10351
  [ "$needs_gen" = "true" ] || return 0
@@ -10360,6 +10377,8 @@ auto_generate_docs_if_needed() {
10360
10377
  local _doc_rc=$?
10361
10378
  if [ "$_doc_rc" -eq 124 ]; then
10362
10379
  log_warn "Auto-documentation: timed out after ${_doc_to}s (gate will score on what exists); continuing"
10380
+ mkdir -p "$project_dir/.loki/docs" 2>/dev/null
10381
+ touch "$timeout_marker" 2>/dev/null
10363
10382
  else
10364
10383
  log_warn "Auto-documentation: generation did not complete (gate will score on what exists)"
10365
10384
  fi
@@ -11238,6 +11257,69 @@ run_code_review() {
11238
11257
  ':(exclude)__pycache__/' ':(exclude)**/__pycache__/**' \
11239
11258
  ':(exclude)vendor/' ':(exclude)**/vendor/**')
11240
11259
 
11260
+ # Client fix (code_review NO_OUTPUT on oversized diffs): the hardcoded excludes
11261
+ # above miss dirs that are git-TRACKED but listed in the target repo's
11262
+ # .gitignore -- e.g. a dir committed before the ignore rule was added. The
11263
+ # `git add -A` temp index below stages those tracked files, so an 11MB
11264
+ # tracked-but-ignored dir bloats the review diff, overflows the reviewer
11265
+ # prompt, and every reviewer returns NO_OUTPUT. Filter them out DYNAMICALLY:
11266
+ # list the tracked files the repo's own .gitignore would ignore (check-ignore
11267
+ # --no-index evaluates the rules even for already-tracked paths) and exclude
11268
+ # each at the EXACT depth of its containing directory -- NOT a collapsed
11269
+ # top-level prefix. Excluding the top-level prefix would drop sibling REAL
11270
+ # changes (e.g. ignoring loki-ts/dist/ must NOT exclude all of loki-ts/), and
11271
+ # because the empty-diff guard below reuses this same pathspec, that
11272
+ # over-exclusion could blind the guard and let a real change PASS unreviewed
11273
+ # (council-caught fail-closed hole). Nested paths are pruned to the shallowest
11274
+ # ignored dir so a/b and a/b/c collapse to a/b (one exclude, still precise).
11275
+ # Capped so a pathological repo cannot build a giant argv. Opt out
11276
+ # LOKI_REVIEW_GITIGNORE_FILTER=0. No-op (byte-identical diff) when nothing is
11277
+ # tracked-but-ignored.
11278
+ if [ "${LOKI_REVIEW_GITIGNORE_FILTER:-1}" != "0" ]; then
11279
+ local _gi_cap="${LOKI_REVIEW_GITIGNORE_MAX_EXCLUDES:-200}"
11280
+ local _gi_dirs _gi_count=0 _gi_d _gi_prev=""
11281
+ # tracked-and-ignored files -> the DIRNAME of each (exact depth), sorted so
11282
+ # a parent dir sorts before its children for the nested-prune below. A file
11283
+ # ignored at repo root (dirname ".") is excluded by its own exact path, not
11284
+ # a directory.
11285
+ _gi_dirs="$( (cd "${TARGET_DIR:-.}" && \
11286
+ git ls-files -z 2>/dev/null | git check-ignore --no-index --stdin -z 2>/dev/null) \
11287
+ | tr '\0' '\n' | grep -v '^$' \
11288
+ | while IFS= read -r _f; do d="$(dirname "$_f")"; [ "$d" = "." ] && printf '%s\n' "$_f" || printf '%s\n' "$d"; done \
11289
+ | sort -u || true )"
11290
+ if [ -n "$_gi_dirs" ]; then
11291
+ while IFS= read -r _gi_d; do
11292
+ [ -n "$_gi_d" ] || continue
11293
+ # nested-prune: if this path is under the previously-kept dir, skip
11294
+ # it (the parent exclude already covers it). Relies on the sort so a
11295
+ # parent precedes its children.
11296
+ if [ -n "$_gi_prev" ] && case "$_gi_d/" in "$_gi_prev"/*) true ;; *) false ;; esac; then
11297
+ continue
11298
+ fi
11299
+ if [ "$_gi_count" -ge "$_gi_cap" ]; then
11300
+ log_warn "Code review: gitignore-exclude cap ($_gi_cap) reached; some tracked-but-ignored paths remain in the diff. Raise LOKI_REVIEW_GITIGNORE_MAX_EXCLUDES or 'git rm --cached' them."
11301
+ break
11302
+ fi
11303
+ # Exact-path exclude (a leading-path pathspec matches that path and
11304
+ # everything under it; it does NOT match siblings). No '**/' variant
11305
+ # -- that would re-introduce the over-broad match this fix removes.
11306
+ # A trailing '/' anchors a DIRECTORY; a root-level ignored FILE
11307
+ # (dirname was ".", so _gi_d is the file itself) must be excluded by
11308
+ # its EXACT path with no '/', else the pathspec matches nothing and
11309
+ # the ignored file leaks back into the diff (council note: safe
11310
+ # direction, but this makes it exact). Decide by what is on disk.
11311
+ if [ -d "${TARGET_DIR:-.}/${_gi_d}" ]; then
11312
+ _review_pathspec+=(":(exclude)${_gi_d}/")
11313
+ else
11314
+ _review_pathspec+=(":(exclude)${_gi_d}")
11315
+ fi
11316
+ _gi_prev="$_gi_d"
11317
+ _gi_count=$((_gi_count + 1))
11318
+ done <<< "$_gi_dirs"
11319
+ [ "$_gi_count" -gt 0 ] && log_info "Code review: excluded $_gi_count tracked-but-gitignored dir(s) from the review diff (e.g. $(printf '%s' "$_gi_dirs" | head -3 | tr '\n' ' '))"
11320
+ fi
11321
+ fi
11322
+
11241
11323
  # Plan #16 (A-2): make the review diff base robust to shallow/fresh history,
11242
11324
  # and surface NEW (untracked) files -- the whole greenfield build is new
11243
11325
  # files, which `git diff <base>` (tracked-only) never shows.
@@ -11281,6 +11363,28 @@ run_code_review() {
11281
11363
  rm -f "$_rev_idx" 2>/dev/null || true
11282
11364
  fi
11283
11365
 
11366
+ # Client fix (fail LOUD on oversized diff): measure the review diff and, if it
11367
+ # exceeds LOKI_REVIEW_MAX_DIFF_BYTES (default 400KB ~ the reviewer prompt
11368
+ # ceiling), emit an EXPLICIT, actionable warning + a telemetry event instead
11369
+ # of letting the reviewers silently overflow to NO_OUTPUT. We still RUN the
11370
+ # review (a large-but-parseable diff may work); the all-NO_OUTPUT block below
11371
+ # references this so the block is self-explanatory. Names the biggest tracked
11372
+ # dirs so the operator sees the culprit without repo archaeology.
11373
+ local _review_diff_bytes=0
11374
+ _review_diff_bytes=$(printf '%s' "$diff_content" | wc -c | tr -d ' ')
11375
+ local _review_max_bytes="${LOKI_REVIEW_MAX_DIFF_BYTES:-400000}"
11376
+ if [ "${_review_diff_bytes:-0}" -gt "$_review_max_bytes" ] 2>/dev/null; then
11377
+ # biggest contributors: top changed dirs by line count in the diff
11378
+ local _big_dirs
11379
+ _big_dirs=$(printf '%s\n' "$changed_files" | sed 's#/.*##' | grep -v '^$' | sort | uniq -c | sort -rn | head -3 | awk '{print $2" ("$1" files)"}' | tr '\n' ' ')
11380
+ log_warn "Code review: review diff is ${_review_diff_bytes} bytes (limit ${_review_max_bytes}). This can overflow reviewer prompts and force NO_OUTPUT. Biggest dirs: ${_big_dirs:-unknown}. Remedy: 'git rm -r --cached <stale-tracked-dir>' if it is gitignored, or raise LOKI_REVIEW_MAX_DIFF_BYTES."
11381
+ emit_event_json "code_review_diff_oversized" \
11382
+ "review_id=$review_id" \
11383
+ "diff_bytes=$_review_diff_bytes" \
11384
+ "limit_bytes=$_review_max_bytes" \
11385
+ "iteration=${ITERATION_COUNT:-0}" 2>/dev/null || true
11386
+ fi
11387
+
11284
11388
  if [ -z "$diff_content" ]; then
11285
11389
  # Honesty (Finding #596 class): a silent PASS on an empty diff is a trust
11286
11390
  # gap ONLY when the run actually produced changes we failed to diff.
@@ -11623,6 +11727,15 @@ BUILD_PROMPT
11623
11727
  unset LOKI_REVIEW_PROMPT_NAME LOKI_REVIEW_PROMPT_FOCUS LOKI_REVIEW_PROMPT_CHECKS
11624
11728
  unset LOKI_REVIEW_PROMPT_DIFF_FILE LOKI_REVIEW_PROMPT_FILES_FILE LOKI_REVIEW_PROMPT_OUT
11625
11729
 
11730
+ # Client fix (log the actual prompt/diff size per reviewer): so a
11731
+ # NO_OUTPUT post-mortem is a one-line log read, not repo archaeology.
11732
+ # Record the prompt byte size (and the shared diff size) both to the log
11733
+ # and to a per-review sizes.json for the dashboard/telemetry.
11734
+ local _prompt_bytes=0
11735
+ [ -f "$review_prompt_file" ] && _prompt_bytes=$(wc -c < "$review_prompt_file" 2>/dev/null | tr -d ' ')
11736
+ log_info "Reviewer $reviewer_name: prompt ${_prompt_bytes:-0} bytes (review diff ${_review_diff_bytes:-0} bytes)"
11737
+ printf '%s\t%s\n' "$reviewer_name" "${_prompt_bytes:-0}" >> "$review_dir/$review_id/sizes.tsv" 2>/dev/null || true
11738
+
11626
11739
  log_step "Dispatching reviewer: $reviewer_name"
11627
11740
 
11628
11741
  # Launch blind review in background (shared dispatch helper).
@@ -11746,6 +11859,12 @@ BUILD_PROMPT
11746
11859
  review_inconclusive=true
11747
11860
  log_error "CODE REVIEW INCONCLUSIVE: only $real_verdict_count of $reviewer_count reviewers returned a usable verdict (no_output=$no_output_count)"
11748
11861
  log_error " A partial review drops dissent; refusing to pass the gate without every reviewer's verdict."
11862
+ # Client fix: make the block self-explanatory. When the diff was oversized,
11863
+ # NO_OUTPUT is almost certainly a prompt overflow -- say so + the remedy,
11864
+ # instead of leaving the operator to guess (the reported failure mode).
11865
+ if [ "${_review_diff_bytes:-0}" -gt "${_review_max_bytes:-400000}" ] 2>/dev/null; then
11866
+ log_error " LIKELY CAUSE: the review diff is ${_review_diff_bytes} bytes (> ${_review_max_bytes} limit), which overflows reviewer prompts -> empty output. Check for a large tracked-but-gitignored dir: 'git ls-files | git check-ignore --no-index --stdin' then 'git rm -r --cached <dir>'. Per-reviewer prompt sizes: $review_dir/$review_id/sizes.tsv"
11867
+ fi
11749
11868
  if [ "${LOKI_REVIEW_RETRY:-1}" = "1" ] && [ "${_LOKI_REVIEW_RETRYING:-0}" != "1" ]; then
11750
11869
  log_warn " Retrying code review once (LOKI_REVIEW_RETRY=1)..."
11751
11870
  _LOKI_REVIEW_RETRYING=1 run_code_review
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "7.129.2"
10
+ __version__ = "7.129.4"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var q8=Object.defineProperty;var J8=($)=>$;function V8($,Q){this[$]=J8.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)q8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:V8.bind(Q,Z)})};var P=($,Q)=>()=>($&&(Q=$($=0)),Q);var J$=import.meta.require;var m1={};b(m1,{lokiDir:()=>j,homeLokiDir:()=>T$,findRepoRootForVersion:()=>$1,REPO_ROOT:()=>h});import{resolve as t,dirname as e$}from"path";import{fileURLToPath as W8}from"url";import{existsSync as N$}from"fs";import{homedir as H8}from"os";function G8(){let $=v1;for(let Q=0;Q<6;Q++){if(N$(t($,"VERSION"))&&N$(t($,"autonomy/run.sh")))return $;let Z=e$($);if(Z===$)break;$=Z}return t(v1,"..","..","..")}function $1($){let Q=$;for(let Z=0;Z<6;Z++){if(N$(t(Q,"VERSION"))&&N$(t(Q,"autonomy/run.sh")))return Q;let z=e$(Q);if(z===Q)break;Q=z}return t($,"..","..","..")}function j(){return process.env.LOKI_DIR??t(process.cwd(),".loki")}function T$(){return t(H8(),".loki")}var v1,h;var C=P(()=>{v1=e$(W8(import.meta.url));h=G8()});import{readFileSync as U8}from"fs";import{resolve as Y8,dirname as B8}from"path";import{fileURLToPath as M8}from"url";function S$(){if(Q$!==null)return Q$;let $="7.129.2";if(typeof $==="string"&&$.length>0)return Q$=$,Q$;try{let Q=B8(M8(import.meta.url)),Z=$1(Q);Q$=U8(Y8(Z,"VERSION"),"utf-8").trim()}catch{Q$="unknown"}return Q$}var Q$=null;var Q1=P(()=>{C()});var p1={};b(p1,{runOrThrow:()=>S8,run:()=>R,readStreamCapped:()=>c1,commandVersion:()=>C8,commandExists:()=>u,ShellError:()=>Z1,MAX_STDOUT_BYTES:()=>u1});async function c1($,Q=u1){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 R($,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([c1(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 S8($,Q={}){let Z=await R($,Q);if(Z.exitCode!==0)throw new Z1(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function u($){let Q=D8($),Z=await R(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function D8($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function C8($,Q="--version"){if(!await u($))return null;let z=await R([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var u1=16777216,Z1;var o=P(()=>{Z1=class Z1 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 r($){return b8?"":$}var b8,M,S,_,tZ,L,k,y,J;var p=P(()=>{b8=(process.env.NO_COLOR??"").length>0;M=r("\x1B[0;31m"),S=r("\x1B[0;32m"),_=r("\x1B[1;33m"),tZ=r("\x1B[0;34m"),L=r("\x1B[0;36m"),k=r("\x1B[1m"),y=r("\x1B[2m"),J=r("\x1B[0m")});import{existsSync as l8}from"fs";async function Z$(){if(A$!==void 0)return A$;let $="/opt/homebrew/bin/python3.12";if(l8($))return A$=$,$;let Q=await u("python3.12");if(Q)return A$=Q,Q;let Z=await u("python3");return A$=Z,Z}async function z$($,Q={}){let Z=await Z$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return R([Z,"-c",$],Q)}var A$;var V$=P(()=>{o()});var V0={};b(V0,{runStatus:()=>U3});import{existsSync as v,readFileSync as H$,readdirSync as $0,statSync as Q0}from"fs";import{resolve as D,basename as z3}from"path";import{homedir as X3}from"os";function Z0($){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($*C$/Q);if(X>C$)X=C$;let q=C$-X,K=S;if(z>=80)K=M;else if(z>=50)K=_;let W="=".repeat(Math.max(0,X))+" ".repeat(Math.max(0,q)),V=Z0($),H=Z0(Q);return` ${k}${Z}${J} ${K}[${W}]${J} ${z}% (${V} / ${H})`}async function q3(){if(await u("jq"))return!0;return process.stdout.write(`${M}Error: jq is required but not installed.${J}
2
+ var q8=Object.defineProperty;var J8=($)=>$;function V8($,Q){this[$]=J8.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)q8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:V8.bind(Q,Z)})};var P=($,Q)=>()=>($&&(Q=$($=0)),Q);var J$=import.meta.require;var m1={};b(m1,{lokiDir:()=>j,homeLokiDir:()=>T$,findRepoRootForVersion:()=>$1,REPO_ROOT:()=>h});import{resolve as t,dirname as e$}from"path";import{fileURLToPath as W8}from"url";import{existsSync as N$}from"fs";import{homedir as H8}from"os";function G8(){let $=v1;for(let Q=0;Q<6;Q++){if(N$(t($,"VERSION"))&&N$(t($,"autonomy/run.sh")))return $;let Z=e$($);if(Z===$)break;$=Z}return t(v1,"..","..","..")}function $1($){let Q=$;for(let Z=0;Z<6;Z++){if(N$(t(Q,"VERSION"))&&N$(t(Q,"autonomy/run.sh")))return Q;let z=e$(Q);if(z===Q)break;Q=z}return t($,"..","..","..")}function j(){return process.env.LOKI_DIR??t(process.cwd(),".loki")}function T$(){return t(H8(),".loki")}var v1,h;var C=P(()=>{v1=e$(W8(import.meta.url));h=G8()});import{readFileSync as U8}from"fs";import{resolve as Y8,dirname as B8}from"path";import{fileURLToPath as M8}from"url";function S$(){if(Q$!==null)return Q$;let $="7.129.4";if(typeof $==="string"&&$.length>0)return Q$=$,Q$;try{let Q=B8(M8(import.meta.url)),Z=$1(Q);Q$=U8(Y8(Z,"VERSION"),"utf-8").trim()}catch{Q$="unknown"}return Q$}var Q$=null;var Q1=P(()=>{C()});var p1={};b(p1,{runOrThrow:()=>S8,run:()=>R,readStreamCapped:()=>c1,commandVersion:()=>C8,commandExists:()=>u,ShellError:()=>Z1,MAX_STDOUT_BYTES:()=>u1});async function c1($,Q=u1){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 R($,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([c1(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 S8($,Q={}){let Z=await R($,Q);if(Z.exitCode!==0)throw new Z1(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function u($){let Q=D8($),Z=await R(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function D8($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function C8($,Q="--version"){if(!await u($))return null;let z=await R([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var u1=16777216,Z1;var o=P(()=>{Z1=class Z1 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 r($){return b8?"":$}var b8,M,S,_,tZ,L,k,y,J;var p=P(()=>{b8=(process.env.NO_COLOR??"").length>0;M=r("\x1B[0;31m"),S=r("\x1B[0;32m"),_=r("\x1B[1;33m"),tZ=r("\x1B[0;34m"),L=r("\x1B[0;36m"),k=r("\x1B[1m"),y=r("\x1B[2m"),J=r("\x1B[0m")});import{existsSync as l8}from"fs";async function Z$(){if(A$!==void 0)return A$;let $="/opt/homebrew/bin/python3.12";if(l8($))return A$=$,$;let Q=await u("python3.12");if(Q)return A$=Q,Q;let Z=await u("python3");return A$=Z,Z}async function z$($,Q={}){let Z=await Z$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return R([Z,"-c",$],Q)}var A$;var V$=P(()=>{o()});var V0={};b(V0,{runStatus:()=>U3});import{existsSync as v,readFileSync as H$,readdirSync as $0,statSync as Q0}from"fs";import{resolve as D,basename as z3}from"path";import{homedir as X3}from"os";function Z0($){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($*C$/Q);if(X>C$)X=C$;let q=C$-X,K=S;if(z>=80)K=M;else if(z>=50)K=_;let W="=".repeat(Math.max(0,X))+" ".repeat(Math.max(0,q)),V=Z0($),H=Z0(Q);return` ${k}${Z}${J} ${K}[${W}]${J} ${z}% (${V} / ${H})`}async function q3(){if(await u("jq"))return!0;return process.stdout.write(`${M}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)
@@ -814,4 +814,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
814
814
  `),2}default:return process.stderr.write(`Unknown command: ${Q}
815
815
  `),process.stderr.write(K8),2}}e1();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var SZ=await NZ(Bun.argv.slice(2));process.exit(SZ);
816
816
 
817
- //# debugId=11A0089D1663FA1464756E2164756E21
817
+ //# debugId=FD0E3987CF628A8F64756E2164756E21
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '7.129.2'
60
+ __version__ = '7.129.4'
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.129.2",
4
+ "version": "7.129.4",
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.129.2",
5
+ "version": "7.129.4",
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",