loki-mode 7.39.0 → 7.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -105,7 +105,7 @@ loki quick "build a landing page with a signup form"
105
105
  |--------|---------|-------|
106
106
  | **Bun (recommended)** | `bun install -g loki-mode` | Fastest startup for CLI commands. |
107
107
  | **Homebrew** | `brew tap asklokesh/tap && brew install loki-mode` | Auto-installs Bun as a dep |
108
- | **Docker** | `docker pull asklokesh/loki-mode:7.31.0 && docker run --rm asklokesh/loki-mode:7.31.0 start prd.md` | Bun pre-installed in image |
108
+ | **Docker** | `docker pull asklokesh/loki-mode:7.40.0 && docker run --rm asklokesh/loki-mode:7.40.0 start prd.md` | Bun pre-installed in image |
109
109
  | **npm (compat)** | `npm install -g loki-mode` | Works without Bun (bash fallback). Migrate any time with `loki self-update --to bun`. |
110
110
 
111
111
  **Upgrading:**
@@ -165,7 +165,7 @@ The next major release sunsets the Bash runtime entirely. There is no firm calen
165
165
  | Method | Command |
166
166
  |--------|---------|
167
167
  | **Homebrew** | `brew tap asklokesh/tap && brew install loki-mode` |
168
- | **Docker** | `docker pull asklokesh/loki-mode:7.31.0` |
168
+ | **Docker** | `docker pull asklokesh/loki-mode:7.40.0` |
169
169
  | **Inside Claude Code** | `claude --dangerously-skip-permissions` then type "Loki Mode" |
170
170
  | **Git clone** | `git clone https://github.com/asklokesh/loki-mode.git` |
171
171
 
package/SKILL.md CHANGED
@@ -3,7 +3,7 @@ name: loki-mode
3
3
  description: Autonomous spec-driven build system with a built-in trust layer. It does not call work done until it is verified (RARV-C closure loop, 11 quality gates, completion council, verified-completion evidence gate). Triggers on "Loki Mode". Takes a spec (PRD, GitHub issue, OpenAPI doc, etc.) to deployed product with minimal human intervention. Provider-agnostic. Requires --dangerously-skip-permissions flag.
4
4
  ---
5
5
 
6
- # Loki Mode v7.39.0
6
+ # Loki Mode v7.40.0
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -398,4 +398,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
398
398
 
399
399
  ---
400
400
 
401
- **v7.39.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
401
+ **v7.40.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 7.39.0
1
+ 7.40.0
package/autonomy/loki CHANGED
@@ -13433,7 +13433,12 @@ def _resolve_session_pin(alias):
13433
13433
  elif _pin_tier == 'fast':
13434
13434
  _m = _provider_model_fast()
13435
13435
  elif _pin_tier == 'fable':
13436
- _m = 'fable'
13436
+ # fable unavailable, collapse to opus. Claude Fable 5 is not available at
13437
+ # the Claude API (advises use Opus 4.8); the runner dispatches opus for a
13438
+ # fable pin (claude.sh resolve_model_for_tier, run.sh dispatch backstop).
13439
+ # Quote opus pricing (\$5/\$25) so the estimator agrees with dispatch and
13440
+ # the dashboard (v7.39.1).
13441
+ _m = 'opus'
13437
13442
  else: # development (and the '*' fallthrough)
13438
13443
  _m = _provider_model_development()
13439
13444
  # Apply the shared LOKI_MAX_TIER clamp with the REAL tier and resolved model
@@ -13467,6 +13472,16 @@ if _is_override:
13467
13472
  else:
13468
13473
  _dispatched_model = _resolve_session_pin(session_model_env)
13469
13474
 
13475
+ # fable unavailable, collapse to opus (final dispatch backstop, mirrors run.sh).
13476
+ # The override-path clamp (_loki_clamp_alias) leaves an uncapped fable override as
13477
+ # 'fable', but the runner's dispatch backstop collapses tier_param=='fable' to
13478
+ # opus before --model. Apply the same collapse here so the OVERRIDE route quotes
13479
+ # opus too, agreeing with dispatch and the dashboard. The session-pin route is
13480
+ # already collapsed inside _resolve_session_pin; this also covers a fable override
13481
+ # file (v7.39.1).
13482
+ if _dispatched_model == 'fable':
13483
+ _dispatched_model = 'opus'
13484
+
13470
13485
  # Keep session_model_env (the alias) for token-volume tier mapping and provenance,
