loki-mode 7.129.1 → 7.129.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 v7.129.1
6
+ # Loki Mode v7.129.3
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.1 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
411
+ **v7.129.3 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 7.129.1
1
+ 7.129.3
package/autonomy/loki CHANGED
@@ -8057,6 +8057,16 @@ fi
8057
8057
  INNER_SCRIPT_EOF
8058
8058
  chmod +x "$run_script"
8059
8059
 
8060
+ # Propagate the nested-agent signal into the detached run. CLAUDECODE (set
8061
+ # by Claude Code / the Loki Mode skill) is NOT in the curated env list the
8062
+ # inner script inherits, so run.sh's nested guard would never see it once
8063
+ # detached. Forward it as LOKI_NESTED_AGENT (which run.sh honors) so the
8064
+ # testing/docs worktree sub-streams stay OFF when nested even after the
8065
+ # nohup fork. Already-set LOKI_NESTED_AGENT wins.
8066
+ local _loki_nested_pass="${LOKI_NESTED_AGENT:-}"
8067
+ if [ -z "$_loki_nested_pass" ] && [ -n "${CLAUDECODE:-}" ]; then
8068
+ _loki_nested_pass=1
8069
+ fi
8060
8070
  # Pass all variables safely via environment
8061
8071
  LOKI_RUN_DIR="$(pwd)" \
8062
8072
  LOKI_CMD="$loki_cmd" \
@@ -8069,6 +8079,7 @@ INNER_SCRIPT_EOF
8069
8079
  LOKI_AUTO_MERGE="$auto_merge" \
8070
8080
  LOKI_PR_TITLE="${title:-Implementation for issue ${issue_ref}}" \
8071
8081
  LOKI_ISSUE_NUMBER="${number:-}" \
8082
+ LOKI_NESTED_AGENT="$_loki_nested_pass" \
8072
8083
  nohup bash "$run_script" > "$log_file" 2>&1 &
8073
8084
 
8074
8085
  local bg_pid=$!
package/autonomy/run.sh CHANGED
@@ -10340,8 +10340,30 @@ auto_generate_docs_if_needed() {
10340
10340
  # Synchronous so docs exist before the gate scores. Provider-agnostic:
10341
10341
  # 'loki docs generate' picks the run's provider and falls back to
10342
10342
  # template-based docs when no provider CLI is available.
10343
- "$loki_bin" docs generate "$project_dir" >/dev/null 2>&1 || \
10344
- log_warn "Auto-documentation: generation did not complete (gate will score on what exists)"
10343
+ #
10344
+ # BOUNDED: 'loki docs generate' spawns a provider (claude) call that can hang
10345
+ # with no cap. This runs AFTER completion, before the detached --pr push/PR
10346
+ # step, so a hang here silently blocks the whole PR from ever being created
10347
+ # (observed: a verified build stuck ~55 min in this step, work committed but
10348
+ # never pushed). Wrap it in a timeout; docs are non-gating, so on timeout we
10349
+ # warn and continue to the gate (which scores whatever docs exist).
10350
+ local _doc_to="${LOKI_DOCS_TIMEOUT:-${LOKI_GATE_TIMEOUT:-300}}"
10351
+ local _doc_cmd=()
10352
+ if command -v gtimeout >/dev/null 2>&1; then
10353
+ _doc_cmd=(gtimeout "${_doc_to}s")
10354
+ elif command -v timeout >/dev/null 2>&1; then
10355
+ _doc_cmd=(timeout "${_doc_to}s")
10356
+ fi
10357
+ if "${_doc_cmd[@]}" "$loki_bin" docs generate "$project_dir" >/dev/null 2>&1; then
10358
+ :
10359
+ else
10360
+ local _doc_rc=$?
10361
+ if [ "$_doc_rc" -eq 124 ]; then
10362
+ log_warn "Auto-documentation: timed out after ${_doc_to}s (gate will score on what exists); continuing"
10363
+ else
10364
+ log_warn "Auto-documentation: generation did not complete (gate will score on what exists)"
10365
+ fi
10366
+ fi
10345
10367
  }
10346
10368
 
10347
10369
  # ============================================================================
@@ -11216,6 +11238,69 @@ run_code_review() {
11216
11238
  ':(exclude)__pycache__/' ':(exclude)**/__pycache__/**' \
11217
11239
  ':(exclude)vendor/' ':(exclude)**/vendor/**')
11218
11240
 
