loki-mode 7.99.0 → 7.100.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/README.md CHANGED
@@ -140,11 +140,33 @@ status check in your repository's branch-protection settings.
140
140
 
141
141
  ```bash
142
142
  bun install -g loki-mode # install (npm/brew/Docker also work, see below)
143
+ loki quickstart # one guided command: your first real build
144
+ ```
145
+
146
+ `loki quickstart` is the recommended way to start. It asks a few quick questions
147
+ (setup check, one-line idea, template pick, plan review), quotes the real
148
+ cost/time estimate before anything is spent, and then runs the build. Pressing
149
+ Enter through every step builds the sample Todo app.
150
+
151
+ Want a taste with zero key and zero spend first? Run:
152
+
153
+ ```bash
154
+ loki demo --offline # replay a real sample Evidence Receipt (no key, no spend)
155
+ ```
156
+
157
+ It replays a real past build's Evidence Receipt so you can see Loki's honest
158
+ verdict (VERIFIED / VERIFIED WITH GAPS / NOT VERIFIED) in seconds, with no
159
+ provider, no API key, no spend, and no network. It is a sample (a replay), not a
160
+ verdict on your own code.
161
+
162
+ Prefer the explicit, scriptable path? Scaffold a PRD and run the build yourself:
163
+
164
+ ```bash
143
165
  loki init my-app --template simple-todo-app # scaffold a starter PRD
144
166
  cd my-app && loki start prd.md # autonomous build from the spec
145
167
  ```
146
168
 
147
- That is the happy path. One thing to know first: Loki drives a separate coding-agent CLI (Claude Code is the recommended one) and needs it plus a couple of common tools on your PATH. Run `loki doctor` any time and it tells you exactly what is present and what is missing, with a copy-pasteable install command for each gap.
169
+ One thing to know first: Loki drives a separate coding-agent CLI (Claude Code is the recommended one) and needs it plus a couple of common tools on your PATH. Run `loki doctor` any time and it tells you exactly what is present and what is missing, with a copy-pasteable install command for each gap.
148
170
 
