loki-mode 7.6.0 → 7.6.2

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: Multi-agent autonomous startup system. Triggers on "Loki Mode". Takes a spec (PRD, GitHub issue, OpenAPI doc, etc.) to deployed product with minimal human intervention. Requires --dangerously-skip-permissions flag.
4
4
  ---
5
5
 
6
- # Loki Mode v7.6.0
6
+ # Loki Mode v7.6.2
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -381,4 +381,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
381
381
 
382
382
  ---
383
383
 
384
- **v7.6.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
384
+ **v7.6.2 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 7.6.0
1
+ 7.6.2
package/autonomy/loki CHANGED
@@ -1869,6 +1869,23 @@ except (json.JSONDecodeError, OSError): pass
1869
1869
 
1870
1870
  # Kill orphaned processes from crashed sessions
1871
1871
  cmd_cleanup() {
1872
+ # v7.6.2 B-13 fix: --help must print help, not run cleanup as a side effect.
1873
+ case "${1:-}" in
1874
+ --help|-h|help)
1875
+ echo -e "${BOLD}Loki Mode -- cleanup orphaned processes${NC}"
1876
+ echo ""
1877
+ echo "Usage: loki cleanup [options]"
1878
+ echo ""
1879
+ echo "Scans \$LOKI_DIR/pids/ for stale PID registrations from prior runs"
1880
+ echo "and kills processes whose PID file points at a dead process."
1881
+ echo ""
1882
+ echo "Options:"
1883
+ echo " --help, -h Show this help and exit"
1884
+ echo ""
1885
+ echo "Side effects: kills processes registered under .loki/pids/."
1886
+ return 0
1887
+ ;;
1888
+ esac
1872
1889
  local pids_dir="$LOKI_DIR/pids"
1873
1890
  local killed=0
1874
1891
  local stale=0
@@ -1949,6 +1966,23 @@ cmd_cleanup() {
1949
1966
 
1950
1967
  # Pause after current session
1951
1968
  cmd_pause() {
1969
+ # v7.6.2 B-13 fix: --help must print help, not act on session state.
1970
+ case "${1:-}" in
1971
+ --help|-h|help)
1972
+ echo -e "${BOLD}Loki Mode -- pause active session${NC}"
1973
+ echo ""
1974
+ echo "Usage: loki pause [options]"
1975
+ echo ""
1976
+ echo "Marks the active session as paused. The runner detects this on"
1977
+ echo "the next iteration boundary and halts cleanly until 'loki resume'."
1978
+ echo ""
1979
+ echo "Options:"
1980
+ echo " --help, -h Show this help and exit"
1981
+ echo ""
1982
+ echo "See also: loki resume, loki stop"
1983
+ return 0
1984
+ ;;
1985
+ esac
1952
1986
  if [ ! -d "$LOKI_DIR" ]; then
1953
1987
  echo -e "${YELLOW}No .loki directory found.${NC}"
1954
1988
  echo "No active session to pause."
@@ -1990,6 +2024,23 @@ cmd_pause() {
1990
2024
 
1991
2025
  # Resume paused execution
1992
2026
  cmd_resume() {
2027
+ # v7.6.2 B-13 fix: --help must print help, not act on session state.
2028
+ case "${1:-}" in
2029
+ --help|-h|help)
2030
+ echo -e "${BOLD}Loki Mode -- resume paused session${NC}"
2031
+ echo ""
2032
+ echo "Usage: loki resume [options]"
2033
+ echo ""
2034
+ echo "Clears the paused flag and lets the autonomous runner continue"
2035
+ echo "from where 'loki pause' stopped it."
2036
+ echo ""
2037
+ echo "Options:"
2038
+ echo " --help, -h Show this help and exit"
2039
+ echo ""
2040
+ echo "See also: loki pause, loki start"
2041
+ return 0
2042
+ ;;
2043
+ esac
1993
2044
  if [ ! -d "$LOKI_DIR" ]; then
1994
2045
  echo -e "${YELLOW}No .loki directory found.${NC}"
1995
2046
  echo "No session to resume. Start a session with: loki start"
@@ -2428,6 +2479,25 @@ print(json.dumps(result, indent=2))
2428
2479
 
2429
2480
  # Session statistics
2430
2481
  cmd_stats() {
2482
+ # v7.6.2 B-13 fix: --help must print help, not render the stats panel.
2483
+ case "${1:-}" in
2484
+ --help|-h|help)
2485
+ echo -e "${BOLD}Loki Mode -- session statistics${NC}"
2486
+ echo ""
2487
+ echo "Usage: loki stats [options]"
2488
+ echo ""
2489
+ echo "Prints session-level statistics: tasks completed, iterations,"
2490
+ echo "tokens used, cost, council votes, gate verdicts."
2491
+ echo ""
2492
+ echo "Options:"
2493
+ echo " --json JSON output (machine-readable)"
2494
+ echo " --efficiency Include cost/token efficiency breakdown"
2495
+ echo " --help, -h Show this help and exit"
2496
+ echo ""
2497
+ echo "See also: loki kpis, loki status"
2498
+ return 0
2499
+ ;;
2500
+ esac
2431
2501
  local show_json=false
2432
2502
  local show_efficiency=false
2433
2503
 
@@ -4083,6 +4153,23 @@ cmd_web_status() {
4083
4153
 
4084
4154
  # Import GitHub issues
4085
4155
  cmd_import() {
4156
+ # v7.6.2 B-13 fix: --help must print help, not start an import.
4157
+ case "${1:-}" in
4158
+ --help|-h|help)
4159
+ echo -e "${BOLD}Loki Mode -- import GitHub issues into the queue${NC}"
4160
+ echo ""
4161
+ echo "Usage: loki import [options]"
4162
+ echo ""
4163
+ echo "Imports issues from the current repo's GitHub project into"
4164
+ echo ".loki/queue/ so loki start picks them up as tasks."
4165
+ echo ""
4166
+ echo "Options:"
4167
+ echo " --help, -h Show this help and exit"
4168
+ echo ""
4169
+ echo "Side effects: makes GitHub API calls, writes to .loki/queue/."
4170
+ return 0
4171
+ ;;
4172
+ esac
4086
4173
  if [ ! -d "$LOKI_DIR" ]; then
4087
4174
  mkdir -p "$LOKI_DIR/queue"
4088
4175
  fi
@@ -6289,6 +6376,23 @@ cmd_config_path() {
6289
6376
 
6290
6377
  # Set up skill symlinks for all providers
6291
6378
  cmd_setup_skill() {
6379
+ # v7.6.2 B-13 fix: --help must print help, not install skills.
6380
+ case "${1:-}" in
6381
+ --help|-h|help)
6382
+ echo -e "${BOLD}Loki Mode -- install Loki skill into provider CLIs${NC}"
6383
+ echo ""
6384
+ echo "Usage: loki setup-skill [options]"
6385
+ echo ""
6386
+ echo "Installs the Loki Mode skill into each detected provider CLI's"
6387
+ echo "skills directory: ~/.claude/skills/, ~/.codex/skills/, etc."
6388
+ echo ""
6389
+ echo "Options:"
6390
+ echo " --help, -h Show this help and exit"
6391
+ echo ""
6392
+ echo "Side effects: writes symlinks/files under ~/.<provider>/skills/loki-mode/."
6393
+ return 0
6394
+ ;;
6395
+ esac
6292
6396
  echo -e "${BOLD}Loki Mode Skill Setup${NC}"
6293
6397
  echo ""
6294
6398
 
@@ -6915,7 +7019,9 @@ cmd_doctor() {
6915
7019
 
6916
7020
  # JSON output for loki doctor --json
6917
7021
  cmd_doctor_json() {
6918
- python3 -c "
7022
+ local _loki_version
7023
+ _loki_version=$(get_version)
7024
+ LOKI_VERSION="$_loki_version" python3 -c "
6919
7025
  import json, os, subprocess, sys, shutil
6920
7026
 
6921
7027
  def get_version(cmd, args=None):
@@ -7008,6 +7114,7 @@ elif disk_status == 'fail': fail_count += 1
7008
7114
  elif disk_status == 'warn': warn_count += 1
7009
7115
 
7010
7116
  result = {
7117
+ 'loki_mode_version': os.environ.get('LOKI_VERSION', 'unknown'),
7011
7118
  'checks': checks,
7012
7119
  'disk': {
7013
7120
  'available_gb': disk_gb,
@@ -7228,6 +7335,18 @@ SENTRUX_RULES_EOF
7228
7335
 
7229
7336
  # Show version
7230
7337
  cmd_version() {
7338
+ # v7.6.2 B-13 fix: --help prints help; bare invocation prints version.
7339
+ case "${1:-}" in
7340
+ --help|-h|help)
7341
+ echo -e "${BOLD}Loki Mode -- print version${NC}"
7342
+ echo ""
7343
+ echo "Usage: loki version"
7344
+ echo ""
7345
+ echo "Prints 'Loki Mode vX.Y.Z' from the VERSION file shipped with"
7346
+ echo "the package. Equivalent to 'loki --version' and 'loki -v'."
7347
+ return 0
7348
+ ;;
7349
+ esac
7231
7350
  echo "Loki Mode v$(get_version)"
7232
7351
  }
7233
7352
 
@@ -12132,10 +12251,10 @@ main() {
12132
12251
  cmd_cleanup "$@"
12133
12252
  ;;
12134
12253
  pause)
12135
- cmd_pause
12254
+ cmd_pause "$@"
12136
12255
  ;;
12137
12256
  resume)
12138
- cmd_resume
12257
+ cmd_resume "$@"
12139
12258
  ;;
12140
12259
  status)
12141
12260
  cmd_status "$@"
@@ -12153,6 +12272,27 @@ main() {
12153
12272
  cmd_logs "$@"
12154
12273
  ;;
12155
12274
  serve)
12275
+ # v7.6.2 B-12 fix: 'serve --help' previously routed to 'cmd_api start --help'
12276
+ # which started the dashboard as a side effect instead of printing help.
12277
+ case "${1:-}" in
12278
+ --help|-h|help)
12279
+ echo -e "${BOLD}Loki Mode -- start dashboard API server${NC}"
12280
+ echo ""
12281
+ echo "Usage: loki serve [options]"
12282
+ echo ""
12283
+ echo "Alias for 'loki api start'. Starts the dashboard HTTP API"
12284
+ echo "server (FastAPI, port 57374 by default)."
12285
+ echo ""
12286
+ echo "Options:"
12287
+ echo " --host HOST Bind host (default: 127.0.0.1)"
12288
+ echo " --port PORT Bind port (default: 57374)"
12289
+ echo " --help, -h Show this help and exit"
12290
+ echo ""
12291
+ echo "Side effects: starts a long-running HTTP server bound to a TCP port."
12292
+ echo "Stop with: loki dashboard stop"
12293
+ return 0
12294
+ ;;
12295
+ esac
12156
12296
  cmd_api start "$@"
12157
12297
  ;;
12158
12298
  api)
@@ -12165,7 +12305,7 @@ main() {
12165
12305
  cmd_notify "$@"
12166
12306
  ;;
12167
12307
  import)
12168
- cmd_import
12308
+ cmd_import "$@"
12169
12309
  ;;
12170
12310
  github)
12171
12311
  cmd_github "$@"
@@ -12310,7 +12450,7 @@ main() {
12310
12450
  cmd_code "$@"
12311
12451
  ;;
12312
12452
  version|--version|-v)
12313
- cmd_version
12453
+ cmd_version "$@"
12314
12454
  ;;
12315
12455
  completions)
12316
12456
  cmd_completions "$@"
package/autonomy/run.sh CHANGED
@@ -5634,13 +5634,29 @@ enforce_static_analysis() {
5634
5634
  if [ "$_ts_project_mode" -eq 1 ]; then
5635
5635
  continue
5636
5636
  fi
5637
+ # v7.6.2 B-18 fix: previously skipped TS/TSX files when
5638
+ # tsc wasn't on PATH, leaving them silently unchecked.
5639
+ # Now fall back to `npx --yes -p typescript@latest tsc`
5640
+ # (uses the cached npm install), then to `bun tsc`
5641
+ # (Bun has built-in TypeScript), before giving up.
5637
5642
  if command -v tsc &>/dev/null; then
5638
5643
  tsc --noEmit --allowJs --jsx preserve --target esnext "$f" 2>&1 || {
5639
5644
  findings=$((findings + 1))
5640
5645
  details="${details}TS syntax error: $f. "
5641
5646
  }
5647
+ elif command -v bun &>/dev/null; then
5648
+ # Bun has built-in TypeScript via `bun --check`.
5649
+ bun --check "$f" 2>&1 || {
5650
+ findings=$((findings + 1))
5651
+ details="${details}TS syntax error (bun --check): $f. "
5652
+ }
5653
+ elif command -v npx &>/dev/null; then
5654
+ npx --yes -p typescript@latest tsc --noEmit --allowJs --jsx preserve --target esnext "$f" 2>&1 || {
5655
+ findings=$((findings + 1))
5656
+ details="${details}TS syntax error (npx tsc): $f. "
5657
+ }
5642
5658
  else
5643
- log_info "Static analysis: skipping $f (tsc not on PATH; node --check cannot parse .ts/.tsx)"
5659
+ log_info "Static analysis: skipping $f (no tsc, bun, or npx available)"
5644
5660
  fi
5645
5661
  ;;
5646
5662
  *)
@@ -11439,7 +11455,22 @@ if __name__ == "__main__":
11439
11455
  # promise text appears in the iteration output.
11440
11456
  # The check_completion_promise() helper encapsulates both.
11441
11457
  # BUG-RUN-001: Use per-iteration output, not stale daily log.
11442
- if check_completion_promise "$iter_output"; then
11458
+ #
11459
+ # v7.6.2 B-17 fix: completion was firing even when code review
11460
+ # BLOCKED the iteration with Critical/High findings. That's a false
11461
+ # success signal -- review-blocked iterations cannot be considered
11462
+ # complete. Check the gate_failures accumulator for code_review and
11463
+ # refuse completion until the review passes.
11464
+ local _gate_block_for_completion=""
11465
+ case "${gate_failures:-}" in
11466
+ *code_review,*|*code_review_ESCALATED*) _gate_block_for_completion="code_review" ;;
11467
+ esac
11468
+ if [ -n "$_gate_block_for_completion" ] && check_completion_promise "$iter_output"; then
11469
+ log_warn "Completion claim rejected: code review is BLOCKED for this iteration (Critical/High findings). Fix review issues before completion."
11470
+ log_warn " Review details under .loki/quality/reviews/ ; gate_failures=${gate_failures}"
11471
+ _gate_block_for_completion=""
11472
+ # Fall through; the gate-failed loop continues normally
11473
+ elif check_completion_promise "$iter_output"; then
11443
11474
  echo ""
11444
11475
  if [ -n "$COMPLETION_PROMISE" ]; then
11445
11476
  log_header "COMPLETION PROMISE FULFILLED: $COMPLETION_PROMISE"
@@ -11551,13 +11582,57 @@ LOKI_PROVIDER_ACTIVE=0
11551
11582
  # v7.5.12: Kill provider pipeline children with SIGTERM, then SIGKILL escalation.
11552
11583
  # Uses pkill -P $$ to target direct children only (the pipeline subshells).
11553
11584
  # Returns 0 if anything was killed, 1 if no children present.
