loki-mode 9.23.1 → 9.24.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 v9.23.1
6
+ # Loki Mode v9.24.0
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -470,4 +470,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
470
470
 
471
471
  ---
472
472
 
473
- **v9.23.1 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
473
+ **v9.24.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 9.23.1
1
+ 9.24.0
package/autonomy/run.sh CHANGED
@@ -16802,7 +16802,32 @@ start_dashboard() {
16802
16802
  return 1
16803
16803
  fi
16804
16804
 
16805
- sleep 2
16805
+ # Wait for the dashboard to come up, but only as long as it actually takes.
16806
+ # This was a flat `sleep 2` on the critical path of every build, before the
16807
+ # first iteration, spent entirely on a process that is typically serving in
16808
+ # a fraction of that. Poll the endpoint the reuse path above already trusts
16809
+ # (/api/status), so this returns the moment the server really serves rather
16810
+ # than on a fixed guess.
16811
+ #
16812
+ # The floor is deliberate and NOT an optimization target. A process that
16813
+ # starts and then dies at t=1.5s would pass an early `kill -0` where the old
16814
+ # flat sleep would have caught it, so polling alone would trade a real
16815
+ # liveness check for a second of wall clock. We keep polling until the
16816
+ # endpoint answers AND require the process to still be alive at the end,
16817
+ # which is strictly stronger than the old single check at t=2s.
16818
+ _dash_ready=0
16819
+ for _ in $(seq 1 40); do
16820
+ kill -0 "$DASHBOARD_PID" 2>/dev/null || break
16821
+ if curl -fsS -m 1 "http://127.0.0.1:${DASHBOARD_PORT}/api/status" >/dev/null 2>&1; then
16822
+ _dash_ready=1
16823
+ break
16824
+ fi
16825
+ sleep 0.05
16826
+ done
16827
+ # A server that never answered still gets the original grace period: some
16828
+ # environments have no curl, and the endpoint is not the only thing that
16829
+ # makes a dashboard useful. Falling back keeps behavior identical there.
16830
+ [ "$_dash_ready" = "1" ] || sleep 2
16806
16831
 
16807
16832
  if kill -0 "$DASHBOARD_PID" 2>/dev/null; then
16808
16833
  DASHBOARD_LAST_ALIVE=$(date +%s)
@@ -26073,6 +26098,18 @@ main() {
26073
26098
  exit 1
26074
26099
  fi
26075
26100
 
26101
+ # BOOT WINDOW (v9.24.0). Everything from here to setup_agent_branch is
26102
+ # pre-loop setup: prerequisites, provider detection, complexity detection,
26103
+ # dashboard start, branch setup. None of it was timed, and none of the nine
26104
+ # existing emit_stage_complete sites lies outside the iteration body -- so
26105
+ # on the one profiled build, stage_total_s summed to 723s against a 960s
26106
+ # wall clock and 237s (25%) was attributed by GUESS, not measurement
26107
+ # (benchmarks/results/gate-profile.json). The guess named "rsync of the
26108
+ # engine copy", which measurement later falsified: the only rsync in the
26109
+ # repo is in the benchmark harness, costs 1.09s, and runs before the timer
26110
+ # starts. Bracket the window instead of arguing about it.
26111
+ _boot_t0=$(date +%s 2>/dev/null)
26112
+
26076
26113
  # Check prerequisites (unless skipped)
26077
26114
  if [ "$SKIP_PREREQS" != "true" ]; then
26078
26115
  if ! check_prerequisites; then
@@ -26288,6 +26325,11 @@ main() {
26288
26325
  # Setup agent branch protection (isolates agent changes to a feature branch)
26289
26326
  setup_agent_branch
26290
26327
 
26328
+ # Close the boot window. Purely additive: emit_stage_complete appends one
26329
+ # event line and swallows every error, so it cannot alter a gate verdict or
26330
+ # control flow (see its contract at run.sh:2523-2531).
26331
+ emit_stage_complete "boot" "pass" "$_boot_t0"
26332
+
26291
26333
  # Log session start for audit
26292
26334
  audit_log "SESSION_START" "prd=$PRD_PATH,dashboard=$ENABLE_DASHBOARD,staged_autonomy=$STAGED_AUTONOMY,parallel=$PARALLEL_MODE"
26293
26335
  audit_agent_action "session_start" "Session started" "prd=$PRD_PATH,provider=${PROVIDER_NAME:-claude}"
@@ -26384,6 +26426,11 @@ main() {
26384
26426
  # a stuck "Planning" state.
26385
26427
  _advance_current_phase "BUILDING"
26386
26428
  run_autonomous "$PRD_PATH" || result=$?
26429
+ # TEARDOWN WINDOW (v9.24.0): the other half of the unmeasured 237s.
26430
+ # Everything after the loop -- pre-edit snapshot, commit, handoff and
26431
+ # learnings writers, proof generation, metrics aggregation -- runs here
26432
+ # and was never timed. Opened immediately so nothing below is missed.
26433
+ _teardown_t0=$(date +%s 2>/dev/null)
26387
26434
  # PRE-EDIT SNAPSHOT: freeze the agent's raw diff HERE, the first
26388
26435
  # instruction after the loop returns, because everything below this line
26389
26436
  # can change the tree -- commit_session_changes commits the work (after
@@ -26578,6 +26625,14 @@ except Exception:
26578
26625
  generate_proof_of_run "$result" || true
26579
26626
  fi
26580
26627
 
26628
+ # Close the teardown window here rather than after cleanup: everything below
26629
+ # is process reaping and file removal, while everything above is the work a
26630
+ # user waits on (commit, PR, summary, proof). Emitting before cleanup also
26631
+ # guarantees the event is written even if a later reap kills this shell.
26632
+ if [ -n "${_teardown_t0:-}" ]; then
26633
+ emit_stage_complete "teardown" "pass" "$_teardown_t0"
26634
+ fi
26635
+
26581
26636
  # Cleanup
26582
26637
  if type app_runner_cleanup &>/dev/null; then
26583
26638
  app_runner_cleanup
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "9.23.1"
10
+ __version__ = "9.24.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:** v9.23.1
5
+ **Version:** v9.24.0
6
6
 
7
7
  ---
8
8
 
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var Rt=Object.create;var{getPrototypeOf:It,defineProperty:OG,getOwnPropertyNames:wt}=Object;var Pt=Object.prototype.hasOwnProperty;function Et($){return this[$]}var xt,kt,St=($,Q,X)=>{var Z=$!=null&&typeof $==="object";if(Z){var K=Q?xt??=new WeakMap:kt??=new WeakMap,z=K.get($);if(z)return z}X=$!=null?Rt(It($)):{};let J=Q||!$||!$.__esModule?OG(X,"default",{value:$,enumerable:!0}):X;for(let V of wt($))if(!Pt.call(J,V))OG(J,V,{get:Et.bind($,V),enumerable:!0});if(Z)K.set($,J);return J};var XV=($,Q)=>()=>(Q||$((Q={exports:{}}).exports,Q),Q.exports);var yt=($)=>$;function bt($,Q){this[$]=yt.bind(null,Q)}var B1=($,Q)=>{for(var X in Q)OG($,X,{get:Q[X],enumerable:!0,configurable:!0,set:bt.bind(Q,X)})};var s=($,Q)=>()=>($&&(Q=$($=0)),Q);var w5=import.meta.require;var zR={};B1(zR,{lokiDir:()=>h0,homeLokiDir:()=>YX,findRepoRootForVersion:()=>LG,REPO_ROOT:()=>L1});import{resolve as h2,dirname as jG}from"path";import{fileURLToPath as _t}from"url";import{existsSync as ZV}from"fs";import{homedir as ft}from"os";function vt(){let $=KR;for(let Q=0;Q<6;Q++){if(ZV(h2($,"VERSION"))&&ZV(h2($,"autonomy/run.sh")))return $;let X=jG($);if(X===$)break;$=X}return h2(KR,"..","..","..")}function LG($){let Q=$;for(let X=0;X<6;X++){if(ZV(h2(Q,"VERSION"))&&ZV(h2(Q,"autonomy/run.sh")))return Q;let Z=jG(Q);if(Z===Q)break;Q=Z}return h2($,"..","..","..")}function h0(){return process.env.LOKI_DIR??h2(process.cwd(),".loki")}function YX(){return h2(ft(),".loki")}var KR,L1;var k1=s(()=>{KR=jG(_t(import.meta.url));L1=vt()});import{readFileSync as ht}from"fs";import{resolve as gt,dirname as mt}from"path";import{fileURLToPath as ut}from"url";function j9(){if(n3!==null)return n3;let $="9.23.1";if(typeof $==="string"&&$.length>0)return n3=$,n3;try{let Q=mt(ut(import.meta.url)),X=LG(Q);n3=ht(gt(X,"VERSION"),"utf-8").trim()}catch{n3="unknown"}return n3}var n3=null;var KV=s(()=>{k1()});var WR={};B1(WR,{runOrThrow:()=>Ze,run:()=>$1,readStreamCapped:()=>zV,commandVersion:()=>ze,commandExists:()=>g5,ShellError:()=>AG,MAX_STDOUT_BYTES:()=>qR});async function zV($,Q=qR){let X=$.getReader(),Z=new TextDecoder,K="",z=0;try{while(z<Q){let{done:J,value:V}=await X.read();if(J)break;if(!V)continue;if(z+=V.byteLength,z>Q){let q=V.byteLength-(z-Q);K+=Z.decode(V.subarray(0,q),{stream:!0});break}K+=Z.decode(V,{stream:!0})}K+=Z.decode()}finally{try{await X.cancel()}catch{}X.releaseLock()}return K}async function $1($,Q={}){let X=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),Z,K;if(Q.timeoutMs&&Q.timeoutMs>0)Z=setTimeout(()=>{try{X.kill("SIGTERM")}catch{}K=setTimeout(()=>{try{X.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[z,J,V]=await Promise.all([zV(X.stdout),new Response(X.stderr).text(),X.exited]);return{stdout:z,stderr:J,exitCode:V}}finally{if(Z)clearTimeout(Z);if(K)clearTimeout(K)}}async function Ze($,Q={}){let X=await $1($,Q);if(X.exitCode!==0)throw new AG(`command failed (${X.exitCode}): ${$.join(" ")}`,X.exitCode,X.stdout,X.stderr);return X}async function g5($){let Q=Ke($),X=await $1(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(X.exitCode===0)return X.stdout.trim()||null;return null}function Ke($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function ze($,Q="--version"){if(!await g5($))return null;let Z=await $1([$,Q],{timeoutMs:5000});if(Z.exitCode!==0)return null;return((Z.stdout||Z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var qR=16777216,AG;var S8=s(()=>{AG=class AG extends Error{message;exitCode;stdout;stderr;constructor($,Q,X,Z){super($);this.message=$;this.exitCode=Q;this.stdout=X;this.stderr=Z;this.name="ShellError"}}});function g2($){return Je?"":$}var Je,p0,$5,V1,J61,A1,_1,m5,r;var t7=s(()=>{Je=(process.env.NO_COLOR??"").length>0;p0=g2("\x1B[0;31m"),$5=g2("\x1B[0;32m"),V1=g2("\x1B[1;33m"),J61=g2("\x1B[0;34m"),A1=g2("\x1B[0;36m"),_1=g2("\x1B[1m"),m5=g2("\x1B[2m"),r=g2("\x1B[0m")});import{existsSync as Ne}from"fs";async function K2(){if(HX!==void 0)return HX;let $="/opt/homebrew/bin/python3.12";if(Ne($))return HX=$,$;let Q=await g5("python3.12");if(Q)return HX=Q,Q;let X=await g5("python3");return HX=X,X}async function I4($,Q={}){let X=await K2();if(!X)return{stdout:"",stderr:"python3 not found",exitCode:127};return $1([X,"-c",$],Q)}var HX;var m2=s(()=>{S8()});var ER={};B1(ER,{runStatus:()=>ue});import{existsSync as u5,readFileSync as A9,readdirSync as TR,statSync as CR}from"fs";import{resolve as A5,basename as Se}from"path";import{homedir as ye}from"os";function DR($){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 FR($,Q,X){if(Q===0)return null;let Z=Math.trunc($*100/Q),K=Math.trunc($*VV/Q);if(K>VV)K=VV;let z=VV-K,J=$5;if(Z>=80)J=p0;else if(Z>=50)J=V1;let V="=".repeat(Math.max(0,K))+" ".repeat(Math.max(0,z)),q=DR($),W=DR(Q);return` ${_1}${X}${r} ${J}[${V}]${r} ${Z}% (${q} / ${W})`}async function _e(){if(await g5("jq"))return!0;return process.stdout.write(`${p0}Error: jq is required but not installed.${r}
2
+ var Rt=Object.create;var{getPrototypeOf:It,defineProperty:OG,getOwnPropertyNames:wt}=Object;var Pt=Object.prototype.hasOwnProperty;function Et($){return this[$]}var xt,kt,St=($,Q,X)=>{var Z=$!=null&&typeof $==="object";if(Z){var K=Q?xt??=new WeakMap:kt??=new WeakMap,z=K.get($);if(z)return z}X=$!=null?Rt(It($)):{};let J=Q||!$||!$.__esModule?OG(X,"default",{value:$,enumerable:!0}):X;for(let V of wt($))if(!Pt.call(J,V))OG(J,V,{get:Et.bind($,V),enumerable:!0});if(Z)K.set($,J);return J};var XV=($,Q)=>()=>(Q||$((Q={exports:{}}).exports,Q),Q.exports);var yt=($)=>$;function bt($,Q){this[$]=yt.bind(null,Q)}var B1=($,Q)=>{for(var X in Q)OG($,X,{get:Q[X],enumerable:!0,configurable:!0,set:bt.bind(Q,X)})};var s=($,Q)=>()=>($&&(Q=$($=0)),Q);var w5=import.meta.require;var zR={};B1(zR,{lokiDir:()=>h0,homeLokiDir:()=>YX,findRepoRootForVersion:()=>LG,REPO_ROOT:()=>L1});import{resolve as h2,dirname as jG}from"path";import{fileURLToPath as _t}from"url";import{existsSync as ZV}from"fs";import{homedir as ft}from"os";function vt(){let $=KR;for(let Q=0;Q<6;Q++){if(ZV(h2($,"VERSION"))&&ZV(h2($,"autonomy/run.sh")))return $;let X=jG($);if(X===$)break;$=X}return h2(KR,"..","..","..")}function LG($){let Q=$;for(let X=0;X<6;X++){if(ZV(h2(Q,"VERSION"))&&ZV(h2(Q,"autonomy/run.sh")))return Q;let Z=jG(Q);if(Z===Q)break;Q=Z}return h2($,"..","..","..")}function h0(){return process.env.LOKI_DIR??h2(process.cwd(),".loki")}function YX(){return h2(ft(),".loki")}var KR,L1;var k1=s(()=>{KR=jG(_t(import.meta.url));L1=vt()});import{readFileSync as ht}from"fs";import{resolve as gt,dirname as mt}from"path";import{fileURLToPath as ut}from"url";function j9(){if(n3!==null)return n3;let $="9.24.0";if(typeof $==="string"&&$.length>0)return n3=$,n3;try{let Q=mt(ut(import.meta.url)),X=LG(Q);n3=ht(gt(X,"VERSION"),"utf-8").trim()}catch{n3="unknown"}return n3}var n3=null;var KV=s(()=>{k1()});var WR={};B1(WR,{runOrThrow:()=>Ze,run:()=>$1,readStreamCapped:()=>zV,commandVersion:()=>ze,commandExists:()=>g5,ShellError:()=>AG,MAX_STDOUT_BYTES:()=>qR});async function zV($,Q=qR){let X=$.getReader(),Z=new TextDecoder,K="",z=0;try{while(z<Q){let{done:J,value:V}=await X.read();if(J)break;if(!V)continue;if(z+=V.byteLength,z>Q){let q=V.byteLength-(z-Q);K+=Z.decode(V.subarray(0,q),{stream:!0});break}K+=Z.decode(V,{stream:!0})}K+=Z.decode()}finally{try{await X.cancel()}catch{}X.releaseLock()}return K}async function $1($,Q={}){let X=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),Z,K;if(Q.timeoutMs&&Q.timeoutMs>0)Z=setTimeout(()=>{try{X.kill("SIGTERM")}catch{}K=setTimeout(()=>{try{X.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[z,J,V]=await Promise.all([zV(X.stdout),new Response(X.stderr).text(),X.exited]);return{stdout:z,stderr:J,exitCode:V}}finally{if(Z)clearTimeout(Z);if(K)clearTimeout(K)}}async function Ze($,Q={}){let X=await $1($,Q);if(X.exitCode!==0)throw new AG(`command failed (${X.exitCode}): ${$.join(" ")}`,X.exitCode,X.stdout,X.stderr);return X}async function g5($){let Q=Ke($),X=await $1(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(X.exitCode===0)return X.stdout.trim()||null;return null}function Ke($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function ze($,Q="--version"){if(!await g5($))return null;let Z=await $1([$,Q],{timeoutMs:5000});if(Z.exitCode!==0)return null;return((Z.stdout||Z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var qR=16777216,AG;var S8=s(()=>{AG=class AG extends Error{message;exitCode;stdout;stderr;constructor($,Q,X,Z){super($);this.message=$;this.exitCode=Q;this.stdout=X;this.stderr=Z;this.name="ShellError"}}});function g2($){return Je?"":$}var Je,p0,$5,V1,J61,A1,_1,m5,r;var t7=s(()=>{Je=(process.env.NO_COLOR??"").length>0;p0=g2("\x1B[0;31m"),$5=g2("\x1B[0;32m"),V1=g2("\x1B[1;33m"),J61=g2("\x1B[0;34m"),A1=g2("\x1B[0;36m"),_1=g2("\x1B[1m"),m5=g2("\x1B[2m"),r=g2("\x1B[0m")});import{existsSync as Ne}from"fs";async function K2(){if(HX!==void 0)return HX;let $="/opt/homebrew/bin/python3.12";if(Ne($))return HX=$,$;let Q=await g5("python3.12");if(Q)return HX=Q,Q;let X=await g5("python3");return HX=X,X}async function I4($,Q={}){let X=await K2();if(!X)return{stdout:"",stderr:"python3 not found",exitCode:127};return $1([X,"-c",$],Q)}var HX;var m2=s(()=>{S8()});var ER={};B1(ER,{runStatus:()=>ue});import{existsSync as u5,readFileSync as A9,readdirSync as TR,statSync as CR}from"fs";import{resolve as A5,basename as Se}from"path";import{homedir as ye}from"os";function DR($){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 FR($,Q,X){if(Q===0)return null;let Z=Math.trunc($*100/Q),K=Math.trunc($*VV/Q);if(K>VV)K=VV;let z=VV-K,J=$5;if(Z>=80)J=p0;else if(Z>=50)J=V1;let V="=".repeat(Math.max(0,K))+" ".repeat(Math.max(0,z)),q=DR($),W=DR(Q);return` ${_1}${X}${r} ${J}[${V}]${r} ${Z}% (${q} / ${W})`}async function _e(){if(await g5("jq"))return!0;return process.stdout.write(`${p0}Error: jq is required but not installed.${r}
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)
@@ -1334,4 +1334,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
1334
1334
  `),2}case"start":{let{runStart:Z}=await Promise.resolve().then(() => (Ct(),Tt));return Z(X)}default:return process.stderr.write(`Unknown command: ${Q}
1335
1335
  `),process.stderr.write(Dt),2}}LR();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var d31=await u31(Bun.argv.slice(2));process.exit(d31);
1336
1336
 
1337
- //# debugId=BF5FA03E78AE076A8F008979132F0AEE
1337
+ //# debugId=D6CD96E410267035372803342475FCE7
package/mcp/__init__.py CHANGED
@@ -75,4 +75,4 @@ try:
75
75
  except ImportError:
76
76
  __all__ = ['mcp']
77
77
 
78
- __version__ = '9.23.1'
78
+ __version__ = '9.24.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": "9.23.1",
4
+ "version": "9.24.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, opencode).",
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": "9.23.1",
5
+ "version": "9.24.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",