149
171
  ```bash
150
172
  loki doctor # check your setup before the first build
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.99.0
6
+ # Loki Mode v7.100.0
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -408,4 +408,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
408
408
 
409
409
  ---
410
410
 
411
- **v7.99.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
411
+ **v7.100.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 7.99.0
1
+ 7.100.0
package/autonomy/loki CHANGED
@@ -11380,15 +11380,23 @@ cmd_demo() {
11380
11380
  echo "runs 'loki start' to build it end-to-end. Shows a summary when done"
11381
11381
  echo "and offers to open the result in a browser."
11382
11382
  echo ""
11383
+ echo "With --offline, instead replays a bundled real Evidence Receipt so"
11384
+ echo "you can see what Loki produces with no provider, no API key, no"
11385
+ echo "spend, and no network. This is a sample (a replay of a past build),"
11386
+ echo "not a verdict on your own code."
11387
+ echo ""
11383
11388
  echo "Usage: loki demo [options]"
11384
11389
  echo ""
11385
11390
  echo "Options:"
11391
+ echo " --offline Replay a bundled sample receipt (no key, no spend)"
11386
11392
  echo " --dir PATH Use a specific directory instead of a temp dir"
11387
11393
  echo " --keep Keep the temp directory on exit (default: kept)"
11388
11394
  echo " --provider P AI provider to use (default: claude)"
11389
11395
  echo " --dry-run Show what would happen without running"
11390
11396
  echo ""
11391
11397
  echo "Examples:"
11398
+ echo " loki demo --offline # Replay a sample receipt (no spend)"
11399
+ echo " loki tour # Same as 'loki demo --offline'"
11392
11400
  echo " loki demo # Build todo app in temp dir"
11393
11401
  echo " loki demo --dir ~/demo-project # Build in specific directory"
11394
11402
  echo " loki demo --provider codex # Use Codex instead of Claude"
@@ -11411,6 +11419,7 @@ cmd_demo() {
11411
11419
  local demo_dir=""
11412
11420
  local provider=""
11413
11421
  local dry_run=false
11422
+ local offline=false
11414
11423
  local start_args=()
11415
11424
  # v7.29.0 (feature #4): demo now shows the FORCED-simple cost estimate
11416
11425
  # before spending and asks for confirmation. --yes / LOKI_ASSUME_YES skip
@@ -11459,6 +11468,10 @@ cmd_demo() {
11459
11468
  dry_run=true
11460
11469
  shift
11461
11470
  ;;
11471
+ --offline|--tour)
11472
+ offline=true
11473
+ shift
11474
+ ;;
11462
11475
  *)
11463
11476
  echo -e "${RED}Unknown option: $1${NC}"
11464
11477
  echo "Run 'loki demo --help' for usage."
@@ -11467,6 +11480,60 @@ cmd_demo() {
11467
11480
  esac
11468
11481
  done
11469
11482
 
11483
+ # Offline taste path (feature #2): replay a bundled REAL Evidence Receipt
11484
+ # with NO provider, NO key, NO spend, NO network. This is the zero-friction
11485
+ # path a newcomer can run before installing or authenticating anything, so
11486
+ # it MUST run before (and bypass) the provider_offer_gate below. It never
11487
+ # builds, never calls cmd_start, and returns in well under 10s.
11488
+ #
11489
+ # Fake-green guardrail: the headline shown is the bundled receipt's REAL
11490
+ # honest headline (rendered by the same render_evidence_receipt_md the PR
11491
+ # path uses, which is never green unless genuinely VERIFIED). It is framed
11492
+ # as a SAMPLE/replay -- never presented as a verdict on the user's code.
11493
+ if [ "$offline" = true ]; then
11494
+ local sample_proof="$SKILL_DIR/skills/fixtures/demo-receipt/proof.json"
11495
+ if [ ! -f "$sample_proof" ]; then
11496
+ echo -e "${RED}Error: bundled sample receipt not found.${NC}" >&2
11497
+ echo "Expected at: $sample_proof" >&2
11498
+ return 1
11499
+ fi
11500
+ if ! declare -f render_evidence_receipt_md >/dev/null 2>&1; then
11501
+ echo -e "${RED}Error: receipt renderer unavailable.${NC}" >&2
11502
+ echo "Expected autonomy/lib/proof-pr.sh to be sourced." >&2
11503
+ return 1
11504
+ fi
11505
+
11506
+ echo -e "${BOLD}Loki Mode v$version - Sample Receipt (offline replay)${NC}"
11507
+ echo ""
11508
+ echo -e "${YELLOW}This is a SAMPLE: a replay of a real past build, not a"
11509
+ echo -e "verdict on your code. No provider, no API key, no spend, no"
11510
+ echo -e "network were used to show it.${NC}"
11511
+ echo ""
11512
+ echo -e "${DIM}It is what Loki produces after a build: an Evidence Receipt"
11513
+ echo -e "with an honest headline you can recompute yourself.${NC}"
11514
+ echo ""
11515
+
11516
+ # Print-only renderer; always returns 0. The headline label inside is
11517
+ # the receipt's genuine honesty.headline.
11518
+ render_evidence_receipt_md "$sample_proof" "" ""
11519
+
11520
+ # The receipt prints a "verify it yourself" command. For a real build
11521
+ # that command works against .loki/proofs/. This bundled sample is for
11522
+ # display only (no local proof store entry, hash neutralized), so make
11523
+ # that explicit rather than let a newcomer run a command that fails.
11524
+ echo ""
11525
+ echo -e "${DIM}The 'verify it yourself' command above is what you run against the"
11526
+ echo -e "real builds you create (their proofs live in .loki/proofs/). This"
11527
+ echo -e "bundled sample is for display only.${NC}"
11528
+
11529
+ echo ""
11530
+ echo -e "${DIM}---------------------------------------${NC}"
11531
+ echo -e "${BOLD}That was a sample. Build your own:${NC}"
11532
+ echo -e " ${CYAN}loki quickstart${NC} # one guided command, your first real build"
11533
+ echo -e " ${CYAN}loki demo${NC} # build a bundled sample app for real"
11534
+ return 0
11535
+ fi
11536
+
11470
11537
  # v7.29.0: provider pre-flight gate (skipped for --dry-run, which never
11471
11538
  # spends). On a TTY, offer to install; on non-TTY/CI, print honest manual
11472
11539
  # instructions and exit 2 before any spend. Detect-first, so the nested
@@ -16450,6 +16517,17 @@ main() {
16450
16517
  demo)
16451
16518
  cmd_demo "$@"
16452
16519
  ;;
16520
+ tour)
16521
+ # Zero-key/zero-spend taste path: an alias for 'demo --offline' that
16522
+ # replays a bundled real Evidence Receipt. --help routes straight to
16523
+ # the demo help (which documents --offline / tour); otherwise inject
16524
+ # --offline ahead of any user args.
16525
+ if [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then
16526
+ cmd_demo --help
16527
+ else
16528
+ cmd_demo --offline "$@"
16529
+ fi
16530
+ ;;
16453
16531
  quickstart)
16454
16532
  cmd_quickstart "$@"
16455
16533
  ;;
@@ -31218,6 +31296,59 @@ PYEOF
31218
31296
  echo "Ready to post:"
31219
31297
  echo " ${hook}"
31220
31298
  fi
31299
+
31300
+ # Copy-paste "Verified by Loki" markdown badge (word-of-mouth
31301
+ # loop). The badge color + message derive ONLY from the receipt's
31302
+ # honest headline (honesty.headline), which proof-generator
31303
+ # computes deterministically from real facts and which is
31304
+ # impossible to forge from an LLM opinion. We exact-match the
31305
+ # three headline strings:
31306
+ # VERIFIED -> brightgreen "verified"
31307
+ # VERIFIED WITH GAPS -> orange "verified with gaps"
31308
+ # NOT VERIFIED -> red "not verified"
31309
+ # Anything else (missing/null/legacy proof) OMITS the badge: we
31310
+ # never paint a non-VERIFIED receipt green, and we do not force a
31311
+ # fine old-format proof red. We deliberately do NOT fall back to
31312
+ # council.final_verdict here -- that is an LLM verdict and the
31313
+ # exact fake-green path the headline exists to override.
31314
+ # The badge links to the gist we just published (no new egress).
31315
+ # shields.io static/v1 query endpoint avoids structural hyphens.
31316
+ local badge
31317
+ badge=$(LOKI_PROOF_JSON="$pj" LOKI_GIST_URL="$LOKI_LAST_GIST_URL" python3 - <<'PYEOF' 2>/dev/null || true
31318
+ import json, os
31319
+ from urllib.parse import quote
31320
+ try:
31321
+ d = json.load(open(os.environ["LOKI_PROOF_JSON"]))
31322
+ except Exception:
31323
+ d = {}
31324
+ headline = (d.get("honesty") or {}).get("headline")
31325
+ # Exact-match ONLY. council.final_verdict is intentionally NOT consulted: it is
31326
+ # an LLM opinion, the precise fake-green source the deterministic headline
31327
+ # overrides. Unmapped headline -> no entry -> badge omitted.
31328
+ mapping = {
31329
+ "VERIFIED": ("verified", "brightgreen"),
31330
+ "VERIFIED WITH GAPS": ("verified with gaps", "orange"),
31331
+ "NOT VERIFIED": ("not verified", "red"),
31332
+ }
31333
+ entry = mapping.get(headline)
31334
+ url = os.environ.get("LOKI_GIST_URL", "").strip()
31335
+ # Omit the badge for any unmapped headline (never green, never forced red),
31336
+ # and when we have no link target to point at.
31337
+ if entry and url:
31338
+ message, color = entry
31339
+ badge_img = (
31340
+ "https://img.shields.io/static/v1?label=Loki"
31341
+ "&message=" + quote(message)
31342
+ + "&color=" + color
31343
+ )
31344
+ print("[![Loki: {msg}]({img})]({url})".format(msg=message, img=badge_img, url=url))
31345
+ PYEOF
31346
+ )
31347
+ if [ -n "$badge" ]; then
31348
+ echo ""
31349
+ echo "Markdown badge (paste into a README or PR):"
31350
+ echo " ${badge}"
31351
+ fi
31221
31352
  fi
31222
31353
  exit 0
31223
31354
  ;;
package/autonomy/run.sh CHANGED
@@ -11246,6 +11246,7 @@ start_dashboard() {
11246
11246
  local original_port=$DASHBOARD_PORT
11247
11247
  local max_attempts=10
11248
11248
  local attempt=0
11249
+ local DASHBOARD_REUSED=0
11249
11250
 
11250
11251
  while lsof -i :$DASHBOARD_PORT &>/dev/null && [ $attempt -lt $max_attempts ]; do
11251
11252
  # Check if it's our own dashboard
@@ -11254,7 +11255,24 @@ start_dashboard() {
11254
11255
  # Only kill if it's a Python/uvicorn dashboard process
11255
11256
  local proc_cmd=$(ps -p "$existing_pid" -o comm= 2>/dev/null || true)
11256
11257
  if [[ "$proc_cmd" == *python* ]] || [[ "$proc_cmd" == *uvicorn* ]]; then
11257
- log_step "Killing existing dashboard on port $DASHBOARD_PORT (PID: $existing_pid)..."
11258
+ # Never kill a HEALTHY dashboard already serving here: it is almost
11259
+ # always the user's own live dashboard (open in their browser), and
11260
+ # killing it on `loki start` drops their session mid-use. A healthy
11261
+ # server is reusable by this build too, so probe /api/status and, if
11262
+ # it answers 200, REUSE it (skip starting our own). Only kill a
11263
+ # dashboard process that is NOT serving (a genuinely stuck/dead one).
11264
+ # Opt out with LOKI_DASHBOARD_FORCE_RECLAIM=1.
11265
+ local _dash_alive=""
11266
+ if [ "${LOKI_DASHBOARD_FORCE_RECLAIM:-}" != "1" ] && command -v curl >/dev/null 2>&1; then
11267
+ _dash_alive=$(curl -s -o /dev/null -w '%{http_code}' --max-time 1 \
11268
+ "http://127.0.0.1:${DASHBOARD_PORT}/api/status" 2>/dev/null || true)
11269
+ fi
11270
+ if [ "$_dash_alive" = "200" ]; then
11271
+ log_info "Reusing the healthy dashboard already serving on port $DASHBOARD_PORT (not killing it)."
11272
+ DASHBOARD_REUSED=1
11273
+ break
11274
+ fi
11275
+ log_step "Killing stuck dashboard on port $DASHBOARD_PORT (PID: $existing_pid, not serving)..."
11258
11276
  kill "$existing_pid" 2>/dev/null || true
11259
11277
  sleep 1
11260
11278
  break
@@ -11276,6 +11294,15 @@ start_dashboard() {
11276
11294
  return 1
11277
11295
  fi
11278
11296
 
11297
+ # If we found a HEALTHY dashboard already serving on the port, reuse it: do
11298
+ # NOT launch a second server (that would fight for the port / kill the user's
11299
+ # live one). The existing server already serves this project's state.
11300
+ if [ "${DASHBOARD_REUSED:-0}" = "1" ]; then
11301
+ export LOKI_DASHBOARD_PORT="$DASHBOARD_PORT"
11302
+ log_info "Dashboard already live on port $DASHBOARD_PORT; reusing it."
11303
+ return 0
11304
+ fi
11305
+
11279
11306
  # Start FastAPI dashboard server (unified UI + API)
11280
11307
  log_step "Starting unified dashboard server..."
11281
11308
  local log_file=".loki/dashboard/logs/dashboard.log"
package/completions/_loki CHANGED
@@ -125,6 +125,7 @@ function _loki_commands {
125
125
  'quickstart:Guided first build from your idea'
126
126
  'monitor:Monitor Docker Compose services with auto-fix'
127
127
  'demo:Interactive 60-second demo'
128
+ 'tour:Offline sample receipt replay (no key, no spend)'
128
129
  'init:Interactive PRD builder'
129
130
  'stop:Stop execution'
130
131
  'pause:Pause execution'
@@ -5,7 +5,7 @@ _loki_completion() {
5
5
  _init_completion || return
6
6
 
7
7
  # Main subcommands (must match autonomy/loki main case statement)
8
- local main_commands="start quick monitor demo init stop pause resume status next ship dashboard web serve api sandbox notify import github issue config provider reset memory compound checkpoint council dogfood projects enterprise secrets doctor watchdog audit metrics syslog onboard share proof explain plan report cost kpis stats test ci watch telemetry agent context ctx code run export review optimize heal modernize migrate cluster worktree wt trigger failover remote deploy docker mcp magic assets analyze compliance crash open otel preview quickstart rc rollback self-update sentrux setup-skill spec state template trust trust-metrics ultracode update verify voice why wiki bench cleanup logs grill docs cp version completions help"
8
+ local main_commands="start quick monitor demo tour init stop pause resume status next ship dashboard web serve api sandbox notify import github issue config provider reset memory compound checkpoint council dogfood projects enterprise secrets doctor watchdog audit metrics syslog onboard share proof explain plan report cost kpis stats test ci watch telemetry agent context ctx code run export review optimize heal modernize migrate cluster worktree wt trigger failover remote deploy docker mcp magic assets analyze compliance crash open otel preview quickstart rc rollback self-update sentrux setup-skill spec state template trust trust-metrics ultracode update verify voice why wiki bench cleanup logs grill docs cp version completions help"
9
9
 
10
10
  # 1. If we are on the first argument (subcommand)
11
11
  if [[ $cword -eq 1 ]]; then
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "7.99.0"
10
+ __version__ = "7.100.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.99.0
5
+ **Version:** v7.100.0
6
6
 
7
7
  ---
8
8
 
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var z8=Object.defineProperty;var X8=($)=>$;function K8($,Q){this[$]=X8.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)z8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:K8.bind(Q,Z)})};var L=($,Q)=>()=>($&&(Q=$($=0)),Q);var J$=import.meta.require;var y1={};b(y1,{lokiDir:()=>P,homeLokiDir:()=>T$,findRepoRootForVersion:()=>e$,REPO_ROOT:()=>h});import{resolve as t,dirname as i$}from"path";import{fileURLToPath as q8}from"url";import{existsSync as E$}from"fs";import{homedir as J8}from"os";function V8(){let $=h1;for(let Q=0;Q<6;Q++){if(E$(t($,"VERSION"))&&E$(t($,"autonomy/run.sh")))return $;let Z=i$($);if(Z===$)break;$=Z}return t(h1,"..","..","..")}function e$($){let Q=$;for(let Z=0;Z<6;Z++){if(E$(t(Q,"VERSION"))&&E$(t(Q,"autonomy/run.sh")))return Q;let z=i$(Q);if(z===Q)break;Q=z}return t($,"..","..","..")}function P(){return process.env.LOKI_DIR??t(process.cwd(),".loki")}function T$(){return t(J8(),".loki")}var h1,h;var C=L(()=>{h1=i$(q8(import.meta.url));h=V8()});import{readFileSync as W8}from"fs";import{resolve as U8,dirname as H8}from"path";import{fileURLToPath as G8}from"url";function N$(){if(Q$!==null)return Q$;let $="7.99.0";if(typeof $==="string"&&$.length>0)return Q$=$,Q$;try{let Q=H8(G8(import.meta.url)),Z=e$(Q);Q$=W8(U8(Z,"VERSION"),"utf-8").trim()}catch{Q$="unknown"}return Q$}var Q$=null;var $1=L(()=>{C()});var u1={};b(u1,{runOrThrow:()=>x8,run:()=>F,readStreamCapped:()=>f1,commandVersion:()=>N8,commandExists:()=>f,ShellError:()=>Q1,MAX_STDOUT_BYTES:()=>m1});async function f1($,Q=m1){let Z=$.getReader(),z=new TextDecoder,X="",q=0;try{while(q<Q){let{done:K,value:W}=await Z.read();if(K)break;if(!W)continue;if(q+=W.byteLength,q>Q){let V=W.byteLength-(q-Q);X+=z.decode(W.subarray(0,V),{stream:!0});break}X+=z.decode(W,{stream:!0})}X+=z.decode()}finally{try{await Z.cancel()}catch{}Z.releaseLock()}return X}async function F($,Q={}){let Z=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),z,X;if(Q.timeoutMs&&Q.timeoutMs>0)z=setTimeout(()=>{try{Z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{Z.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[q,K,W]=await Promise.all([f1(Z.stdout),new Response(Z.stderr).text(),Z.exited]);return{stdout:q,stderr:K,exitCode:W}}finally{if(z)clearTimeout(z);if(X)clearTimeout(X)}}async function x8($,Q={}){let Z=await F($,Q);if(Z.exitCode!==0)throw new Q1(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function f($){let Q=E8($),Z=await F(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function E8($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function N8($,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 m1=16777216,Q1;var d=L(()=>{Q1=class Q1 extends Error{message;exitCode;stdout;stderr;constructor($,Q,Z,z){super($);this.message=$;this.exitCode=Q;this.stdout=Z;this.stderr=z;this.name="ShellError"}}});function r($){return S8?"":$}var S8,T,S,_,lZ,I,k,y,J;var c=L(()=>{S8=(process.env.NO_COLOR??"").length>0;T=r("\x1B[0;31m"),S=r("\x1B[0;32m"),_=r("\x1B[1;33m"),lZ=r("\x1B[0;34m"),I=r("\x1B[0;36m"),k=r("\x1B[1m"),y=r("\x1B[2m"),J=r("\x1B[0m")});import{existsSync as u8}from"fs";async function Z$(){if(A$!==void 0)return A$;let $="/opt/homebrew/bin/python3.12";if(u8($))return A$=$,$;let Q=await f("python3.12");if(Q)return A$=Q,Q;let Z=await f("python3");return A$=Z,Z}async function z$($,Q={}){let Z=await Z$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return F([Z,"-c",$],Q)}var A$;var V$=L(()=>{d()});var q0={};b(q0,{runStatus:()=>W3});import{existsSync as v,readFileSync as U$,readdirSync as i1,statSync as e1}from"fs";import{resolve as D,basename as $3}from"path";import{homedir as Q3}from"os";function $0($){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 Q0($,Q,Z){if(Q===0)return null;let z=Math.trunc($*100/Q),X=Math.trunc($*D$/Q);if(X>D$)X=D$;let q=D$-X,K=S;if(z>=80)K=T;else if(z>=50)K=_;let W="=".repeat(Math.max(0,X))+" ".repeat(Math.max(0,q)),V=$0($),U=$0(Q);return` ${k}${Z}${J} ${K}[${W}]${J} ${z}% (${V} / ${U})`}async function z3(){if(await f("jq"))return!0;return process.stdout.write(`${T}Error: jq is required but not installed.${J}
2
+ var z8=Object.defineProperty;var X8=($)=>$;function K8($,Q){this[$]=X8.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)z8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:K8.bind(Q,Z)})};var L=($,Q)=>()=>($&&(Q=$($=0)),Q);var J$=import.meta.require;var y1={};b(y1,{lokiDir:()=>P,homeLokiDir:()=>T$,findRepoRootForVersion:()=>e$,REPO_ROOT:()=>h});import{resolve as t,dirname as i$}from"path";import{fileURLToPath as q8}from"url";import{existsSync as E$}from"fs";import{homedir as J8}from"os";function V8(){let $=h1;for(let Q=0;Q<6;Q++){if(E$(t($,"VERSION"))&&E$(t($,"autonomy/run.sh")))return $;let Z=i$($);if(Z===$)break;$=Z}return t(h1,"..","..","..")}function e$($){let Q=$;for(let Z=0;Z<6;Z++){if(E$(t(Q,"VERSION"))&&E$(t(Q,"autonomy/run.sh")))return Q;let z=i$(Q);if(z===Q)break;Q=z}return t($,"..","..","..")}function P(){return process.env.LOKI_DIR??t(process.cwd(),".loki")}function T$(){return t(J8(),".loki")}var h1,h;var C=L(()=>{h1=i$(q8(import.meta.url));h=V8()});import{readFileSync as W8}from"fs";import{resolve as U8,dirname as H8}from"path";import{fileURLToPath as G8}from"url";function N$(){if(Q$!==null)return Q$;let $="7.100.0";if(typeof $==="string"&&$.length>0)return Q$=$,Q$;try{let Q=H8(G8(import.meta.url)),Z=e$(Q);Q$=W8(U8(Z,"VERSION"),"utf-8").trim()}catch{Q$="unknown"}return Q$}var Q$=null;var $1=L(()=>{C()});var u1={};b(u1,{runOrThrow:()=>x8,run:()=>F,readStreamCapped:()=>f1,commandVersion:()=>N8,commandExists:()=>f,ShellError:()=>Q1,MAX_STDOUT_BYTES:()=>m1});async function f1($,Q=m1){let Z=$.getReader(),z=new TextDecoder,X="",q=0;try{while(q<Q){let{done:K,value:W}=await Z.read();if(K)break;if(!W)continue;if(q+=W.byteLength,q>Q){let V=W.byteLength-(q-Q);X+=z.decode(W.subarray(0,V),{stream:!0});break}X+=z.decode(W,{stream:!0})}X+=z.decode()}finally{try{await Z.cancel()}catch{}Z.releaseLock()}return X}async function F($,Q={}){let Z=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),z,X;if(Q.timeoutMs&&Q.timeoutMs>0)z=setTimeout(()=>{try{Z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{Z.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[q,K,W]=await Promise.all([f1(Z.stdout),new Response(Z.stderr).text(),Z.exited]);return{stdout:q,stderr:K,exitCode:W}}finally{if(z)clearTimeout(z);if(X)clearTimeout(X)}}async function x8($,Q={}){let Z=await F($,Q);if(Z.exitCode!==0)throw new Q1(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function f($){let Q=E8($),Z=await F(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function E8($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function N8($,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 m1=16777216,Q1;var d=L(()=>{Q1=class Q1 extends Error{message;exitCode;stdout;stderr;constructor($,Q,Z,z){super($);this.message=$;this.exitCode=Q;this.stdout=Z;this.stderr=z;this.name="ShellError"}}});function r($){return S8?"":$}var S8,T,S,_,lZ,I,k,y,J;var c=L(()=>{S8=(process.env.NO_COLOR??"").length>0;T=r("\x1B[0;31m"),S=r("\x1B[0;32m"),_=r("\x1B[1;33m"),lZ=r("\x1B[0;34m"),I=r("\x1B[0;36m"),k=r("\x1B[1m"),y=r("\x1B[2m"),J=r("\x1B[0m")});import{existsSync as u8}from"fs";async function Z$(){if(A$!==void 0)return A$;let $="/opt/homebrew/bin/python3.12";if(u8($))return A$=$,$;let Q=await f("python3.12");if(Q)return A$=Q,Q;let Z=await f("python3");return A$=Z,Z}async function z$($,Q={}){let Z=await Z$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return F([Z,"-c",$],Q)}var A$;var V$=L(()=>{d()});var q0={};b(q0,{runStatus:()=>W3});import{existsSync as v,readFileSync as U$,readdirSync as i1,statSync as e1}from"fs";import{resolve as D,basename as $3}from"path";import{homedir as Q3}from"os";function $0($){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 Q0($,Q,Z){if(Q===0)return null;let z=Math.trunc($*100/Q),X=Math.trunc($*D$/Q);if(X>D$)X=D$;let q=D$-X,K=S;if(z>=80)K=T;else if(z>=50)K=_;let W="=".repeat(Math.max(0,X))+" ".repeat(Math.max(0,q)),V=$0($),U=$0(Q);return` ${k}${Z}${J} ${K}[${W}]${J} ${z}% (${V} / ${U})`}async function z3(){if(await f("jq"))return!0;return process.stdout.write(`${T}Error: jq is required but not installed.${J}
3
3
  `),process.stdout.write(`Install with:
4
4
  `),process.stdout.write(` brew install jq (macOS)
5
5
  `),process.stdout.write(` apt install jq (Debian/Ubuntu)
@@ -802,4 +802,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
802
802
  `),2}default:return process.stderr.write(`Unknown command: ${Q}
803
803
  `),process.stderr.write(Z8),2}}r1();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var FZ=await jZ(Bun.argv.slice(2));process.exit(FZ);
804
804
 
805
- //# debugId=2654E1AA878C0D9664756E2164756E21
805
+ //# debugId=D8F1E717478FB03A64756E2164756E21
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '7.99.0'
60
+ __version__ = '7.100.0'
@@ -856,12 +856,24 @@ class MemoryRetrieval:
856
856
  return self.retrieve_by_keyword(
857
857
  query.split(), collection)[:top_k]
858
858
 
859
- # Generate query embedding
860
- query_embedding = self.embedding_engine.embed(query)
861
-
862
- # Search vector index
859
+ # Generate query embedding and search the vector index. If the embedding
860
+ # engine has fallen back to a different model/dimension since the index
861
+ # was built, the query vector dimension will not match the stored index
862
+ # and VectorSearchIndex.search raises ValueError. That must NOT crash
863
+ # retrieval or return wrong-dimension neighbors -- degrade to keyword
864
+ # search (the honest, accurate fallback), same as the staleness path.
863
865
  index = self.vector_indices[collection]
864
- results = index.search(query_embedding, top_k)
866
+ try:
867
+ query_embedding = self.embedding_engine.embed(query)
868
+ results = index.search(query_embedding, top_k)
869
+ except ValueError as exc:
870
+ logger.info(
871
+ "%s vector search failed (%s); likely an embedding "
872
+ "dimension change since index build. Falling back to keyword "
873
+ "search for accuracy.",
874
+ collection, exc,
875
+ )
876
+ return self.retrieve_by_keyword(query.split(), collection)[:top_k]
865
877
 
866
878
  # Convert to standard format
867
879
  items: List[Dict[str, Any]] = []
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.99.0",
4
+ "version": "7.100.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.99.0",
5
+ "version": "7.100.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",
@@ -0,0 +1,18 @@
1
+ # Demo Evidence Receipt fixture
2
+
3
+ `proof.json` here is a real Loki Mode Evidence Receipt, copied from an actual
4
+ build and sanitized (no machine paths, no secrets). It is bundled so that
5
+ `loki demo --offline` (and `loki tour`) can show a newcomer a real receipt with
6
+ zero provider, zero key, zero spend, and zero network.
7
+
8
+ What is real: the headline (`VERIFIED WITH GAPS`), the test result, the security
9
+ findings, the cost, and the diff stats are the genuine recorded results of that
10
+ build.
11
+
12
+ What is changed for sanitization: `run_id`, `loki_version`, timestamps, the
13
+ spec brief, and `verification.hash` are neutralized. `verification.hash` is set
14
+ to `sample-not-reproducible` on purpose so this replay fixture is never mistaken
15
+ for a locally-reproducible run. It is for display only.
16
+
17
+ This is a SAMPLE (a replay of a past real build), not a verdict on the user's
18
+ own code. The CLI labels it as such.
@@ -0,0 +1,166 @@
1
+ {
2
+ "schema_version": "1.1",
3
+ "run_id": "run-sample-demo-replay",
4
+ "generated_at": "2026-06-27T17:21:37Z",
5
+ "loki_version": "sample",
6
+ "started_at": "2026-06-27T16:42:44Z",
7
+ "wall_clock_sec": 2333,
8
+ "spec": {
9
+ "source": "./.loki/generated-prd.md",
10
+ "brief": "Sample Evidence Receipt bundled with Loki Mode for the offline demo (loki demo --offline / loki tour). Copied from a real build and sanitized: no machine paths, no secrets. The verification facts below (tests, security, cost, diff) are the real recorded results of that build. This file is for replay/display only."
11
+ },
12
+ "provider": {
13
+ "name": "claude",
14
+ "model": "opus"
15
+ },
16
+ "iterations": {
17
+ "count": 3,
18
+ "succeeded": 3,
19
+ "failed": 0
20
+ },
21
+ "files_changed": {
22
+ "count": 8,
23
+ "insertions": 1413,
24
+ "deletions": 35,
25
+ "files": [
26
+ { "path": "API.md", "insertions": 227, "deletions": 0, "status": "modified" },
27
+ { "path": "ARCHITECTURE.md", "insertions": 197, "deletions": 0, "status": "modified" },
28
+ { "path": "CLAUDE.md", "insertions": 94, "deletions": 0, "status": "modified" },
29
+ { "path": "COMPONENTS.md", "insertions": 206, "deletions": 0, "status": "modified" },
30
+ { "path": "DECISIONS.md", "insertions": 146, "deletions": 0, "status": "modified" },
31
+ { "path": "README.md", "insertions": 157, "deletions": 35, "status": "modified" },
32
+ { "path": "SETUP.md", "insertions": 236, "deletions": 0, "status": "modified" },
33
+ { "path": "TESTING.md", "insertions": 150, "deletions": 0, "status": "modified" }
34
+ ]
35
+ },
36
+ "diffs": null,
37
+ "council": {
38
+ "enabled": true,
39
+ "final_verdict": "",
40
+ "threshold": null,
41
+ "reviewers": [],
42
+ "findings_link": null
43
+ },
44
+ "quality_gates": {
45
+ "passed": 0,
46
+ "total": 0,
47
+ "gates": []
48
+ },
49
+ "cost": {
50
+ "usd": 10.3218,
51
+ "input_tokens": 18484,
52
+ "output_tokens": 43491,
53
+ "cache_read_tokens": 5868340,
54
+ "cache_creation_tokens": 246851
55
+ },
56
+ "deployment": {
57
+ "deployed_url": null,
58
+ "public_url": null
59
+ },
60
+ "facts": {
61
+ "git": {
62
+ "base_sha": "255d91cf3d688a42a6e9a5f0e1c042ecfc29de0c",
63
+ "head_sha": "a42cb14ae996eb7459c81b7a43c5686f3612072e",
64
+ "diff": {
65
+ "count": 8,
66
+ "insertions": 1413,
67
+ "deletions": 35,
68
+ "files": [
69
+ { "path": "API.md", "insertions": 227, "deletions": 0, "status": "modified" },
70
+ { "path": "ARCHITECTURE.md", "insertions": 197, "deletions": 0, "status": "modified" },
71
+ { "path": "CLAUDE.md", "insertions": 94, "deletions": 0, "status": "modified" },
72
+ { "path": "COMPONENTS.md", "insertions": 206, "deletions": 0, "status": "modified" },
73
+ { "path": "DECISIONS.md", "insertions": 146, "deletions": 0, "status": "modified" },
74
+ { "path": "README.md", "insertions": 157, "deletions": 35, "status": "modified" },
75
+ { "path": "SETUP.md", "insertions": 236, "deletions": 0, "status": "modified" },
76
+ { "path": "TESTING.md", "insertions": 150, "deletions": 0, "status": "modified" }
77
+ ]
78
+ },
79
+ "diff_sha256": "c2be6fff3e774c387f276277b25fc424f07b667b0f2c73d8804f954f37bd1356"
80
+ },
81
+ "build": {
82
+ "command": "",
83
+ "exit_code": null,
84
+ "ran": false,
85
+ "duration_sec": null,
86
+ "status": "not_run"
87
+ },
88
+ "tests": {
89
+ "runner": "node-test",
90
+ "command": "node-test",
91
+ "exit_code": 0,
92
+ "passed_count": null,
93
+ "failed_count": null,
94
+ "status": "verified",
95
+ "duration_sec": null
96
+ },
97
+ "quality_gates": [],
98
+ "security": {
99
+ "ran": true,
100
+ "total": 2,
101
+ "active": 2,
102
+ "waived": 0,
103
+ "high_active": 0,
104
+ "status": "findings",
105
+ "findings": [
106
+ { "rule": "cors-wildcard-credentials", "severity": "MEDIUM", "waived": false },
107
+ { "rule": "cors-wildcard-credentials", "severity": "MEDIUM", "waived": false }
108
+ ]
109
+ },
110
+ "cost": {
111
+ "usd": 10.3218,
112
+ "input_tokens": 18484,
113
+ "output_tokens": 43491,
114
+ "cache_read_tokens": 5868340,
115
+ "cache_creation_tokens": 246851
116
+ },
117
+ "meta": {
118
+ "run_id": "run-sample-demo-replay",
119
+ "loki_version": "sample",
120
+ "provider": "claude",
121
+ "model": "opus",
122
+ "started_at": "2026-06-27T16:42:44Z",
123
+ "generated_at": "2026-06-27T17:21:37Z",
124
+ "wall_clock_sec": 2333
125
+ }
126
+ },
127
+ "assessments": {
128
+ "_note": "AI judgment, not deterministic proof",
129
+ "council": {
130
+ "enabled": true,
131
+ "final_verdict": "",
132
+ "threshold": null,
133
+ "reviewers": [],
134
+ "findings_link": null
135
+ },
136
+ "completion_claim": {
137
+ "claimed": true,
138
+ "evidence_gate_verdict": "pass"
139
+ }
140
+ },
141
+ "honesty": {
142
+ "headline": "VERIFIED WITH GAPS",
143
+ "degraded": [
144
+ {
145
+ "item": "build",
146
+ "status": "not_run",
147
+ "reason": "build not run"
148
+ }
149
+ ],
150
+ "evidence_gate": {
151
+ "ran": true,
152
+ "verdict": "pass",
153
+ "baseline_established": true
154
+ }
155
+ },
156
+ "redaction": {
157
+ "applied": false,
158
+ "rules_version": "1.0",
159
+ "redactions_count": 0
160
+ },
161
+ "verification": {
162
+ "hash": "sample-not-reproducible",
163
+ "algo": "sha256",
164
+ "scope": "integrity"
165
+ }
166
+ }