loki-mode 7.34.1 → 7.36.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/LICENSE CHANGED
@@ -1,5 +1,3 @@
1
- SPDX-License-Identifier: BUSL-1.1
2
-
3
1
  Business Source License 1.1
4
2
 
5
3
  Parameters
@@ -12,40 +10,13 @@ Licensed Work: Loki Mode and all associated components, including but not
12
10
  rebranded or successor versions of this software.
13
11
  The Licensed Work is (c) 2024-2026 Autonomi, Inc.
14
12
  All rights reserved.
15
- Additional Use Grant: You may make production use of the Licensed Work, provided
16
- that such use does not include offering the Licensed Work,
17
- or any derivative work or adaptation of it, to third parties
18
- as a commercial product, hosted service, managed service, or
19
- embedded component of a commercial offering that competes
20
- with any product or service provided by the Licensor.
21
-
22
- For purposes of this license, "compete" means offering a
23
- product or service that provides substantially the same or
24
- similar functionality as the Licensed Work to third parties,
25
- whether for a fee or free of charge, including but not
26
- limited to:
27
- (a) multi-agent AI development orchestration platforms or
28
- frameworks;
29
- (b) autonomous software development products or services;
30
- (c) AI-powered code generation, review, or deployment
31
- services that incorporate or replicate the Licensed
32
- Work's orchestration, review, verification, or agent
33
- coordination systems; or
34
- (d) products or services that substantially replicate the
35
- Licensed Work's core architectural patterns, including
36
- the RARV (Reason-Act-Reflect-Verify) cycle, Completion
37
- Council peer review system, or multi-agent swarm
38
- coordination model.
39
-
40
- The following uses are permitted without a commercial license:
41
- (i) individual and non-commercial use;
42
- (ii) internal business use where the Licensed Work is used
43
- as a tool and is not itself the product or service
44
- being offered to third parties;
45
- (iii) academic, educational, and research use;
46
- (iv) evaluation and testing in non-production environments;
47
- (v) contributions back to the Licensed Work under a signed
48
- Contributor License Agreement.
13
+ Additional Use Grant: You may make production use of the Licensed Work for
14
+ individual, non-commercial, internal-business (as a tool,
15
+ not as a product offered to third parties), academic, and
16
+ evaluation purposes, subject to the full scope, the
17
+ definition of "compete," and the Intellectual Property and
18
+ Contributions terms set out in COMMERCIAL-TERMS.md, which is
19
+ incorporated into this Additional Use Grant by reference.
49
20
 
50
21
  Change Date: March 19, 2030
51
22
  Change License: Apache License, Version 2.0
@@ -53,25 +24,8 @@ Change License: Apache License, Version 2.0
53
24
  For information about alternative licensing arrangements for the Licensed Work,
54
25
  please contact: founder@autonomi.dev
55
26
 
56
- Intellectual Property Notice
57
-
58
- The Licensed Work embodies proprietary methodologies, architectural patterns,
59
- and research developed by the Licensor. These include, without limitation, the
60
- RARV execution cycle, the Completion Council consensus-based review system,
61
- context persistence mechanisms for long-running autonomous sessions, multi-
62
- provider orchestration protocols, and swarm-based agent coordination models.
63
- Use of these methodologies, whether through the Licensed Work's code or through
64
- independent reimplementation based on the Licensed Work's documentation or
65
- published research, is subject to the terms of this License.
66
-
67
- Contributions
68
-
69
- External contributions to the Licensed Work are accepted only under a signed
70
- Contributor License Agreement (CLA) that assigns sufficient rights to the
71
- Licensor to sublicense, relicense, and commercially distribute the contribution
72
- as part of the Licensed Work. Contributing code to the Licensed Work without a
73
- signed CLA does not grant the Licensor any rights to the contribution, and such
74
- contributions may be removed.
27
+ The Intellectual Property Notice and Contributions (CLA) terms that supplement
28
+ this License are set out in COMMERCIAL-TERMS.md.
75
29
 
76
30
  Notice
77
31
 
package/README.md CHANGED
@@ -4,11 +4,12 @@
4
4
 
5
5
  ### The spec-driven autonomous builder with verified completion.
6
6
 