11241
+ # Client fix (code_review NO_OUTPUT on oversized diffs): the hardcoded excludes
11242
+ # above miss dirs that are git-TRACKED but listed in the target repo's
11243
+ # .gitignore -- e.g. a dir committed before the ignore rule was added. The
11244
+ # `git add -A` temp index below stages those tracked files, so an 11MB
11245
+ # tracked-but-ignored dir bloats the review diff, overflows the reviewer
11246
+ # prompt, and every reviewer returns NO_OUTPUT. Filter them out DYNAMICALLY:
11247
+ # list the tracked files the repo's own .gitignore would ignore (check-ignore
11248
+ # --no-index evaluates the rules even for already-tracked paths) and exclude
11249
+ # each at the EXACT depth of its containing directory -- NOT a collapsed
11250
+ # top-level prefix. Excluding the top-level prefix would drop sibling REAL
11251
+ # changes (e.g. ignoring loki-ts/dist/ must NOT exclude all of loki-ts/), and
11252
+ # because the empty-diff guard below reuses this same pathspec, that
11253
+ # over-exclusion could blind the guard and let a real change PASS unreviewed
11254
+ # (council-caught fail-closed hole). Nested paths are pruned to the shallowest
11255
+ # ignored dir so a/b and a/b/c collapse to a/b (one exclude, still precise).
11256
+ # Capped so a pathological repo cannot build a giant argv. Opt out
11257
+ # LOKI_REVIEW_GITIGNORE_FILTER=0. No-op (byte-identical diff) when nothing is
11258
+ # tracked-but-ignored.
11259
+ if [ "${LOKI_REVIEW_GITIGNORE_FILTER:-1}" != "0" ]; then
11260
+ local _gi_cap="${LOKI_REVIEW_GITIGNORE_MAX_EXCLUDES:-200}"
11261
+ local _gi_dirs _gi_count=0 _gi_d _gi_prev=""
11262
+ # tracked-and-ignored files -> the DIRNAME of each (exact depth), sorted so
11263
+ # a parent dir sorts before its children for the nested-prune below. A file
11264
+ # ignored at repo root (dirname ".") is excluded by its own exact path, not
11265
+ # a directory.
11266
+ _gi_dirs="$( (cd "${TARGET_DIR:-.}" && \
11267
+ git ls-files -z 2>/dev/null | git check-ignore --no-index --stdin -z 2>/dev/null) \
11268
+ | tr '\0' '\n' | grep -v '^$' \
11269
+ | while IFS= read -r _f; do d="$(dirname "$_f")"; [ "$d" = "." ] && printf '%s\n' "$_f" || printf '%s\n' "$d"; done \
11270
+ | sort -u || true )"
11271
+ if [ -n "$_gi_dirs" ]; then
11272
+ while IFS= read -r _gi_d; do
11273
+ [ -n "$_gi_d" ] || continue
11274
+ # nested-prune: if this path is under the previously-kept dir, skip
11275
+ # it (the parent exclude already covers it). Relies on the sort so a
11276
+ # parent precedes its children.
11277
+ if [ -n "$_gi_prev" ] && case "$_gi_d/" in "$_gi_prev"/*) true ;; *) false ;; esac; then
11278
+ continue
11279
+ fi
11280
+ if [ "$_gi_count" -ge "$_gi_cap" ]; then
11281
+ 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."
11282
+ break
11283
+ fi
11284
+ # Exact-path exclude (a leading-path pathspec matches that path and
11285
+ # everything under it; it does NOT match siblings). No '**/' variant
11286
+ # -- that would re-introduce the over-broad match this fix removes.
11287
+ # A trailing '/' anchors a DIRECTORY; a root-level ignored FILE
11288
+ # (dirname was ".", so _gi_d is the file itself) must be excluded by
11289
+ # its EXACT path with no '/', else the pathspec matches nothing and
11290
+ # the ignored file leaks back into the diff (council note: safe
11291
+ # direction, but this makes it exact). Decide by what is on disk.
11292
+ if [ -d "${TARGET_DIR:-.}/${_gi_d}" ]; then
11293
+ _review_pathspec+=(":(exclude)${_gi_d}/")
11294
+ else
11295
+ _review_pathspec+=(":(exclude)${_gi_d}")
11296
+ fi
11297
+ _gi_prev="$_gi_d"
11298
+ _gi_count=$((_gi_count + 1))
11299
+ done <<< "$_gi_dirs"
11300
+ [ "$_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' ' '))"
11301
+ fi
11302
+ fi
11303
+
11219
11304
  # Plan #16 (A-2): make the review diff base robust to shallow/fresh history,
11220
11305
  # and surface NEW (untracked) files -- the whole greenfield build is new
11221
11306
  # files, which `git diff <base>` (tracked-only) never shows.
@@ -11259,6 +11344,28 @@ run_code_review() {
11259
11344
  rm -f "$_rev_idx" 2>/dev/null || true
11260
11345
  fi
11261
11346
 
11347
+ # Client fix (fail LOUD on oversized diff): measure the review diff and, if it
11348
+ # exceeds LOKI_REVIEW_MAX_DIFF_BYTES (default 400KB ~ the reviewer prompt
11349
+ # ceiling), emit an EXPLICIT, actionable warning + a telemetry event instead
11350
+ # of letting the reviewers silently overflow to NO_OUTPUT. We still RUN the
11351
+ # review (a large-but-parseable diff may work); the all-NO_OUTPUT block below
11352
+ # references this so the block is self-explanatory. Names the biggest tracked
11353
+ # dirs so the operator sees the culprit without repo archaeology.
11354
+ local _review_diff_bytes=0
11355
+ _review_diff_bytes=$(printf '%s' "$diff_content" | wc -c | tr -d ' ')
11356
+ local _review_max_bytes="${LOKI_REVIEW_MAX_DIFF_BYTES:-400000}"
11357
+ if [ "${_review_diff_bytes:-0}" -gt "$_review_max_bytes" ] 2>/dev/null; then
11358
+ # biggest contributors: top changed dirs by line count in the diff
11359
+ local _big_dirs
11360
+ _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' ' ')
11361
+ 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."
11362
+ emit_event_json "code_review_diff_oversized" \
11363
+ "review_id=$review_id" \
11364
+ "diff_bytes=$_review_diff_bytes" \
11365
+ "limit_bytes=$_review_max_bytes" \
11366
+ "iteration=${ITERATION_COUNT:-0}" 2>/dev/null || true
11367
+ fi
11368
+
11262
11369
  if [ -z "$diff_content" ]; then
11263
11370
  # Honesty (Finding #596 class): a silent PASS on an empty diff is a trust
11264
11371
  # gap ONLY when the run actually produced changes we failed to diff.
@@ -11601,6 +11708,15 @@ BUILD_PROMPT
11601
11708
  unset LOKI_REVIEW_PROMPT_NAME LOKI_REVIEW_PROMPT_FOCUS LOKI_REVIEW_PROMPT_CHECKS
11602
11709
  unset LOKI_REVIEW_PROMPT_DIFF_FILE LOKI_REVIEW_PROMPT_FILES_FILE LOKI_REVIEW_PROMPT_OUT
11603
11710
 
11711
+ # Client fix (log the actual prompt/diff size per reviewer): so a
11712
+ # NO_OUTPUT post-mortem is a one-line log read, not repo archaeology.
11713
+ # Record the prompt byte size (and the shared diff size) both to the log
11714
+ # and to a per-review sizes.json for the dashboard/telemetry.
11715
+ local _prompt_bytes=0
11716
+ [ -f "$review_prompt_file" ] && _prompt_bytes=$(wc -c < "$review_prompt_file" 2>/dev/null | tr -d ' ')
11717
+ log_info "Reviewer $reviewer_name: prompt ${_prompt_bytes:-0} bytes (review diff ${_review_diff_bytes:-0} bytes)"
11718
+ printf '%s\t%s\n' "$reviewer_name" "${_prompt_bytes:-0}" >> "$review_dir/$review_id/sizes.tsv" 2>/dev/null || true
11719
+
11604
11720
  log_step "Dispatching reviewer: $reviewer_name"
11605
11721
 
11606
11722
  # Launch blind review in background (shared dispatch helper).
@@ -11724,6 +11840,12 @@ BUILD_PROMPT
11724
11840
  review_inconclusive=true
11725
11841
  log_error "CODE REVIEW INCONCLUSIVE: only $real_verdict_count of $reviewer_count reviewers returned a usable verdict (no_output=$no_output_count)"
11726
11842
  log_error " A partial review drops dissent; refusing to pass the gate without every reviewer's verdict."
11843
+ # Client fix: make the block self-explanatory. When the diff was oversized,
11844
+ # NO_OUTPUT is almost certainly a prompt overflow -- say so + the remedy,
11845
+ # instead of leaving the operator to guess (the reported failure mode).
11846
+ if [ "${_review_diff_bytes:-0}" -gt "${_review_max_bytes:-400000}" ] 2>/dev/null; then
11847
+ 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"
11848
+ fi
11727
11849
  if [ "${LOKI_REVIEW_RETRY:-1}" = "1" ] && [ "${_LOKI_REVIEW_RETRYING:-0}" != "1" ]; then
11728
11850
  log_warn " Retrying code review once (LOKI_REVIEW_RETRY=1)..."
11729
11851
  _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.1"
10
+ __version__ = "7.129.3"
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.129.1
5
+ **Version:** v7.129.2
6
6
 
7
7
  ---
8
8
 
@@ -396,7 +396,7 @@ provider works inside the container. Provide auth with your Anthropic API key:
396
396
  # Run Loki Mode in Docker (Claude provider, API-key auth)
397
397
  docker run --rm -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
398
398
  -v $(pwd):/workspace -w /workspace \
399
- asklokesh/loki-mode:7.129.1 start ./my-spec.md
399
+ asklokesh/loki-mode:7.129.2 start ./my-spec.md
400
400
  ```
401
401
 
402
402
  ##### docker compose + .env (no host install)
@@ -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.1";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.3";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=E67A22525236ED6C64756E2164756E21
817
+ //# debugId=D9F70CA0E419CE7264756E2164756E21
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '7.129.1'
60
+ __version__ = '7.129.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": "7.129.1",
4
+ "version": "7.129.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": "7.129.1",
5
+ "version": "7.129.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",