13471
13486
  # but record when the cost ceiling actually changed the dispatched model so the
13472
13487
  # provenance line is honest about the clamp.
@@ -13757,12 +13772,16 @@ for i in range(estimated_iterations):
13757
13772
  # IS the dispatched model. In session-pinned mode (the default) the runner
13758
13773
  # resolves the pin through provider config (_dispatched_model): a sonnet pin
13759
13774
  # dispatches opus, so we price Opus, not the tier's static 'Sonnet' label.
13760
- # The fable-architect iteration 0 genuinely dispatches fable (already cap-
13761
- # cleared above when it would clamp), so it keeps the advisor tier's Fable.
13775
+ # The fable-architect iteration 0 collapses to opus at dispatch (Fable 5 is
13776
+ # unavailable at the claude API), so it is priced as Opus, not Fable (v7.39.1).
13762
13777
  if legacy_tier_switching:
13763
13778
  model = info['model']
13764
13779
  elif _fable_architect and i == 0:
13765
- model = info['model'] # advisor tier -> Fable
13780
+ # Architect opt-in iter-0: fable unavailable, the runner dispatches opus
13781
+ # for the architecture pass (LOKI_FABLE_ARCHITECT collapses to opus in
13782
+ # run.sh / claude.sh). Token VOLUME stays the advisor/planning tier
13783
+ # (50k/8k), but the priced model is Opus, not Fable (v7.39.1).
13784
+ model = 'Opus'
13766
13785
  else:
13767
13786
  model = _priced_model_for(_dispatched_model)
13768
13787
  inp = info['input']
@@ -13969,7 +13988,10 @@ else:
13969
13988
  else:
13970
13989
  print(f' {DIM}(pinned via {_session_model_source}={session_model_env}; set LOKI_LEGACY_TIER_SWITCHING=true to rotate){NC}')
13971
13990
  if _fable_architect:
13972
- print(f' {DIM}(+ 1 architecture iteration on Fable via LOKI_FABLE_ARCHITECT=1){NC}')
13991
+ # fable unavailable: the architect opt-in now runs the architecture pass
13992
+ # on opus (Claude Fable 5 is not available at the Claude API). Disclose
13993
+ # Opus so the quote matches dispatch (v7.39.1).
13994
+ print(f' {DIM}(+ 1 architecture iteration on Opus via LOKI_FABLE_ARCHITECT=1){NC}')
13973
13995
  if fable_n > 0:
13974
13996
  print(f' {YELLOW}Fable 5 is the top-tier advisory model priced at 2x Opus ({chr(36)}10/{chr(36)}50 per MTok).{NC}')
13975
13997
 
package/autonomy/run.sh CHANGED
@@ -1822,18 +1822,23 @@ get_provider_tier_param() {
1822
1822
  if [ -n "${PROVIDER_MODEL_PLANNING:-}" ]; then
1823
1823
  echo "${PROVIDER_MODEL_PLANNING}"
1824
1824
  elif [ "${LOKI_FABLE_ARCHITECT:-0}" = "1" ]; then
1825
- echo "fable"
1825
+ # fable unavailable, collapse to opus. Claude Fable 5 is
1826
+ # not available at the Claude API ("use Opus 4.8"); the
1827
+ # architect opt-in now runs opus. Matches claude.sh
1828
+ # resolve_model_for_tier and the estimator/dashboard.
1829
+ echo "opus"
1826
1830
  else
1827
1831
  echo "opus"
1828
1832
  fi
1829
1833
  ;;
1830
1834
  development) echo "${PROVIDER_MODEL_DEVELOPMENT:-opus}" ;;
1831
1835
  fast) echo "${PROVIDER_MODEL_FAST:-sonnet}" ;;
1832
- # Honor the fable lever here too: without this arm an
1836
+ # fable unavailable, collapse to opus. Without this arm an
1833
1837
  # unsourced-claude.sh environment (this static fallback) would
1834
1838
  # silently downgrade a fable-pinned tier to sonnet via the `*`
1835
- # default. Matches resolve_model_for_tier's explicit fable) arm.
1836
- fable) echo "fable" ;;
1839
+ # default. Matches resolve_model_for_tier's explicit fable) arm,
1840
+ # which now resolves to opus (Fable 5 unavailable at the API).
1841
+ fable) echo "opus" ;;
1837
1842
  *) echo "sonnet" ;;
1838
1843
  esac
1839
1844
  ;;