7
+ _The free, source-available autonomous coding agent by [Autonomi](https://www.autonomi.dev/). Same Loki CLI, SDK, and MCP for everyone; the commercial editions for teams and enterprises are sold under the **Autonomi** brand (Autonomi Cloud, Autonomi Enterprise)._
8
+
7
9
  **Hand it a spec. It does not accept "done" on an empty diff or failing tests.**
8
10
 
9
11
  [![npm version](https://img.shields.io/npm/v/loki-mode?style=for-the-badge&logo=npm&logoColor=white&color=553DE9)](https://www.npmjs.com/package/loki-mode)
10
12
  [![npm downloads](https://img.shields.io/npm/dt/loki-mode?style=for-the-badge&logo=npm&logoColor=white&color=1FC5A8&label=downloads)](https://www.npmjs.com/package/loki-mode)
11
- [![GitHub stars](https://img.shields.io/github/stars/asklokesh/loki-mode?style=for-the-badge&logo=github&color=553DE9&cacheSeconds=86400)](https://github.com/asklokesh/loki-mode/stargazers)
12
13
  [![Docker Pulls](https://img.shields.io/docker/pulls/asklokesh/loki-mode?style=for-the-badge&logo=docker&logoColor=white&color=2F71E3)](https://hub.docker.com/r/asklokesh/loki-mode)
13
14
  [![License](https://img.shields.io/badge/License-BUSL--1.1-36342E?style=for-the-badge)](LICENSE)
14
15
 
@@ -289,7 +290,7 @@ TLS, OIDC/SSO, RBAC, OTEL tracing, policy engine, audit trails. Activated via en
289
290
 
290
291
  ## Purple Lab
291
292
 
292
- The hosted development platform. A Replit-like web UI for visual PRD-to-code workflow with AI chat for iterative development.
293
+ The hosted development platform. A Replit-like web UI for visual PRD-to-code workflow, with the Loki agent for iterative development. The same software is free and source-available as the local Loki Mode dashboard; offered managed to teams and enterprises under the **Autonomi** brand (Autonomi Cloud).
293
294
 
294
295
  ```bash
295
296
  loki web # launches at http://localhost:57375
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.34.1
6
+ # Loki Mode v7.36.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.34.1 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
401
+ **v7.36.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 7.34.1
1
+ 7.36.0
@@ -227,6 +227,61 @@ loki_review_guard_denylist() {
227
227
  printf '%s' "Edit,Write,NotebookEdit,Bash(git commit:*),Bash(git reset:*),Bash(git push:*),Bash(git checkout:*),Bash(git clean:*),Bash(git rm:*),Bash(git stash:*),Bash(git -C:*),Bash(git --git-dir:*),Bash(git -c:*)"
228
228
  }
229
229
 
230
+ # EMBED 3b (v7.35.0, GitHub #167) -- --allowedTools POSITIVE ALLOWLIST for the
231
+ # reviewer / adversarial / council subcalls. Complements the v7.33 denylist with
232
+ # a least-privilege grant: the value lists ONLY the read/inspect tools a voter
233
+ # needs (Read, Grep, Glob, read-only git, and a small set of read-only shell
234
+ # commands). Per `claude --help` and the official permission docs
235
+ # (https://code.claude.com/docs/en/cli-reference,
236
+ # https://code.claude.com/docs/en/permissions) --allowedTools entries are
237
+ # permission ALLOW rules ("tools that execute without prompting"), using the same
238
+ # Tool / Tool(specifier) / Bash(cmd:*) prefix syntax as --disallowedTools.
239
+ #
240
+ # PRECEDENCE (proven empirically + documented): the docs state rules are
241
+ # "evaluated in order: deny, then ask, then allow ... the first match in that
242
+ # order determines the outcome", and "if a tool is denied at any level, no other
243
+ # level can allow it." Verified live against claude 2.1.177 on 2026-06-13 with a
244
+ # clean minimal env (temp git dir, --strict-mcp-config --mcp-config
245
+ # '{"mcpServers":{}}', --advisor opus to dodge the Fable-pinned default advisor
246
+ # tool, --tools "Bash,Read"):
247
+ # - allow-only Bash(git status:*) -> PERMITTED (control)
248
+ # - deny-only Bash(git status:*) -> BLOCKED (control)
249
+ # - allow + deny BOTH on Bash(git status:*) -> BLOCKED (deny wins)
250
+ # - allow + deny BOTH, under --dangerously-skip-permissions -> BLOCKED
251
+ # So DENY PRECEDENCE holds even in bypassPermissions mode (the mode every council
252
+ # subcall uses). This is the SAFE outcome: the allowlist and the denylist can be
253
+ # emitted TOGETHER. The denylist still blocks every mutation form even though the
254
+ # allowlist names read-only git; the allowlist additionally narrows the surface
255
+ # to least-privilege. They are NOT mutually exclusive; we ship both.
256
+ #
257
+ # This is a GUARDRAIL, not a sandbox (same caveat as the denylist): --allowedTools
258
+ # governs whether a tool runs WITHOUT A PROMPT, and these subcalls already run
259
+ # under --dangerously-skip-permissions, so the practical effect is to restrict the
260
+ # in-context tool surface to the allowlisted set while the denylist hard-blocks
261
+ # the dangerous forms. echo>/sed -i/python -c style writes are not enumerable and
262
+ # remain possible; the real net is commit-before-agent-wave (see CLAUDE.md).
263
+ #
264
+ # DEFAULT OFF (opt-in LOKI_REVIEW_ALLOWLIST=1) so the default argv on BOTH routes
265
+ # stays byte-identical to v7.34. Gated on CLI support so an older claude degrades
266
+ # gracefully (emits nothing). Predicate + token so call sites append uniformly:
267
+ # if loki_review_allowlist_enabled; then
268
+ # argv+=("--allowedTools" "$(loki_review_allowlist)")
269
+ # fi
270
+ loki_review_allowlist_enabled() {
271
+ [ "${LOKI_REVIEW_ALLOWLIST:-0}" = "1" ] || return 1
272
+ loki_claude_flag_supported "--allowedTools"
273
+ }
274
+ loki_review_allowlist() {
275
+ # Comma-separated single token (the flag is variadic; one token keeps the
276
+ # following -p prompt from being swallowed as additional tool names). Grants
277
+ # ONLY read/inspect tools: the file-read tools (Read/Grep/Glob), read-only
278
+ # git (diff/log/show/status/ls-files/rev-parse/blame), and a small set of
279
+ # read-only shell commands a reviewer uses to inspect the tree. No Edit,
280
+ # Write, NotebookEdit, or any mutation form. Kept BYTE-IDENTICAL to
281
+ # REVIEW_ALLOWLIST_TOKEN in loki-ts/src/providers/claude_flags.ts.
282
+ printf '%s' "Read,Grep,Glob,Bash(git diff:*),Bash(git log:*),Bash(git show:*),Bash(git status:*),Bash(git ls-files:*),Bash(git rev-parse:*),Bash(git blame:*),Bash(cat:*),Bash(ls:*),Bash(grep:*),Bash(rg:*),Bash(find:*),Bash(head:*),Bash(tail:*),Bash(wc:*)"
283
+ }
284
+
230
285
  # ---------- v7.34.0 Claude session-id stamping (Phase 1, correlation-only) -----
231
286
  # Derive a deterministic per-run UUID from the existing trust-run-id so the same
232
287
  # run always maps to the same claude session UUID, and the bash + Bun routes
@@ -287,3 +342,39 @@ loki_session_stamp_enabled() {
287
342
  [ "${LOKI_SESSION_STAMP:-0}" = "1" ] || return 1
288
343
  loki_claude_flag_supported "--session-id"
289
344
  }
345
+
346
+ # ---------- v7.36.0 ultrareview (cloud multi-agent review) gates ----------
347
+ # `claude ultrareview [options] [target]` (Claude Code 2.1.x) runs a
348
+ # cloud-hosted multi-agent code review of the current branch / a PR number / a
349
+ # base branch and prints the findings. It is a PAID CLOUD operation billed by
350
+ # Anthropic, SEPARATE from local model spend, and can take up to 30 minutes.
351
+ #
352
+ # These two predicates are the ONLY policy surface for the optional `loki review
353
+ # --ultra` tier (issue #168). Both are read-only and side-effect-free.
354
+ #
355
+ # DESIGN (issue #168, architected):
356
+ # - This is an EXPLICIT, on-demand user action (`loki review --ultra`), NEVER
357
+ # an automatic completion-council voice. The council runs many times per
358
+ # build; auto-running a paid cloud call there would be a silent-billing
359
+ # footgun. The user typing "ultra" IS the consent signal; a confirmation
360
+ # prompt (or --yes / LOKI_ULTRAREVIEW=1) is still required before any spend.
361
+ # - There is NO price API, so we CANNOT show a dollar figure. We disclose the
362
+ # cost-CLASS (paid cloud op) and require confirmation. Never claim to show
363
+ # an estimated dollar amount here (that would be a lie).
364
+
365
+ # Capability gate: does the installed `claude` CLI ship the `ultrareview`
366
+ # subcommand? loki_claude_flag_supported greps `claude --help`, whose top-level
367
+ # command list includes "ultrareview", so it matches subcommands too. Returns 0
368
+ # when supported, 1 otherwise (so callers can emit an honest upgrade message).
369
+ loki_ultrareview_supported() {
370
+ loki_claude_flag_supported "ultrareview"
371
+ }
372
+
373
+ # Non-interactive opt-in: is LOKI_ULTRAREVIEW=1 set? This is the env equivalent
374
+ # of passing --yes to THIS command only. It is deliberately NOT wired into the
375
+ # completion council or any auto path -- it only suppresses the interactive
376
+ # confirmation prompt for an explicit `loki review --ultra` invocation. Any
377
+ # other value (unset, 0, "true", etc.) returns 1 so only the exact "1" opts in.
378
+ loki_ultrareview_enabled() {
379
+ [ "${LOKI_ULTRAREVIEW:-0}" = "1" ]
380
+ }
package/autonomy/loki CHANGED
@@ -13898,6 +13898,7 @@ if show_json:
13898
13898
  'recommended': recommended_provider,
13899
13899
  'reason': provider_reason,
13900
13900
  },
13901
+ 'moat': {'verified_completion': True, 'cost_honest': True},
13901
13902
  }
13902
13903
  if show_verbose:
13903
13904
  result['iteration_details'] = iteration_plan
@@ -14034,6 +14035,12 @@ if show_verbose:
14034
14035
 
14035
14036
  print(f'\n{DIM}This is an estimate. Actual usage depends on PRD complexity,')
14036
14037
  print(f'code review cycles, and test failures.{NC}')
14038
+
14039
+ print(f'\n{CYAN}Why this estimate is trustworthy{NC}')
14040
+ print(f'{DIM}Verified completion. The completion council refuses to call work done')
14041
+ print(f'without evidence - a real diff and green tests, or it blocks.{NC}')
14042
+ print(f'{DIM}Cost honesty. The model quoted here is the model the run dispatches')
14043
+ print(f'and the dashboard reports. Quote, dashboard, and dispatch agree.{NC}')
14037
14044
  print()
14038
14045
  " "$prd_path" "$show_json" "$show_verbose"
14039
14046
  }
@@ -14520,6 +14527,117 @@ main() {
14520
14527
  ;;
14521
14528
  esac
14522
14529
  }
14530
+ # v7.36.0 (issue #168): optional cloud multi-agent review tier (`loki review
14531
+ # --ultra`). Wraps the upstream `claude ultrareview` subcommand. ALWAYS opt-in,
14532
+ # cost-disclosed, default OFF, and advisory only (it does NOT block any gate).
14533
+ #
14534
+ # Consent model (modeled on cmd_demo's cost-confirm, autonomy/loki ~9488-9540):
14535
+ # - The disclosure ALWAYS prints (paid cloud op + may take up to 30 min).
14536
+ # - There is NO price API: we disclose the cost-CLASS, never a dollar figure.
14537
+ # - Interactive TTY without --yes: prompt, default NO.
14538
+ # - Non-TTY/CI without --yes (and no LOKI_ULTRAREVIEW=1): REFUSE with exit 2
14539
+ # and ZERO ultrareview calls (the no-silent-bill guard). Never hang on read.
14540
+ # - --yes or LOKI_ULTRAREVIEW=1 proceeds without prompting (this command only).
14541
+ # Args: <format> <assume_yes> <timeout_minutes> <target>
14542
+ _review_ultra() {
14543
+ local fmt="${1:-text}"
14544
+ local assume_yes="${2:-false}"
14545
+ local timeout_min="${3:-}"
14546
+ local target="${4:-}"
14547
+
14548
+ # Make the ultrareview gate predicates available even though autonomy/loki
14549
+ # does not source claude-flags.sh globally (same on-demand pattern as
14550
+ # autonomy/lib/voter-agents.sh). Degrade honestly if the lib is missing.
14551
+ if ! declare -F loki_ultrareview_supported >/dev/null 2>&1; then
14552
+ local _ur_lib=""
14553
+ if [ -f "${_LOKI_SCRIPT_DIR}/lib/claude-flags.sh" ]; then
14554
+ _ur_lib="${_LOKI_SCRIPT_DIR}/lib/claude-flags.sh"
14555
+ elif [ -f "$(dirname "$0")/lib/claude-flags.sh" ]; then
14556
+ _ur_lib="$(dirname "$0")/lib/claude-flags.sh"
14557
+ fi
14558
+ if [ -n "$_ur_lib" ]; then
14559
+ # shellcheck source=autonomy/lib/claude-flags.sh
14560
+ . "$_ur_lib" 2>/dev/null || true
14561
+ fi
14562
+ fi
14563
+
14564
+ # 1. claude CLI present at all?
14565
+ if ! command -v claude >/dev/null 2>&1; then
14566
+ echo -e "${RED}Error: 'claude' CLI not found on PATH.${NC}" >&2
14567
+ echo "loki review --ultra requires the Claude Code CLI (2.1.x) for cloud review." >&2
14568
+ return 1
14569
+ fi
14570
+
14571
+ # 2. Capability gate: does this claude support the ultrareview subcommand?
14572
+ # If absent, honest message + clean exit, never a half-feature.
14573
+ if ! declare -F loki_ultrareview_supported >/dev/null 2>&1 \
14574
+ || ! loki_ultrareview_supported; then
14575
+ echo -e "${RED}Error: your 'claude' CLI does not support 'ultrareview'.${NC}" >&2
14576
+ echo "Cloud multi-agent review needs Claude Code 2.1.x or newer; please upgrade." >&2
14577
+ return 1
14578
+ fi
14579
+
14580
+ # 3. Cost disclosure -- ALWAYS printed, even with --yes, so spend is never
14581
+ # hidden. NO dollar figure (there is no price API); cost-CLASS only.
14582
+ echo -e "${BOLD}loki review --ultra${NC} - cloud multi-agent code review (optional, paid)" >&2
14583
+ echo "" >&2
14584
+ echo -e "${YELLOW}ultrareview is a PAID cloud operation billed by Anthropic, separate${NC}" >&2
14585
+ echo -e "${YELLOW}from local model spend. It may take up to 30 minutes.${NC}" >&2
14586
+ echo "Findings are advisory only and do not block Loki's completion gate." >&2
14587
+ echo "" >&2
14588
+
14589
+ # 4. Consent. LOKI_ULTRAREVIEW=1 is the non-interactive opt-in equivalent of
14590
+ # --yes for THIS command only (never a council auto-trigger).
14591
+ local proceed=false
14592
+ if [ "$assume_yes" = true ]; then
14593
+ proceed=true
14594
+ elif declare -F loki_ultrareview_enabled >/dev/null 2>&1 && loki_ultrareview_enabled; then
14595
+ proceed=true
14596
+ else
14597
+ # Interactive only when stdin is a real TTY and CI is not forcing
14598
+ # non-interactive (matches the project's first-run gate semantics).
14599
+ local ultra_interactive=true
14600
+ if [ ! -t 0 ] || [ -n "${CI:-}" ]; then
14601
+ ultra_interactive=false
14602
+ fi
14603
+ if [ "$ultra_interactive" = true ]; then
14604
+ local ans=""
14605
+ echo -n "Run cloud ultrareview now? [y/N] " >&2
14606
+ read -r ans </dev/tty 2>/dev/null || ans=""
14607
+ if [[ "$ans" =~ ^[Yy] ]]; then
14608
+ proceed=true
14609
+ else
14610
+ echo "Cancelled. Nothing was spent." >&2
14611
+ return 0
14612
+ fi
14613
+ else
14614
+ # Non-TTY/CI without --yes: never hang; refuse with exit 2 and make
14615
+ # ZERO ultrareview calls (the no-silent-bill guard).
14616
+ echo "ultrareview needs confirmation; re-run with --yes (or set LOKI_ULTRAREVIEW=1) to proceed non-interactively" >&2
14617
+ return 2
14618
+ fi
14619
+ fi
14620
+
14621
+ [ "$proceed" = true ] || { echo "Cancelled. Nothing was spent." >&2; return 0; }
14622
+
14623
+ # 5. Invoke `claude ultrareview [--json] [--timeout N] [target]` exactly once.
14624
+ local ur_argv=("ultrareview")
14625
+ if [ "$fmt" = "json" ]; then
14626
+ ur_argv+=("--json")
14627
+ fi
14628
+ if [ -n "$timeout_min" ]; then
14629
+ ur_argv+=("--timeout" "$timeout_min")
14630
+ fi
14631
+ if [ -n "$target" ]; then
14632
+ ur_argv+=("$target")
14633
+ fi
14634
+
14635
+ echo -e "${DIM}Running: claude ${ur_argv[*]}${NC}" >&2
14636
+ echo "" >&2
14637
+ claude "${ur_argv[@]}"
14638
+ return $?
14639
+ }
14640
+
14523
14641
  # Standalone code review - diff-based quality gates (v6.20.0)
14524
14642
  cmd_review() {
14525
14643
  local review_staged=false
@@ -14528,6 +14646,9 @@ cmd_review() {
14528
14646
  local review_target=""
14529
14647
  local review_format="text"
14530
14648
  local review_severity="all"
14649
+ local review_ultra=false
14650
+ local review_assume_yes=false
14651
+ local review_timeout=""
14531
14652
 
14532
14653
  while [[ $# -gt 0 ]]; do
14533
14654
  case "$1" in
@@ -14548,6 +14669,15 @@ cmd_review() {
14548
14669
  echo "Options:"
14549
14670
  echo " --format json Output as JSON (for CI integration)"
14550
14671
  echo " --severity <level> Filter: critical, high, medium, low, info (shows level+above)"
14672
+ echo " --ultra Run an OPTIONAL cloud multi-agent review via"
14673
+ echo " 'claude ultrareview' (PAID cloud op, opt-in,"
14674
+ echo " default OFF, advisory only). Requires"
14675
+ echo " confirmation; --yes or LOKI_ULTRAREVIEW=1 to"
14676
+ echo " skip the prompt. Optional [file-or-dir] is"
14677
+ echo " passed as the ultrareview target (branch / PR"
14678
+ echo " number / base branch)."
14679
+ echo " --yes, -y Skip the --ultra confirmation prompt"
14680
+ echo " --timeout <minutes> --ultra: max minutes to wait (default 30)"
14551
14681
  echo " --help, -h Show this help"
14552
14682
  echo ""
14553
14683
  echo "Exit codes:"
@@ -14563,8 +14693,22 @@ cmd_review() {
14563
14693
  echo " loki review --since HEAD~5 # Changes in last 5 commits"
14564
14694
  echo " loki review --format json # JSON output for CI"
14565
14695
  echo " loki review --severity high # Only HIGH+ findings"
14696
+ echo " loki review --ultra # Cloud multi-agent review (paid, opt-in)"
14697
+ echo " loki review --ultra 42 # Cloud review of GitHub PR #42"
14698
+ echo " loki review --ultra --yes # Skip confirmation (e.g. in a script)"
14566
14699
  return 0
14567
14700
  ;;
14701
+ --ultra) review_ultra=true; shift ;;
14702
+ --yes|-y) review_assume_yes=true; shift ;;
14703
+ --timeout)
14704
+ shift
14705
+ review_timeout="${1:-}"
14706
+ if [ -z "$review_timeout" ]; then
14707
+ echo -e "${RED}Error: --timeout requires a value in minutes${NC}"
14708
+ return 1
14709
+ fi
14710
+ shift
14711
+ ;;
14568
14712
  --staged) review_staged=true; shift ;;
14569
14713
  --pr)
14570
14714
  shift
@@ -14600,6 +14744,16 @@ cmd_review() {
14600
14744
  esac
14601
14745
  done
14602
14746
 
14747
+ # v7.36.0 (issue #168): optional cloud multi-agent review tier. This is an
14748
+ # explicit, on-demand, opt-in, cost-disclosed path that wraps the existing
14749
+ # `claude ultrareview` subcommand. It short-circuits the local-only review
14750
+ # below: --ultra is a different operation (paid cloud), not a flag that
14751
+ # augments the local static analysis.
14752
+ if [ "$review_ultra" = true ]; then
14753
+ _review_ultra "$review_format" "$review_assume_yes" "$review_timeout" "$review_target"
14754
+ return $?
14755
+ fi
14756
+
14603
14757
  # Severity level ordering (higher number = more severe)
14604
14758
  _review_sev_level() {
14605
14759
  case "$1" in
package/autonomy/run.sh CHANGED
@@ -4557,10 +4557,21 @@ _loki_hash_stdin() {
4557
4557
  # path+size pairs PLUS file content (v7.32.3, #569: path+size alone was blind to
4558
4558
  # a same-size content edit, so a stale PRD could be silently reused with a
4559
4559
  # false "codebase unchanged" disclosure). Content hashing is clone-stable
4560
- # (mtime is not, which is why mtime was never used). Trees larger than
4561
- # LOKI_PRD_SIG_CONTENT_BUDGET bytes (default 50MB) skip the content pass and
4562
- # emit a "files-shallow:" signature so startup stays fast; the safe failure
4563
- # mode there is unchanged-from-before (size-blind), never a false "changed".
4560
+ # (mtime is not, which is why mtime was never used). Three content tiers:
4561
+ # 1. full content hash ("files:") when the tree is both at-or-under
4562
+ # LOKI_PRD_SIG_CONTENT_BUDGET bytes (default 50MB) AND at-or-under
4563
+ # LOKI_PRD_SIG_CONTENT_MAXFILES files (default 20000). Detects any edit.
4564
+ # 2. sampled content hash ("files-sampled:", #171) when the tree exceeds
4565
+ # either bound: hashes the head + tail (first 4096 + last 4096 bytes) of
4566
+ # every file. Catches same-size edits at the start or end of a file
4567
+ # without a full read of a huge tree. Residual honest gap: a same-size
4568
+ # edit in the MIDDLE of a large file (>8192 bytes) that touches neither
4569
+ # 4KB window is still invisible. Far narrower than the old size-blind
4570
+ # fallback, which missed ALL same-size edits.
4571
+ # 3. (historical) "files-shallow:" was the old content-blind fallback. It is
4572
+ # no longer emitted, but is still ACCEPTED when read from a stored pre-#171
4573
+ # signature so the first post-upgrade run reuses instead of falsely
4574
+ # claiming "codebase changed" (one-run format-transition, see consumer).
4564
4575
  # Echoes the signature.
4565
4576
  compute_codebase_signature() {
4566
4577
  local dir="${1:-.}"
@@ -4576,7 +4587,7 @@ compute_codebase_signature() {
4576
4587
  fi
4577
4588
  echo "git:${head}:${dirty}"
4578
4589
  else
4579
- local listing count total_sz budget
4590
+ local listing count total_sz budget maxfiles
4580
4591
  listing=$(find . \
4581
4592
  -type d \( -name .loki -o -name .git -o -name node_modules -o -name dist \
4582
4593
  -o -name build -o -name .next -o -name target -o -name vendor \
@@ -4592,20 +4603,35 @@ compute_codebase_signature() {
4592
4603
  count=$(printf '%s\n' "$listing" | grep -c . || true)
4593
4604
  total_sz=$(printf '%s\n' "$listing" | awk -F'\t' '{s+=$2} END {printf "%d", s}')
4594
4605
  budget="${LOKI_PRD_SIG_CONTENT_BUDGET:-52428800}"
4595
- if [ "${total_sz:-0}" -le "$budget" ] 2>/dev/null; then
4596
- # Content pass: stream all file contents through one hash in the
4597
- # same sorted order as the listing. Detects same-size edits.
4598
- # xargs -0 batches the reads into a handful of cat invocations,
4599
- # so cost scales with BYTES (which the budget above bounds), not
4600
- # file count: a fork-per-file loop here measured ~38s of added
4601
- # startup on a 30k-small-file tree. Renames and content swaps
4602
- # are still caught by the listing hash (paths+sizes) below.
4606
+ maxfiles="${LOKI_PRD_SIG_CONTENT_MAXFILES:-20000}"
4607
+ if [ "${total_sz:-0}" -le "$budget" ] 2>/dev/null \
4608
+ && [ "${count:-0}" -le "$maxfiles" ] 2>/dev/null; then
4609
+ # Tier 1 -- full content pass: stream all file contents through one
4610
+ # hash in the same sorted order as the listing. Detects any edit,
4611
+ # including same-size ones. xargs -0 batches the reads into a
4612
+ # handful of cat invocations, so cost scales with BYTES (which the
4613
+ # budget above bounds), not file count: a fork-per-file loop here
4614
+ # measured ~38s of added startup on a 30k-small-file tree. Renames
4615
+ # and content swaps are still caught by the listing hash below.
4603
4616
  local content_hash
4604
4617
  content_hash=$(printf '%s\n' "$listing" | cut -f1 | tr '\n' '\0' \
4605
4618
  | xargs -0 cat 2>/dev/null | _loki_hash_stdin)
4606
4619
  echo "files:$(printf '%s' "$listing" | _loki_hash_stdin):${count}:${content_hash}"
4607
4620
  else
4608
- echo "files-shallow:$(printf '%s' "$listing" | _loki_hash_stdin):${count}"
4621
+ # Tier 2 -- sampled content pass (#171): the tree is over the byte
4622
+ # budget OR over the file-count cap, so a full read would be slow.
4623
+ # Hash the head + tail (first 4096 + last 4096 bytes) of every file
4624
+ # instead. This catches same-size edits at the start or end of a
4625
+ # file (which the old size-blind "files-shallow:" missed entirely),
4626
+ # at a fixed <=8KB-per-file cost. -n 64 batches files per sh fork
4627
+ # to avoid a fork-per-file storm on large trees. Same sorted order
4628
+ # as the listing keeps the hash deterministic. Residual honest gap:
4629
+ # a same-size edit in the middle of a >8KB file is still invisible.
4630
+ local sample_hash
4631
+ sample_hash=$(printf '%s\n' "$listing" | cut -f1 | tr '\n' '\0' \
4632
+ | xargs -0 -n 64 sh -c 'for f in "$@"; do head -c 4096 -- "$f" 2>/dev/null; tail -c 4096 -- "$f" 2>/dev/null; done' _ 2>/dev/null \
4633
+ | _loki_hash_stdin)
4634
+ echo "files-sampled:$(printf '%s' "$listing" | _loki_hash_stdin):${count}:${sample_hash}"
4609
4635
  fi
4610
4636
  fi
4611
4637
  )
@@ -4698,6 +4724,30 @@ except Exception:
4698
4724
  echo "reuse"; return 0
4699
4725
  fi
4700
4726
  ;;
4727
+ files-shallow:*:*)
4728
+ # #171 format transition: a stored pre-#171 size-blind signature
4729
+ # ("files-shallow:<listing>:<count>", 3 fields) compared against
4730
+ # the new sampled signature ("files-sampled:<listing>:<count>:
4731
+ # <samplehash>") would falsely claim "codebase changed" on the
4732
+ # first post-upgrade run. When the listing-hash and count match
4733
+ # (i.e. the stored value with its prefix swapped to files-sampled:
4734
+ # is a prefix of the current sampled signature), the tree is
4735
+ # unchanged at the OLD format's trust level (paths+sizes): reuse,
4736
+ # honestly. The next persist upgrades the stored format to the
4737
+ # sampled tier. A same-size edit made BEFORE the upgrade stays
4738
+ # invisible for this one run, exactly as on the old version (no
4739
+ # regression, no false disclosure).
4740
+ if [ "$(printf '%s' "$stored" | tr -dc ':' | wc -c | tr -d ' ')" = "2" ]; then
4741
+ local stored_sampled="files-sampled:${stored#files-shallow:}"
4742
+ case "$current" in
4743
+ files-sampled:*)
4744
+ if [ "${current#"${stored_sampled}":}" != "$current" ]; then
4745
+ echo "reuse"; return 0
4746
+ fi
4747
+ ;;
4748
+ esac
4749
+ fi
4750
+ ;;
4701
4751
  esac
4702
4752
  echo "update"
4703
4753
  fi
@@ -4756,7 +4806,16 @@ _legacy_upgrade = (
4756
4806
  and prev_sig.count(':') == 2
4757
4807
  and sig.startswith(prev_sig + ':')
4758
4808
  )
4759
- if prev_at and (prev_sig == sig or _legacy_upgrade):
4809
+ # #171 format upgrade: a stored pre-#171 size-blind 'files-shallow:<listing>:
4810
+ # <count>' (3 fields) reused into the new sampled 'files-sampled:<listing>:
4811
+ # <count>:<samplehash>' whose listing fields match. Same trust level (the
4812
+ # decide returned reuse), so the PRD content did not change: preserve the date.
4813
+ _sampled_upgrade = (
4814
+ isinstance(prev_sig, str) and prev_sig.startswith('files-shallow:')
4815
+ and prev_sig.count(':') == 2
4816
+ and sig.startswith('files-sampled:' + prev_sig[len('files-shallow:'):] + ':')
4817
+ )
4818
+ if prev_at and (prev_sig == sig or _legacy_upgrade or _sampled_upgrade):
4760
4819
  generated_at = prev_at
4761
4820
  else:
4762
4821
  generated_at = datetime.datetime.now(datetime.timezone.utc).isoformat().replace('+00:00','Z')
@@ -7838,6 +7897,16 @@ BUILD_PROMPT
7838
7897
  if type loki_review_guard_enabled >/dev/null 2>&1 && loki_review_guard_enabled; then
7839
7898
  _rv_argv+=("--disallowedTools" "$(loki_review_guard_denylist)")
7840
7899
  fi
7900
+ # EMBED 3b (--allowedTools, #167): positive least-privilege
7901
+ # allowlist. DEFAULT OFF (opt-in LOKI_REVIEW_ALLOWLIST=1).
7902
+ # Emitted ALONGSIDE the denylist: verified live (claude
7903
+ # 2.1.177) that deny precedence holds even under
7904
+ # --dangerously-skip-permissions, so the denylist still
7905
+ # hard-blocks mutations while this narrows the surface to
7906
+ # read/inspect tools. See loki_review_allowlist.
7907
+ if type loki_review_allowlist_enabled >/dev/null 2>&1 && loki_review_allowlist_enabled; then
7908
+ _rv_argv+=("--allowedTools" "$(loki_review_allowlist)")
7909
+ fi
7841
7910
  claude "${_rv_argv[@]}" -p "$prompt_text" \
7842
7911
  --output-format text > "$review_output" 2>/dev/null
7843
7912
  ;;
@@ -8069,6 +8138,14 @@ ADVERSARIAL_EOF
8069
8138
  if type loki_review_guard_enabled >/dev/null 2>&1 && loki_review_guard_enabled; then
8070
8139
  _adv_argv+=("--disallowedTools" "$(loki_review_guard_denylist)")
8071
8140
  fi
8141
+ # EMBED 3b (--allowedTools, #167): positive least-privilege
8142
+ # allowlist. DEFAULT OFF (opt-in LOKI_REVIEW_ALLOWLIST=1).
8143
+ # Emitted ALONGSIDE the denylist (deny precedence verified
8144
+ # live, holds under --dangerously-skip-permissions). See
8145
+ # loki_review_allowlist.
8146
+ if type loki_review_allowlist_enabled >/dev/null 2>&1 && loki_review_allowlist_enabled; then
8147
+ _adv_argv+=("--allowedTools" "$(loki_review_allowlist)")
8148
+ fi
8072
8149
  claude "${_adv_argv[@]}" -p "$adversarial_prompt" \
8073
8150
  --output-format text > "$result_file" 2>/dev/null || true
8074
8151
  fi
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "7.34.1"
10
+ __version__ = "7.36.0"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -329,6 +329,27 @@
329
329
  text-overflow: ellipsis;
330
330
  white-space: nowrap;
331
331
  }
332
+ /* v7.35: the chip name is a switch-project affordance. Clickable names get
333
+ a pointer + hover accent; the active project's name is emphasized and
334
+ not clickable (already focused). */
335
+ .project-stop-row .project-stop-name.is-clickable {
336
+ cursor: pointer;
337
+ border-radius: 6px;
338
+ padding: 0 2px;
339
+ transition: color 0.12s ease, background 0.12s ease;
340
+ }
341
+ .project-stop-row .project-stop-name.is-clickable:hover {
342
+ color: var(--loki-accent, #553DE9);
343
+ background: var(--loki-bg-hover, rgba(85, 61, 233, 0.08));
344
+ }
345
+ .project-stop-row .project-stop-name.is-clickable:focus-visible {
346
+ outline: 2px solid var(--loki-accent, #553DE9);
347
+ outline-offset: 1px;
348
+ }
349
+ .project-stop-row .project-stop-name.is-active {
350
+ font-weight: 600;
351
+ color: var(--loki-accent, #553DE9);
352
+ }
332
353
  .project-stop-row button {
333
354
  padding: 2px 8px;
334
355
  background: transparent;
@@ -13799,6 +13820,16 @@ document.addEventListener('DOMContentLoaded', function() {
13799
13820
  var sel = document.getElementById('project-switcher');
13800
13821
  if (!sel) return;
13801
13822
  var stopList = document.getElementById('project-stop-list');
13823
+ // v7.35: focus a project by its working dir, then reload so every panel
13824
+ // re-fetches against it. The active section lives in the URL hash now, so
13825
+ // the reload lands the user on the SAME section (no reset to overview).
13826
+ // Shared by the dropdown <select> and the clickable project chips.
13827
+ function focusProject(dir) {
13828
+ var req = dir
13829
+ ? fetch('/api/focus', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ project_dir: dir }) })
13830
+ : fetch('/api/focus', { method: 'DELETE' });
13831
+ return req.then(function(){ window.location.reload(); }).catch(function(){ /* ignore */ });
13832
+ }
13802
13833
  // v7.7.30: build a per-row Stop control for each running project using
13803
13834
  // createElement + textContent only (never innerHTML for project-supplied
13804
13835
  // strings), so a project name can never inject markup.
@@ -13812,6 +13843,24 @@ document.addEventListener('DOMContentLoaded', function() {
13812
13843
  var name = document.createElement('span');
13813
13844
  name.className = 'project-stop-name';
13814
13845
  name.textContent = p.name || p.path || 'project';
13846
+ // v7.35: the chip name is now a clickable affordance that focuses
13847
+ // that project (same path as the dropdown). The Stop button keeps its
13848
+ // own handler; clicking the name never triggers Stop. is_active chips
13849
+ // are marked so the current project is visually obvious and its click
13850
+ // is a no-op (already focused).
13851
+ if (p.is_active) {
13852
+ name.classList.add('is-active');
13853
+ } else if (p.path) {
13854
+ name.classList.add('is-clickable');
13855
+ name.setAttribute('role', 'button');
13856
+ name.setAttribute('tabindex', '0');
13857
+ name.setAttribute('title', 'Switch to ' + (p.name || p.path));
13858
+ var go = function(ev){ if (ev) ev.stopPropagation(); focusProject(p.path); };
13859
+ name.addEventListener('click', go);
13860
+ name.addEventListener('keydown', function(ev){
13861
+ if (ev.key === 'Enter' || ev.key === ' ') { ev.preventDefault(); go(ev); }
13862
+ });
13863
+ }
13815
13864
  var btn = document.createElement('button');
13816
13865
  btn.type = 'button';
13817
13866
  btn.textContent = 'Stop';
@@ -13858,14 +13907,7 @@ document.addEventListener('DOMContentLoaded', function() {
13858
13907
  .catch(function(){ /* offline / no endpoint: leave as-is */ });
13859
13908
  }
13860
13909
  sel.addEventListener('change', function(){
13861
- var dir = sel.value;
13862
- var req = dir
13863
- ? fetch('/api/focus', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ project_dir: dir }) })
13864
- : fetch('/api/focus', { method: 'DELETE' });
13865
- req.then(function(){
13866
- // Reload so every panel re-fetches against the newly focused project.
13867
- window.location.reload();
13868
- }).catch(function(){ /* ignore */ });
13910
+ focusProject(sel.value);
13869
13911
  });
13870
13912
  refresh();
13871
13913
  setInterval(refresh, 15000);
@@ -14091,6 +14133,17 @@ document.addEventListener('DOMContentLoaded', function() {
14091
14133
  // Scroll main content to top on section switch
14092
14134
  mainContent.scrollTop = 0;
14093
14135
  localStorage.setItem('loki-active-section', sectionId);
14136
+ // v7.35: reflect the active section in the URL hash so a refresh,
14137
+ // back-button, or a project switch (which reloads the page) lands the
14138
+ // user back on the SAME section instead of resetting to overview. The
14139
+ // hash is updated in place (replaceState) so it does not stack history
14140
+ // entries on every tab click.
14141
+ try {
14142
+ var nh = '#section=' + sectionId;
14143
+ if (window.location.hash !== nh) {
14144
+ history.replaceState(null, '', window.location.pathname + window.location.search + nh);
14145
+ }
14146
+ } catch (e) { /* file:// or sandboxed: hash routing best-effort */ }
14094
14147
  }
14095
14148
 
14096
14149
  navLinks.forEach(function(link) {
@@ -14103,8 +14156,35 @@ document.addEventListener('DOMContentLoaded', function() {
14103
14156
  });
14104
14157
  });
14105
14158
 
14106
- // Show the default section (overview) on load
14107
- switchSection('overview');
14159
+ // v7.35: restore the active section on load. Priority: URL hash
14160
+ // (#section=<id>, shareable + survives the project-switch reload) ->
14161
+ // localStorage (last-used on this machine) -> overview (default). Only
14162
+ // honor a section id that maps to a real nav link, so a stale/hand-edited
14163
+ // hash can never blank the page.
14164
+ function lokiInitialSection() {
14165
+ var fromHash = '';
14166
+ try {
14167
+ var m = (window.location.hash || '').match(/section=([A-Za-z0-9_-]+)/);
14168
+ if (m) fromHash = m[1];
14169
+ } catch (e) { /* ignore */ }
14170
+ var fromStore = '';
14171
+ try { fromStore = localStorage.getItem('loki-active-section') || ''; } catch (e) { /* ignore */ }
14172
+ var candidate = fromHash || fromStore || 'overview';
14173
+ if (!document.querySelector('.nav-link[data-section="' + candidate + '"]')) {
14174
+ candidate = 'overview';
14175
+ }
14176
+ return candidate;
14177
+ }
14178
+ switchSection(lokiInitialSection());
14179
+
14180
+ // Keep the view in sync if the hash changes (back/forward button, or a
14181
+ // shared link opened in the same tab).
14182
+ window.addEventListener('hashchange', function() {
14183
+ var m = (window.location.hash || '').match(/section=([A-Za-z0-9_-]+)/);
14184
+ if (m && document.querySelector('.nav-link[data-section="' + m[1] + '"]')) {
14185
+ switchSection(m[1]);
14186
+ }
14187
+ });
14108
14188
 
14109
14189
  // Keyboard shortcuts: Cmd/Ctrl + 1-7
14110
14190
  document.addEventListener('keydown', function(e) {
@@ -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.34.1
5
+ **Version:** v7.36.0
6
6
 
7
7
  ---
8
8
 
@@ -0,0 +1,65 @@
1
+ <!-- SPDX-License-Identifier: BUSL-1.1 -->
2
+
3
+ # License Change Notice
4
+
5
+ ## Summary
6
+
7
+ Effective March 19, 2026, Loki Mode transitioned from the MIT License to the
8
+ Business Source License 1.1 (BSL 1.1). The Licensed Work is owned by
9
+ Autonomi, Inc. (autonomi.dev).
10
+
11
+ ## What this means
12
+
13
+ **If you are using Loki Mode for personal projects, learning, research, or
14
+ internal tooling:** Nothing changes for you. You can continue using, modifying,
15
+ and redistributing Loki Mode freely.
16
+
17
+ **If you are building a competing commercial product or service:** You need a
18
+ commercial license. Contact founder@autonomi.dev.
19
+
20
+ ## What counts as "competing"
21
+
22
+ Offering a product or service to third parties that provides substantially
23
+ the same or similar functionality as Loki Mode, including multi-agent AI
24
+ development orchestration, autonomous software development services, AI code
25
+ generation or deployment services that incorporate or replicate Loki Mode's
26
+ orchestration, review, verification, or agent coordination systems, or
27
+ products that substantially replicate core architectural patterns such as the
28
+ RARV cycle, Completion Council, or swarm coordination model.
29
+
30
+ ## What does NOT require a commercial license
31
+
32
+ - Using Loki Mode to build your own software products
33
+ - Internal company use where Loki Mode is a tool, not the product
34
+ - Academic, educational, and research use
35
+ - Personal and non-commercial use
36
+ - Evaluation and testing in non-production environments
37
+ - Contributing improvements back under a signed CLA
38
+
39
+ ## Contributing
40
+
41
+ All external contributions require a signed Contributor License Agreement
42
+ (CLA). This ensures Autonomi maintains the rights needed to license and
43
+ distribute the Licensed Work, including in commercial contexts. Contributions
44
+ submitted without a signed CLA may be removed from the codebase.
45
+
46
+ ## Intellectual Property
47
+
48
+ Loki Mode embodies proprietary methodologies and architectural patterns
49
+ developed by Autonomi, including the RARV execution cycle, the Completion
50
+ Council consensus review system, context persistence for long-running
51
+ autonomous sessions, multi-provider orchestration protocols, and swarm-based
52
+ agent coordination. These are protected under this License regardless of
53
+ whether they are used via the code directly or reimplemented independently
54
+ from the documentation.
55
+
56
+ ## Open Source Conversion
57
+
58
+ On March 19, 2030 (or four years from the release date of each version,
59
+ whichever comes first), each version automatically converts to the
60
+ Apache License 2.0.
61
+
62
+ ## Contact
63
+
64
+ founder@autonomi.dev for commercial licensing, partnership inquiries, or
65
+ questions about permitted use.
@@ -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.34.1";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.36.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=F6F45F7E10405C2564756E2164756E21
792
+ //# debugId=948D8F6B9A2EE4AF64756E2164756E21
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '7.34.1'
60
+ __version__ = '7.36.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.34.1",
4
+ "version": "7.36.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",