loki-mode 7.74.0 → 7.75.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.74.0
6
+ # Loki Mode v7.75.0
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -406,4 +406,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
406
406
 
407
407
  ---
408
408
 
409
- **v7.74.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
409
+ **v7.75.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 7.74.0
1
+ 7.75.0
package/autonomy/run.sh CHANGED
@@ -3112,6 +3112,16 @@ on_run_complete() {
3112
3112
  local pr_title
3113
3113
  pr_title="Loki Mode: ${branch}"
3114
3114
  local pr_url=""
3115
+ # ENT-4 (idempotent PR): reuse an existing OPEN PR for this head instead of
3116
+ # attempting a second create on a platform retry / resume.
3117
+ local existing_pr
3118
+ existing_pr="$( (cd "${TARGET_DIR:-.}" && _loki_net gh pr list --head "$branch" --state open --json url --jq '.[0].url') 2>/dev/null || true )"
3119
+ if [ -n "$existing_pr" ]; then
3120
+ _LOKI_DELEGATE_PR_URL="$existing_pr"
3121
+ export _LOKI_DELEGATE_PR_URL
3122
+ log_info "LOKI_DELEGATE_PR=1: PR already exists for branch '$branch': $existing_pr (skipping create)."
3123
+ return 0
3124
+ fi
3115
3125
  pr_url="$( (cd "${TARGET_DIR:-.}" && _loki_net gh pr create --title "$pr_title" --body "Opened by Loki Mode (delegate mode). Review locally before merge." --head "$branch") 2>/dev/null || true )"
3116
3126
  if [ -n "$pr_url" ]; then
3117
3127
  # Export so build_completion_summary folds the url into the summary.
@@ -6688,6 +6698,19 @@ create_session_pr() {
6688
6698
  # Create PR if gh CLI is available
6689
6699
  if command -v gh &>/dev/null; then
6690
6700
  local pr_url
6701
+ # ENT-4 (idempotent PR): check-before-create. On a platform retry (k8s Job
6702
+ # backoffLimit / ECS / pod-loss resume) the run can reach this completion
6703
+ # path more than once for the SAME head branch. `gh pr create` dedupes by
6704
+ # head only because the branch name is stable, but we make the no-duplicate
6705
+ # guarantee explicit and the log honest: if an OPEN PR already exists for
6706
+ # this head, reuse its URL instead of attempting a second create.
6707
+ local existing_pr
6708
+ existing_pr=$(gh pr list --head "$branch_name" --state open --json url --jq '.[0].url' 2>/dev/null || true)
6709
+ if [ -n "$existing_pr" ]; then
6710
+ log_info "PR already exists for branch $branch_name: $existing_pr (skipping create)"
6711
+ audit_log "PR_EXISTS" "branch=$branch_name,url=$existing_pr"
6712
+ return 0
6713
+ fi
6691
6714
  pr_url=$(gh pr create \
6692
6715
  --title "Loki Mode: Agent session changes ($branch_name)" \
6693
6716
  --body "Automated changes from Loki Mode agent session.
@@ -10343,6 +10366,53 @@ if top:
10343
10366
  SOLUTIONS_SCRIPT
10344
10367
  }
10345
10368
 
10369
+ # ============================================================================
10370
+ # Durable-state assertion (enterprise container deployment)
10371
+ # ============================================================================
10372
+ # In a containerized deployment (k8s Job / ECS task / docker-run), all per-build
10373
+ # state -- .loki/state/checkpoints, .loki/ state, .loki/queue, .loki/signals,
10374
+ # .loki/logs, the agent feature branch, and the refs/loki/cp/* checkpoint refs
10375
+ # in the checkout's .git -- lives UNDER the working directory (TARGET_DIR), since
10376
+ # run_autonomous() runs with cwd == TARGET_DIR and every state path is relative
10377
+ # (or ${TARGET_DIR}/.loki). Mounting ONE durable volume at the working checkout
10378
+ # therefore makes the whole per-build state survive pod loss with no code change.
10379
+ # The machine-global registry (~/.loki/dashboard/projects.json) is intentionally
10380
+ # NOT per-build and stays off the volume; /tmp scratch (the staged run script,
10381
+ # mktemp temp files) is intentionally ephemeral and re-created on restart.
10382
+ #
10383
+ # This assertion is OPT-IN via LOKI_DURABLE_STATE=1 (set by the container
10384
+ # ENTRYPOINT / Helm chart). Local runs are unaffected. When enabled it fails
10385
+ # loudly BEFORE any work if TARGET_DIR is not a writable directory, so a
10386
+ # misconfigured mount (wrong mountPath, read-only volume, missing PVC) surfaces
10387
+ # as an immediate honest error instead of silent state loss on the first crash.
10388
+ assert_durable_state_mount() {
10389
+ [ "${LOKI_DURABLE_STATE:-0}" = "1" ] || return 0
10390
+ local dir="${TARGET_DIR:-.}"
10391
+ # A misconfigured mount is a DETERMINISTIC config error: re-running on the same
10392
+ # broken mount fails identically. Exit 20 (the terminal-failure contract code)
10393
+ # so the Job's podFailurePolicy fails it immediately instead of burning the
10394
+ # whole backoffLimit retrying a config error that cannot self-heal.
10395
+ if [ ! -d "$dir" ]; then
10396
+ log_error "LOKI_DURABLE_STATE=1 but working directory does not exist: $dir"
10397
+ log_error "Mount a durable volume (PVC / EFS / bind mount) at the working checkout."
10398
+ exit 20
10399
+ fi
10400
+ # Probe writability with an actual write+remove (a read-only mount passes -w
10401
+ # on some filesystems but rejects the write). This proves the mount is
10402
+ # WRITABLE; durability (survival across pod loss) is a property of the volume
10403
+ # the operator mounts here (a PVC / EFS / bind mount), which a write probe
10404
+ # cannot detect -- so the success message claims only writability.
10405
+ local probe="${dir}/.loki-durable-probe.$$"
10406
+ if ! ( mkdir -p "${dir}/.loki" 2>/dev/null && : > "$probe" ) 2>/dev/null; then
10407
+ log_error "LOKI_DURABLE_STATE=1 but the working directory is not writable: $dir"
10408
+ log_error "Pod-loss resume requires a writable durable volume mounted here."
10409
+ rm -f "$probe" 2>/dev/null || true
10410
+ exit 20
10411
+ fi
10412
+ rm -f "$probe" 2>/dev/null || true
10413
+ log_info "Durable state: writable mount verified at $dir (per-build state persists here; mount a durable volume for pod-loss survival)"
10414
+ }
10415
+
10346
10416
  # ============================================================================
10347
10417
  # Checkpoint/Snapshot System (v5.34.0)
10348
10418
  # Git-based checkpoints after task completion with state snapshots
@@ -12791,8 +12861,38 @@ except (json.JSONDecodeError, KeyError, TypeError, OSError):
12791
12861
  # signals), so this closes the crash-rerun toothless-gate path.
12792
12862
  # Deliberately NOT reset (genuine resume / user re-run expecting to
12793
12863
  # continue): paused, interrupted, budget_exceeded, stopped.
12864
+ #
12865
+ # ENT-2 (enterprise pod-loss resume): a crashed "running" run is
12866
+ # normally reset (a fresh `loki start` on a dev box after a crash is a
12867
+ # new run, and resetting closes the toothless-gate path). BUT in a
12868
+ # containerized deployment (LOKI_DURABLE_STATE=1) the platform RESTARTS
12869
+ # the SAME build on the SAME durable volume after a pod loss, and the
12870
+ # operator's intent is RESUME, not restart-from-scratch. In that mode
12871
+ # only, a "running" status with a still-valid run-start-SHA baseline on
12872
+ # the durable volume RESUMES (ITERATION_COUNT preserved). The gate stays
12873
+ # sharp because $_start_sha_file survived on the volume, so the run-start
12874
+ # SHA recapture (run_autonomous) keys on THIS run's real start SHA, not
12875
+ # the prior run's -- and resume re-enters the normal RARV iteration,
12876
+ # which re-runs verification; a crash never inherits a gate PASS (the
12877
+ # success terminals council_approved/.../completion_promise_fulfilled
12878
+ # are still reset, so a completed-then-rerun is a NEW run as before).
12879
+ local _resume_crashed_running=0
12880
+ if [ "$prev_status" = "running" ] \
12881
+ && [ "${LOKI_DURABLE_STATE:-0}" = "1" ] \
12882
+ && [ -s ".loki/state/start-sha" ]; then
12883
+ _resume_crashed_running=1
12884
+ fi
12794
12885
  case "$prev_status" in
12795
- failed|max_iterations_reached|max_retries_exceeded|exited|council_approved|council_force_approved|completion_promise_fulfilled|running)
12886
+ running)
12887
+ if [ "$_resume_crashed_running" = "1" ]; then
12888
+ log_info "Durable resume: previous build crashed mid-run (status: running). Resuming from iteration ${ITERATION_COUNT} on the durable volume; verification re-runs (a crash never inherits a gate PASS)."
12889
+ else
12890
+ log_info "Previous session ended with status: $prev_status. Resetting for new session."
12891
+ RETRY_COUNT=0
12892
+ ITERATION_COUNT=0
12893
+ fi
12894
+ ;;
12895
+ failed|max_iterations_reached|max_retries_exceeded|exited|council_approved|council_force_approved|completion_promise_fulfilled)
12796
12896
  log_info "Previous session ended with status: $prev_status. Resetting for new session."