@@ -10882,6 +10887,20 @@ build_prompt() {
10882
10887
  # the result is diff-friendly for later incremental updates).
10883
10888
  local analysis_instruction="CODEBASE_ANALYSIS_MODE: No PRD provided. Reverse-engineer a precise PRD from the existing code in three passes, cheaply and without blind full scans. PASS 1 (orient): list the top two directory levels; read ONLY high-signal manifests that exist (package.json, requirements.txt, pyproject.toml, Cargo.toml, go.mod, pom.xml, build.gradle, composer.json) to identify language, framework, and scripts; read README and any docs index. PASS 2 (locate): from the manifests and conventional layout, identify the entrypoints, the public API or CLI surface, the test directory and runner, and the config or env contract; read those first; skip generated, vendored, and lockfile content; prefer LSP workspace symbols when the lsp-proxy server is available. PASS 3 (write): write .loki/generated-prd.md with these sections: Overview, Detected Stack, Entrypoints and Components, Existing Behavior and Requirements (reverse-engineered, observable), Test and Build Setup, Gaps and TODOs, Out of Scope. Keep it under 200 lines, plain Markdown, no emojis, no em dashes. Do not invent features not evidenced by the code. THEN execute SDLC phases against that PRD."
10884
10889
 
10890
+ # v7.40.0 (#584): when the autonomous complexity-gated decision in
10891
+ # run_autonomous chose the Claude Code Dynamic Workflow path
10892
+ # (USE_WORKFLOW_ANALYSIS=1), prefix the read-only analysis instruction with
10893
+ # "ultracode: " so the three-pass reverse-engineer-a-PRD flow runs as a
10894
+ # workflow fan-out. The decision (and its one-time stderr disclosure) is made
10895
+ # ONCE in run_autonomous, not here -- this subshell only reads the resolved
10896
+ # global so the prefix is deterministic per iteration. When the global is 0 /
10897
+ # unset (simple/standard + var unset, or non-Claude, or degraded, or escape
10898
+ # hatch =0), the instruction stays byte-identical to before. Parity-locked
10899
+ # with analysisInstruction() in loki-ts/src/runner/build_prompt.ts.
10900
+ if [ "${USE_WORKFLOW_ANALYSIS:-0}" = "1" ]; then
10901
+ analysis_instruction="ultracode: ${analysis_instruction}"
10902
+ fi
10903
+
10885
10904
  # v7.8.1: incremental-update instruction for when a generated PRD already
10886
10905
  # exists and the codebase changed (GENERATED_PRD_ACTION=update). Reconcile,
10887
10906
  # do not regenerate, so the PRD stays continuous and the update is cheap.
@@ -12542,6 +12561,62 @@ except Exception as exc:
12542
12561
  return 1
12543
12562
  fi
12544
12563
 
12564
+ # v7.40.0 (#584): autonomous complexity-gated decision for the no-PRD
12565
+ # codebase-analysis dispatch. detect_complexity() is defined (~1611) but was
12566
+ # never called on the bash route, so DETECTED_COMPLEXITY stayed "" and the
12567
+ # gate was inert here. Call it ONCE per run, before the first build_prompt,
12568
+ # so DETECTED_COMPLEXITY is populated for the gate AND for the existing
12569
+ # complexity consumers (effort-for-tier, telemetry, phase selection) that
12570
+ # previously always fell back to "standard" on bash. detect_complexity()
12571
+ # sets the DETECTED_COMPLEXITY global directly (no echo); call once and let
12572
+ # the cached value carry through every iteration so the static prompt prefix
12573
+ # is stable across the run.
12574
+ if [ -z "${DETECTED_COMPLEXITY:-}" ]; then
12575
+ detect_complexity "$prd_path"
12576
+ fi
12577
+
12578
+ # Decide the workflow-analysis dispatch ONCE here (parent shell), not inside
12579
+ # build_prompt (which runs in a $(...) subshell where a write would not
12580
+ # propagate). The decision is parity-locked with the Bun route
12581
+ # (useClaudeWorkflowsForAnalysis in loki-ts/src/runner/build_prompt.ts) and
12582
+ # evaluated in the SAME order:
12583
+ # provider != claude -> three-pass (USE_WORKFLOW_ANALYSIS=0)
12584
+ # PROVIDER_DEGRADED=true -> three-pass
12585
+ # LOKI_USE_CLAUDE_WORKFLOWS=0 -> three-pass (escape hatch)
12586
+ # LOKI_USE_CLAUDE_WORKFLOWS=1 -> workflow (force on)
12587
+ # var UNSET -> workflow IFF DETECTED_COMPLEXITY == complex
12588
+ # When the workflow path is chosen AUTONOMOUSLY (var unset + complex), emit a
12589
+ # one-time cost disclosure to stderr (NOT stdout, which is the prompt). The
12590
+ # decision is read by build_prompt to prefix the analysis instruction with
12591
+ # "ultracode: ". This ONLY affects the read-only no-PRD analysis instruction,
12592
+ # so the entire decision is gated on the no-PRD case ([ -z "$prd_path" ]).
12593
+ # When a PRD exists, build_prompt never emits the analysis instruction, so
12594
+ # USE_WORKFLOW_ANALYSIS stays 0 and no disclosure fires (the "no PRD found"
12595
+ # message would be false otherwise). detect_complexity above stays
12596
+ # unconditional because its other consumers run in PRD mode too.
12597
+ USE_WORKFLOW_ANALYSIS=0
12598
+ local _wf_autonomous=0
12599
+ if [ -z "$prd_path" ] && [ "${LOKI_PROVIDER:-claude}" = "claude" ] && [ "${PROVIDER_DEGRADED:-false}" != "true" ]; then
12600
+ if [ "${LOKI_USE_CLAUDE_WORKFLOWS:-}" = "0" ]; then
12601
+ USE_WORKFLOW_ANALYSIS=0
12602
+ elif [ "${LOKI_USE_CLAUDE_WORKFLOWS:-}" = "1" ]; then
12603
+ USE_WORKFLOW_ANALYSIS=1
12604
+ elif [ -z "${LOKI_USE_CLAUDE_WORKFLOWS:-}" ] && [ "${DETECTED_COMPLEXITY:-}" = "complex" ]; then
12605
+ USE_WORKFLOW_ANALYSIS=1
12606
+ _wf_autonomous=1
12607
+ fi
12608
+ fi
12609
+ # One-time autonomous-decision disclosure to stderr. Only fires when this run
12610
+ # took the workflow path on its own (not when forced via =1, not when off).
12611
+ # No fabricated dollar figure (no price API): name the cost CLASS and the
12612
+ # opt-out. Guard so it prints once per run.
12613
+ if [ "$_wf_autonomous" = "1" ] && [ -z "${_LOKI_WORKFLOW_DISCLOSED:-}" ]; then
12614
+ echo "Loki: no PRD found and this repo looks complex (complexity=complex), so the codebase-analysis pass is dispatching a Claude Code Dynamic Workflow (parallel fan-out). Workflows are more thorough but cost meaningfully more than the default three-pass analysis. Set LOKI_USE_CLAUDE_WORKFLOWS=0 to keep the cheaper three-pass pass." >&2
12615
+ _LOKI_WORKFLOW_DISCLOSED=1
12616
+ export _LOKI_WORKFLOW_DISCLOSED
12617
+ fi
12618
+ export USE_WORKFLOW_ANALYSIS
12619
+
12545
12620
  while [ $retry -lt $MAX_RETRIES ]; do
12546
12621
  # Check for human intervention BEFORE incrementing iteration count
12547
12622
  # BUG-ST-010: Moved pause/stop checks before ITERATION_COUNT increment
@@ -12781,6 +12856,17 @@ except Exception as exc:
12781
12856
  log_warn "Ignoring invalid model override '$_loki_override_file' (allowed: haiku, sonnet, opus, fable); using tier $tier_param"
12782
12857
  fi
12783
12858
  fi
12859
+ # fable unavailable, collapse to opus (final dispatch backstop). Claude
12860
+ # Fable 5 is not available at the Claude API ("use Opus 4.8"). Any path
12861
+ # that left tier_param="fable" (static fallback, override file, architect
12862
+ # opt-in) is collapsed to opus here, after all tier_param mutations and
12863
+ # before the claude argv is built (--model "$tier_param"). Keeps dispatch,
12864
+ # the cost quote, and the dashboard effective-model in agreement. Only for
12865
+ # the claude provider: codex/cline/aider map tier_param to their own
12866
+ # effort/model strings and have no fable equivalent (v7.39.1).
12867
+ if [ "${PROVIDER_NAME:-claude}" = "claude" ] && [ "$tier_param" = "fable" ]; then
12868
+ tier_param="opus"
12869
+ fi
12784
12870
  echo "=== RARV Phase: $rarv_phase, Tier: $CURRENT_TIER ($tier_param) ===" | tee -a "$log_file" "$agent_log"
12785
12871
  log_info "RARV Phase: $rarv_phase -> Tier: $CURRENT_TIER ($tier_param)"
12786
12872
 
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "7.39.0"
10
+ __version__ = "7.40.0"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -2406,7 +2406,12 @@ def _resolve_session_pin(alias: str) -> str:
2406
2406
  elif pin_tier == "fast":
2407
2407
  model = _provider_model_fast()
2408
2408
  elif pin_tier == "fable":
2409
- model = "fable"
2409
+ # fable unavailable, collapse to opus. Claude Fable 5 is not available at
2410
+ # the Claude API ("use Opus 4.8"); the runner dispatches opus for a fable
2411
+ # pin (claude.sh resolve_model_for_tier, run.sh dispatch backstop), and
2412
+ # the estimator quotes opus. The dashboard effective model agrees so the
2413
+ # session-pin parity matrix stays green (v7.39.1).
2414
+ model = "opus"
2410
2415
  else: # development (and the unknown-alias '*' fallthrough)
2411
2416
  model = _provider_model_development()
2412
2417
  max_tier = (os.environ.get("LOKI_MAX_TIER") or "").strip().lower()
@@ -2483,6 +2488,13 @@ async def get_session_model():
2483
2488
  effective = _clamp_to_max_tier(override)
2484
2489
  else:
2485
2490
  effective = _resolve_session_pin(default)
2491
+ # fable unavailable, collapse to opus (final dispatch backstop, mirrors run.sh
2492
+ # and the estimator). The override-path clamp leaves an uncapped fable override
2493
+ # as "fable", but the runner dispatches opus for it; the session-pin route is
2494
+ # already collapsed inside _resolve_session_pin. Apply the same collapse here so
2495
+ # the reported effective model agrees with dispatch on BOTH routes (v7.39.1).
2496
+ if effective == "fable":
2497
+ effective = "opus"
2486
2498
  return {
2487
2499
  "override": override,
2488
2500
  "default": default,
@@ -2533,7 +2545,16 @@ async def set_session_model(request: SessionModelRequest):
2533
2545
  except OSError as exc:
2534
2546
  raise HTTPException(status_code=500, detail=f"Could not write override: {exc}")
2535
2547
  effective = _clamp_to_max_tier(model)
2536
- return {"model": model, "effective": effective, "clamped": effective != model}
2548
+ # `clamped` reflects ONLY the LOKI_MAX_TIER cost ceiling, computed before the
2549
+ # fable->opus collapse below (the collapse is model unavailability, not a cost
2550
+ # clamp, so it must not flip `clamped`).
2551
+ clamped = effective != model
2552
+ # fable unavailable, collapse to opus (final dispatch backstop, mirrors run.sh
2553
+ # and the estimator). An uncapped fable override clamps to "fable" above but the
2554
+ # runner dispatches opus for it, so report opus as the effective model (v7.39.1).
2555
+ if effective == "fable":
2556
+ effective = "opus"
2557
+ return {"model": model, "effective": effective, "clamped": clamped}
2537
2558
 
2538
2559
 
2539
2560
  @app.get("/api/running-projects")
@@ -2,7 +2,7 @@
2
2
 
3
3
  The flagship product of [Autonomi](https://www.autonomi.dev/). Loki Mode is a spec-driven autonomous builder with a built-in trust layer that takes any spec to a deployed product and verifies completion with evidence (quality gates plus a completion council), not just a "done" claim. Complete installation instructions for all platforms and use cases.
4
4
 
5
- **Version:** v7.39.0
5
+ **Version:** v7.40.0
6
6
 
7
7
  ---
8
8
 
@@ -40,7 +40,8 @@ setting any flag to `0`.
40
40
 
41
41
  ## Table of Contents
42
42
 
43
- - [npm (Recommended)](#npm-recommended)
43
+ - [Bun (Recommended)](#bun-recommended)
44
+ - [npm](#npm)
44
45
  - [Homebrew](#homebrew)
45
46
  - [Quick Start](#quick-start)
46
47
  - [Verify Installation](#verify-installation)
@@ -57,17 +58,39 @@ setting any flag to `0`.
57
58
 
58
59
  ---
59
60
 
60
- ## npm (Recommended)
61
+ ## Bun (Recommended)
62
+
63
+ ```bash
64
+ bun install -g loki-mode
65
+ ```
66
+
67
+ Installs the `loki` CLI. Bun is the recommended path: the `loki` shim runs the
68
+ faster TypeScript runtime when `bun` is on `PATH`, and the stable bash engine
69
+ (the autonomous loop, quality gates, and completion council) runs underneath on
70
+ every route, so you lose nothing by choosing Bun. Run `loki setup-skill` once
71
+ after install to create the per-provider skill symlinks (Claude Code, Codex CLI).
72
+
73
+ **Prerequisites:** Bun 1.3+ (`curl -fsSL https://bun.sh/install | bash`, or
74
+ `brew install oven-sh/bun/bun`). No separate Node install is required.
75
+
76
+ **Update:** `bun update -g loki-mode`
77
+
78
+ **Uninstall:** `bun remove -g loki-mode`
79
+
80
+ ---
81
+
82
+ ## npm
61
83
 
62
84
  ```bash
63
85
  npm install -g loki-mode
64
86
  ```
65
87
 
66
- Installs the `loki` CLI. As of v7.4.12 there is no postinstall step; run
67
- `loki setup-skill` once after install to create the per-provider skill
68
- symlinks (Claude Code, Codex CLI). The `loki` shim auto-routes
69
- read-only commands to the Bun runtime when `bun` is on `PATH` and falls
70
- back to the bash CLI otherwise.
88
+ A fully supported alternative when you prefer npm or do not have Bun. As of
89
+ v7.4.12 there is no postinstall step; run `loki setup-skill` once after install
90
+ to create the per-provider skill symlinks (Claude Code, Codex CLI). The `loki`
91
+ shim auto-routes read-only commands to the Bun runtime when `bun` is on `PATH`
92
+ and falls back to the bash CLI otherwise (the core autonomous engine is the
93
+ same on both routes).
71
94
 
72
95
  **Prerequisites:** Node.js 18+. Bun 1.3+ optional but recommended for the
73
96
  faster routed commands and forward-compat with v8.0.0.
@@ -117,7 +140,7 @@ and orchestrator components ship via npm, Docker, and Homebrew only.
117
140
  pip install loki-mode-sdk
118
141
 
119
142
  # Install the full CLI (recommended)
120
- npm install -g loki-mode # or: brew tap asklokesh/tap && brew install loki-mode
143
+ bun install -g loki-mode # or: npm install -g loki-mode, or: brew tap asklokesh/tap && brew install loki-mode
121
144
  ```
122
145
 
123
146
  The naming asymmetry (`loki-mode` on npm vs `loki-mode-sdk` on PyPI) is
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var n6=Object.defineProperty;var a6=($)=>$;function s6($,Q){this[$]=a6.bind(null,Q)}var h=($,Q)=>{for(var Z in Q)n6($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:s6.bind(Q,Z)})};var L=($,Q)=>()=>($&&(Q=$($=0)),Q);var K$=import.meta.require;var S1={};h(S1,{lokiDir:()=>P,homeLokiDir:()=>o$,findRepoRootForVersion:()=>d$,REPO_ROOT:()=>m});import{resolve as n,dirname as l$}from"path";import{fileURLToPath as t6}from"url";import{existsSync as P$}from"fs";import{homedir as r6}from"os";function i6(){let $=N1;for(let Q=0;Q<6;Q++){if(P$(n($,"VERSION"))&&P$(n($,"autonomy/run.sh")))return $;let Z=l$($);if(Z===$)break;$=Z}return n(N1,"..","..","..")}function d$($){let Q=$;for(let Z=0;Z<6;Z++){if(P$(n(Q,"VERSION"))&&P$(n(Q,"autonomy/run.sh")))return Q;let z=l$(Q);if(z===Q)break;Q=z}return n($,"..","..","..")}function P(){return process.env.LOKI_DIR??n(process.cwd(),".loki")}function o$(){return n(r6(),".loki")}var N1,m;var C=L(()=>{N1=l$(t6(import.meta.url));m=i6()});import{readFileSync as e6}from"fs";import{resolve as $Q,dirname as QQ}from"path";import{fileURLToPath as ZQ}from"url";function F$(){if($$!==null)return $$;let $="7.39.0";if(typeof $==="string"&&$.length>0)return $$=$,$$;try{let Q=QQ(ZQ(import.meta.url)),Z=d$(Q);$$=e6($Q(Z,"VERSION"),"utf-8").trim()}catch{$$="unknown"}return $$}var $$=null;var n$=L(()=>{C()});var C1={};h(C1,{runOrThrow:()=>zQ,run:()=>j,commandVersion:()=>KQ,commandExists:()=>f,ShellError:()=>a$});async function j($,Q={}){let Z=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),z,X;if(Q.timeoutMs&&Q.timeoutMs>0)z=setTimeout(()=>{try{Z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{Z.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[W,K,U]=await Promise.all([new Response(Z.stdout).text(),new Response(Z.stderr).text(),Z.exited]);return{stdout:W,stderr:K,exitCode:U}}finally{if(z)clearTimeout(z);if(X)clearTimeout(X)}}async function zQ($,Q={}){let Z=await j($,Q);if(Z.exitCode!==0)throw new a$(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function f($){let Q=XQ($),Z=await j(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function XQ($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function KQ($,Q="--version"){if(!await f($))return null;let z=await j([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var a$;var d=L(()=>{a$=class a$ extends Error{message;exitCode;stdout;stderr;constructor($,Q,Z,z){super($);this.message=$;this.exitCode=Q;this.stdout=Z;this.stderr=z;this.name="ShellError"}}});function a($){return WQ?"":$}var WQ,T,S,I,TZ,w,R,y,q;var c=L(()=>{WQ=(process.env.NO_COLOR??"").length>0;T=a("\x1B[0;31m"),S=a("\x1B[0;32m"),I=a("\x1B[1;33m"),TZ=a("\x1B[0;34m"),w=a("\x1B[0;36m"),R=a("\x1B[1m"),y=a("\x1B[2m"),q=a("\x1B[0m")});import{existsSync as TQ}from"fs";async function Q$(){if(B$!==void 0)return B$;let $="/opt/homebrew/bin/python3.12";if(TQ($))return B$=$,$;let Q=await f("python3.12");if(Q)return B$=Q,Q;let Z=await f("python3");return B$=Z,Z}async function Z$($,Q={}){let Z=await Q$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return j([Z,"-c",$],Q)}var B$;var W$=L(()=>{d()});var t1={};h(t1,{runStatus:()=>gQ});import{existsSync as v,readFileSync as U$,readdirSync as l1,statSync as d1}from"fs";import{resolve as D,basename as xQ}from"path";import{homedir as NQ}from"os";async function DQ(){if(await f("jq"))return!0;return process.stdout.write(`${T}Error: jq is required but not installed.${q}
2
+ var n6=Object.defineProperty;var a6=($)=>$;function s6($,Q){this[$]=a6.bind(null,Q)}var h=($,Q)=>{for(var Z in Q)n6($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:s6.bind(Q,Z)})};var L=($,Q)=>()=>($&&(Q=$($=0)),Q);var K$=import.meta.require;var S1={};h(S1,{lokiDir:()=>P,homeLokiDir:()=>o$,findRepoRootForVersion:()=>d$,REPO_ROOT:()=>m});import{resolve as n,dirname as l$}from"path";import{fileURLToPath as t6}from"url";import{existsSync as P$}from"fs";import{homedir as r6}from"os";function i6(){let $=N1;for(let Q=0;Q<6;Q++){if(P$(n($,"VERSION"))&&P$(n($,"autonomy/run.sh")))return $;let Z=l$($);if(Z===$)break;$=Z}return n(N1,"..","..","..")}function d$($){let Q=$;for(let Z=0;Z<6;Z++){if(P$(n(Q,"VERSION"))&&P$(n(Q,"autonomy/run.sh")))return Q;let z=l$(Q);if(z===Q)break;Q=z}return n($,"..","..","..")}function P(){return process.env.LOKI_DIR??n(process.cwd(),".loki")}function o$(){return n(r6(),".loki")}var N1,m;var C=L(()=>{N1=l$(t6(import.meta.url));m=i6()});import{readFileSync as e6}from"fs";import{resolve as $Q,dirname as QQ}from"path";import{fileURLToPath as ZQ}from"url";function F$(){if($$!==null)return $$;let $="7.40.0";if(typeof $==="string"&&$.length>0)return $$=$,$$;try{let Q=QQ(ZQ(import.meta.url)),Z=d$(Q);$$=e6($Q(Z,"VERSION"),"utf-8").trim()}catch{$$="unknown"}return $$}var $$=null;var n$=L(()=>{C()});var C1={};h(C1,{runOrThrow:()=>zQ,run:()=>j,commandVersion:()=>KQ,commandExists:()=>f,ShellError:()=>a$});async function j($,Q={}){let Z=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),z,X;if(Q.timeoutMs&&Q.timeoutMs>0)z=setTimeout(()=>{try{Z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{Z.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[W,K,U]=await Promise.all([new Response(Z.stdout).text(),new Response(Z.stderr).text(),Z.exited]);return{stdout:W,stderr:K,exitCode:U}}finally{if(z)clearTimeout(z);if(X)clearTimeout(X)}}async function zQ($,Q={}){let Z=await j($,Q);if(Z.exitCode!==0)throw new a$(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function f($){let Q=XQ($),Z=await j(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function XQ($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function KQ($,Q="--version"){if(!await f($))return null;let z=await j([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var a$;var d=L(()=>{a$=class a$ extends Error{message;exitCode;stdout;stderr;constructor($,Q,Z,z){super($);this.message=$;this.exitCode=Q;this.stdout=Z;this.stderr=z;this.name="ShellError"}}});function a($){return WQ?"":$}var WQ,T,S,I,TZ,w,R,y,q;var c=L(()=>{WQ=(process.env.NO_COLOR??"").length>0;T=a("\x1B[0;31m"),S=a("\x1B[0;32m"),I=a("\x1B[1;33m"),TZ=a("\x1B[0;34m"),w=a("\x1B[0;36m"),R=a("\x1B[1m"),y=a("\x1B[2m"),q=a("\x1B[0m")});import{existsSync as TQ}from"fs";async function Q$(){if(B$!==void 0)return B$;let $="/opt/homebrew/bin/python3.12";if(TQ($))return B$=$,$;let Q=await f("python3.12");if(Q)return B$=Q,Q;let Z=await f("python3");return B$=Z,Z}async function Z$($,Q={}){let Z=await Q$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return j([Z,"-c",$],Q)}var B$;var W$=L(()=>{d()});var t1={};h(t1,{runStatus:()=>gQ});import{existsSync as v,readFileSync as U$,readdirSync as l1,statSync as d1}from"fs";import{resolve as D,basename as xQ}from"path";import{homedir as NQ}from"os";async function DQ(){if(await f("jq"))return!0;return process.stdout.write(`${T}Error: jq is required but not installed.${q}
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)
@@ -789,4 +789,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
789
789
  `),2}default:return process.stderr.write(`Unknown command: ${Q}
790
790
  `),process.stderr.write(o6),2}}p1();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var ZZ=await QZ(Bun.argv.slice(2));process.exit(ZZ);
791
791
 
792
- //# debugId=0053955F19A92CF864756E2164756E21
792
+ //# debugId=CE2296A3A5523BAE64756E2164756E21
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '7.39.0'
60
+ __version__ = '7.40.0'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "loki-mode",
3
3
  "mcpName": "io.github.asklokesh/loki-mode",
4
- "version": "7.39.0",
4
+ "version": "7.40.0",
5
5
  "description": "Loki Mode by Autonomi. Autonomous spec-to-product system: takes a PRD, GitHub issue, OpenAPI/JSON/YAML, or one-line brief to a deployed app via the RARV-C closure loop with 11 quality gates. Provider-agnostic (Claude Code, OpenAI Codex, Cline, Aider).",
6
6
  "keywords": [
7
7
  "agent",
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "loki-mode",
4
4
  "displayName": "Loki Mode",
5
- "version": "7.39.0",
5
+ "version": "7.40.0",
6
6
  "description": "Autonomous spec-to-product build system with a built-in trust layer (RARV-C closure loop, 11 quality gates, completion council). Ships Loki's spec-hardening, drift-detection, and deterministic PR verification commands plus the Loki MCP server.",
7
7
  "author": {
8
8
  "name": "Autonomi",
@@ -398,7 +398,12 @@ resolve_model_for_tier() {
398
398
  planning) model="$PROVIDER_MODEL_PLANNING" ;;
399
399
  development) model="$PROVIDER_MODEL_DEVELOPMENT" ;;
400
400
  fast) model="$PROVIDER_MODEL_FAST" ;;
401
- fable) model="fable" ;;
401
+ # fable unavailable, collapse to opus. Claude Fable 5 is not available at
402
+ # the Claude API ("use Opus 4.8"). The fable tier label and session-pin
403
+ # parsing (loki_normalize_model_alias still accepts "fable") stay; only
404
+ # the RESOLVED dispatch model becomes opus, matching the estimator and
405
+ # dashboard so the session-pin parity matrix agrees (v7.39.1).
406
+ fable) model="opus" ;;
402
407
  *) model="$PROVIDER_MODEL_DEVELOPMENT" ;;
403
408
  esac
404
409