11585
+ #
11586
+ # v7.6.2 B-15 fix: previously `pkill -P $$` was indiscriminate -- it caught
11587
+ # the dashboard server (started via nohup but still parented to this shell
11588
+ # until the OS reparents it). The dashboard PID 29716 was killed mid-session
11589
+ # after "Aggregating verdicts", breaking the browser UI. Now we explicitly
11590
+ # exclude any PID registered in .loki/pids/ (dashboard, app-runner, etc.).
11554
11591
  kill_provider_child() {
11555
11592
  local killed=0
11556
- # First pass: SIGTERM to direct children of this shell. Pipeline subshells
11557
- # for `claude -p | tee | python` are direct children of $$.
11558
- if pkill -TERM -P $$ 2>/dev/null; then
11559
- killed=1
11593
+ local protected_pids=""
11594
+ # Build list of PIDs that MUST survive a provider kill (dashboard, app-runner,
11595
+ # status monitor, resource monitor). These are recorded as PID files when
11596
+ # cmd_dashboard_start / cmd_api_start spawn them.
11597
+ local pid_root="${TARGET_DIR:-.}/.loki/pids"
11598
+ if [ -d "$pid_root" ]; then
11599
+ local pid_file pid
11600
+ for pid_file in "$pid_root"/*.pid; do
11601
+ [ -f "$pid_file" ] || continue
11602
+ pid=$(cat "$pid_file" 2>/dev/null | head -1 | tr -d '[:space:]')
11603
+ if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
11604
+ protected_pids="${protected_pids} ${pid}"
11605
+ fi
11606
+ done
11607
+ fi
11608
+ # Also protect the dashboard PID file at .loki/dashboard/dashboard.pid (older path).
11609
+ local dash_pid_file="${TARGET_DIR:-.}/.loki/dashboard/dashboard.pid"
11610
+ if [ -f "$dash_pid_file" ]; then
11611
+ local dpid
11612
+ dpid=$(cat "$dash_pid_file" 2>/dev/null | head -1 | tr -d '[:space:]')
11613
+ if [ -n "$dpid" ] && kill -0 "$dpid" 2>/dev/null; then
11614
+ protected_pids="${protected_pids} ${dpid}"
11615
+ fi
11560
11616
  fi
11617
+
11618
+ # Helper: returns 0 if $1 is in protected_pids list.
11619
+ _is_protected() {
11620
+ local target="$1"
11621
+ local p
11622
+ for p in $protected_pids; do
11623
+ [ "$p" = "$target" ] && return 0
11624
+ done
11625
+ return 1
11626
+ }
11627
+
11628
+ # First pass: SIGTERM each direct child individually so we can skip protected PIDs.
11629
+ local child_pid
11630
+ for child_pid in $(pgrep -P $$ 2>/dev/null); do
11631
+ if _is_protected "$child_pid"; then
11632
+ continue
11633
+ fi
11634
+ kill -TERM "$child_pid" 2>/dev/null && killed=1
11635
+ done
11561
11636
  # Also kill provider leaf processes by name in case they were reparented.
11562
11637
  local proc
11563
11638
  for proc in claude codex aider cline; do
@@ -11567,18 +11642,27 @@ kill_provider_child() {
11567
11642
  # Brief wait for graceful exit (max ~2s).
11568
11643
  local i=0
11569
11644
  while [ $i -lt 20 ]; do
11570
- if ! pgrep -P $$ >/dev/null 2>&1; then
11645
+ local survivors=""
11646
+ for child_pid in $(pgrep -P $$ 2>/dev/null); do
11647
+ if ! _is_protected "$child_pid"; then
11648
+ survivors="${survivors} ${child_pid}"
11649
+ fi
11650
+ done
11651
+ if [ -z "$survivors" ]; then
11571
11652
  break
11572
11653
  fi
11573
11654
  sleep 0.1
11574
11655
  i=$((i + 1))
11575
11656
  done
11576
11657
 
11577
- # Escalate to SIGKILL for any survivors.
11578
- if pgrep -P $$ >/dev/null 2>&1; then
11579
- pkill -KILL -P $$ 2>/dev/null || true
11658
+ # Escalate to SIGKILL for unprotected survivors only.
11659
+ for child_pid in $(pgrep -P $$ 2>/dev/null); do
11660
+ if _is_protected "$child_pid"; then
11661
+ continue
11662
+ fi
11663
+ kill -KILL "$child_pid" 2>/dev/null
11580
11664
  killed=1
11581
- fi
11665
+ done
11582
11666
 
11583
11667
  LOKI_PROVIDER_ACTIVE=0
11584
11668
  if [ $killed -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.6.0"
10
+ __version__ = "7.6.2"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -3023,9 +3023,20 @@ async def get_learning_metrics(
3023
3023
 
3024
3024
  total_count = len(events) + len(all_signals)
3025
3025
 
3026
- # Calculate average confidence across both sources
3027
- total_conf = sum(e.get("data", {}).get("confidence", 0) for e in events)
3028
- total_conf += sum(s.get("confidence", 0) for s in all_signals)
3026
+ # Calculate average confidence across both sources. Coerce values to float
3027
+ # because some legacy events/signals stored confidence as a string, which
3028
+ # made sum() raise TypeError: unsupported operand type(s) for +: 'int' and 'str'.
3029
+ # B-7 fix (v7.6.1): silently skip non-numeric confidence values.
3030
+ def _as_num(v: object) -> float:
3031
+ if isinstance(v, (int, float)):
3032
+ return float(v)
3033
+ try:
3034
+ return float(v) if v is not None else 0.0
3035
+ except (TypeError, ValueError):
3036
+ return 0.0
3037
+
3038
+ total_conf = sum(_as_num(e.get("data", {}).get("confidence", 0)) for e in events)
3039
+ total_conf += sum(_as_num(s.get("confidence", 0)) for s in all_signals)
3029
3040
 
3030
3041
  # Load aggregation data from file if available
3031
3042
  aggregation = {
@@ -6388,7 +6399,21 @@ async def get_escalation(filename: str):
6388
6399
  # ---------------------------------------------------------------------------
6389
6400
  @app.get("/{full_path:path}", include_in_schema=False)
6390
6401
  async def serve_spa_catchall(full_path: str):
6391
- """Serve static files or fall back to index.html for SPA routing."""
6402
+ """Serve static files or fall back to index.html for SPA routing.
6403
+
6404
+ v7.6.1 B-10 fix: requests under /api/, /lab/api/, or /ws/ that fall through
6405
+ here are missing routes, not SPA navigation. Returning index.html (text/html)
6406
+ silently masks 404s and breaks JSON clients (the dashboard UI's loki-memory-browser
6407
+ pinged /api/learning/metrics expecting JSON and got an HTML SPA on prior
6408
+ failures). Return a JSON 404 instead so clients fail loud.
6409
+ """
6410
+ # API paths that fell through are real 404s, not SPA routes.
6411
+ api_like = full_path.startswith("api/") or full_path.startswith("lab/api/") or full_path.startswith("ws/")
6412
+ if api_like:
6413
+ return JSONResponse(
6414
+ status_code=404,
6415
+ content={"error": "Not Found", "path": f"/{full_path}"},
6416
+ )
6392
6417
  if STATIC_DIR:
6393
6418
  static_root = os.path.realpath(STATIC_DIR)
6394
6419
  # Try to serve the exact file first (e.g. /vite.svg, /manifest.json)
@@ -1339,14 +1339,22 @@ var LokiDashboard=(()=>{var wt=Object.defineProperty;var ae=Object.getOwnPropert
1339
1339
  <div class="card-label">App Runner</div>
1340
1340
  <div class="card-value small-text">${this._data.status==="running"||this._data.status==="autonomous"?"Waiting...":"Not started"}</div>
1341
1341
  </div>
1342
- `;let a={running:"var(--loki-green, #22c55e)",starting:"var(--loki-yellow, #f59e0b)",crashed:"var(--loki-red, #ef4444)",stopped:"var(--loki-text-muted, #a1a1aa)"}[t.status]||"var(--loki-text-muted)",i=t.status==="running"?"active":t.status==="crashed"?"error":"offline",s=(t.status||"unknown").toUpperCase(),r=t.port?`:${t.port}`:"";return`
1342
+ `;let a={running:"var(--loki-green, #22c55e)",starting:"var(--loki-yellow, #f59e0b)",crashed:"var(--loki-red, #ef4444)",stopped:"var(--loki-text-muted, #a1a1aa)"}[t.status]||"var(--loki-text-muted)",i=t.status==="running"?"active":t.status==="crashed"?"error":"offline",s=(t.status||"unknown").toUpperCase(),r=t.port?`:${t.port}`:"",o=t.url&&typeof t.url=="string"?t.url:null;!o&&t.port&&t.status==="running"&&(o=`http://localhost:${t.port}`);let n=`
1343
+ <div class="card-label">App Runner${o?' <span style="font-size:10px;color:var(--loki-text-muted);">(click to open)</span>':""}</div>
1344
+ <div class="card-value small-text">
1345
+ <span class="status-dot ${i}"></span>
1346
+ ${s}${r}
1347
+ </div>
1348
+ ${t.method?`<div style="font-size:10px;color:var(--loki-text-muted);margin-top:2px;">${this._escapeHtml(t.method)}</div>`:""}
1349
+ `;return o?`
1350
+ <a class="overview-card overview-card-link" href="${this._escapeHtml(o)}" target="_blank" rel="noopener noreferrer"
1351
+ style="text-decoration:none;color:inherit;display:block;cursor:pointer;"
1352
+ title="Open ${this._escapeHtml(o)} in new tab">
1353
+ ${n}
1354
+ </a>
1355
+ `:`
1343
1356
  <div class="overview-card">
1344
- <div class="card-label">App Runner</div>
1345
- <div class="card-value small-text">
1346
- <span class="status-dot ${i}"></span>
1347
- ${s}${r}
1348
- </div>
1349
- ${t.method?`<div style="font-size:10px;color:var(--loki-text-muted);margin-top:2px;">${this._escapeHtml(t.method)}</div>`:""}
1357
+ ${n}
1350
1358
  </div>
1351
1359
  `}_renderPlaywrightCard(){let t=this._playwrightResults;if(!t||t==="null"||!t.verified_at)return`
1352
1360
  <div class="overview-card">
@@ -2,7 +2,7 @@
2
2
 
3
3
  The flagship product of [Autonomi](https://www.autonomi.dev/). Complete installation instructions for all platforms and use cases.
4
4
 
5
- **Version:** v7.6.0
5
+ **Version:** v7.6.2
6
6
 
7
7
  ---
8
8
 
@@ -1,10 +1,10 @@
1
1
  // @bun
2
- var j7=Object.defineProperty;var _7=(K)=>K;function I7(K,$){this[K]=_7.bind(null,$)}var b=(K,$)=>{for(var z in $)j7(K,z,{get:$[z],enumerable:!0,configurable:!0,set:I7.bind($,z)})};var L=(K,$)=>()=>(K&&($=K(K=0)),$);var U1=import.meta.require;var t1={};b(t1,{lokiDir:()=>P,homeLokiDir:()=>N1,findRepoRootForVersion:()=>S1,REPO_ROOT:()=>u});import{resolve as f,dirname as w1}from"path";import{fileURLToPath as P7}from"url";import{existsSync as J1}from"fs";import{homedir as L7}from"os";function R7(){let K=r1;for(let $=0;$<6;$++){if(J1(f(K,"VERSION"))&&J1(f(K,"autonomy/run.sh")))return K;let z=w1(K);if(z===K)break;K=z}return f(r1,"..","..","..")}function S1(K){let $=K;for(let z=0;z<6;z++){if(J1(f($,"VERSION"))&&J1(f($,"autonomy/run.sh")))return $;let Q=w1($);if(Q===$)break;$=Q}return f(K,"..","..","..")}function P(){return process.env.LOKI_DIR??f(process.cwd(),".loki")}function N1(){return f(L7(),".loki")}var r1,u;var y=L(()=>{r1=w1(P7(import.meta.url));u=R7()});var K0={};b(K0,{runOrThrow:()=>S7,run:()=>w,commandVersion:()=>k7,commandExists:()=>h,ShellError:()=>k1});async function w(K,$={}){let z=Bun.spawn({cmd:[...K],stdout:"pipe",stderr:"pipe",env:$.env?{...process.env,...$.env}:process.env,cwd:$.cwd}),Q,X;if($.timeoutMs&&$.timeoutMs>0)Q=setTimeout(()=>{try{z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{z.kill("SIGKILL")}catch{}},2000)},$.timeoutMs);try{let[H,Z,V]=await Promise.all([new Response(z.stdout).text(),new Response(z.stderr).text(),z.exited]);return{stdout:H,stderr:Z,exitCode:V}}finally{if(Q)clearTimeout(Q);if(X)clearTimeout(X)}}async function S7(K,$={}){let z=await w(K,$);if(z.exitCode!==0)throw new k1(`command failed (${z.exitCode}): ${K.join(" ")}`,z.exitCode,z.stdout,z.stderr);return z}async function h(K){let $=N7(K),z=await w(["sh","-c",`command -v ${$}`],{timeoutMs:5000});if(z.exitCode===0)return z.stdout.trim()||null;return null}function N7(K){if(!/^[A-Za-z0-9._/-]+$/.test(K))throw Error(`refused to shell-escape suspect token: ${K}`);return K}async function k7(K,$="--version"){if(!await h(K))return null;let Q=await w([K,$],{timeoutMs:5000});if(Q.exitCode!==0)return null;return((Q.stdout||Q.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var k1;var p=L(()=>{k1=class k1 extends Error{message;exitCode;stdout;stderr;constructor(K,$,z,Q){super(K);this.message=K;this.exitCode=$;this.stdout=z;this.stderr=Q;this.name="ShellError"}}});function c(K){return D7?"":K}var D7,R,D,E,Y6,O,k,x,W;var n=L(()=>{D7=(process.env.NO_COLOR??"").length>0;R=c("\x1B[0;31m"),D=c("\x1B[0;32m"),E=c("\x1B[1;33m"),Y6=c("\x1B[0;34m"),O=c("\x1B[0;36m"),k=c("\x1B[1m"),x=c("\x1B[2m"),W=c("\x1B[0m")});import{existsSync as p7}from"fs";async function r(){if(z1!==void 0)return z1;let K="/opt/homebrew/bin/python3.12";if(p7(K))return z1=K,K;let $=await h("python3.12");if($)return z1=$,$;let z=await h("python3");return z1=z,z}async function a(K,$={}){let z=await r();if(!z)return{stdout:"",stderr:"python3 not found",exitCode:127};return w([z,"-c",K],$)}var z1;var Q1=L(()=>{p()});var J0={};b(J0,{runStatus:()=>$5});import{existsSync as S,readFileSync as Z1,readdirSync as Z0,statSync as H0}from"fs";import{resolve as F,basename as n7}from"path";async function s7(){if(await h("jq"))return!0;return process.stdout.write(`${R}Error: jq is required but not installed.${W}
2
+ var _7=Object.defineProperty;var I7=(K)=>K;function P7(K,$){this[K]=I7.bind(null,$)}var b=(K,$)=>{for(var z in $)_7(K,z,{get:$[z],enumerable:!0,configurable:!0,set:P7.bind($,z)})};var L=(K,$)=>()=>(K&&($=K(K=0)),$);var V1=import.meta.require;var e1={};b(e1,{lokiDir:()=>P,homeLokiDir:()=>k1,findRepoRootForVersion:()=>N1,REPO_ROOT:()=>u});import{resolve as f,dirname as S1}from"path";import{fileURLToPath as L7}from"url";import{existsSync as J1}from"fs";import{homedir as R7}from"os";function E7(){let K=i1;for(let $=0;$<6;$++){if(J1(f(K,"VERSION"))&&J1(f(K,"autonomy/run.sh")))return K;let z=S1(K);if(z===K)break;K=z}return f(i1,"..","..","..")}function N1(K){let $=K;for(let z=0;z<6;z++){if(J1(f($,"VERSION"))&&J1(f($,"autonomy/run.sh")))return $;let Q=S1($);if(Q===$)break;$=Q}return f(K,"..","..","..")}function P(){return process.env.LOKI_DIR??f(process.cwd(),".loki")}function k1(){return f(R7(),".loki")}var i1,u;var y=L(()=>{i1=S1(L7(import.meta.url));u=E7()});import{readFileSync as x7}from"fs";import{resolve as F7,dirname as w7}from"path";import{fileURLToPath as S7}from"url";function G1(){if(o!==null)return o;let K="7.6.2";if(typeof K==="string"&&K.length>0)return o=K,o;try{let $=w7(S7(import.meta.url)),z=N1($);o=x7(F7(z,"VERSION"),"utf-8").trim()}catch{o="unknown"}return o}var o=null;var D1=L(()=>{y()});var $0={};b($0,{runOrThrow:()=>N7,run:()=>w,commandVersion:()=>D7,commandExists:()=>h,ShellError:()=>C1});async function w(K,$={}){let z=Bun.spawn({cmd:[...K],stdout:"pipe",stderr:"pipe",env:$.env?{...process.env,...$.env}:process.env,cwd:$.cwd}),Q,X;if($.timeoutMs&&$.timeoutMs>0)Q=setTimeout(()=>{try{z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{z.kill("SIGKILL")}catch{}},2000)},$.timeoutMs);try{let[H,Z,q]=await Promise.all([new Response(z.stdout).text(),new Response(z.stderr).text(),z.exited]);return{stdout:H,stderr:Z,exitCode:q}}finally{if(Q)clearTimeout(Q);if(X)clearTimeout(X)}}async function N7(K,$={}){let z=await w(K,$);if(z.exitCode!==0)throw new C1(`command failed (${z.exitCode}): ${K.join(" ")}`,z.exitCode,z.stdout,z.stderr);return z}async function h(K){let $=k7(K),z=await w(["sh","-c",`command -v ${$}`],{timeoutMs:5000});if(z.exitCode===0)return z.stdout.trim()||null;return null}function k7(K){if(!/^[A-Za-z0-9._/-]+$/.test(K))throw Error(`refused to shell-escape suspect token: ${K}`);return K}async function D7(K,$="--version"){if(!await h(K))return null;let Q=await w([K,$],{timeoutMs:5000});if(Q.exitCode!==0)return null;return((Q.stdout||Q.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var C1;var p=L(()=>{C1=class C1 extends Error{message;exitCode;stdout;stderr;constructor(K,$,z,Q){super(K);this.message=K;this.exitCode=$;this.stdout=z;this.stderr=Q;this.name="ShellError"}}});function c(K){return C7?"":K}var C7,R,D,E,O6,O,k,x,W;var n=L(()=>{C7=(process.env.NO_COLOR??"").length>0;R=c("\x1B[0;31m"),D=c("\x1B[0;32m"),E=c("\x1B[1;33m"),O6=c("\x1B[0;34m"),O=c("\x1B[0;36m"),k=c("\x1B[1m"),x=c("\x1B[2m"),W=c("\x1B[0m")});import{existsSync as c7}from"fs";async function r(){if(z1!==void 0)return z1;let K="/opt/homebrew/bin/python3.12";if(c7(K))return z1=K,K;let $=await h("python3.12");if($)return z1=$,$;let z=await h("python3");return z1=z,z}async function a(K,$={}){let z=await r();if(!z)return{stdout:"",stderr:"python3 not found",exitCode:127};return w([z,"-c",K],$)}var z1;var Q1=L(()=>{p()});var G0={};b(G0,{runStatus:()=>z5});import{existsSync as S,readFileSync as Z1,readdirSync as H0,statSync as W0}from"fs";import{resolve as F,basename as a7}from"path";async function r7(){if(await h("jq"))return!0;return process.stdout.write(`${R}Error: jq is required but not installed.${W}
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)
6
6
  `),process.stdout.write(` yum install jq (RHEL/CentOS)
7
- `),!1}function G1(K){if(!Number.isFinite(K)||K<=0)return!1;try{return process.kill(K,0),!0}catch{return!1}}function B1(K){if(!S(K))return null;try{let $=Z1(K,"utf-8").trim();if(!$)return null;let z=Number.parseInt($,10);return Number.isFinite(z)?z:null}catch{return null}}function r7(K){let $=[],z=B1(F(K,"loki.pid"));if(z!==null&&G1(z))$.push(`global:${z}`);let Q=F(K,"sessions");if(S(Q)){let X=[];try{X=Z0(Q)}catch{X=[]}for(let H of X){let Z=F(Q,H);try{if(!H0(Z).isDirectory())continue}catch{continue}let V=F(Z,"loki.pid"),q=B1(V);if(q!==null&&G1(q))$.push(`${H}:${q}`)}}if(S(K)){let X=[];try{X=Z0(K)}catch{X=[]}for(let H of X){if(!H.startsWith("run-")||!H.endsWith(".pid"))continue;let Z=F(K,H);try{if(!H0(Z).isFile())continue}catch{continue}let V=n7(H,".pid").slice(4),q=B1(Z);if(q!==null&&G1(q)){if(!$.some((G)=>G.startsWith(`${V}:`)))$.push(`${V}:${q}`)}}}return $}async function W0(K,$){let z=await w(["jq","-r",K,$]);if(z.exitCode!==0)return null;return z.stdout.trim()}function V0(K,$){try{let z=Z1(K,"utf-8"),X=JSON.parse(z)[$];if(typeof X==="number"){if($==="budget_used"){let H=Math.round(X*100)/100;if(Number.isInteger(H))return String(H);return String(H)}return String(X)}if(X===void 0||X===null)return"0";return String(X)}catch{return"0"}}function q0(K,$,z){try{let Q=Z1(K,"utf-8"),H=JSON.parse(Q)[$];if(typeof H==="number"&&Number.isFinite(H))return H;return z}catch{return z}}async function t7(){let K=P();if(!await s7())return 1;if(!S(K))return process.stdout.write(`${k}Loki Mode Status${W}
7
+ `),!1}function B1(K){if(!Number.isFinite(K)||K<=0)return!1;try{return process.kill(K,0),!0}catch{return!1}}function M1(K){if(!S(K))return null;try{let $=Z1(K,"utf-8").trim();if(!$)return null;let z=Number.parseInt($,10);return Number.isFinite(z)?z:null}catch{return null}}function t7(K){let $=[],z=M1(F(K,"loki.pid"));if(z!==null&&B1(z))$.push(`global:${z}`);let Q=F(K,"sessions");if(S(Q)){let X=[];try{X=H0(Q)}catch{X=[]}for(let H of X){let Z=F(Q,H);try{if(!W0(Z).isDirectory())continue}catch{continue}let q=F(Z,"loki.pid"),U=M1(q);if(U!==null&&B1(U))$.push(`${H}:${U}`)}}if(S(K)){let X=[];try{X=H0(K)}catch{X=[]}for(let H of X){if(!H.startsWith("run-")||!H.endsWith(".pid"))continue;let Z=F(K,H);try{if(!W0(Z).isFile())continue}catch{continue}let q=a7(H,".pid").slice(4),U=M1(Z);if(U!==null&&B1(U)){if(!$.some((G)=>G.startsWith(`${q}:`)))$.push(`${q}:${U}`)}}}return $}async function q0(K,$){let z=await w(["jq","-r",K,$]);if(z.exitCode!==0)return null;return z.stdout.trim()}function U0(K,$){try{let z=Z1(K,"utf-8"),X=JSON.parse(z)[$];if(typeof X==="number"){if($==="budget_used"){let H=Math.round(X*100)/100;if(Number.isInteger(H))return String(H);return String(H)}return String(X)}if(X===void 0||X===null)return"0";return String(X)}catch{return"0"}}function V0(K,$,z){try{let Q=Z1(K,"utf-8"),H=JSON.parse(Q)[$];if(typeof H==="number"&&Number.isFinite(H))return H;return z}catch{return z}}async function i7(){let K=P();if(!await r7())return 1;if(!S(K))return process.stdout.write(`${k}Loki Mode Status${W}
8
8
  `),process.stdout.write(`
9
9
  `),process.stdout.write(`${E}No active session found.${W}
10
10
  `),process.stdout.write(`Loki Mode has not been initialized in this directory.
@@ -19,7 +19,7 @@ var j7=Object.defineProperty;var _7=(K)=>K;function I7(K,$){this[K]=_7.bind(null
19
19
  `);let $="",z=F(K,"state","provider");if(S(z))try{$=Z1(z,"utf-8").trim()}catch{$=""}let Q=$||process.env.LOKI_PROVIDER||"claude",X="full features";switch(Q){case"codex":case"aider":X="degraded mode";break;case"cline":X="near-full mode";break;default:X="full features";break}process.stdout.write(`${O}Provider:${W} ${Q} (${X})
20
20
  `),process.stdout.write(`${x} Switch with: loki provider set <claude|codex|cline|aider>${W}
21
21
  `),process.stdout.write(`
22
- `);let H=r7(K);if(H.length>0){process.stdout.write(`${D}Active Sessions: ${H.length}${W}
22
+ `);let H=t7(K);if(H.length>0){process.stdout.write(`${D}Active Sessions: ${H.length}${W}
23
23
  `);for(let J of H){let Y=J.indexOf(":"),j=Y>=0?J.slice(0,Y):J,_=Y>=0?J.slice(Y+1):"";if(j==="global")process.stdout.write(` ${O}[global]${W} PID ${_}
24
24
  `);else process.stdout.write(` ${O}[#${j}]${W} PID ${_}
25
25
  `)}process.stdout.write(`
@@ -34,27 +34,27 @@ var j7=Object.defineProperty;var _7=(K)=>K;function I7(K,$){this[K]=_7.bind(null
34
34
  `),process.stdout.write(`
35
35
  `);let Z=F(K,"STATUS.txt");if(S(Z)){process.stdout.write(`${O}Session Info:${W}
36
36
  `);try{process.stdout.write(Z1(Z,"utf-8"))}catch{}process.stdout.write(`
37
- `)}let V=F(K,"state","orchestrator.json");if(S(V)){process.stdout.write(`${O}Orchestrator State:${W}
38
- `);let J=await W0('.currentPhase // "unknown"',V);process.stdout.write(`${J??"unknown"}
39
- `)}let q=F(K,"queue","pending.json");if(S(q)){let J=await W0('if type == "array" then length elif .tasks then .tasks | length else 0 end',q);process.stdout.write(`${O}Pending Tasks:${W} ${J??"0"}
40
- `)}let U=F(K,"metrics","budget.json");if(S(U)){let J=V0(U,"budget_limit"),Y=V0(U,"budget_used");if(J!=="0")process.stdout.write(`${O}Budget:${W} $${Y} / $${J}
37
+ `)}let q=F(K,"state","orchestrator.json");if(S(q)){process.stdout.write(`${O}Orchestrator State:${W}
38
+ `);let J=await q0('.currentPhase // "unknown"',q);process.stdout.write(`${J??"unknown"}
39
+ `)}let U=F(K,"queue","pending.json");if(S(U)){let J=await q0('if type == "array" then length elif .tasks then .tasks | length else 0 end',U);process.stdout.write(`${O}Pending Tasks:${W} ${J??"0"}
40
+ `)}let V=F(K,"metrics","budget.json");if(S(V)){let J=U0(V,"budget_limit"),Y=U0(V,"budget_used");if(J!=="0")process.stdout.write(`${O}Budget:${W} $${Y} / $${J}
41
41
  `);else process.stdout.write(`${O}Cost:${W} $${Y} (no limit)
42
- `)}let G=F(K,"state","context-usage.json");if(S(G)){let J=q0(G,"window_size",200000),Y=q0(G,"used_tokens",0),j=0;if(J>0)j=Math.floor(Y*100/J);process.stdout.write(`${O}Context:${W} ${j}% (${Y} / ${J} tokens)
43
- `)}let B=F(K,"dashboard","dashboard.pid");if(S(B)){let J=B1(B);if(J!==null&&G1(J)){let Y=process.env.LOKI_DASHBOARD_PORT||"57374";process.stdout.write(`${O}Dashboard:${W} http://127.0.0.1:${Y}/
44
- `)}}return await i7(K),process.stdout.write(`
42
+ `)}let G=F(K,"state","context-usage.json");if(S(G)){let J=V0(G,"window_size",200000),Y=V0(G,"used_tokens",0),j=0;if(J>0)j=Math.floor(Y*100/J);process.stdout.write(`${O}Context:${W} ${j}% (${Y} / ${J} tokens)
43
+ `)}let B=F(K,"dashboard","dashboard.pid");if(S(B)){let J=M1(B);if(J!==null&&B1(J)){let Y=process.env.LOKI_DASHBOARD_PORT||"57374";process.stdout.write(`${O}Dashboard:${W} http://127.0.0.1:${Y}/
44
+ `)}}return await e7(K),process.stdout.write(`
45
45
  `),process.stdout.write(`${x} Tip: loki context show - detailed token breakdown${W}
46
46
  `),process.stdout.write(`${x} Tip: loki code overview - codebase intelligence${W}
47
- `),0}async function i7(K){let $=F(K,"state"),z=e7($),Q=F($,"relevant-learnings.json"),X=F(K,"escalations"),H=z.length>0,Z=S(Q),V=S(X);if(!H&&!Z&&!V)return;if(process.stdout.write(`
47
+ `),0}async function e7(K){let $=F(K,"state"),z=K5($),Q=F($,"relevant-learnings.json"),X=F(K,"escalations"),H=z.length>0,Z=S(Q),q=S(X);if(!H&&!Z&&!q)return;if(process.stdout.write(`
48
48
  ${O}Phase 1 artifacts:${W}
49
- `),H){let q=z[z.length-1],U=U0(q);if(U&&Array.isArray(U.findings)){let G={Critical:0,High:0,Medium:0,Low:0};for(let J of U.findings){let Y=String(J.severity??"");if(Y in G)G[Y]=(G[Y]??0)+1}let B=Object.entries(G).filter(([,J])=>J>0).map(([J,Y])=>`${Y} ${J.toLowerCase()}`).join(", ");process.stdout.write(` Findings (iter ${U.iteration??"?"}): ${B||"none"} -- ${U.findings.length} total
50
- `)}}if(Z){let q=U0(Q);if(q&&Array.isArray(q.learnings)&&q.learnings.length>0){let U=new Map;for(let B of q.learnings){let J=String(B.trigger??"unknown");U.set(J,(U.get(J)??0)+1)}let G=[...U.entries()].sort((B,J)=>J[1]-B[1]).slice(0,3).map(([B,J])=>`${J} ${B}`).join(", ");process.stdout.write(` Learnings: ${q.learnings.length} total (${G})
51
- `)}}if(V){let q=0,U="";try{let B=(await import("fs")).readdirSync(X).filter((J)=>J.endsWith(".md"));if(q=B.length,B.length>0)B.sort(),U=B[B.length-1]??""}catch{}if(q>0)process.stdout.write(` Escalations: ${q} handoff doc${q===1?"":"s"} (latest: ${U})
52
- `)}}function e7(K){if(!S(K))return[];try{return U1("fs").readdirSync(K).filter((Q)=>/^findings-\d+\.json$/.test(Q)).sort((Q,X)=>{let H=Number.parseInt(Q.replace(/[^0-9]/g,""),10)||0,Z=Number.parseInt(X.replace(/[^0-9]/g,""),10)||0;return H-Z}).map((Q)=>F(K,Q))}catch{return[]}}function U0(K){try{let $=U1("fs");return JSON.parse($.readFileSync(K,"utf-8"))}catch{return null}}async function K5(){let K=await r();if(!K)return process.stderr.write(`{"error": "Failed to generate JSON status. Ensure python3 is available."}
53
- `),1;let $=u,z=P(),Q=process.env.LOKI_DASHBOARD_PORT||"57374",X=process.env.LOKI_PROVIDER||"claude",H=await w([K,"-c",a7,$,z,Q,X],{timeoutMs:30000});if(H.exitCode!==0)return process.stderr.write(`{"error": "Failed to generate JSON status. Ensure python3 is available."}
54
- `),1;return process.stdout.write(H.stdout),0}async function $5(K){let $=[...K];while($.length>0){let z=$[0];if(z==="--json")return K5();if(z==="--help"||z==="-h")return process.stdout.write(`Usage: loki status [--json]
49
+ `),H){let U=z[z.length-1],V=J0(U);if(V&&Array.isArray(V.findings)){let G={Critical:0,High:0,Medium:0,Low:0};for(let J of V.findings){let Y=String(J.severity??"");if(Y in G)G[Y]=(G[Y]??0)+1}let B=Object.entries(G).filter(([,J])=>J>0).map(([J,Y])=>`${Y} ${J.toLowerCase()}`).join(", ");process.stdout.write(` Findings (iter ${V.iteration??"?"}): ${B||"none"} -- ${V.findings.length} total
50
+ `)}}if(Z){let U=J0(Q);if(U&&Array.isArray(U.learnings)&&U.learnings.length>0){let V=new Map;for(let B of U.learnings){let J=String(B.trigger??"unknown");V.set(J,(V.get(J)??0)+1)}let G=[...V.entries()].sort((B,J)=>J[1]-B[1]).slice(0,3).map(([B,J])=>`${J} ${B}`).join(", ");process.stdout.write(` Learnings: ${U.learnings.length} total (${G})
51
+ `)}}if(q){let U=0,V="";try{let B=(await import("fs")).readdirSync(X).filter((J)=>J.endsWith(".md"));if(U=B.length,B.length>0)B.sort(),V=B[B.length-1]??""}catch{}if(U>0)process.stdout.write(` Escalations: ${U} handoff doc${U===1?"":"s"} (latest: ${V})
52
+ `)}}function K5(K){if(!S(K))return[];try{return V1("fs").readdirSync(K).filter((Q)=>/^findings-\d+\.json$/.test(Q)).sort((Q,X)=>{let H=Number.parseInt(Q.replace(/[^0-9]/g,""),10)||0,Z=Number.parseInt(X.replace(/[^0-9]/g,""),10)||0;return H-Z}).map((Q)=>F(K,Q))}catch{return[]}}function J0(K){try{let $=V1("fs");return JSON.parse($.readFileSync(K,"utf-8"))}catch{return null}}async function $5(){let K=await r();if(!K)return process.stderr.write(`{"error": "Failed to generate JSON status. Ensure python3 is available."}
53
+ `),1;let $=u,z=P(),Q=process.env.LOKI_DASHBOARD_PORT||"57374",X=process.env.LOKI_PROVIDER||"claude",H=await w([K,"-c",s7,$,z,Q,X],{timeoutMs:30000});if(H.exitCode!==0)return process.stderr.write(`{"error": "Failed to generate JSON status. Ensure python3 is available."}
54
+ `),1;return process.stdout.write(H.stdout),0}async function z5(K){let $=[...K];while($.length>0){let z=$[0];if(z==="--json")return $5();if(z==="--help"||z==="-h")return process.stdout.write(`Usage: loki status [--json]
55
55
  `),0;return process.stdout.write(`${R}Unknown flag: ${z}${W}
56
56
  `),process.stdout.write(`Usage: loki status [--json]
57
- `),1}return t7()}var a7=`
57
+ `),1}return i7()}var s7=`
58
58
  import json, os, sys, time
59
59
 
60
60
  skill_dir = sys.argv[1]
@@ -261,9 +261,9 @@ if os.path.isfile(gate_count_file):
261
261
  result['phase1'] = phase1
262
262
 
263
263
  print(json.dumps(result, indent=2))
264
- `;var G0=L(()=>{p();Q1();n();y()});var O0={};b(O0,{runStats:()=>W5,computeStats:()=>Y0});import{readdirSync as B0,readFileSync as z5,statSync as M0}from"fs";import{join as l}from"path";function i(K){try{if(!M0(K).isFile())return null;return JSON.parse(z5(K,"utf-8"))}catch{return null}}function b1(K){try{return M0(K).isDirectory()}catch{return!1}}function Q5(K){if(!b1(K))return[];try{let $=B0(K).filter((z)=>z.startsWith("iteration-")&&z.endsWith(".json"));return $.sort(),$.map((z)=>l(K,z))}catch{return[]}}function e(K){return Math.trunc(K).toLocaleString("en-US")}function C1(K){let $=Math.trunc(K);if($<60)return`${$}s`;let z=Math.trunc($/3600),Q=Math.trunc($%3600/60),X=$%60;if(z>0)return`${z}h ${String(Q).padStart(2,"0")}m`;return`${Q}m ${String(X).padStart(2,"0")}s`}function d(K,$=0){let z=Math.pow(10,$);return Math.round(K*z)/z}function K1(K,$){return K.toFixed($)}function h1(K,$){return K.length>=$?K:K+" ".repeat($-K.length)}function X5(K){let $="N/A",z=0,Q=i(l(K,"state","orchestrator.json"));if(Q&&typeof Q==="object"){if(typeof Q.currentPhase==="string")$=Q.currentPhase;if(typeof Q.currentIteration==="number")z=Q.currentIteration}let X=l(K,"metrics","efficiency"),H=Q5(X),Z=[];for(let T of H){let I=i(T);if(I&&typeof I==="object")Z.push(I)}if(Z.length>0)z=Math.max(z,Z.length);let V=Z.reduce((T,I)=>T+(I.input_tokens??0),0),q=Z.reduce((T,I)=>T+(I.output_tokens??0),0),U=V+q,G=Z.reduce((T,I)=>T+(I.cost_usd??0),0),B=Z.reduce((T,I)=>T+(I.duration_seconds??0),0),J=0,Y=0,j=i(l(K,"metrics","budget.json"));if(j&&typeof j==="object"){if(typeof j.budget_limit==="number")J=j.budget_limit;if(typeof j.budget_used==="number")Y=j.budget_used}let _=0,C=0,g=i(l(K,"state","quality-gates.json"));if(g&&typeof g==="object"){if(Array.isArray(g)){for(let T of g)if(C+=1,T===!0)_+=1;else if(T&&typeof T==="object"){let I=T;if(I.passed===!0||I.status==="passed")_+=1}}else for(let T of Object.values(g))if(typeof T==="boolean"){if(C+=1,T)_+=1}else if(T&&typeof T==="object"){C+=1;let I=T;if(I.passed===!0||I.status==="passed")_+=1}}let m={},A=i(l(K,"quality","gate-failure-count.json"));if(A&&typeof A==="object"&&!Array.isArray(A)){let T={};for(let[I,v]of Object.entries(A))if(typeof v==="number")T[I]=v;m=T}let N=0,$1=0,a1=0,F1=l(K,"quality");if(b1(F1)){let T=[];try{T=B0(F1)}catch{T=[]}for(let I of T){if(!I.endsWith(".json")||I==="gate-failure-count.json")continue;let v=i(l(F1,I));if(!v||typeof v!=="object")continue;if(!(("verdict"in v)||("approved"in v)||("reviewers"in v)))continue;N+=1;let s1=(v.verdict??"").toString().toLowerCase();if(v.approved===!0||["approved","approve","pass"].includes(s1))$1+=1;else if(["revision","revise","changes_requested","reject"].includes(s1))a1+=1}}return{phase:$,iterationCount:z,iterations:Z,totalInput:V,totalOutput:q,totalTokens:U,totalCost:G,totalDuration:B,budgetLimit:J,budgetUsed:Y,gatesPassed:_,gatesTotal:C,gateFailures:m,reviewsTotal:N,reviewsApproved:$1,reviewsRevision:a1}}function Z5(K,$){let z=K.iterationCount,Q={session:{iterations:z,duration_seconds:K.totalDuration,phase:K.phase},tokens:{input:K.totalInput,output:K.totalOutput,total:K.totalTokens,cost_usd:d(K.totalCost,2)},quality:{gates_passed:K.gatesPassed,gates_total:K.gatesTotal,reviews_total:K.reviewsTotal,reviews_approved:K.reviewsApproved,reviews_revision:K.reviewsRevision,gate_failures:K.gateFailures},efficiency:{avg_tokens_per_iteration:z>0?d(K.totalTokens/z,0):0,avg_cost_per_iteration:z>0?d(K.totalCost/z,2):0,avg_duration_per_iteration:z>0?d(K.totalDuration/z,1):0},budget:{used:d(K.budgetUsed,2),limit:K.budgetLimit,percent:K.budgetLimit>0?d(K.budgetUsed/K.budgetLimit*100,1):0}};if($)Q.iterations=K.iterations.map((Z,V)=>({number:V+1,input_tokens:Z.input_tokens??0,output_tokens:Z.output_tokens??0,cost_usd:d(Z.cost_usd??0,2),duration_seconds:Z.duration_seconds??0}));let X=JSON.stringify(Q,null,2);function H(Z,V){if(!V)return;let q=new RegExp(`("${Z}": )(-?\\d+)(,?)$`,"m");X=X.replace(q,(U,G,B,J)=>`${G}${B}.0${J}`)}if(H("avg_duration_per_iteration",z>0&&Number.isInteger(Q.efficiency.avg_duration_per_iteration)),H("percent",K.budgetLimit>0&&Number.isInteger(Q.budget.percent)),H("cost_usd",z>0&&Number.isInteger(Q.tokens.cost_usd)),$)X=X.replace(/("cost_usd": )(-?\d+)(,?)$/gm,(Z,V,q,U)=>`${V}${q}.0${U}`);return X}function H5(K,$){let z=[];if(z.push("Loki Mode Session Statistics"),z.push("============================"),z.push(""),z.push("Session"),z.push(` Iterations completed: ${K.iterationCount}`),z.push(` Duration: ${C1(K.totalDuration)}`),z.push(` Current phase: ${K.phase}`),z.push(""),z.push("Token Usage"),K.iterations.length>0)z.push(` Input tokens: ${e(K.totalInput)}`),z.push(` Output tokens: ${e(K.totalOutput)}`),z.push(` Total tokens: ${e(K.totalTokens)}`),z.push(` Estimated cost: $${K1(K.totalCost,2)}`);else z.push(" N/A (no iteration metrics found)");if(z.push(""),z.push("Quality Gates"),K.gatesTotal>0){let Q=Math.round(K.gatesPassed/K.gatesTotal*100);z.push(` Gates passed: ${K.gatesPassed}/${K.gatesTotal} (${Q}%)`)}else z.push(" Gates passed: N/A");if(K.reviewsTotal>0){let Q=[];if(K.reviewsApproved>0)Q.push(`${K.reviewsApproved} approved`);if(K.reviewsRevision>0)Q.push(`${K.reviewsRevision} revision requested`);let X=Q.length>0?Q.join(", "):"N/A";z.push(` Code reviews: ${K.reviewsTotal} (${X})`)}if(Object.keys(K.gateFailures).length>0){let Q=Object.entries(K.gateFailures).filter(([,X])=>X>0).map(([X,H])=>`${X} (${H})`);if(Q.length>0)z.push(` Gate failures: ${Q.join(", ")}`)}if(z.push(""),z.push("Efficiency"),K.iterationCount>0&&K.iterations.length>0){let Q=Math.round(K.totalTokens/K.iterationCount),X=K.totalCost/K.iterationCount,H=K.totalDuration/K.iterationCount;z.push(` Avg tokens/iteration: ${e(Q)}`),z.push(` Avg cost/iteration: $${K1(X,2)}`),z.push(` Avg duration/iteration: ${C1(H)}`)}else z.push(" N/A (no iteration metrics found)");if(z.push(""),z.push("Budget"),K.budgetLimit>0){let Q=d(K.budgetUsed/K.budgetLimit*100,1),X=Number.isInteger(Q)?`${Q}.0`:`${Q}`;z.push(` Used: $${K1(K.budgetUsed,2)} / $${K1(K.budgetLimit,2)} (${X}%)`)}else if(K.budgetUsed>0)z.push(` Used: $${K1(K.budgetUsed,2)} (no limit set)`);else z.push(" N/A");if($&&K.iterations.length>0)z.push(""),z.push("Per-Iteration Breakdown"),K.iterations.forEach((Q,X)=>{let H=X+1,Z=h1(e(Q.input_tokens??0),10),V=h1(e(Q.output_tokens??0),10),q=Q.cost_usd??0,U=C1(Q.duration_seconds??0),G=h1(`${H}`,3);z.push(` #${G} input: ${Z} output: ${V} cost: $${K1(q,2)} time: ${U}`)});return z.join(`
265
- `)}function Y0(K){let $=!1,z=!1;for(let Z of K)if(Z==="--json")$=!0;else if(Z==="--efficiency")z=!0;let Q=P();if(!b1(Q)){if($)return{exitCode:0,stdout:'{"error": "No active session"}'};return{exitCode:0,stdout:`${E}No active session found.${W}
266
- Start a session with: loki start <prd>`}}let X=X5(Q);return{exitCode:0,stdout:$?Z5(X,z):H5(X,z)}}async function W5(K){let $=Y0(K);return console.log($.stdout),$.exitCode}var T0=L(()=>{y();n()});var x0={};b(x0,{runDoctor:()=>_5,pythonImportOk:()=>g1,httpReachable:()=>y1,checkTool:()=>P0,checkSkills:()=>L0,checkDisk:()=>v1,buildDoctorJson:()=>E0,_setPythonImportOkForTest:()=>M5});import{existsSync as V5,lstatSync as q5,readlinkSync as U5,statfsSync as J5}from"fs";import{homedir as j0}from"os";import{resolve as A0}from"path";function B5(K){let $=K.match(G5);return $?$[1]:null}async function _0(K){try{let $=await w([K,"--version"],{timeoutMs:5000}),z=($.stdout||$.stderr||"").trim();return B5(z)}catch{return null}}function I0(K,$){let z=K.split(".").map((X)=>parseInt(X,10)),Q=$.split(".").map((X)=>parseInt(X,10));while(z.length<2)z.push(0);while(Q.length<2)Q.push(0);for(let X=0;X<2;X++){let H=z[X]??0,Z=Q[X]??0;if(Number.isNaN(H)||Number.isNaN(Z))return 0;if(H!==Z)return H-Z}return 0}async function P0(K,$,z,Q=null){let X=await h($),H=X!==null,Z=H?await _0($):null,V="pass";if(!H)V=z==="required"?"fail":"warn";else if(Q&&Z){if(I0(Z,Q)<0)V=z==="required"?"fail":"warn"}return{name:K,command:$,found:H,version:Z,required:z,min_version:Q,status:V,path:X}}function v1(){let K=null;try{let z=J5(j0()),Q=Number(z.bavail)*Number(z.bsize);K=Math.round(Q/1073741824*10)/10}catch{K=null}let $="pass";if(K!==null){if(K<1)$="fail";else if(K<5)$="warn"}return{available_gb:K,status:$}}async function y1(K,$=2000){try{return(await fetch(K,{signal:AbortSignal.timeout($)})).ok}catch{return!1}}async function g1(K,$=!1){let z=`import ${K}`,Q=$?30000:5000;if(!$)return(await a(z,{timeoutMs:Q})).exitCode===0;let X=await r();if(!X)return!1;return(await w([X,"-c",z],{timeoutMs:Q})).exitCode===0}function M5(K){O1.fn=K??g1}function L0(){let K=j0();return Y5.map(({name:$,dir:z})=>{let Q=A0(K,z),X=Q,H=A0(Q,"SKILL.md");if(V5(H))return{name:$,path:X,status:"pass",detail:""};try{if(q5(Q).isSymbolicLink()){let V="unknown";try{V=U5(Q)}catch{}return{name:$,path:X,status:"fail",detail:`(broken symlink -> ${V})`}}}catch{}return{name:$,path:X,status:"warn",detail:"(not found - run 'loki setup-skill')"}})}async function R0(){return Promise.all(O5.map(async(K)=>{return{...await P0(K.jsonName,K.cmd,K.required,K.min??null),displayName:K.displayName}}))}async function T5(){let $=await h("sentrux")!==null,z=$?await _0("sentrux"):null;return{found:$,version:z,status:$?"pass":"warn",required:"optional"}}async function E0(){let $=(await R0()).map(({displayName:V,...q})=>q),z=v1(),Q=await T5(),X=0,H=0,Z=0;for(let V of $)if(V.status==="pass")X++;else if(V.status==="fail")H++;else Z++;if(z.status==="pass")X++;else if(z.status==="fail")H++;else Z++;return{checks:$,disk:z,sentrux:Q,summary:{passed:X,failed:H,warnings:Z,ok:H===0}}}function M(K){switch(K){case"pass":return`${D}PASS${W}`;case"fail":return`${R}FAIL${W}`;case"warn":return`${E}WARN${W}`}}function M1(K){let $=K.version?` (v${K.version})`:"",z=K.displayName;if(!K.found){let Q=K.required==="required"?"not found":K.required==="recommended"?"not found (recommended)":"not found (optional)";return` ${M(K.status)} ${z} - ${Q}`}if(K.min_version&&K.version&&I0(K.version,K.min_version)<0){let Q=K.required==="required"?"requires":"recommended";return` ${M(K.status)} ${z}${$} - ${Q} >= ${K.min_version}`}return` ${M(K.status)} ${z}${$}`}function Y1(K,$){if($==="pass")K.pass++;else if($==="fail")K.fail++;else K.warn++}function A5(){process.stdout.write(`${k}loki doctor${W} - Check system prerequisites
264
+ `;var B0=L(()=>{p();Q1();n();y()});var T0={};b(T0,{runStats:()=>q5,computeStats:()=>O0});import{readdirSync as M0,readFileSync as Q5,statSync as Y0}from"fs";import{join as l}from"path";function i(K){try{if(!Y0(K).isFile())return null;return JSON.parse(Q5(K,"utf-8"))}catch{return null}}function v1(K){try{return Y0(K).isDirectory()}catch{return!1}}function X5(K){if(!v1(K))return[];try{let $=M0(K).filter((z)=>z.startsWith("iteration-")&&z.endsWith(".json"));return $.sort(),$.map((z)=>l(K,z))}catch{return[]}}function e(K){return Math.trunc(K).toLocaleString("en-US")}function b1(K){let $=Math.trunc(K);if($<60)return`${$}s`;let z=Math.trunc($/3600),Q=Math.trunc($%3600/60),X=$%60;if(z>0)return`${z}h ${String(Q).padStart(2,"0")}m`;return`${Q}m ${String(X).padStart(2,"0")}s`}function d(K,$=0){let z=Math.pow(10,$);return Math.round(K*z)/z}function K1(K,$){return K.toFixed($)}function y1(K,$){return K.length>=$?K:K+" ".repeat($-K.length)}function Z5(K){let $="N/A",z=0,Q=i(l(K,"state","orchestrator.json"));if(Q&&typeof Q==="object"){if(typeof Q.currentPhase==="string")$=Q.currentPhase;if(typeof Q.currentIteration==="number")z=Q.currentIteration}let X=l(K,"metrics","efficiency"),H=X5(X),Z=[];for(let T of H){let I=i(T);if(I&&typeof I==="object")Z.push(I)}if(Z.length>0)z=Math.max(z,Z.length);let q=Z.reduce((T,I)=>T+(I.input_tokens??0),0),U=Z.reduce((T,I)=>T+(I.output_tokens??0),0),V=q+U,G=Z.reduce((T,I)=>T+(I.cost_usd??0),0),B=Z.reduce((T,I)=>T+(I.duration_seconds??0),0),J=0,Y=0,j=i(l(K,"metrics","budget.json"));if(j&&typeof j==="object"){if(typeof j.budget_limit==="number")J=j.budget_limit;if(typeof j.budget_used==="number")Y=j.budget_used}let _=0,C=0,g=i(l(K,"state","quality-gates.json"));if(g&&typeof g==="object"){if(Array.isArray(g)){for(let T of g)if(C+=1,T===!0)_+=1;else if(T&&typeof T==="object"){let I=T;if(I.passed===!0||I.status==="passed")_+=1}}else for(let T of Object.values(g))if(typeof T==="boolean"){if(C+=1,T)_+=1}else if(T&&typeof T==="object"){C+=1;let I=T;if(I.passed===!0||I.status==="passed")_+=1}}let m={},A=i(l(K,"quality","gate-failure-count.json"));if(A&&typeof A==="object"&&!Array.isArray(A)){let T={};for(let[I,v]of Object.entries(A))if(typeof v==="number")T[I]=v;m=T}let N=0,$1=0,r1=0,w1=l(K,"quality");if(v1(w1)){let T=[];try{T=M0(w1)}catch{T=[]}for(let I of T){if(!I.endsWith(".json")||I==="gate-failure-count.json")continue;let v=i(l(w1,I));if(!v||typeof v!=="object")continue;if(!(("verdict"in v)||("approved"in v)||("reviewers"in v)))continue;N+=1;let t1=(v.verdict??"").toString().toLowerCase();if(v.approved===!0||["approved","approve","pass"].includes(t1))$1+=1;else if(["revision","revise","changes_requested","reject"].includes(t1))r1+=1}}return{phase:$,iterationCount:z,iterations:Z,totalInput:q,totalOutput:U,totalTokens:V,totalCost:G,totalDuration:B,budgetLimit:J,budgetUsed:Y,gatesPassed:_,gatesTotal:C,gateFailures:m,reviewsTotal:N,reviewsApproved:$1,reviewsRevision:r1}}function H5(K,$){let z=K.iterationCount,Q={session:{iterations:z,duration_seconds:K.totalDuration,phase:K.phase},tokens:{input:K.totalInput,output:K.totalOutput,total:K.totalTokens,cost_usd:d(K.totalCost,2)},quality:{gates_passed:K.gatesPassed,gates_total:K.gatesTotal,reviews_total:K.reviewsTotal,reviews_approved:K.reviewsApproved,reviews_revision:K.reviewsRevision,gate_failures:K.gateFailures},efficiency:{avg_tokens_per_iteration:z>0?d(K.totalTokens/z,0):0,avg_cost_per_iteration:z>0?d(K.totalCost/z,2):0,avg_duration_per_iteration:z>0?d(K.totalDuration/z,1):0},budget:{used:d(K.budgetUsed,2),limit:K.budgetLimit,percent:K.budgetLimit>0?d(K.budgetUsed/K.budgetLimit*100,1):0}};if($)Q.iterations=K.iterations.map((Z,q)=>({number:q+1,input_tokens:Z.input_tokens??0,output_tokens:Z.output_tokens??0,cost_usd:d(Z.cost_usd??0,2),duration_seconds:Z.duration_seconds??0}));let X=JSON.stringify(Q,null,2);function H(Z,q){if(!q)return;let U=new RegExp(`("${Z}": )(-?\\d+)(,?)$`,"m");X=X.replace(U,(V,G,B,J)=>`${G}${B}.0${J}`)}if(H("avg_duration_per_iteration",z>0&&Number.isInteger(Q.efficiency.avg_duration_per_iteration)),H("percent",K.budgetLimit>0&&Number.isInteger(Q.budget.percent)),H("cost_usd",z>0&&Number.isInteger(Q.tokens.cost_usd)),$)X=X.replace(/("cost_usd": )(-?\d+)(,?)$/gm,(Z,q,U,V)=>`${q}${U}.0${V}`);return X}function W5(K,$){let z=[];if(z.push("Loki Mode Session Statistics"),z.push("============================"),z.push(""),z.push("Session"),z.push(` Iterations completed: ${K.iterationCount}`),z.push(` Duration: ${b1(K.totalDuration)}`),z.push(` Current phase: ${K.phase}`),z.push(""),z.push("Token Usage"),K.iterations.length>0)z.push(` Input tokens: ${e(K.totalInput)}`),z.push(` Output tokens: ${e(K.totalOutput)}`),z.push(` Total tokens: ${e(K.totalTokens)}`),z.push(` Estimated cost: $${K1(K.totalCost,2)}`);else z.push(" N/A (no iteration metrics found)");if(z.push(""),z.push("Quality Gates"),K.gatesTotal>0){let Q=Math.round(K.gatesPassed/K.gatesTotal*100);z.push(` Gates passed: ${K.gatesPassed}/${K.gatesTotal} (${Q}%)`)}else z.push(" Gates passed: N/A");if(K.reviewsTotal>0){let Q=[];if(K.reviewsApproved>0)Q.push(`${K.reviewsApproved} approved`);if(K.reviewsRevision>0)Q.push(`${K.reviewsRevision} revision requested`);let X=Q.length>0?Q.join(", "):"N/A";z.push(` Code reviews: ${K.reviewsTotal} (${X})`)}if(Object.keys(K.gateFailures).length>0){let Q=Object.entries(K.gateFailures).filter(([,X])=>X>0).map(([X,H])=>`${X} (${H})`);if(Q.length>0)z.push(` Gate failures: ${Q.join(", ")}`)}if(z.push(""),z.push("Efficiency"),K.iterationCount>0&&K.iterations.length>0){let Q=Math.round(K.totalTokens/K.iterationCount),X=K.totalCost/K.iterationCount,H=K.totalDuration/K.iterationCount;z.push(` Avg tokens/iteration: ${e(Q)}`),z.push(` Avg cost/iteration: $${K1(X,2)}`),z.push(` Avg duration/iteration: ${b1(H)}`)}else z.push(" N/A (no iteration metrics found)");if(z.push(""),z.push("Budget"),K.budgetLimit>0){let Q=d(K.budgetUsed/K.budgetLimit*100,1),X=Number.isInteger(Q)?`${Q}.0`:`${Q}`;z.push(` Used: $${K1(K.budgetUsed,2)} / $${K1(K.budgetLimit,2)} (${X}%)`)}else if(K.budgetUsed>0)z.push(` Used: $${K1(K.budgetUsed,2)} (no limit set)`);else z.push(" N/A");if($&&K.iterations.length>0)z.push(""),z.push("Per-Iteration Breakdown"),K.iterations.forEach((Q,X)=>{let H=X+1,Z=y1(e(Q.input_tokens??0),10),q=y1(e(Q.output_tokens??0),10),U=Q.cost_usd??0,V=b1(Q.duration_seconds??0),G=y1(`${H}`,3);z.push(` #${G} input: ${Z} output: ${q} cost: $${K1(U,2)} time: ${V}`)});return z.join(`
265
+ `)}function O0(K){let $=!1,z=!1;for(let Z of K)if(Z==="--json")$=!0;else if(Z==="--efficiency")z=!0;let Q=P();if(!v1(Q)){if($)return{exitCode:0,stdout:'{"error": "No active session"}'};return{exitCode:0,stdout:`${E}No active session found.${W}
266
+ Start a session with: loki start <prd>`}}let X=Z5(Q);return{exitCode:0,stdout:$?H5(X,z):W5(X,z)}}async function q5(K){let $=O0(K);return console.log($.stdout),$.exitCode}var A0=L(()=>{y();n()});var F0={};b(F0,{runDoctor:()=>I5,pythonImportOk:()=>f1,httpReachable:()=>g1,checkTool:()=>L0,checkSkills:()=>R0,checkDisk:()=>m1,buildDoctorJson:()=>x0,_setPythonImportOkForTest:()=>Y5});import{existsSync as U5,lstatSync as V5,readlinkSync as J5,statfsSync as G5}from"fs";import{homedir as _0}from"os";import{resolve as j0}from"path";function M5(K){let $=K.match(B5);return $?$[1]:null}async function I0(K){try{let $=await w([K,"--version"],{timeoutMs:5000}),z=($.stdout||$.stderr||"").trim();return M5(z)}catch{return null}}function P0(K,$){let z=K.split(".").map((X)=>parseInt(X,10)),Q=$.split(".").map((X)=>parseInt(X,10));while(z.length<2)z.push(0);while(Q.length<2)Q.push(0);for(let X=0;X<2;X++){let H=z[X]??0,Z=Q[X]??0;if(Number.isNaN(H)||Number.isNaN(Z))return 0;if(H!==Z)return H-Z}return 0}async function L0(K,$,z,Q=null){let X=await h($),H=X!==null,Z=H?await I0($):null,q="pass";if(!H)q=z==="required"?"fail":"warn";else if(Q&&Z){if(P0(Z,Q)<0)q=z==="required"?"fail":"warn"}return{name:K,command:$,found:H,version:Z,required:z,min_version:Q,status:q,path:X}}function m1(){let K=null;try{let z=G5(_0()),Q=Number(z.bavail)*Number(z.bsize);K=Math.round(Q/1073741824*10)/10}catch{K=null}let $="pass";if(K!==null){if(K<1)$="fail";else if(K<5)$="warn"}return{available_gb:K,status:$}}async function g1(K,$=2000){try{return(await fetch(K,{signal:AbortSignal.timeout($)})).ok}catch{return!1}}async function f1(K,$=!1){let z=`import ${K}`,Q=$?30000:5000;if(!$)return(await a(z,{timeoutMs:Q})).exitCode===0;let X=await r();if(!X)return!1;return(await w([X,"-c",z],{timeoutMs:Q})).exitCode===0}function Y5(K){T1.fn=K??f1}function R0(){let K=_0();return O5.map(({name:$,dir:z})=>{let Q=j0(K,z),X=Q,H=j0(Q,"SKILL.md");if(U5(H))return{name:$,path:X,status:"pass",detail:""};try{if(V5(Q).isSymbolicLink()){let q="unknown";try{q=J5(Q)}catch{}return{name:$,path:X,status:"fail",detail:`(broken symlink -> ${q})`}}}catch{}return{name:$,path:X,status:"warn",detail:"(not found - run 'loki setup-skill')"}})}async function E0(){return Promise.all(T5.map(async(K)=>{return{...await L0(K.jsonName,K.cmd,K.required,K.min??null),displayName:K.displayName}}))}async function A5(){let $=await h("sentrux")!==null,z=$?await I0("sentrux"):null;return{found:$,version:z,status:$?"pass":"warn",required:"optional"}}async function x0(){let $=(await E0()).map(({displayName:q,...U})=>U),z=m1(),Q=await A5(),X=0,H=0,Z=0;for(let q of $)if(q.status==="pass")X++;else if(q.status==="fail")H++;else Z++;if(z.status==="pass")X++;else if(z.status==="fail")H++;else Z++;return{loki_mode_version:G1(),checks:$,disk:z,sentrux:Q,summary:{passed:X,failed:H,warnings:Z,ok:H===0}}}function M(K){switch(K){case"pass":return`${D}PASS${W}`;case"fail":return`${R}FAIL${W}`;case"warn":return`${E}WARN${W}`}}function Y1(K){let $=K.version?` (v${K.version})`:"",z=K.displayName;if(!K.found){let Q=K.required==="required"?"not found":K.required==="recommended"?"not found (recommended)":"not found (optional)";return` ${M(K.status)} ${z} - ${Q}`}if(K.min_version&&K.version&&P0(K.version,K.min_version)<0){let Q=K.required==="required"?"requires":"recommended";return` ${M(K.status)} ${z}${$} - ${Q} >= ${K.min_version}`}return` ${M(K.status)} ${z}${$}`}function O1(K,$){if($==="pass")K.pass++;else if($==="fail")K.fail++;else K.warn++}function j5(){process.stdout.write(`${k}loki doctor${W} - Check system prerequisites
267
267
 
268
268
  `),process.stdout.write(`Usage: loki doctor [--json]
269
269
 
@@ -272,43 +272,43 @@ Start a session with: loki start <prd>`}}let X=X5(Q);return{exitCode:0,stdout:$?
272
272
 
273
273
  `),process.stdout.write(`Checks: node, python3, jq, git, curl, bash version,
274
274
  `),process.stdout.write(` claude/codex CLIs, and disk space.
275
- `)}async function j5(){process.stdout.write(`${k}Loki Mode Doctor${W}
275
+ `)}async function _5(){process.stdout.write(`${k}Loki Mode Doctor${W}
276
276
 
277
277
  `),process.stdout.write(`Checking system prerequisites...
278
278
 
279
- `);let K={pass:0,fail:0,warn:0},$=await R0(),z=new Map($.map((A)=>[A.command,A]));process.stdout.write(`${O}Required:${W}
280
- `);for(let A of["node","python3","jq","git","curl"]){let N=z.get(A);process.stdout.write(M1(N)+`
281
- `),Y1(K,N.status)}process.stdout.write(`
279
+ `);let K={pass:0,fail:0,warn:0},$=await E0(),z=new Map($.map((A)=>[A.command,A]));process.stdout.write(`${O}Required:${W}
280
+ `);for(let A of["node","python3","jq","git","curl"]){let N=z.get(A);process.stdout.write(Y1(N)+`
281
+ `),O1(K,N.status)}process.stdout.write(`
282
282
  `),process.stdout.write(`${O}AI Providers:${W}
283
- `);let Q=["claude","codex","cline","aider"],X=!1;for(let A of Q){let N=z.get(A);if(process.stdout.write(M1(N)+`
284
- `),Y1(K,N.status),N.found)X=!0}if(!X)process.stdout.write(` ${M("fail")} No AI provider CLI installed -- at least one is required
283
+ `);let Q=["claude","codex","cline","aider"],X=!1;for(let A of Q){let N=z.get(A);if(process.stdout.write(Y1(N)+`
284
+ `),O1(K,N.status),N.found)X=!0}if(!X)process.stdout.write(` ${M("fail")} No AI provider CLI installed -- at least one is required
285
285
  `),process.stdout.write(` ${E}Install: npm install -g @anthropic-ai/claude-code${W}
286
286
  `),K.fail++;process.stdout.write(`
287
287
  `),process.stdout.write(`${O}API Keys:${W}
288
- `);let H=z.get("claude")?.found??!1,Z=z.get("codex")?.found??!1,V=process.env;if(V.ANTHROPIC_API_KEY)process.stdout.write(` ${M("pass")} ANTHROPIC_API_KEY is set
288
+ `);let H=z.get("claude")?.found??!1,Z=z.get("codex")?.found??!1,q=process.env;if(q.ANTHROPIC_API_KEY)process.stdout.write(` ${M("pass")} ANTHROPIC_API_KEY is set
289
289
  `),K.pass++;else if(H)process.stdout.write(` ${x} -- ${W} ANTHROPIC_API_KEY not set (Claude CLI uses its own login)
290
- `);if(V.OPENAI_API_KEY)process.stdout.write(` ${M("pass")} OPENAI_API_KEY is set
290
+ `);if(q.OPENAI_API_KEY)process.stdout.write(` ${M("pass")} OPENAI_API_KEY is set
291
291
  `),K.pass++;else if(Z)process.stdout.write(` ${x} -- ${W} OPENAI_API_KEY not set (Codex CLI uses its own login)
292
- `);if(V.ANTHROPIC_BASE_URL){let A=V.ANTHROPIC_BASE_URL;if(process.stdout.write(` ${M("pass")} ANTHROPIC_BASE_URL: ${A}
293
- `),K.pass++,!V.LOKI_MODEL_OVERRIDE)process.stdout.write(` ${M("warn")} LOKI_MODEL_OVERRIDE not set -- opus/sonnet/haiku aliases may not resolve on alt-provider
294
- `),K.warn++;else process.stdout.write(` ${M("pass")} LOKI_MODEL_OVERRIDE: ${V.LOKI_MODEL_OVERRIDE}
292
+ `);if(q.ANTHROPIC_BASE_URL){let A=q.ANTHROPIC_BASE_URL;if(process.stdout.write(` ${M("pass")} ANTHROPIC_BASE_URL: ${A}
293
+ `),K.pass++,!q.LOKI_MODEL_OVERRIDE)process.stdout.write(` ${M("warn")} LOKI_MODEL_OVERRIDE not set -- opus/sonnet/haiku aliases may not resolve on alt-provider
294
+ `),K.warn++;else process.stdout.write(` ${M("pass")} LOKI_MODEL_OVERRIDE: ${q.LOKI_MODEL_OVERRIDE}
295
295
  `),K.pass++}process.stdout.write(`
296
296
  `),process.stdout.write(`${O}Skills:${W}
297
- `);for(let A of L0())if(A.status==="pass")process.stdout.write(` ${M("pass")} ${A.name} ${x}${A.path}${W}
297
+ `);for(let A of R0())if(A.status==="pass")process.stdout.write(` ${M("pass")} ${A.name} ${x}${A.path}${W}
298
298
  `),K.pass++;else if(A.status==="fail")process.stdout.write(` ${M("fail")} ${A.name} ${x}${A.detail}${W}
299
299
  `),process.stdout.write(` ${E}Fix: loki setup-skill${W}
300
300
  `),K.fail++;else process.stdout.write(` ${M("warn")} ${A.name} ${x}${A.detail}${W}
301
301
  `),K.warn++;process.stdout.write(`
302
302
  `),process.stdout.write(`${O}Integrations:${W}
303
- `);let[q,U,G]=await Promise.all([O1.fn("mcp"),O1.fn("numpy",!0),O1.fn("sentence_transformers",!0)]);if(q)process.stdout.write(` ${M("pass")} MCP SDK (Python)
303
+ `);let[U,V,G]=await Promise.all([T1.fn("mcp"),T1.fn("numpy",!0),T1.fn("sentence_transformers",!0)]);if(U)process.stdout.write(` ${M("pass")} MCP SDK (Python)
304
304
  `),K.pass++;else process.stdout.write(` ${M("warn")} MCP SDK - not installed (pip3 install mcp)
305
- `),K.warn++;if(U)process.stdout.write(` ${M("pass")} numpy (vector search)
305
+ `),K.warn++;if(V)process.stdout.write(` ${M("pass")} numpy (vector search)
306
306
  `),K.pass++;else process.stdout.write(` ${M("warn")} numpy - not installed (pip3 install numpy)
307
307
  `),K.warn++;if(G)process.stdout.write(` ${M("pass")} sentence-transformers (embeddings)
308
308
  `),K.pass++;else process.stdout.write(` ${M("warn")} sentence-transformers - not installed (loki memory vectors setup)
309
- `),K.warn++;if(await y1("http://localhost:8100/api/v2/heartbeat"))process.stdout.write(` ${M("pass")} ChromaDB server (port 8100)
309
+ `),K.warn++;if(await g1("http://localhost:8100/api/v2/heartbeat"))process.stdout.write(` ${M("pass")} ChromaDB server (port 8100)
310
310
  `),K.pass++;else process.stdout.write(` ${M("warn")} ChromaDB - not running (docker start loki-chroma)
311
- `),K.warn++;let B=process.env.LOKI_MIROFISH_URL;if(B)if(await y1(`${B}/health`))process.stdout.write(` ${M("pass")} MiroFish server (${B})
311
+ `),K.warn++;let B=process.env.LOKI_MIROFISH_URL;if(B)if(await g1(`${B}/health`))process.stdout.write(` ${M("pass")} MiroFish server (${B})
312
312
  `),K.pass++;else process.stdout.write(` ${M("warn")} MiroFish - not running (loki start --mirofish-docker <image>)
313
313
  `),K.warn++;if(process.env.LOKI_OTEL_ENDPOINT)process.stdout.write(` ${M("pass")} OTEL endpoint: ${process.env.LOKI_OTEL_ENDPOINT}
314
314
  `),K.pass++;else process.stdout.write(` ${M("warn")} OTEL - not configured (set LOKI_OTEL_ENDPOINT)
@@ -316,9 +316,9 @@ Start a session with: loki start <prd>`}}let X=X5(Q);return{exitCode:0,stdout:$?
316
316
  `),K.pass++}else process.stdout.write(` ${M("warn")} sentrux - not installed (optional, brew install sentrux/tap/sentrux)
317
317
  `),K.warn++;process.stdout.write(`
318
318
  `),process.stdout.write(`${O}System:${W}
319
- `);let J=z.get("bash");process.stdout.write(M1(J)+`
320
- `),Y1(K,J.status);let Y=z.get("bun");if(Y)process.stdout.write(M1(Y)+`
321
- `),Y1(K,Y.status);let j=v1(),_=j.available_gb===null?null:Math.floor(j.available_gb);if(_===null)process.stdout.write(` ${M("warn")} Disk space: unable to determine
319
+ `);let J=z.get("bash");process.stdout.write(Y1(J)+`
320
+ `),O1(K,J.status);let Y=z.get("bun");if(Y)process.stdout.write(Y1(Y)+`
321
+ `),O1(K,Y.status);let j=m1(),_=j.available_gb===null?null:Math.floor(j.available_gb);if(_===null)process.stdout.write(` ${M("warn")} Disk space: unable to determine
322
322
  `),K.warn++;else if(j.status==="fail")process.stdout.write(` ${M("fail")} Disk space: ${_}GB available (need >= 1GB)
323
323
  `),K.fail++;else if(j.status==="warn")process.stdout.write(` ${M("warn")} Disk space: ${_}GB available (low)
324
324
  `),K.warn++;else process.stdout.write(` ${M("pass")} Disk space: ${_}GB available
@@ -339,15 +339,15 @@ Start a session with: loki start <prd>`}}let X=X5(Q);return{exitCode:0,stdout:$?
339
339
  `),process.stdout.write(`Install missing dependencies and run 'loki doctor' again.
340
340
  `),1;if(K.warn>0)return process.stdout.write(`${E}All required checks passed with some warnings.${W}
341
341
  `),0;return process.stdout.write(`${D}All checks passed. System is ready for Loki Mode.${W}
342
- `),0}async function _5(K){let $=!1;for(let z of K)if(z==="--json")$=!0;else if(z==="--help"||z==="-h")return A5(),0;else return process.stderr.write(`${R}Unknown option: ${z}${W}
342
+ `),0}async function I5(K){let $=!1;for(let z of K)if(z==="--json")$=!0;else if(z==="--help"||z==="-h")return j5(),0;else return process.stderr.write(`${R}Unknown option: ${z}${W}
343
343
  `),process.stderr.write(`Usage: loki doctor [--json]
344
- `),1;if($){let z=await E0();return process.stdout.write(JSON.stringify(z,null,2)+`
345
- `),0}return j5()}var G5,O1,Y5,O5;var F0=L(()=>{p();Q1();n();G5=/(\d+\.\d+(?:\.\d+)*)/;O1={fn:g1};Y5=[{name:"Claude Code",dir:".claude/skills/loki-mode"},{name:"Codex CLI",dir:".codex/skills/loki-mode"},{name:"Cline CLI",dir:".cline/skills/loki-mode"},{name:"Aider CLI",dir:".aider/skills/loki-mode"}];O5=[{displayName:"Node.js (>= 18)",jsonName:"Node.js",cmd:"node",required:"required",min:"18.0"},{displayName:"Python 3 (>= 3.8)",jsonName:"Python 3",cmd:"python3",required:"required",min:"3.8"},{displayName:"jq",jsonName:"jq",cmd:"jq",required:"required"},{displayName:"git",jsonName:"git",cmd:"git",required:"required"},{displayName:"curl",jsonName:"curl",cmd:"curl",required:"required"},{displayName:"bash (>= 4.0)",jsonName:"bash",cmd:"bash",required:"recommended",min:"4.0"},{displayName:"Bun (>= 1.3)",jsonName:"Bun",cmd:"bun",required:"recommended",min:"1.3"},{displayName:"Claude CLI",jsonName:"Claude CLI",cmd:"claude",required:"optional"},{displayName:"Codex CLI",jsonName:"Codex CLI",cmd:"codex",required:"optional"},{displayName:"Cline CLI",jsonName:"Cline CLI",cmd:"cline",required:"optional"},{displayName:"Aider CLI",jsonName:"Aider CLI",cmd:"aider",required:"optional"}]});import{existsSync as N0,mkdirSync as t6,readdirSync as I5,readFileSync as k0,renameSync as i6,writeFileSync as e6}from"fs";import{dirname as P5,join as L5,resolve as R5}from"path";import{fileURLToPath as E5}from"url";function x5(){try{let K=P5(E5(import.meta.url)),$=R5(K,"..","..","data","model-pricing.json");if(!N0($))return H1;let Q=JSON.parse(k0($,"utf8")).pricing;if(!Q||typeof Q!=="object")return H1;let X={};for(let[H,Z]of Object.entries(Q))if(Z!==null&&typeof Z==="object"&&typeof Z.input==="number"&&typeof Z.output==="number")X[H]={input:Z.input,output:Z.output};for(let H of Object.keys(H1))if(!(H in X))return H1;return X}catch{return H1}}function F5(K){return Math.round((K+Number.EPSILON)*1e4)/1e4}function w5(K){let $=(K??S0).toLowerCase();return w0[$]??w0[S0]}function D0(K){let $=0;for(let z of K){if(typeof z.cost_usd==="number"&&Number.isFinite(z.cost_usd)){$+=z.cost_usd;continue}let Q=w5(z.model),X=typeof z.input_tokens==="number"?z.input_tokens:0,H=typeof z.output_tokens==="number"?z.output_tokens:0;$+=X/1e6*Q.input+H/1e6*Q.output}return F5($)}function C0(K){if(!N0(K))return[];let $=[],z;try{z=I5(K)}catch{return[]}for(let Q of z){if(!Q.endsWith(".json"))continue;let X=L5(K,Q);try{let H=k0(X,"utf8"),Z=JSON.parse(H);if(Z&&typeof Z==="object")$.push(Z)}catch{}}return $}var H1,w0,S0="sonnet";var h0=L(()=>{y();H1={opus:{input:5,output:25},sonnet:{input:3,output:15},haiku:{input:1,output:5},"gpt-5.3-codex":{input:1.5,output:12}};w0=Object.freeze(x5())});import{existsSync as T1,readdirSync as S5,readFileSync as N5,statSync as k5}from"fs";import{join as A1}from"path";function D5(K){let $=[],z=A1(K,"votes");if(!T1(z))return $;let Q;try{Q=S5(z)}catch{return $}for(let X of Q){if(!X.startsWith("round-")||!X.endsWith(".json"))continue;try{let H=A1(z,X);if(!k5(H).isFile())continue;let Z=JSON.parse(N5(H,"utf8"));$.push({iteration:typeof Z.iteration==="number"?Z.iteration:void 0,verdict:typeof Z.verdict==="string"?Z.verdict:void 0,complete_votes:typeof Z.complete_votes==="number"?Z.complete_votes:void 0,total_members:typeof Z.total_members==="number"?Z.total_members:void 0,threshold:typeof Z.threshold==="number"?Z.threshold:void 0})}catch{}}return $}function C5(){return{iteration_count:0,total_cost_usd:0,avg_cost_per_iteration:null,total_input_tokens:0,total_output_tokens:0,total_duration_ms:0,avg_duration_ms_per_iteration:null,model_breakdown:{},phase_breakdown:{},status_breakdown:{}}}function h5(){return{council_rounds:0,unanimous_rate:null,approval_rate:null,iteration_success_rate:null}}function b5(K){let $=C5();if(K.length===0)return $;$.iteration_count=K.length,$.total_cost_usd=Math.round(D0(K)*1e4)/1e4;for(let z of K){if(typeof z.input_tokens==="number")$.total_input_tokens+=z.input_tokens;if(typeof z.output_tokens==="number")$.total_output_tokens+=z.output_tokens;let Q=z;if(typeof Q.duration_ms==="number")$.total_duration_ms+=Q.duration_ms;if(typeof z.model==="string")$.model_breakdown[z.model]=($.model_breakdown[z.model]??0)+1;if(typeof Q.phase==="string")$.phase_breakdown[Q.phase]=($.phase_breakdown[Q.phase]??0)+1;if(typeof Q.status==="string")$.status_breakdown[Q.status]=($.status_breakdown[Q.status]??0)+1}return $.avg_cost_per_iteration=Math.round($.total_cost_usd/$.iteration_count*1e4)/1e4,$.avg_duration_ms_per_iteration=Math.round($.total_duration_ms/$.iteration_count),$}function y5(K,$,z){let Q=h5();if(Q.council_rounds=K.length,K.length>0){let X=0,H=0;for(let Z of K){if(typeof Z.complete_votes==="number"&&typeof Z.total_members==="number"&&Z.total_members>0&&Z.complete_votes===Z.total_members)X+=1;if(Z.verdict==="COMPLETE")H+=1}Q.unanimous_rate=Math.round(X/K.length*1e4)/1e4,Q.approval_rate=Math.round(H/K.length*1e4)/1e4}if(z>0)Q.iteration_success_rate=Math.round($/z*1e4)/1e4;return Q}function b0(K){let $=[],z=A1(K,"metrics","efficiency"),Q=A1(K,"council"),X=T1(z)?C0(z):[];if(!T1(z))$.push("no .loki/metrics/efficiency/ dir (efficiency KPIs zeroed)");else if(X.length===0)$.push(".loki/metrics/efficiency/ exists but no iteration files found");let H=D5(Q);if(!T1(Q))$.push("no .loki/council/ dir (accuracy KPIs zeroed)");else if(H.length===0)$.push(".loki/council/ exists but no round-N.json files found");let Z=b5(X),V=Z.status_breakdown.success??0,q=y5(H,V,Z.iteration_count);return{schema_version:1,generated_at:new Date().toISOString(),loki_dir:K,efficiency:Z,accuracy:q,notes:$}}function y0(K){return JSON.stringify(K,null,2)}function v0(K){let $=[];$.push(`Loki Mode KPIs (snapshot at ${K.generated_at})`),$.push(`Source: ${K.loki_dir}`),$.push(""),$.push("Efficiency"),$.push(` Iterations: ${K.efficiency.iteration_count}`),$.push(` Total cost USD: ${K.efficiency.total_cost_usd}`),$.push(` Avg cost per iter: ${K.efficiency.avg_cost_per_iteration??"n/a"}`),$.push(` Total input tokens: ${K.efficiency.total_input_tokens}`),$.push(` Total output tokens: ${K.efficiency.total_output_tokens}`),$.push(` Total duration (ms): ${K.efficiency.total_duration_ms}`),$.push(` Avg duration / iter: ${K.efficiency.avg_duration_ms_per_iteration??"n/a"}`);let z=Object.entries(K.efficiency.model_breakdown).sort((H,Z)=>H[0].localeCompare(Z[0]));if(z.length>0)$.push(` Model breakdown: ${z.map(([H,Z])=>`${H}=${Z}`).join(", ")}`);let Q=Object.entries(K.efficiency.phase_breakdown).sort((H,Z)=>H[0].localeCompare(Z[0]));if(Q.length>0)$.push(` Phase breakdown: ${Q.map(([H,Z])=>`${H}=${Z}`).join(", ")}`);let X=Object.entries(K.efficiency.status_breakdown).sort((H,Z)=>H[0].localeCompare(Z[0]));if(X.length>0)$.push(` Status breakdown: ${X.map(([H,Z])=>`${H}=${Z}`).join(", ")}`);if($.push(""),$.push("Accuracy"),$.push(` Council rounds: ${K.accuracy.council_rounds}`),$.push(` Unanimous rate: ${K.accuracy.unanimous_rate??"n/a"}`),$.push(` Approval rate: ${K.accuracy.approval_rate??"n/a"}`),$.push(` Iter success rate: ${K.accuracy.iteration_success_rate??"n/a"}`),K.notes.length>0){$.push(""),$.push("Notes");for(let H of K.notes)$.push(` - ${H}`)}return $.join(`
346
- `)}var g0=L(()=>{h0()});var m0={};b(m0,{runKpis:()=>g5});function g5(K){let $=!1;for(let Q of K){if(Q==="--help"||Q==="-h"||Q==="help")return process.stdout.write(v5),0;if(Q==="--json"){$=!0;continue}return process.stderr.write(`loki kpis: unknown arg: ${Q}
344
+ `),1;if($){let z=await x0();return process.stdout.write(JSON.stringify(z,null,2)+`
345
+ `),0}return _5()}var B5,T1,O5,T5;var w0=L(()=>{p();Q1();n();D1();B5=/(\d+\.\d+(?:\.\d+)*)/;T1={fn:f1};O5=[{name:"Claude Code",dir:".claude/skills/loki-mode"},{name:"Codex CLI",dir:".codex/skills/loki-mode"},{name:"Cline CLI",dir:".cline/skills/loki-mode"},{name:"Aider CLI",dir:".aider/skills/loki-mode"}];T5=[{displayName:"Node.js (>= 18)",jsonName:"Node.js",cmd:"node",required:"required",min:"18.0"},{displayName:"Python 3 (>= 3.8)",jsonName:"Python 3",cmd:"python3",required:"required",min:"3.8"},{displayName:"jq",jsonName:"jq",cmd:"jq",required:"required"},{displayName:"git",jsonName:"git",cmd:"git",required:"required"},{displayName:"curl",jsonName:"curl",cmd:"curl",required:"required"},{displayName:"bash (>= 4.0)",jsonName:"bash",cmd:"bash",required:"recommended",min:"4.0"},{displayName:"Bun (>= 1.3)",jsonName:"Bun",cmd:"bun",required:"recommended",min:"1.3"},{displayName:"Claude CLI",jsonName:"Claude CLI",cmd:"claude",required:"optional"},{displayName:"Codex CLI",jsonName:"Codex CLI",cmd:"codex",required:"optional"},{displayName:"Cline CLI",jsonName:"Cline CLI",cmd:"cline",required:"optional"},{displayName:"Aider CLI",jsonName:"Aider CLI",cmd:"aider",required:"optional"}]});import{existsSync as k0,mkdirSync as e6,readdirSync as P5,readFileSync as D0,renameSync as K8,writeFileSync as $8}from"fs";import{dirname as L5,join as R5,resolve as E5}from"path";import{fileURLToPath as x5}from"url";function F5(){try{let K=L5(x5(import.meta.url)),$=E5(K,"..","..","data","model-pricing.json");if(!k0($))return H1;let Q=JSON.parse(D0($,"utf8")).pricing;if(!Q||typeof Q!=="object")return H1;let X={};for(let[H,Z]of Object.entries(Q))if(Z!==null&&typeof Z==="object"&&typeof Z.input==="number"&&typeof Z.output==="number")X[H]={input:Z.input,output:Z.output};for(let H of Object.keys(H1))if(!(H in X))return H1;return X}catch{return H1}}function w5(K){return Math.round((K+Number.EPSILON)*1e4)/1e4}function S5(K){let $=(K??N0).toLowerCase();return S0[$]??S0[N0]}function C0(K){let $=0;for(let z of K){if(typeof z.cost_usd==="number"&&Number.isFinite(z.cost_usd)){$+=z.cost_usd;continue}let Q=S5(z.model),X=typeof z.input_tokens==="number"?z.input_tokens:0,H=typeof z.output_tokens==="number"?z.output_tokens:0;$+=X/1e6*Q.input+H/1e6*Q.output}return w5($)}function h0(K){if(!k0(K))return[];let $=[],z;try{z=P5(K)}catch{return[]}for(let Q of z){if(!Q.endsWith(".json"))continue;let X=R5(K,Q);try{let H=D0(X,"utf8"),Z=JSON.parse(H);if(Z&&typeof Z==="object")$.push(Z)}catch{}}return $}var H1,S0,N0="sonnet";var b0=L(()=>{y();H1={opus:{input:5,output:25},sonnet:{input:3,output:15},haiku:{input:1,output:5},"gpt-5.3-codex":{input:1.5,output:12}};S0=Object.freeze(F5())});import{existsSync as A1,readdirSync as N5,readFileSync as k5,statSync as D5}from"fs";import{join as j1}from"path";function C5(K){let $=[],z=j1(K,"votes");if(!A1(z))return $;let Q;try{Q=N5(z)}catch{return $}for(let X of Q){if(!X.startsWith("round-")||!X.endsWith(".json"))continue;try{let H=j1(z,X);if(!D5(H).isFile())continue;let Z=JSON.parse(k5(H,"utf8"));$.push({iteration:typeof Z.iteration==="number"?Z.iteration:void 0,verdict:typeof Z.verdict==="string"?Z.verdict:void 0,complete_votes:typeof Z.complete_votes==="number"?Z.complete_votes:void 0,total_members:typeof Z.total_members==="number"?Z.total_members:void 0,threshold:typeof Z.threshold==="number"?Z.threshold:void 0})}catch{}}return $}function h5(){return{iteration_count:0,total_cost_usd:0,avg_cost_per_iteration:null,total_input_tokens:0,total_output_tokens:0,total_duration_ms:0,avg_duration_ms_per_iteration:null,model_breakdown:{},phase_breakdown:{},status_breakdown:{}}}function b5(){return{council_rounds:0,unanimous_rate:null,approval_rate:null,iteration_success_rate:null}}function y5(K){let $=h5();if(K.length===0)return $;$.iteration_count=K.length,$.total_cost_usd=Math.round(C0(K)*1e4)/1e4;for(let z of K){if(typeof z.input_tokens==="number")$.total_input_tokens+=z.input_tokens;if(typeof z.output_tokens==="number")$.total_output_tokens+=z.output_tokens;let Q=z;if(typeof Q.duration_ms==="number")$.total_duration_ms+=Q.duration_ms;if(typeof z.model==="string")$.model_breakdown[z.model]=($.model_breakdown[z.model]??0)+1;if(typeof Q.phase==="string")$.phase_breakdown[Q.phase]=($.phase_breakdown[Q.phase]??0)+1;if(typeof Q.status==="string")$.status_breakdown[Q.status]=($.status_breakdown[Q.status]??0)+1}return $.avg_cost_per_iteration=Math.round($.total_cost_usd/$.iteration_count*1e4)/1e4,$.avg_duration_ms_per_iteration=Math.round($.total_duration_ms/$.iteration_count),$}function v5(K,$,z){let Q=b5();if(Q.council_rounds=K.length,K.length>0){let X=0,H=0;for(let Z of K){if(typeof Z.complete_votes==="number"&&typeof Z.total_members==="number"&&Z.total_members>0&&Z.complete_votes===Z.total_members)X+=1;if(Z.verdict==="COMPLETE")H+=1}Q.unanimous_rate=Math.round(X/K.length*1e4)/1e4,Q.approval_rate=Math.round(H/K.length*1e4)/1e4}if(z>0)Q.iteration_success_rate=Math.round($/z*1e4)/1e4;return Q}function y0(K){let $=[],z=j1(K,"metrics","efficiency"),Q=j1(K,"council"),X=A1(z)?h0(z):[];if(!A1(z))$.push("no .loki/metrics/efficiency/ dir (efficiency KPIs zeroed)");else if(X.length===0)$.push(".loki/metrics/efficiency/ exists but no iteration files found");let H=C5(Q);if(!A1(Q))$.push("no .loki/council/ dir (accuracy KPIs zeroed)");else if(H.length===0)$.push(".loki/council/ exists but no round-N.json files found");let Z=y5(X),q=Z.status_breakdown.success??0,U=v5(H,q,Z.iteration_count);return{schema_version:1,generated_at:new Date().toISOString(),loki_dir:K,efficiency:Z,accuracy:U,notes:$}}function v0(K){return JSON.stringify(K,null,2)}function g0(K){let $=[];$.push(`Loki Mode KPIs (snapshot at ${K.generated_at})`),$.push(`Source: ${K.loki_dir}`),$.push(""),$.push("Efficiency"),$.push(` Iterations: ${K.efficiency.iteration_count}`),$.push(` Total cost USD: ${K.efficiency.total_cost_usd}`),$.push(` Avg cost per iter: ${K.efficiency.avg_cost_per_iteration??"n/a"}`),$.push(` Total input tokens: ${K.efficiency.total_input_tokens}`),$.push(` Total output tokens: ${K.efficiency.total_output_tokens}`),$.push(` Total duration (ms): ${K.efficiency.total_duration_ms}`),$.push(` Avg duration / iter: ${K.efficiency.avg_duration_ms_per_iteration??"n/a"}`);let z=Object.entries(K.efficiency.model_breakdown).sort((H,Z)=>H[0].localeCompare(Z[0]));if(z.length>0)$.push(` Model breakdown: ${z.map(([H,Z])=>`${H}=${Z}`).join(", ")}`);let Q=Object.entries(K.efficiency.phase_breakdown).sort((H,Z)=>H[0].localeCompare(Z[0]));if(Q.length>0)$.push(` Phase breakdown: ${Q.map(([H,Z])=>`${H}=${Z}`).join(", ")}`);let X=Object.entries(K.efficiency.status_breakdown).sort((H,Z)=>H[0].localeCompare(Z[0]));if(X.length>0)$.push(` Status breakdown: ${X.map(([H,Z])=>`${H}=${Z}`).join(", ")}`);if($.push(""),$.push("Accuracy"),$.push(` Council rounds: ${K.accuracy.council_rounds}`),$.push(` Unanimous rate: ${K.accuracy.unanimous_rate??"n/a"}`),$.push(` Approval rate: ${K.accuracy.approval_rate??"n/a"}`),$.push(` Iter success rate: ${K.accuracy.iteration_success_rate??"n/a"}`),K.notes.length>0){$.push(""),$.push("Notes");for(let H of K.notes)$.push(` - ${H}`)}return $.join(`
346
+ `)}var m0=L(()=>{b0()});var f0={};b(f0,{runKpis:()=>m5});function m5(K){let $=!1;for(let Q of K){if(Q==="--help"||Q==="-h"||Q==="help")return process.stdout.write(g5),0;if(Q==="--json"){$=!0;continue}return process.stderr.write(`loki kpis: unknown arg: ${Q}
347
347
  Run 'loki kpis --help' for usage.
348
- `),1}let z=b0(P());return process.stdout.write($?y0(z)+`
349
- `:v0(z)+`
350
- `),0}var v5=`loki kpis -- accuracy + efficiency KPI snapshot (v7.5.28 MVP)
348
+ `),1}let z=y0(P());return process.stdout.write($?v0(z)+`
349
+ `:g0(z)+`
350
+ `),0}var g5=`loki kpis -- accuracy + efficiency KPI snapshot (v7.5.28 MVP)
351
351
 
352
352
  Usage:
353
353
  loki kpis Pretty-print KPI snapshot
@@ -367,23 +367,23 @@ iteration success rate.
367
367
  This is the Phase K MVP -- read-only derivation. Per-iteration
368
368
  emission, dashboard panel, and the loki-bench harness are deferred
369
369
  follow-ups (see project_v7_5_18_arc_status.md).
370
- `;var f0=L(()=>{g0();y()});import{closeSync as J8,fstatSync as G8,lstatSync as B8,mkdirSync as m5,openSync as M8,readSync as Y8,renameSync as f5,rmSync as O8,statSync as T8,unlinkSync as A8,writeFileSync as u5,writeSync as j8}from"fs";import{dirname as p5}from"path";function W1(K,$){m5(p5(K),{recursive:!0});let z=`${K}.tmp.${process.pid}.${++c5}`;u5(z,`${JSON.stringify($,null,2)}
371
- `),f5(z,K)}async function u0(K,$){let z=j1.get(K)??Promise.resolve(),Q=()=>{},X=new Promise((Z)=>{Q=Z}),H=z.catch(()=>{}).then(()=>X);j1.set(K,H);try{return await z.catch(()=>{}),await $()}finally{if(Q(),j1.get(K)===H)j1.delete(K)}}var c5=0,j1;var _1=L(()=>{j1=new Map});import{existsSync as I1,mkdirSync as l5,copyFileSync as d5,readFileSync as o5,readdirSync as n5,statSync as a5,writeFileSync as R8,renameSync as s5,appendFileSync as E8,rmSync as x8}from"fs";import{join as s,dirname as r5}from"path";function P1(K){return s(K,"state","checkpoints")}function i5(K){let $=P1(K);if(!I1($))return[];return n5($).filter((z)=>z.startsWith("cp-")).filter((z)=>{try{return a5(s($,z)).isDirectory()}catch{return!1}})}function e5(K){return[...K].sort(($,z)=>{let Q=p0($),X=p0(z);return Q-X})}function p0(K){let $=K.split("-");if($.length<3)return 0;let z=$[$.length-1],Q=Number.parseInt(z??"0",10);return Number.isFinite(Q)?Q:0}function f1(K){let $=K??P(),z=e5(i5($)),Q=[];for(let X of z){let H=c0($,X);if(H)Q.push(H)}return Q}function c0(K,$){let z=s(P1(K),$,"metadata.json");if(!I1(z))return null;try{let Q=JSON.parse(o5(z,"utf-8"));return KK(Q,z)}catch{return null}}function KK(K,$){let z=QK(K,$);return z.ok?z.value:null}function QK(K,$){if(K===null||typeof K!=="object")return console.warn(`[checkpoint] invalid metadata at ${$}: not an object`),{ok:!1,reason:"invalid_type",field:"<root>"};let z=K,Q=["id","timestamp","task_id","task_description","git_sha","git_branch","provider","phase"];for(let X of Q){if(!(X in z))return console.warn(`[checkpoint] invalid metadata at ${$}: field "${X}" missing`),{ok:!1,reason:"missing_field",field:X};if(typeof z[X]!=="string")return console.warn(`[checkpoint] invalid metadata at ${$}: field "${X}" not a string`),{ok:!1,reason:"invalid_type",field:X}}if(!Object.prototype.hasOwnProperty.call(z,"iteration"))return console.warn(`[checkpoint] invalid metadata at ${$}: field "iteration" missing`),{ok:!1,reason:"missing_field",field:"iteration"};if(typeof z.iteration!=="number"||!Number.isFinite(z.iteration))return console.warn(`[checkpoint] invalid metadata at ${$}: field "iteration" not a finite number`),{ok:!1,reason:"invalid_type",field:"iteration"};for(let X of zK){let H=z[X];if($K.test(H))return console.warn(`[checkpoint] invalid metadata at ${$}: field "${X}" contains control characters`),{ok:!1,reason:"control_chars",field:X}}return{ok:!0,value:{id:z.id,timestamp:z.timestamp,iteration:z.iteration,task_id:z.task_id,task_description:z.task_description,git_sha:z.git_sha,git_branch:z.git_branch,provider:z.provider,phase:z.phase}}}function u1(K,$){if(!XK.test(K))throw new l0(K);let z=$??P(),Q=s(P1(z),K);if(!I1(Q))throw new m1(K);let X=c0(z,K);if(!X)throw new m1(K);return X}function d0(K,$){let z=u1(K,$),Q=$??P(),X=s(P1(Q),K),H=[];for(let Z of ZK){let V=s(X,Z);if(!I1(V))continue;H.push({from:V,to:s(Q,Z)})}return{id:K,metadata:z,restore:H}}function o0(K){let $=[],z=0;for(let Q of K.restore)try{l5(r5(Q.to),{recursive:!0});let X=`${Q.to}.tmp.${process.pid}.${++t5}`;d5(Q.from,X),s5(X,Q.to),z+=1}catch(X){$.push(`${Q.from} -> ${Q.to}: ${X.message}`)}return{restored:z,errors:$}}var k8,t5=0,$K,zK,XK,m1,l0,ZK;var n0=L(()=>{y();p();_1();k8=Promise.resolve();$K=/[\x00-\x08\x0a-\x1f\x7f-\x9f]/,zK=["id","task_id","git_sha","git_branch","provider","phase"];XK=/^[a-zA-Z0-9_-]+$/;m1=class m1 extends Error{id;constructor(K){super(`Checkpoint not found: ${K}`);this.id=K;this.name="CheckpointNotFoundError"}};l0=class l0 extends Error{id;constructor(K){super(`Invalid checkpoint ID: must be alphanumeric, hyphens, underscores only (got: ${K})`);this.id=K;this.name="InvalidCheckpointIdError"}};ZK=["state/orchestrator.json","queue/pending.json","queue/completed.json","queue/in-progress.json","queue/current-task.json"]});var r0={};b(r0,{runRollback:()=>HK});async function HK(K){let $=K[0],z=K.slice(1);if($===void 0||$==="help"||$==="--help"||$==="-h")return process.stdout.write(a0),$===void 0?1:0;switch($){case"list":{let Q=[...f1()].reverse();if(Q.length===0)return process.stdout.write(`${E}No checkpoints found.${W}
370
+ `;var u0=L(()=>{m0();y()});import{closeSync as B8,fstatSync as M8,lstatSync as Y8,mkdirSync as f5,openSync as O8,readSync as T8,renameSync as u5,rmSync as A8,statSync as j8,unlinkSync as _8,writeFileSync as p5,writeSync as I8}from"fs";import{dirname as c5}from"path";function W1(K,$){f5(c5(K),{recursive:!0});let z=`${K}.tmp.${process.pid}.${++l5}`;p5(z,`${JSON.stringify($,null,2)}
371
+ `),u5(z,K)}async function p0(K,$){let z=_1.get(K)??Promise.resolve(),Q=()=>{},X=new Promise((Z)=>{Q=Z}),H=z.catch(()=>{}).then(()=>X);_1.set(K,H);try{return await z.catch(()=>{}),await $()}finally{if(Q(),_1.get(K)===H)_1.delete(K)}}var l5=0,_1;var I1=L(()=>{_1=new Map});import{existsSync as P1,mkdirSync as d5,copyFileSync as o5,readFileSync as n5,readdirSync as a5,statSync as s5,writeFileSync as x8,renameSync as r5,appendFileSync as F8,rmSync as w8}from"fs";import{join as s,dirname as t5}from"path";function L1(K){return s(K,"state","checkpoints")}function e5(K){let $=L1(K);if(!P1($))return[];return a5($).filter((z)=>z.startsWith("cp-")).filter((z)=>{try{return s5(s($,z)).isDirectory()}catch{return!1}})}function KK(K){return[...K].sort(($,z)=>{let Q=c0($),X=c0(z);return Q-X})}function c0(K){let $=K.split("-");if($.length<3)return 0;let z=$[$.length-1],Q=Number.parseInt(z??"0",10);return Number.isFinite(Q)?Q:0}function p1(K){let $=K??P(),z=KK(e5($)),Q=[];for(let X of z){let H=l0($,X);if(H)Q.push(H)}return Q}function l0(K,$){let z=s(L1(K),$,"metadata.json");if(!P1(z))return null;try{let Q=JSON.parse(n5(z,"utf-8"));return $K(Q,z)}catch{return null}}function $K(K,$){let z=XK(K,$);return z.ok?z.value:null}function XK(K,$){if(K===null||typeof K!=="object")return console.warn(`[checkpoint] invalid metadata at ${$}: not an object`),{ok:!1,reason:"invalid_type",field:"<root>"};let z=K,Q=["id","timestamp","task_id","task_description","git_sha","git_branch","provider","phase"];for(let X of Q){if(!(X in z))return console.warn(`[checkpoint] invalid metadata at ${$}: field "${X}" missing`),{ok:!1,reason:"missing_field",field:X};if(typeof z[X]!=="string")return console.warn(`[checkpoint] invalid metadata at ${$}: field "${X}" not a string`),{ok:!1,reason:"invalid_type",field:X}}if(!Object.prototype.hasOwnProperty.call(z,"iteration"))return console.warn(`[checkpoint] invalid metadata at ${$}: field "iteration" missing`),{ok:!1,reason:"missing_field",field:"iteration"};if(typeof z.iteration!=="number"||!Number.isFinite(z.iteration))return console.warn(`[checkpoint] invalid metadata at ${$}: field "iteration" not a finite number`),{ok:!1,reason:"invalid_type",field:"iteration"};for(let X of QK){let H=z[X];if(zK.test(H))return console.warn(`[checkpoint] invalid metadata at ${$}: field "${X}" contains control characters`),{ok:!1,reason:"control_chars",field:X}}return{ok:!0,value:{id:z.id,timestamp:z.timestamp,iteration:z.iteration,task_id:z.task_id,task_description:z.task_description,git_sha:z.git_sha,git_branch:z.git_branch,provider:z.provider,phase:z.phase}}}function c1(K,$){if(!ZK.test(K))throw new d0(K);let z=$??P(),Q=s(L1(z),K);if(!P1(Q))throw new u1(K);let X=l0(z,K);if(!X)throw new u1(K);return X}function o0(K,$){let z=c1(K,$),Q=$??P(),X=s(L1(Q),K),H=[];for(let Z of HK){let q=s(X,Z);if(!P1(q))continue;H.push({from:q,to:s(Q,Z)})}return{id:K,metadata:z,restore:H}}function n0(K){let $=[],z=0;for(let Q of K.restore)try{d5(t5(Q.to),{recursive:!0});let X=`${Q.to}.tmp.${process.pid}.${++i5}`;o5(Q.from,X),r5(X,Q.to),z+=1}catch(X){$.push(`${Q.from} -> ${Q.to}: ${X.message}`)}return{restored:z,errors:$}}var C8,i5=0,zK,QK,ZK,u1,d0,HK;var a0=L(()=>{y();p();I1();C8=Promise.resolve();zK=/[\x00-\x08\x0a-\x1f\x7f-\x9f]/,QK=["id","task_id","git_sha","git_branch","provider","phase"];ZK=/^[a-zA-Z0-9_-]+$/;u1=class u1 extends Error{id;constructor(K){super(`Checkpoint not found: ${K}`);this.id=K;this.name="CheckpointNotFoundError"}};d0=class d0 extends Error{id;constructor(K){super(`Invalid checkpoint ID: must be alphanumeric, hyphens, underscores only (got: ${K})`);this.id=K;this.name="InvalidCheckpointIdError"}};HK=["state/orchestrator.json","queue/pending.json","queue/completed.json","queue/in-progress.json","queue/current-task.json"]});var t0={};b(t0,{runRollback:()=>WK});async function WK(K){let $=K[0],z=K.slice(1);if($===void 0||$==="help"||$==="--help"||$==="-h")return process.stdout.write(s0),$===void 0?1:0;switch($){case"list":{let Q=[...p1()].reverse();if(Q.length===0)return process.stdout.write(`${E}No checkpoints found.${W}
372
372
  `),0;process.stdout.write(`${k}Checkpoints${W} (${Q.length}, newest first):
373
373
  `);for(let X of Q)process.stdout.write(` ${O}${X.id}${W} iter=${X.iteration} ${X.git_branch||"(no branch)"}@${(X.git_sha||"").slice(0,7)} ${X.timestamp}
374
374
  `);return 0}case"show":{let Q=z[0];if(!Q)return process.stderr.write(`${R}Missing checkpoint id.${W} Use \`loki rollback list\`.
375
- `),2;try{let X=u1(Q);return process.stdout.write(`${JSON.stringify(X,null,2)}
375
+ `),2;try{let X=c1(Q);return process.stdout.write(`${JSON.stringify(X,null,2)}
376
376
  `),0}catch(X){return process.stderr.write(`${R}Failed to read checkpoint:${W} ${X.message}
377
377
  `),1}}case"to":{let Q=z[0];if(!Q)return process.stderr.write(`${R}Missing checkpoint id.${W} Use \`loki rollback list\`.
378
- `),2;return s0(Q)}case"latest":{let Q=f1(),X=Q[Q.length-1];if(!X)return process.stderr.write(`${R}No checkpoints found to roll back to.${W}
378
+ `),2;return r0(Q)}case"latest":{let Q=p1(),X=Q[Q.length-1];if(!X)return process.stderr.write(`${R}No checkpoints found to roll back to.${W}
379
379
  `),1;return process.stdout.write(`Rolling back to latest checkpoint: ${O}${X.id}${W}
380
- `),s0(X.id)}default:return process.stderr.write(`Unknown subcommand: ${$}
381
- `),process.stderr.write(a0),2}}function s0(K){let $;try{$=d0(K)}catch(Q){return process.stderr.write(`${R}Cannot plan rollback:${W} ${Q.message}
380
+ `),r0(X.id)}default:return process.stderr.write(`Unknown subcommand: ${$}
381
+ `),process.stderr.write(s0),2}}function r0(K){let $;try{$=o0(K)}catch(Q){return process.stderr.write(`${R}Cannot plan rollback:${W} ${Q.message}
382
382
  `),1}if($.restore.length===0)return process.stdout.write(`${E}Checkpoint ${K} has no restorable state files; nothing to do.${W}
383
- `),0;let z=o0($);if(z.errors.length>0){for(let Q of z.errors)process.stderr.write(`${R}restore error:${W} ${Q}
383
+ `),0;let z=n0($);if(z.errors.length>0){for(let Q of z.errors)process.stderr.write(`${R}restore error:${W} ${Q}
384
384
  `);return process.stderr.write(`${R}Partial rollback: ${z.restored}/${$.restore.length} files restored.${W}
385
385
  `),1}return process.stdout.write(`${D}Rolled back ${z.restored}/${$.restore.length} state files from ${K}.${W}
386
- `),process.stdout.write("Run `loki start` to resume from the restored state.\n"),0}var a0=`Usage: loki rollback <subcommand>
386
+ `),process.stdout.write("Run `loki start` to resume from the restored state.\n"),0}var s0=`Usage: loki rollback <subcommand>
387
387
 
388
388
  Subcommands:
389
389
  list List checkpoints (newest first)
@@ -398,8 +398,8 @@ Restored files (matches autonomy/run.sh:7028 byte-for-byte):
398
398
  Note: only state files are restored. Source code, git history, and the
399
399
  session's autonomy-state.json are unchanged. Re-run \`loki start\` to
400
400
  resume from the restored state.
401
- `;var t0=L(()=>{n0();n()});var c1={};b(c1,{renderFindingsForPrompt:()=>JK,loadPreviousFindings:()=>p1,findLatestReviewDir:()=>z7,_parseReviewerOutputForTests:()=>GK});import{existsSync as e0,readFileSync as i0,readdirSync as K7,statSync as WK}from"fs";import{join as L1}from"path";function UK(K){let $=K.toLowerCase();if($==="critical")return"Critical";if($==="high")return"High";if($==="medium")return"Medium";return"Low"}function $7(K,$,z,Q){let X=[],H=K.split(/\r?\n/);for(let Z of H){let V=Z.trim();if(V.length===0)continue;let q=V.replace(/^[-*]\s*/,""),U=VK.exec(q);if(!U||!U[1]||!U[2])continue;let G=UK(U[1]),B=U[2].trim(),J=qK.exec(B),Y=J&&J[1]?J[1]:null,j=J&&J[2]?Number.parseInt(J[2],10):null;X.push({reviewId:z,iteration:Q,reviewer:$,severity:G,description:B,file:Y,line:Number.isFinite(j)?j:null,raw:V})}return X}function z7(K,$){let z=L1(K,"quality","reviews");if(!e0(z))return null;let Q;try{Q=K7(z)}catch{return null}let X=$===void 0?Q.filter((V)=>V.startsWith("review-")):Q.filter((V)=>V.endsWith(`-${$}`)&&V.startsWith("review-"));if(X.length===0)return null;X.sort();let H=X[X.length-1];if(!H)return null;let Z=L1(z,H);try{if(!WK(Z).isDirectory())return null}catch{return null}return Z}function p1(K,$){let z=z7(K,$);if(z===null)return{reviewDir:null,reviewId:null,iteration:null,findings:[]};let Q=null,X=null,H=L1(z,"aggregate.json");if(e0(H))try{let U=i0(H,"utf-8"),G=JSON.parse(U);if(typeof G.review_id==="string")Q=G.review_id;if(typeof G.iteration==="number")X=G.iteration}catch{}let Z;try{Z=K7(z)}catch{return{reviewDir:z,reviewId:Q,iteration:X,findings:[]}}let V=new Set(["diff.txt","files.txt","anti-sycophancy.txt"]),q=[];for(let U of Z){if(!U.endsWith(".txt"))continue;if(V.has(U))continue;if(U.endsWith("-prompt.txt"))continue;let G=U.replace(/\.txt$/,""),B;try{B=i0(L1(z,U),"utf-8")}catch{continue}q.push(...$7(B,G,Q??"",X??-1))}return{reviewDir:z,reviewId:Q,iteration:X,findings:q}}function JK(K){if(K.length===0)return"";let $=["Critical","High","Medium","Low"],z=new Map;for(let X of $)z.set(X,[]);for(let X of K){let H=z.get(X.severity);if(H)H.push(X)}let Q=[];Q.push("PREVIOUS REVIEWER FINDINGS (must address each, or supply counter-evidence in .loki/state/counter-evidence-<iter>.json):");for(let X of $){let H=z.get(X)??[];if(H.length===0)continue;Q.push(` [${X}] (${H.length}):`);for(let Z of H){let V=Z.file?` (${Z.file}${Z.line!==null?":"+Z.line:""})`:"";Q.push(` - ${Z.description}${V} -- via ${Z.reviewer}`)}}return Q.join(`
402
- `)}function GK(K,$,z="review-test",Q=0){return $7(K,$,z,Q)}var VK,qK;var R1=L(()=>{VK=/\[(Critical|High|Medium|Low)\]\s*(.+)/i,qK=/([\w.\-/]+\.[a-zA-Z]+):(\d+)/});import{existsSync as BK}from"fs";import{join as MK}from"path";async function Q7(K,$){let z=MK(K,"memory");if(!BK(z))return{stored:!1,reason:"memory dir not initialized"};let Q=Math.max(0,Math.floor($.durationSeconds??0)),X={_LOKI_PROJECT_DIR:u,_LOKI_TARGET_DIR:process.cwd(),_LOKI_TASK_ID:$.taskId,_LOKI_OUTCOME:$.outcome,_LOKI_PHASE:$.phase,_LOKI_GOAL:$.goal,_LOKI_DURATION:String(Q),_LOKI_LOKI_DIR:K},Z=await a(`
401
+ `;var i0=L(()=>{a0();n()});var d1={};b(d1,{renderFindingsForPrompt:()=>GK,loadPreviousFindings:()=>l1,findLatestReviewDir:()=>Q7,_parseReviewerOutputForTests:()=>BK});import{existsSync as K7,readFileSync as e0,readdirSync as $7,statSync as qK}from"fs";import{join as R1}from"path";function JK(K){let $=K.toLowerCase();if($==="critical")return"Critical";if($==="high")return"High";if($==="medium")return"Medium";return"Low"}function z7(K,$,z,Q){let X=[],H=K.split(/\r?\n/);for(let Z of H){let q=Z.trim();if(q.length===0)continue;let U=q.replace(/^[-*]\s*/,""),V=UK.exec(U);if(!V||!V[1]||!V[2])continue;let G=JK(V[1]),B=V[2].trim(),J=VK.exec(B),Y=J&&J[1]?J[1]:null,j=J&&J[2]?Number.parseInt(J[2],10):null;X.push({reviewId:z,iteration:Q,reviewer:$,severity:G,description:B,file:Y,line:Number.isFinite(j)?j:null,raw:q})}return X}function Q7(K,$){let z=R1(K,"quality","reviews");if(!K7(z))return null;let Q;try{Q=$7(z)}catch{return null}let X=$===void 0?Q.filter((q)=>q.startsWith("review-")):Q.filter((q)=>q.endsWith(`-${$}`)&&q.startsWith("review-"));if(X.length===0)return null;X.sort();let H=X[X.length-1];if(!H)return null;let Z=R1(z,H);try{if(!qK(Z).isDirectory())return null}catch{return null}return Z}function l1(K,$){let z=Q7(K,$);if(z===null)return{reviewDir:null,reviewId:null,iteration:null,findings:[]};let Q=null,X=null,H=R1(z,"aggregate.json");if(K7(H))try{let V=e0(H,"utf-8"),G=JSON.parse(V);if(typeof G.review_id==="string")Q=G.review_id;if(typeof G.iteration==="number")X=G.iteration}catch{}let Z;try{Z=$7(z)}catch{return{reviewDir:z,reviewId:Q,iteration:X,findings:[]}}let q=new Set(["diff.txt","files.txt","anti-sycophancy.txt"]),U=[];for(let V of Z){if(!V.endsWith(".txt"))continue;if(q.has(V))continue;if(V.endsWith("-prompt.txt"))continue;let G=V.replace(/\.txt$/,""),B;try{B=e0(R1(z,V),"utf-8")}catch{continue}U.push(...z7(B,G,Q??"",X??-1))}return{reviewDir:z,reviewId:Q,iteration:X,findings:U}}function GK(K){if(K.length===0)return"";let $=["Critical","High","Medium","Low"],z=new Map;for(let X of $)z.set(X,[]);for(let X of K){let H=z.get(X.severity);if(H)H.push(X)}let Q=[];Q.push("PREVIOUS REVIEWER FINDINGS (must address each, or supply counter-evidence in .loki/state/counter-evidence-<iter>.json):");for(let X of $){let H=z.get(X)??[];if(H.length===0)continue;Q.push(` [${X}] (${H.length}):`);for(let Z of H){let q=Z.file?` (${Z.file}${Z.line!==null?":"+Z.line:""})`:"";Q.push(` - ${Z.description}${q} -- via ${Z.reviewer}`)}}return Q.join(`
402
+ `)}function BK(K,$,z="review-test",Q=0){return z7(K,$,z,Q)}var UK,VK;var E1=L(()=>{UK=/\[(Critical|High|Medium|Low)\]\s*(.+)/i,VK=/([\w.\-/]+\.[a-zA-Z]+):(\d+)/});import{existsSync as MK}from"fs";import{join as YK}from"path";async function X7(K,$){let z=YK(K,"memory");if(!MK(z))return{stored:!1,reason:"memory dir not initialized"};let Q=Math.max(0,Math.floor($.durationSeconds??0)),X={_LOKI_PROJECT_DIR:u,_LOKI_TARGET_DIR:process.cwd(),_LOKI_TASK_ID:$.taskId,_LOKI_OUTCOME:$.outcome,_LOKI_PHASE:$.phase,_LOKI_GOAL:$.goal,_LOKI_DURATION:String(Q),_LOKI_LOKI_DIR:K},Z=await a(`
403
403
  import os, sys
404
404
  project = os.environ.get('_LOKI_PROJECT_DIR', '')
405
405
  loki = os.environ.get('_LOKI_LOKI_DIR', '.loki')
@@ -426,23 +426,23 @@ try:
426
426
  print('OK')
427
427
  except Exception as e:
428
428
  print('ERR:' + str(e))
429
- `,{env:X,timeoutMs:15000});if(Z.exitCode===127)return{stored:!1,reason:"python3 not found"};let V=Z.stdout.trim();if(V==="OK")return{stored:!0,reason:"stored"};if(V.startsWith("ERR:"))return{stored:!1,reason:V.replace(/^ERR:/,"")};return{stored:!1,reason:Z.stderr.trim()||"unknown"}}var X7=L(()=>{Q1();y()});var V7={};b(V7,{loadLearnings:()=>l1,appendLearning:()=>V1,appendFromGateFailure:()=>PK});import{existsSync as YK,readFileSync as OK}from"fs";import{join as Z7}from"path";import{createHash as TK}from"crypto";function H7(K){return Z7(K,AK)}function jK(K){if(K===null||typeof K!=="object")return!1;let $=K;return typeof $.id==="string"&&typeof $.timestamp==="string"&&typeof $.iteration==="number"&&typeof $.trigger==="string"&&typeof $.rootCause==="string"&&typeof $.fix==="string"&&typeof $.preventInFuture==="string"&&typeof $.evidence==="object"&&$.evidence!==null}function W7(K){if(!YK(K))return{version:1,learnings:[]};try{let $=OK(K,"utf-8"),z=JSON.parse($);if(z.version===1&&Array.isArray(z.learnings))return{version:1,learnings:z.learnings.filter(jK)}}catch{}return{version:1,learnings:[]}}function _K(K,$){return TK("sha256").update(`${K}\x00${$}`).digest("hex").slice(0,16)}async function V1(K,$,z={}){let Q=_K($.trigger,$.rootCause),X=new Date().toISOString(),H={id:Q,timestamp:X,...$},Z=H7(K);if(await u0(Z,()=>{let q=W7(Z),U=q.learnings.findIndex((G)=>G.id===Q);if(U>=0){let G=q.learnings[U];q.learnings[U]={...G,timestamp:X,iteration:H.iteration}}else q.learnings.push(H);W1(Z,q)}),z.episodeBridge!==null&&(z.episodeBridge!==void 0||process.env.LOKI_AUTO_LEARNINGS_EPISODE==="1")){let q=z.episodeBridge??Q7,U=z.bridgeFailureLog??IK;try{let G=await q(K,{taskId:`learning-${Q}`,outcome:"failure",phase:"VERIFY",goal:`${$.trigger}: ${$.rootCause}`});if(G&&!G.stored){if(!new Set(["memory dir not initialized","stub"]).has(G.reason))U(`episode_bridge skipped: ${G.reason}`)}}catch(G){U(`episode_bridge threw: ${G.message}`)}}return H}function IK(K){process.stderr.write(`[learnings_writer] ${K}
430
- `)}async function PK(K,$,z,Q={}){let X=`[${z.severity}] ${z.description}`;return V1(K,{iteration:$,trigger:"gate_failure",rootCause:X,fix:"pending: dev agent must address in next iteration or supply counter-evidence",preventInFuture:"if this finding recurs, lower its severity threshold or add a regression test",evidence:{reviewId:z.reviewId,file:z.file??void 0,line:z.line??void 0,severity:z.severity,reviewer:z.reviewer}},Q)}function l1(K){return W7(H7(K))}var AK;var E1=L(()=>{_1();X7();AK=Z7("state","relevant-learnings.json")});var U7={};b(U7,{runOverrideCouncil:()=>wK,recordOverrideOutcome:()=>SK,loadCounterEvidence:()=>FK,canonicalFindingId:()=>d1,DEFAULT_OVERRIDE_JUDGES:()=>q7});import{existsSync as LK,readFileSync as RK}from"fs";import{join as EK}from"path";function FK(K,$){let z=EK(K,"state",`counter-evidence-${$}.json`);if(!LK(z))return null;try{let Q=RK(z,"utf-8"),X=JSON.parse(Q);if(typeof X.iteration!=="number")return null;let H=Array.isArray(X.evidence)?X.evidence:[],Z=[];for(let V of H){if(typeof V!=="object"||V===null)continue;let q=V;if(typeof q.findingId!=="string")continue;if(typeof q.claim!=="string")continue;let U=q.proofType;if(typeof U!=="string"||!xK.has(U))continue;let G=U,B=Array.isArray(q.artifacts)?q.artifacts:[];Z.push({findingId:q.findingId,claim:q.claim,proofType:G,artifacts:B.filter((J)=>typeof J==="string")})}return{iteration:X.iteration,evidence:Z}}catch{return null}}async function wK(K,$,z,Q={}){let X=Q.judges??q7,H=new Set,Z=new Set,V={},q=new Map;for(let U of $.evidence)q.set(U.findingId,U);for(let U of K){let G=d1(U),B=q.get(G);if(!B){Z.add(G);continue}let J=await Promise.all(X.map((j)=>z({finding:U,evidence:B,judge:j})));if(V[G]=J,J.filter((j)=>j.verdict==="APPROVE_OVERRIDE").length>=2)H.add(G);else Z.add(G)}return{approvedFindingIds:H,rejectedFindingIds:Z,votes:V}}function d1(K){let $=K.raw.slice(0,80).replace(/\s+/g," ").trim();return`${K.reviewer}::${$}`}async function SK(K,$,z,Q,X={}){let H={episodeBridge:X.episodeBridge===void 0?null:X.episodeBridge};for(let Z of Q){let V=d1(Z);if(z.approvedFindingIds.has(V))await V1(K,{iteration:$,trigger:"override_approved",rootCause:`[${Z.severity}] ${Z.description}`,fix:"override council approved counter-evidence; finding lifted",preventInFuture:"if this reviewer/file pair recurs, narrow the reviewer's selector OR add a baseline doc",evidence:{findingId:V,reviewId:Z.reviewId,file:Z.file??void 0,line:Z.line??void 0,severity:Z.severity,reviewer:Z.reviewer}},H);else if(z.rejectedFindingIds.has(V))await V1(K,{iteration:$,trigger:"override_rejected",rootCause:`[${Z.severity}] ${Z.description}`,fix:"override council rejected -- dev agent must fix the finding",preventInFuture:"address this finding in the next iteration",evidence:{findingId:V,reviewId:Z.reviewId,file:Z.file??void 0,line:Z.line??void 0,severity:Z.severity,reviewer:Z.reviewer}},H)}}var xK,q7;var J7=L(()=>{E1();xK=new Set(["file-exists","test-passes","grep-miss","reviewer-misread","duplicate-code-path","out-of-scope"]);q7=["judge-primary","judge-secondary","judge-tertiary"]});var M7={};b(M7,{writeEscalationHandoff:()=>uK,renderHandoff:()=>G7,readLatestHandoff:()=>pK});import{existsSync as NK,mkdirSync as kK,readdirSync as DK,readFileSync as CK,renameSync as hK,writeFileSync as bK}from"fs";import{dirname as yK,join as x1}from"path";function vK(){return new Date().toISOString()}function gK(K){let $=K.file?` (${K.file}${K.line!==null?":"+K.line:""})`:"";return` - [${K.severity}] ${K.description}${$} -- ${K.reviewer}`}function mK(K){let $=K.evidence,z=$.file?` ${$.file}${$.line!==void 0?":"+$.line:""}`:"";return` - **${K.trigger}** (iter ${K.iteration})${z}: ${K.rootCause}`}function G7(K,$,z){let Q=[];if(Q.push(`# Loki escalation handoff -- ${vK()}`),Q.push(""),Q.push(`Gate **${K.gateName}** has failed ${K.consecutiveFailures} consecutive times at iteration ${K.iteration}.`),Q.push(""),Q.push(`Reason: ${K.detail}`),Q.push(""),$.length>0){Q.push(`## Outstanding findings (${$.length})`),Q.push("");for(let X of $)Q.push(gK(X));Q.push("")}else Q.push("## Outstanding findings"),Q.push(""),Q.push("(no per-finding records captured -- gate failed without populating reviewer outputs)"),Q.push("");if(z.length>0){Q.push(`## Recent learnings (${Math.min(z.length,10)})`),Q.push("");for(let X of z.slice(-10))Q.push(mK(X));Q.push("")}return Q.push("## What the human must decide"),Q.push(""),Q.push("- Approve override? Write `.loki/state/counter-evidence-<iter>.json` with one entry per finding to dispute, then `rm .loki/PAUSE` to resume."),Q.push("- Disable a gate? Set `LOKI_GATE_<NAME>=false` in env (see skills/quality-gates.md)."),Q.push("- Tweak escalation? Set `LOKI_GATE_PAUSE_LIMIT` or `LOKI_GATE_ESCALATE_LIMIT`."),Q.push("- Roll back? Switch to `LOKI_LEGACY_BASH=1` and re-run; the bash route does not consult this handoff doc."),Q.push(""),Q.push("To resume: address the findings (or supply counter-evidence) and `rm .loki/PAUSE`."),Q.join(`
431
- `)}function fK(K,$){kK(yK(K),{recursive:!0});let z=`${K}.tmp.${process.pid}.${++B7}`;bK(z,$),hK(z,K)}function uK(K,$,z={}){let Q=z.findings??p1(K,$.iteration).findings,X=z.learnings??l1(K).learnings,H=G7($,Q,X),Z=(z.now?.()??new Date).toISOString().replace(/[-:.]/g,""),V=x1(K,"escalations"),q=++B7,U=x1(V,`handoff-${Z}-${process.pid}-${q}-${$.gateName}.md`);return fK(U,H),{path:U,bytes:H.length}}function pK(K){let $=x1(K,"escalations");if(!NK($))return null;let z;try{z=DK($).filter((H)=>H.endsWith(".md"))}catch{return null}if(z.length===0)return null;z.sort();let Q=z[z.length-1];if(!Q)return null;let X=x1($,Q);try{return{path:X,body:CK(X,"utf-8")}}catch{return null}}var B7=0;var Y7=L(()=>{R1();E1()});var O7={};b(O7,{runInternalPhase1Hooks:()=>aK,_resolveForTests:()=>nK,_internalPhase1HooksHelp:()=>iK});import{existsSync as cK,mkdirSync as lK,readdirSync as dK,statSync as oK}from"fs";import{join as q1,resolve as nK}from"path";async function aK(K){let[$,...z]=K;switch($){case void 0:case"help":case"--help":case"-h":return process.stdout.write(o1),$===void 0?1:0;case"reflect":return sK(z);case"override":return rK(z);case"handoff":return tK(z);default:return process.stderr.write(`Unknown subcommand: ${$}
432
- `),process.stderr.write(o1),2}}async function sK(K){let $=n1(K[0]);if($===null)return process.stderr.write(`reflect: missing or invalid <iter>
433
- `),2;let z=P();try{let X=(await Promise.resolve().then(() => (R1(),c1))).loadPreviousFindings(z,$);if(X.findings.length===0)return process.stdout.write(`reflect: no findings for iter ${$} (nothing to do)
434
- `),0;let H=q1(z,"state");lK(H,{recursive:!0}),W1(q1(H,`findings-${$}.json`),{review_id:X.reviewId,iteration:$,findings:X.findings});let Z=await Promise.resolve().then(() => (E1(),V7)),V=0;if(process.env.LOKI_AUTO_LEARNINGS!=="0"){for(let q of X.findings)if(q.severity==="Critical"||q.severity==="High")await Z.appendFromGateFailure(z,$,q,{episodeBridge:null}),V+=1}return process.stdout.write(`reflect: persisted ${X.findings.length} findings + ${V} learnings (iter ${$})
429
+ `,{env:X,timeoutMs:15000});if(Z.exitCode===127)return{stored:!1,reason:"python3 not found"};let q=Z.stdout.trim();if(q==="OK")return{stored:!0,reason:"stored"};if(q.startsWith("ERR:"))return{stored:!1,reason:q.replace(/^ERR:/,"")};return{stored:!1,reason:Z.stderr.trim()||"unknown"}}var Z7=L(()=>{Q1();y()});var U7={};b(U7,{loadLearnings:()=>o1,appendLearning:()=>q1,appendFromGateFailure:()=>LK});import{existsSync as OK,readFileSync as TK}from"fs";import{join as H7}from"path";import{createHash as AK}from"crypto";function W7(K){return H7(K,jK)}function _K(K){if(K===null||typeof K!=="object")return!1;let $=K;return typeof $.id==="string"&&typeof $.timestamp==="string"&&typeof $.iteration==="number"&&typeof $.trigger==="string"&&typeof $.rootCause==="string"&&typeof $.fix==="string"&&typeof $.preventInFuture==="string"&&typeof $.evidence==="object"&&$.evidence!==null}function q7(K){if(!OK(K))return{version:1,learnings:[]};try{let $=TK(K,"utf-8"),z=JSON.parse($);if(z.version===1&&Array.isArray(z.learnings))return{version:1,learnings:z.learnings.filter(_K)}}catch{}return{version:1,learnings:[]}}function IK(K,$){return AK("sha256").update(`${K}\x00${$}`).digest("hex").slice(0,16)}async function q1(K,$,z={}){let Q=IK($.trigger,$.rootCause),X=new Date().toISOString(),H={id:Q,timestamp:X,...$},Z=W7(K);if(await p0(Z,()=>{let U=q7(Z),V=U.learnings.findIndex((G)=>G.id===Q);if(V>=0){let G=U.learnings[V];U.learnings[V]={...G,timestamp:X,iteration:H.iteration}}else U.learnings.push(H);W1(Z,U)}),z.episodeBridge!==null&&(z.episodeBridge!==void 0||process.env.LOKI_AUTO_LEARNINGS_EPISODE==="1")){let U=z.episodeBridge??X7,V=z.bridgeFailureLog??PK;try{let G=await U(K,{taskId:`learning-${Q}`,outcome:"failure",phase:"VERIFY",goal:`${$.trigger}: ${$.rootCause}`});if(G&&!G.stored){if(!new Set(["memory dir not initialized","stub"]).has(G.reason))V(`episode_bridge skipped: ${G.reason}`)}}catch(G){V(`episode_bridge threw: ${G.message}`)}}return H}function PK(K){process.stderr.write(`[learnings_writer] ${K}
430
+ `)}async function LK(K,$,z,Q={}){let X=`[${z.severity}] ${z.description}`;return q1(K,{iteration:$,trigger:"gate_failure",rootCause:X,fix:"pending: dev agent must address in next iteration or supply counter-evidence",preventInFuture:"if this finding recurs, lower its severity threshold or add a regression test",evidence:{reviewId:z.reviewId,file:z.file??void 0,line:z.line??void 0,severity:z.severity,reviewer:z.reviewer}},Q)}function o1(K){return q7(W7(K))}var jK;var x1=L(()=>{I1();Z7();jK=H7("state","relevant-learnings.json")});var J7={};b(J7,{runOverrideCouncil:()=>SK,recordOverrideOutcome:()=>NK,loadCounterEvidence:()=>wK,canonicalFindingId:()=>n1,DEFAULT_OVERRIDE_JUDGES:()=>V7});import{existsSync as RK,readFileSync as EK}from"fs";import{join as xK}from"path";function wK(K,$){let z=xK(K,"state",`counter-evidence-${$}.json`);if(!RK(z))return null;try{let Q=EK(z,"utf-8"),X=JSON.parse(Q);if(typeof X.iteration!=="number")return null;let H=Array.isArray(X.evidence)?X.evidence:[],Z=[];for(let q of H){if(typeof q!=="object"||q===null)continue;let U=q;if(typeof U.findingId!=="string")continue;if(typeof U.claim!=="string")continue;let V=U.proofType;if(typeof V!=="string"||!FK.has(V))continue;let G=V,B=Array.isArray(U.artifacts)?U.artifacts:[];Z.push({findingId:U.findingId,claim:U.claim,proofType:G,artifacts:B.filter((J)=>typeof J==="string")})}return{iteration:X.iteration,evidence:Z}}catch{return null}}async function SK(K,$,z,Q={}){let X=Q.judges??V7,H=new Set,Z=new Set,q={},U=new Map;for(let V of $.evidence)U.set(V.findingId,V);for(let V of K){let G=n1(V),B=U.get(G);if(!B){Z.add(G);continue}let J=await Promise.all(X.map((j)=>z({finding:V,evidence:B,judge:j})));if(q[G]=J,J.filter((j)=>j.verdict==="APPROVE_OVERRIDE").length>=2)H.add(G);else Z.add(G)}return{approvedFindingIds:H,rejectedFindingIds:Z,votes:q}}function n1(K){let $=K.raw.slice(0,80).replace(/\s+/g," ").trim();return`${K.reviewer}::${$}`}async function NK(K,$,z,Q,X={}){let H={episodeBridge:X.episodeBridge===void 0?null:X.episodeBridge};for(let Z of Q){let q=n1(Z);if(z.approvedFindingIds.has(q))await q1(K,{iteration:$,trigger:"override_approved",rootCause:`[${Z.severity}] ${Z.description}`,fix:"override council approved counter-evidence; finding lifted",preventInFuture:"if this reviewer/file pair recurs, narrow the reviewer's selector OR add a baseline doc",evidence:{findingId:q,reviewId:Z.reviewId,file:Z.file??void 0,line:Z.line??void 0,severity:Z.severity,reviewer:Z.reviewer}},H);else if(z.rejectedFindingIds.has(q))await q1(K,{iteration:$,trigger:"override_rejected",rootCause:`[${Z.severity}] ${Z.description}`,fix:"override council rejected -- dev agent must fix the finding",preventInFuture:"address this finding in the next iteration",evidence:{findingId:q,reviewId:Z.reviewId,file:Z.file??void 0,line:Z.line??void 0,severity:Z.severity,reviewer:Z.reviewer}},H)}}var FK,V7;var G7=L(()=>{x1();FK=new Set(["file-exists","test-passes","grep-miss","reviewer-misread","duplicate-code-path","out-of-scope"]);V7=["judge-primary","judge-secondary","judge-tertiary"]});var Y7={};b(Y7,{writeEscalationHandoff:()=>pK,renderHandoff:()=>B7,readLatestHandoff:()=>cK});import{existsSync as kK,mkdirSync as DK,readdirSync as CK,readFileSync as hK,renameSync as bK,writeFileSync as yK}from"fs";import{dirname as vK,join as F1}from"path";function gK(){return new Date().toISOString()}function mK(K){let $=K.file?` (${K.file}${K.line!==null?":"+K.line:""})`:"";return` - [${K.severity}] ${K.description}${$} -- ${K.reviewer}`}function fK(K){let $=K.evidence,z=$.file?` ${$.file}${$.line!==void 0?":"+$.line:""}`:"";return` - **${K.trigger}** (iter ${K.iteration})${z}: ${K.rootCause}`}function B7(K,$,z){let Q=[];if(Q.push(`# Loki escalation handoff -- ${gK()}`),Q.push(""),Q.push(`Gate **${K.gateName}** has failed ${K.consecutiveFailures} consecutive times at iteration ${K.iteration}.`),Q.push(""),Q.push(`Reason: ${K.detail}`),Q.push(""),$.length>0){Q.push(`## Outstanding findings (${$.length})`),Q.push("");for(let X of $)Q.push(mK(X));Q.push("")}else Q.push("## Outstanding findings"),Q.push(""),Q.push("(no per-finding records captured -- gate failed without populating reviewer outputs)"),Q.push("");if(z.length>0){Q.push(`## Recent learnings (${Math.min(z.length,10)})`),Q.push("");for(let X of z.slice(-10))Q.push(fK(X));Q.push("")}return Q.push("## What the human must decide"),Q.push(""),Q.push("- Approve override? Write `.loki/state/counter-evidence-<iter>.json` with one entry per finding to dispute, then `rm .loki/PAUSE` to resume."),Q.push("- Disable a gate? Set `LOKI_GATE_<NAME>=false` in env (see skills/quality-gates.md)."),Q.push("- Tweak escalation? Set `LOKI_GATE_PAUSE_LIMIT` or `LOKI_GATE_ESCALATE_LIMIT`."),Q.push("- Roll back? Switch to `LOKI_LEGACY_BASH=1` and re-run; the bash route does not consult this handoff doc."),Q.push(""),Q.push("To resume: address the findings (or supply counter-evidence) and `rm .loki/PAUSE`."),Q.join(`
431
+ `)}function uK(K,$){DK(vK(K),{recursive:!0});let z=`${K}.tmp.${process.pid}.${++M7}`;yK(z,$),bK(z,K)}function pK(K,$,z={}){let Q=z.findings??l1(K,$.iteration).findings,X=z.learnings??o1(K).learnings,H=B7($,Q,X),Z=(z.now?.()??new Date).toISOString().replace(/[-:.]/g,""),q=F1(K,"escalations"),U=++M7,V=F1(q,`handoff-${Z}-${process.pid}-${U}-${$.gateName}.md`);return uK(V,H),{path:V,bytes:H.length}}function cK(K){let $=F1(K,"escalations");if(!kK($))return null;let z;try{z=CK($).filter((H)=>H.endsWith(".md"))}catch{return null}if(z.length===0)return null;z.sort();let Q=z[z.length-1];if(!Q)return null;let X=F1($,Q);try{return{path:X,body:hK(X,"utf-8")}}catch{return null}}var M7=0;var O7=L(()=>{E1();x1()});var T7={};b(T7,{runInternalPhase1Hooks:()=>sK,_resolveForTests:()=>aK,_internalPhase1HooksHelp:()=>eK});import{existsSync as lK,mkdirSync as dK,readdirSync as oK,statSync as nK}from"fs";import{join as U1,resolve as aK}from"path";async function sK(K){let[$,...z]=K;switch($){case void 0:case"help":case"--help":case"-h":return process.stdout.write(a1),$===void 0?1:0;case"reflect":return rK(z);case"override":return tK(z);case"handoff":return iK(z);default:return process.stderr.write(`Unknown subcommand: ${$}
432
+ `),process.stderr.write(a1),2}}async function rK(K){let $=s1(K[0]);if($===null)return process.stderr.write(`reflect: missing or invalid <iter>
433
+ `),2;let z=P();try{let X=(await Promise.resolve().then(() => (E1(),d1))).loadPreviousFindings(z,$);if(X.findings.length===0)return process.stdout.write(`reflect: no findings for iter ${$} (nothing to do)
434
+ `),0;let H=U1(z,"state");dK(H,{recursive:!0}),W1(U1(H,`findings-${$}.json`),{review_id:X.reviewId,iteration:$,findings:X.findings});let Z=await Promise.resolve().then(() => (x1(),U7)),q=0;if(process.env.LOKI_AUTO_LEARNINGS!=="0"){for(let U of X.findings)if(U.severity==="Critical"||U.severity==="High")await Z.appendFromGateFailure(z,$,U,{episodeBridge:null}),q+=1}return process.stdout.write(`reflect: persisted ${X.findings.length} findings + ${q} learnings (iter ${$})
435
435
  `),0}catch(Q){return process.stderr.write(`reflect: ${Q.message}
436
- `),1}}async function rK(K){let $=n1(K[0]);if($===null)return process.stderr.write(`override: missing or invalid <iter>
437
- `),2;let z=P();try{let Q=await Promise.resolve().then(() => (J7(),U7)),X=Q.loadCounterEvidence(z,$);if(X===null||X.evidence.length===0)return process.stdout.write(`override: no counter-evidence for iter ${$} (skip)
438
- `),0;let Z=(await Promise.resolve().then(() => (R1(),c1))).loadPreviousFindings(z,$),V=Z.findings.filter((_)=>_.severity==="Critical"||_.severity==="High");if(V.length===0)return process.stdout.write(`override: no blocking findings for iter ${$} (skip)
439
- `),0;let q=new Set(["duplicate-code-path","file-exists","test-passes","grep-miss","out-of-scope"]),U=async(_)=>{let C=q.has(_.evidence.proofType);return{judge:_.judge,verdict:C?"APPROVE_OVERRIDE":"REJECT_OVERRIDE",reasoning:C?`[stub] proofType=${_.evidence.proofType} trusted`:`[stub] proofType=${_.evidence.proofType} requires manual review`}},G=await Q.runOverrideCouncil(V,X,U);await Q.recordOverrideOutcome(z,$,G,V);let B=q1(z,"quality","reviews");if(cK(B))try{let _=dK(B).filter((g)=>g.startsWith("review-")).sort(),C=_[_.length-1];if(C&&oK(q1(B,C)).isDirectory())W1(q1(B,C,`override-${$}.json`),{review_id:Z.reviewId,iteration:$,approved_finding_ids:Array.from(G.approvedFindingIds),rejected_finding_ids:Array.from(G.rejectedFindingIds),votes:G.votes})}catch{}let J=G.approvedFindingIds.size,Y=G.rejectedFindingIds.size;if(Y===0&&J>0)process.stdout.write(`override: LIFTED -- ${J} approved, ${Y} rejected
436
+ `),1}}async function tK(K){let $=s1(K[0]);if($===null)return process.stderr.write(`override: missing or invalid <iter>
437
+ `),2;let z=P();try{let Q=await Promise.resolve().then(() => (G7(),J7)),X=Q.loadCounterEvidence(z,$);if(X===null||X.evidence.length===0)return process.stdout.write(`override: no counter-evidence for iter ${$} (skip)
438
+ `),0;let Z=(await Promise.resolve().then(() => (E1(),d1))).loadPreviousFindings(z,$),q=Z.findings.filter((_)=>_.severity==="Critical"||_.severity==="High");if(q.length===0)return process.stdout.write(`override: no blocking findings for iter ${$} (skip)
439
+ `),0;let U=new Set(["duplicate-code-path","file-exists","test-passes","grep-miss","out-of-scope"]),V=async(_)=>{let C=U.has(_.evidence.proofType);return{judge:_.judge,verdict:C?"APPROVE_OVERRIDE":"REJECT_OVERRIDE",reasoning:C?`[stub] proofType=${_.evidence.proofType} trusted`:`[stub] proofType=${_.evidence.proofType} requires manual review`}},G=await Q.runOverrideCouncil(q,X,V);await Q.recordOverrideOutcome(z,$,G,q);let B=U1(z,"quality","reviews");if(lK(B))try{let _=oK(B).filter((g)=>g.startsWith("review-")).sort(),C=_[_.length-1];if(C&&nK(U1(B,C)).isDirectory())W1(U1(B,C,`override-${$}.json`),{review_id:Z.reviewId,iteration:$,approved_finding_ids:Array.from(G.approvedFindingIds),rejected_finding_ids:Array.from(G.rejectedFindingIds),votes:G.votes})}catch{}let J=G.approvedFindingIds.size,Y=G.rejectedFindingIds.size;if(Y===0&&J>0)process.stdout.write(`override: LIFTED -- ${J} approved, ${Y} rejected
440
440
  `);else process.stdout.write(`override: BLOCKED -- ${J} approved, ${Y} rejected
441
441
  `);return 0}catch(Q){return process.stderr.write(`override: ${Q.message}
442
- `),1}}async function tK(K){let $=K[0],z=Number.parseInt(K[1]??"0",10),Q=n1(K[2]);if(!$||!Number.isFinite(z)||Q===null)return process.stderr.write(`handoff: usage: handoff <gate> <consecutive-failures> <iter>
443
- `),2;let X=P();try{let Z=(await Promise.resolve().then(() => (Y7(),M7))).writeEscalationHandoff(X,{gateName:$,iteration:Q,consecutiveFailures:z,detail:`${$} hit PAUSE_LIMIT (${z} consecutive failures)`});return process.stdout.write(`handoff: wrote ${Z.path} (${Z.bytes}B)
442
+ `),1}}async function iK(K){let $=K[0],z=Number.parseInt(K[1]??"0",10),Q=s1(K[2]);if(!$||!Number.isFinite(z)||Q===null)return process.stderr.write(`handoff: usage: handoff <gate> <consecutive-failures> <iter>
443
+ `),2;let X=P();try{let Z=(await Promise.resolve().then(() => (O7(),Y7))).writeEscalationHandoff(X,{gateName:$,iteration:Q,consecutiveFailures:z,detail:`${$} hit PAUSE_LIMIT (${z} consecutive failures)`});return process.stdout.write(`handoff: wrote ${Z.path} (${Z.bytes}B)
444
444
  `),0}catch(H){return process.stderr.write(`handoff: ${H.message}
445
- `),1}}function n1(K){if(K===void 0)return null;let $=Number.parseInt(K,10);return Number.isFinite($)&&$>=0?$:null}var o1=`loki internal phase1-hooks <subcommand>
445
+ `),1}}function s1(K){if(K===void 0)return null;let $=Number.parseInt(K,10);return Number.isFinite($)&&$>=0?$:null}var a1=`loki internal phase1-hooks <subcommand>
446
446
 
447
447
  Subcommands:
448
448
  reflect <iter> Persist structured findings + auto-learnings.
@@ -451,8 +451,8 @@ Subcommands:
451
451
 
452
452
  This command is invoked by autonomy/run.sh between iterations. Users
453
453
  should not run it directly -- run \`loki start\` instead.
454
- `,iK;var T7=L(()=>{y();_1();iK=o1});y();import{readFileSync as E7}from"fs";import{resolve as x7,dirname as F7}from"path";import{fileURLToPath as w7}from"url";var o=null;function i1(){if(o!==null)return o;let K="7.6.0";if(typeof K==="string"&&K.length>0)return o=K,o;try{let $=F7(w7(import.meta.url)),z=S1($);o=E7(x7(z,"VERSION"),"utf-8").trim()}catch{o="unknown"}return o}function e1(){return process.stdout.write(`Loki Mode v${i1()}
455
- `),0}p();n();y();import{readFileSync as C7,existsSync as h7}from"fs";import{resolve as b7}from"path";var y7=["claude","codex","cline","aider"];function $0(){let K=b7(P(),"state","provider");if(!h7(K))return"";try{return C7(K,"utf-8").trim()}catch{return""}}function v7(K,$){return K||$||process.env.LOKI_PROVIDER||"claude"}function g7(K){let $=$0(),z=v7(K,$);switch(process.stdout.write(`${k}Current Provider${W}
454
+ `,eK;var A7=L(()=>{y();I1();eK=a1});D1();function K0(){return process.stdout.write(`Loki Mode v${G1()}
455
+ `),0}p();n();y();import{readFileSync as h7,existsSync as b7}from"fs";import{resolve as y7}from"path";var v7=["claude","codex","cline","aider"];function z0(){let K=y7(P(),"state","provider");if(!b7(K))return"";try{return h7(K,"utf-8").trim()}catch{return""}}function g7(K,$){return K||$||process.env.LOKI_PROVIDER||"claude"}function m7(K){let $=z0(),z=g7(K,$);switch(process.stdout.write(`${k}Current Provider${W}
456
456
  `),process.stdout.write(`
457
457
  `),process.stdout.write(`${O}Provider:${W} ${z}
458
458
  `),z){case"claude":process.stdout.write(`${D}Status:${W} Full features (subagents, parallel, MCP)
@@ -463,12 +463,12 @@ should not run it directly -- run \`loki start\` instead.
463
463
  `);return process.stdout.write(`
464
464
  `),process.stdout.write(`Switch provider: ${O}loki provider set <name>${W}
465
465
  `),process.stdout.write(`Available: ${O}loki provider list${W}
466
- `),0}async function m7(){let $=$0()||process.env.LOKI_PROVIDER||"claude";process.stdout.write(`${k}Available Providers${W}
466
+ `),0}async function f7(){let $=z0()||process.env.LOKI_PROVIDER||"claude";process.stdout.write(`${k}Available Providers${W}
467
467
  `),process.stdout.write(`
468
- `);let z=await Promise.all(y7.map(async(H)=>[H,await h(H)!==null])),Q=new Map;for(let[H,Z]of z)Q.set(H,Z?`${D}installed${W}`:`${R}not installed${W}`);let X=[["claude","claude - Claude Code (Anthropic) "],["codex","codex - Codex CLI (OpenAI) "],["cline","cline - Cline (multi-provider) "],["aider","aider - Aider (terminal pair prog) "]];for(let[H,Z]of X){let V=$===H?` ${O}(current)${W}`:"";process.stdout.write(` ${Z} ${Q.get(H)}${V}
468
+ `);let z=await Promise.all(v7.map(async(H)=>[H,await h(H)!==null])),Q=new Map;for(let[H,Z]of z)Q.set(H,Z?`${D}installed${W}`:`${R}not installed${W}`);let X=[["claude","claude - Claude Code (Anthropic) "],["codex","codex - Codex CLI (OpenAI) "],["cline","cline - Cline (multi-provider) "],["aider","aider - Aider (terminal pair prog) "]];for(let[H,Z]of X){let q=$===H?` ${O}(current)${W}`:"";process.stdout.write(` ${Z} ${Q.get(H)}${q}
469
469
  `)}return process.stdout.write(`
470
470
  `),process.stdout.write(`Set provider: ${O}loki provider set <name>${W}
471
- `),0}function f7(){return process.stdout.write(`${k}Loki Mode Provider Management${W}
471
+ `),0}function u7(){return process.stdout.write(`${k}Loki Mode Provider Management${W}
472
472
  `),process.stdout.write(`
473
473
  `),process.stdout.write(`Usage: loki provider <command>
474
474
  `),process.stdout.write(`
@@ -486,8 +486,8 @@ should not run it directly -- run \`loki start\` instead.
486
486
  `),process.stdout.write(` loki provider list
487
487
  `),process.stdout.write(` loki provider info codex
488
488
  `),process.stdout.write(` loki provider models
489
- `),0}async function z0(K){let $=K[0]??"show",z=K.slice(1);switch($){case"show":case"current":return g7(z[0]);case"list":return m7();case"set":case"info":case"models":return u7(["provider",$,...z]);default:return f7()}}async function u7(K){let{run:$}=await Promise.resolve().then(() => (p(),K0)),{resolve:z}=await import("path"),{REPO_ROOT:Q}=await Promise.resolve().then(() => (y(),t1)),X=z(Q,"autonomy","loki"),H=await $([X,...K],{env:{LOKI_LEGACY_BASH:"1"},timeoutMs:3600000});return process.stdout.write(H.stdout),process.stderr.write(H.stderr),H.exitCode}n();y();Q1();p();import{existsSync as Q0,readFileSync as c7}from"fs";import{resolve as t}from"path";import{mkdir as l7}from"fs/promises";var X1=t(N1(),"learnings");function D1(K){if(!Q0(K))return 0;try{let $=c7(K,"utf-8"),z=0;for(let Q of $.split(`
490
- `))if(Q.includes('"description"'))z++;return z}catch{return 0}}async function d7(){await l7(X1,{recursive:!0});let K=D1(t(X1,"patterns.jsonl")),$=D1(t(X1,"mistakes.jsonl")),z=D1(t(X1,"successes.jsonl"));return process.stdout.write(`${k}Cross-Project Learnings${W}
489
+ `),0}async function Q0(K){let $=K[0]??"show",z=K.slice(1);switch($){case"show":case"current":return m7(z[0]);case"list":return f7();case"set":case"info":case"models":return p7(["provider",$,...z]);default:return u7()}}async function p7(K){let{run:$}=await Promise.resolve().then(() => (p(),$0)),{resolve:z}=await import("path"),{REPO_ROOT:Q}=await Promise.resolve().then(() => (y(),e1)),X=z(Q,"autonomy","loki"),H=await $([X,...K],{env:{LOKI_LEGACY_BASH:"1"},timeoutMs:3600000});return process.stdout.write(H.stdout),process.stderr.write(H.stderr),H.exitCode}n();y();Q1();p();import{existsSync as X0,readFileSync as l7}from"fs";import{resolve as t}from"path";import{mkdir as d7}from"fs/promises";var X1=t(k1(),"learnings");function h1(K){if(!X0(K))return 0;try{let $=l7(K,"utf-8"),z=0;for(let Q of $.split(`
490
+ `))if(Q.includes('"description"'))z++;return z}catch{return 0}}async function o7(){await d7(X1,{recursive:!0});let K=h1(t(X1,"patterns.jsonl")),$=h1(t(X1,"mistakes.jsonl")),z=h1(t(X1,"successes.jsonl"));return process.stdout.write(`${k}Cross-Project Learnings${W}
491
491
  `),process.stdout.write(`
492
492
  `),process.stdout.write(` Patterns: ${D}${K}${W}
493
493
  `),process.stdout.write(` Mistakes: ${E}${$}${W}
@@ -496,7 +496,7 @@ should not run it directly -- run \`loki start\` instead.
496
496
  `),process.stdout.write(`Location: ${X1}
497
497
  `),process.stdout.write(`
498
498
  `),process.stdout.write(`Use 'loki memory show <type>' to view entries
499
- `),0}async function o7(K){if(K){let Q=`
499
+ `),0}async function n7(K){if(K){let Q=`
500
500
  try:
501
501
  from memory.layers import IndexLayer
502
502
  layer = IndexLayer('.loki/memory')
@@ -506,9 +506,9 @@ except ImportError:
506
506
  print('Error: memory.layers module not found')
507
507
  except Exception as e:
508
508
  print(f'Error: {e}')
509
- `.trim(),X=await a(Q,{cwd:u});return process.stdout.write(X.stdout),0}let $=t(P(),"memory","index.json");if(!Q0($))return process.stdout.write(`No index found
509
+ `.trim(),X=await a(Q,{cwd:u});return process.stdout.write(X.stdout),0}let $=t(P(),"memory","index.json");if(!X0($))return process.stdout.write(`No index found
510
510
  `),0;let z=await a(`import json, sys; sys.stdout.write(json.dumps(json.load(open(${JSON.stringify($)})), indent=4) + "\\n")`);if(z.exitCode!==0)return process.stdout.write(`No index found
511
- `),0;return process.stdout.write(z.stdout),0}async function X0(K){switch(K[0]??"list"){case"list":case"ls":return d7();case"index":return o7(K[1]==="rebuild");default:{let z=t(u,"autonomy","loki"),Q=await w([z,"memory",...K],{env:{LOKI_LEGACY_BASH:"1"},timeoutMs:3600000});return process.stdout.write(Q.stdout),process.stderr.write(Q.stderr),Q.exitCode}}}var A7=`Loki Mode (TypeScript port, Phase 2 of bash->Bun migration)
511
+ `),0;return process.stdout.write(z.stdout),0}async function Z0(K){switch(K[0]??"list"){case"list":case"ls":return o7();case"index":return n7(K[1]==="rebuild");default:{let z=t(u,"autonomy","loki"),Q=await w([z,"memory",...K],{env:{LOKI_LEGACY_BASH:"1"},timeoutMs:3600000});return process.stdout.write(Q.stdout),process.stderr.write(Q.stderr),Q.exitCode}}}var j7=`Loki Mode (TypeScript port, Phase 2 of bash->Bun migration)
512
512
 
513
513
  Usage: loki <command> [args...]
514
514
 
@@ -526,12 +526,12 @@ Phase 2 ported (Bun-native, fast):
526
526
 
527
527
  All other commands fall through to the bash CLI (autonomy/loki).
528
528
  Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
529
- `;function eK(){let K=process.env.LOKI_LEGACY_BASH;if(K===void 0)return;let $=K.trim().toLowerCase();if($!=="1"&&$!=="true"&&$!=="yes"&&$!=="on")return;if(process.env.LOKI_SUPPRESS_BUN_DIRECT_WARN==="1")return;process.stderr.write(`warning: LOKI_LEGACY_BASH is set, but you are running the Bun runtime directly (src/cli.ts). The env var only takes effect via the bin/loki shim, which dispatches between Bun and bash. Behavior is unchanged; this message is informational.
530
- `)}async function K6(K){eK();let $=K[0],z=K.slice(1);switch($){case void 0:case"help":case"--help":case"-h":return process.stdout.write(A7),0;case"version":case"--version":case"-v":return e1();case"provider":return z0(z);case"memory":return X0(z);case"status":{let{runStatus:Q}=await Promise.resolve().then(() => (G0(),J0));return Q(z)}case"stats":{let{runStats:Q}=await Promise.resolve().then(() => (T0(),O0));return Q(z)}case"doctor":{let{runDoctor:Q}=await Promise.resolve().then(() => (F0(),x0));return Q(z)}case"kpis":{let{runKpis:Q}=await Promise.resolve().then(() => (f0(),m0));return Q(z)}case"rollback":{let{runRollback:Q}=await Promise.resolve().then(() => (t0(),r0));return Q(z)}case"internal":{let Q=z[0];if(!Q||Q==="--help"||Q==="-h"||Q==="help"){let H=["loki internal -- runtime hooks driven by autonomy/run.sh","","Subcommands:"," phase1-hooks Persist structured findings, run override council,"," append learnings, and write the escalation handoff"," doc once per iteration. Driven by run.sh; not"," intended for direct invocation.","","Phase 1 (RARV-C closure) env vars:"," LOKI_INJECT_FINDINGS=1 Persist structured reviewer findings to"," .loki/state/findings-<iter>.json so the"," next iteration can address them."," LOKI_OVERRIDE_COUNCIL=1 Allow a 3-LLM override panel to lift a"," BLOCK when counter-evidence is presented."," See LOKI_OVERRIDE_JUDGES (csv),"," LOKI_OVERRIDE_PANEL_SIZE,"," LOKI_OVERRIDE_REAL_JUDGE."," LOKI_AUTO_LEARNINGS=1 Append failure rootcauses to"," .loki/state/relevant-learnings.json via"," the episodic memory bridge."," LOKI_HANDOFF_MD=1 Write a structured human handoff doc to"," .loki/escalations/<ts>.md before PAUSE.","","All four are default-on as of v7.5.3. Set to 0 to disable.","Reference: CHANGELOG.md (search 'Phase 1') and skills/healing.md.","","These commands are wired into the autonomous loop and may change","without notice. Do not script against them.",""].join(`
529
+ `;function K6(){let K=process.env.LOKI_LEGACY_BASH;if(K===void 0)return;let $=K.trim().toLowerCase();if($!=="1"&&$!=="true"&&$!=="yes"&&$!=="on")return;if(process.env.LOKI_SUPPRESS_BUN_DIRECT_WARN==="1")return;process.stderr.write(`warning: LOKI_LEGACY_BASH is set, but you are running the Bun runtime directly (src/cli.ts). The env var only takes effect via the bin/loki shim, which dispatches between Bun and bash. Behavior is unchanged; this message is informational.
530
+ `)}async function $6(K){K6();let $=K[0],z=K.slice(1);switch($){case void 0:case"help":case"--help":case"-h":return process.stdout.write(j7),0;case"version":case"--version":case"-v":return K0();case"provider":return Q0(z);case"memory":return Z0(z);case"status":{let{runStatus:Q}=await Promise.resolve().then(() => (B0(),G0));return Q(z)}case"stats":{let{runStats:Q}=await Promise.resolve().then(() => (A0(),T0));return Q(z)}case"doctor":{let{runDoctor:Q}=await Promise.resolve().then(() => (w0(),F0));return Q(z)}case"kpis":{let{runKpis:Q}=await Promise.resolve().then(() => (u0(),f0));return Q(z)}case"rollback":{let{runRollback:Q}=await Promise.resolve().then(() => (i0(),t0));return Q(z)}case"internal":{let Q=z[0];if(!Q||Q==="--help"||Q==="-h"||Q==="help"){let H=["loki internal -- runtime hooks driven by autonomy/run.sh","","Subcommands:"," phase1-hooks Persist structured findings, run override council,"," append learnings, and write the escalation handoff"," doc once per iteration. Driven by run.sh; not"," intended for direct invocation.","","Phase 1 (RARV-C closure) env vars:"," LOKI_INJECT_FINDINGS=1 Persist structured reviewer findings to"," .loki/state/findings-<iter>.json so the"," next iteration can address them."," LOKI_OVERRIDE_COUNCIL=1 Allow a 3-LLM override panel to lift a"," BLOCK when counter-evidence is presented."," See LOKI_OVERRIDE_JUDGES (csv),"," LOKI_OVERRIDE_PANEL_SIZE,"," LOKI_OVERRIDE_REAL_JUDGE."," LOKI_AUTO_LEARNINGS=1 Append failure rootcauses to"," .loki/state/relevant-learnings.json via"," the episodic memory bridge."," LOKI_HANDOFF_MD=1 Write a structured human handoff doc to"," .loki/escalations/<ts>.md before PAUSE.","","All four are default-on as of v7.5.3. Set to 0 to disable.","Reference: CHANGELOG.md (search 'Phase 1') and skills/healing.md.","","These commands are wired into the autonomous loop and may change","without notice. Do not script against them.",""].join(`
531
531
  `);return process.stdout.write(`${H}
532
- `),0}if(Q==="phase1-hooks"){let{runInternalPhase1Hooks:H}=await Promise.resolve().then(() => (T7(),O7));return H(z.slice(1))}return process.stderr.write(`Unknown internal subcommand: ${Q}
532
+ `),0}if(Q==="phase1-hooks"){let{runInternalPhase1Hooks:H}=await Promise.resolve().then(() => (A7(),T7));return H(z.slice(1))}return process.stderr.write(`Unknown internal subcommand: ${Q}
533
533
  `),process.stderr.write(`Run 'loki internal --help' for the supported list.
534
534
  `),2}default:return process.stderr.write(`Unknown command: ${$}
535
- `),process.stderr.write(A7),2}}process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var $6=await K6(Bun.argv.slice(2));process.exit($6);
535
+ `),process.stderr.write(j7),2}}process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var z6=await $6(Bun.argv.slice(2));process.exit(z6);
536
536
 
537
- //# debugId=1FE99E821A4C6C7B64756E2164756E21
537
+ //# debugId=9A266FF314C7FA3664756E2164756E21
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '7.6.0'
60
+ __version__ = '7.6.2'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loki-mode",
3
- "version": "7.6.0",
3
+ "version": "7.6.2",
4
4
  "description": "Loki Mode by Autonomi. Multi-agent autonomous SDLC framework. Spec to deployed app: PRD, GitHub issue, OpenAPI/JSON/YAML, or one-line brief. 4 AI providers (Claude Code, OpenAI Codex, Cline, Aider). 11 quality gates.",
5
5
  "keywords": [
6
6
  "agent",