12797
12897
  RETRY_COUNT=0
12798
12898
  ITERATION_COUNT=0
@@ -17679,6 +17779,11 @@ main() {
17679
17779
  load_solutions_context "general development"
17680
17780
  fi
17681
17781
 
17782
+ # Durable-state mount check (enterprise containers): fail loudly before any
17783
+ # work if LOKI_DURABLE_STATE=1 and the working checkout is not a writable
17784
+ # durable mount, so a misconfigured volume never silently loses build state.
17785
+ assert_durable_state_mount
17786
+
17682
17787
  # Setup agent branch protection (isolates agent changes to a feature branch)
17683
17788
  setup_agent_branch
17684
17789
 
@@ -17874,6 +17979,47 @@ main() {
17874
17979
  rm -f "$loki_dir/sessions/${LOKI_SESSION_ID}/loki.pid" \
17875
17980
  "$loki_dir/sessions/${LOKI_SESSION_ID}/loki.pgid" 2>/dev/null
17876
17981
  fi
17982
+ # ENT-3 (enterprise pod-loss / platform-retry contract): translate the
17983
+ # terminal RUN STATE into a stable PROCESS exit code so a k8s Job's
17984
+ # backoffLimit (or ECS/systemd retry) can distinguish "completed but failed
17985
+ # the gate -> do NOT retry" from "crashed -> retry and resume". Without this
17986
+ # both look like a generic exit 1 and the platform either loops a
17987
+ # deterministically-failing build forever or gives up on a recoverable crash.
17988
+ #
17989
+ # Contract (LOKI_DURABLE_STATE=1 only; local/CI exit codes are unchanged):
17990
+ # 0 = success / human-controlled clean stop (council approved, completion
17991
+ # promise, force-stop, or paused/interrupted/budget/stopped where a
17992
+ # human will resume). Job -> Complete, no retry.
17993
+ # 20 = deterministic terminal failure (failed, max_iterations_reached,
17994
+ # max_retries_exceeded, exited, policy_blocked). Re-running on the same
17995
+ # inputs fails the same way -> Job must NOT retry. The Helm Job pairs
17996
+ # this with restartPolicy: Never + a podFailurePolicy rule that maps
17997
+ # exit 20 to FailJob (no retry), so a deterministic failure does not
17998
+ # burn the backoffLimit (the Job records the failure; an operator
17999
+ # changes the spec/budget and re-submits a NEW Job). K8s 1.31+.
18000
+ # anything else nonzero = crash/unexpected (e.g. status still "running"
18001
+ # because the process was SIGKILLed before reaching here). Retryable;
18002
+ # the restarted Job resumes via the ENT-2 durable-resume path.
18003
+ if [ "${LOKI_DURABLE_STATE:-0}" = "1" ]; then
18004
+ local _final_status
18005
+ _final_status=$(python3 -c "import json; print(json.load(open('.loki/autonomy-state.json')).get('status','unknown'))" 2>/dev/null || echo "unknown")
18006
+ case "$_final_status" in
18007
+ council_approved|council_force_approved|completion_promise_fulfilled|force_stopped|paused|interrupted|budget_exceeded|stopped)
18008
+ result=0 ;;
18009
+ failed|max_iterations_reached|max_retries_exceeded|policy_blocked)
18010
+ result=20 ;;
18011
+ *)
18012
+ # Unknown/running/exited terminal: leave $result as-is (nonzero on a
18013
+ # real failure path) so the platform treats it as a retryable crash.
18014
+ # "exited" is a TRANSIENT per-iteration status (save_state at the end
18015
+ # of each iteration), never a legitimate deterministic terminal, so
18016
+ # it must NOT map to no-retry: a SIGKILL while "exited" is persisted
18017
+ # is a recoverable crash that should resume, not a FailJob.
18018
+ [ "$result" = "0" ] && result=1 ;;
18019
+ esac
18020
+ log_info "Durable-state exit contract: final status '$_final_status' -> exit ${result} ($([ "$result" = "0" ] && echo "complete, no retry" || { [ "$result" = "20" ] && echo "terminal failure, no retry" || echo "crash, retryable"; }))"
18021
+ fi
18022
+
17877
18023
  # Mark session.json as stopped
17878
18024
  if [ -f "$loki_dir/session.json" ]; then
17879
18025
  # BUG-ST-008: Atomic session.json update via temp file + mv
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "7.74.0"
10
+ __version__ = "7.75.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.74.0
5
+ **Version:** v7.75.0
6
6
 
7
7
  ---
8
8
 
@@ -395,7 +395,7 @@ provider works inside the container. Provide auth with your Anthropic API key:
395
395
  # Run Loki Mode in Docker (Claude provider, API-key auth)
396
396
  docker run --rm -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
397
397
  -v $(pwd):/workspace -w /workspace \
398
- asklokesh/loki-mode:7.74.0 start ./my-spec.md
398
+ asklokesh/loki-mode:7.75.0 start ./my-spec.md
399
399
  ```
400
400
 
401
401
  ##### docker compose + .env (no host install)
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var r6=Object.defineProperty;var i6=($)=>$;function e6($,Q){this[$]=i6.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)r6($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:e6.bind(Q,Z)})};var L=($,Q)=>()=>($&&(Q=$($=0)),Q);var q$=import.meta.require;var D1={};b(D1,{lokiDir:()=>P,homeLokiDir:()=>t$,findRepoRootForVersion:()=>s$,REPO_ROOT:()=>g});import{resolve as a,dirname as a$}from"path";import{fileURLToPath as $Q}from"url";import{existsSync as F$}from"fs";import{homedir as QQ}from"os";function ZQ(){let $=N1;for(let Q=0;Q<6;Q++){if(F$(a($,"VERSION"))&&F$(a($,"autonomy/run.sh")))return $;let Z=a$($);if(Z===$)break;$=Z}return a(N1,"..","..","..")}function s$($){let Q=$;for(let Z=0;Z<6;Z++){if(F$(a(Q,"VERSION"))&&F$(a(Q,"autonomy/run.sh")))return Q;let z=a$(Q);if(z===Q)break;Q=z}return a($,"..","..","..")}function P(){return process.env.LOKI_DIR??a(process.cwd(),".loki")}function t$(){return a(QQ(),".loki")}var N1,g;var C=L(()=>{N1=a$($Q(import.meta.url));g=ZQ()});import{readFileSync as zQ}from"fs";import{resolve as XQ,dirname as KQ}from"path";import{fileURLToPath as qQ}from"url";function R$(){if(Q$!==null)return Q$;let $="7.74.0";if(typeof $==="string"&&$.length>0)return Q$=$,Q$;try{let Q=KQ(qQ(import.meta.url)),Z=s$(Q);Q$=zQ(XQ(Z,"VERSION"),"utf-8").trim()}catch{Q$="unknown"}return Q$}var Q$=null;var r$=L(()=>{C()});var b1={};b(b1,{runOrThrow:()=>VQ,run:()=>F,commandVersion:()=>WQ,commandExists:()=>f,ShellError:()=>i$});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([new Response(Z.stdout).text(),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 VQ($,Q={}){let Z=await F($,Q);if(Z.exitCode!==0)throw new i$(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function f($){let Q=JQ($),Z=await F(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function JQ($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function WQ($,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 i$;var d=L(()=>{i$=class i$ 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 s($){return UQ?"":$}var UQ,O,N,_,_Z,I,R,h,V;var c=L(()=>{UQ=(process.env.NO_COLOR??"").length>0;O=s("\x1B[0;31m"),N=s("\x1B[0;32m"),_=s("\x1B[1;33m"),_Z=s("\x1B[0;34m"),I=s("\x1B[0;36m"),R=s("\x1B[1m"),h=s("\x1B[2m"),V=s("\x1B[0m")});import{existsSync as _Q}from"fs";async function Z$(){if(Y$!==void 0)return Y$;let $="/opt/homebrew/bin/python3.12";if(_Q($))return Y$=$,$;let Q=await f("python3.12");if(Q)return Y$=Q,Q;let Z=await f("python3");return Y$=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 Y$;var V$=L(()=>{d()});var e1={};b(e1,{runStatus:()=>cQ});import{existsSync as y,readFileSync as W$,readdirSync as d1,statSync as o1}from"fs";import{resolve as D,basename as CQ}from"path";import{homedir as bQ}from"os";function n1($){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 a1($,Q,Z){if(Q===0)return null;let z=Math.trunc($*100/Q),X=Math.trunc($*x$/Q);if(X>x$)X=x$;let q=x$-X,K=N;if(z>=80)K=O;else if(z>=50)K=_;let W="=".repeat(Math.max(0,X))+" ".repeat(Math.max(0,q)),J=n1($),U=n1(Q);return` ${R}${Z}${V} ${K}[${W}]${V} ${z}% (${J} / ${U})`}async function yQ(){if(await f("jq"))return!0;return process.stdout.write(`${O}Error: jq is required but not installed.${V}
2
+ var r6=Object.defineProperty;var i6=($)=>$;function e6($,Q){this[$]=i6.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)r6($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:e6.bind(Q,Z)})};var L=($,Q)=>()=>($&&(Q=$($=0)),Q);var q$=import.meta.require;var D1={};b(D1,{lokiDir:()=>P,homeLokiDir:()=>t$,findRepoRootForVersion:()=>s$,REPO_ROOT:()=>g});import{resolve as a,dirname as a$}from"path";import{fileURLToPath as $Q}from"url";import{existsSync as F$}from"fs";import{homedir as QQ}from"os";function ZQ(){let $=N1;for(let Q=0;Q<6;Q++){if(F$(a($,"VERSION"))&&F$(a($,"autonomy/run.sh")))return $;let Z=a$($);if(Z===$)break;$=Z}return a(N1,"..","..","..")}function s$($){let Q=$;for(let Z=0;Z<6;Z++){if(F$(a(Q,"VERSION"))&&F$(a(Q,"autonomy/run.sh")))return Q;let z=a$(Q);if(z===Q)break;Q=z}return a($,"..","..","..")}function P(){return process.env.LOKI_DIR??a(process.cwd(),".loki")}function t$(){return a(QQ(),".loki")}var N1,g;var C=L(()=>{N1=a$($Q(import.meta.url));g=ZQ()});import{readFileSync as zQ}from"fs";import{resolve as XQ,dirname as KQ}from"path";import{fileURLToPath as qQ}from"url";function R$(){if(Q$!==null)return Q$;let $="7.75.0";if(typeof $==="string"&&$.length>0)return Q$=$,Q$;try{let Q=KQ(qQ(import.meta.url)),Z=s$(Q);Q$=zQ(XQ(Z,"VERSION"),"utf-8").trim()}catch{Q$="unknown"}return Q$}var Q$=null;var r$=L(()=>{C()});var b1={};b(b1,{runOrThrow:()=>VQ,run:()=>F,commandVersion:()=>WQ,commandExists:()=>f,ShellError:()=>i$});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([new Response(Z.stdout).text(),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 VQ($,Q={}){let Z=await F($,Q);if(Z.exitCode!==0)throw new i$(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function f($){let Q=JQ($),Z=await F(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function JQ($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function WQ($,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 i$;var d=L(()=>{i$=class i$ 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 s($){return UQ?"":$}var UQ,O,N,_,_Z,I,R,h,V;var c=L(()=>{UQ=(process.env.NO_COLOR??"").length>0;O=s("\x1B[0;31m"),N=s("\x1B[0;32m"),_=s("\x1B[1;33m"),_Z=s("\x1B[0;34m"),I=s("\x1B[0;36m"),R=s("\x1B[1m"),h=s("\x1B[2m"),V=s("\x1B[0m")});import{existsSync as _Q}from"fs";async function Z$(){if(Y$!==void 0)return Y$;let $="/opt/homebrew/bin/python3.12";if(_Q($))return Y$=$,$;let Q=await f("python3.12");if(Q)return Y$=Q,Q;let Z=await f("python3");return Y$=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 Y$;var V$=L(()=>{d()});var e1={};b(e1,{runStatus:()=>cQ});import{existsSync as y,readFileSync as W$,readdirSync as d1,statSync as o1}from"fs";import{resolve as D,basename as CQ}from"path";import{homedir as bQ}from"os";function n1($){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 a1($,Q,Z){if(Q===0)return null;let z=Math.trunc($*100/Q),X=Math.trunc($*x$/Q);if(X>x$)X=x$;let q=x$-X,K=N;if(z>=80)K=O;else if(z>=50)K=_;let W="=".repeat(Math.max(0,X))+" ".repeat(Math.max(0,q)),J=n1($),U=n1(Q);return` ${R}${Z}${V} ${K}[${W}]${V} ${z}% (${J} / ${U})`}async function yQ(){if(await f("jq"))return!0;return process.stdout.write(`${O}Error: jq is required but not installed.${V}
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)
@@ -793,4 +793,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
793
793
  `),2}default:return process.stderr.write(`Unknown command: ${Q}
794
794
  `),process.stderr.write(t6),2}}l1();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var qZ=await KZ(Bun.argv.slice(2));process.exit(qZ);
795
795
 
796
- //# debugId=034E8130DD935D9E64756E2164756E21
796
+ //# debugId=F10A6D4C426653AC64756E2164756E21
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '7.74.0'
60
+ __version__ = '7.75.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.74.0",
4
+ "version": "7.75.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.74.0",
5
+ "version": "7.75.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",