loki-mode 7.32.3 → 7.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/SKILL.md CHANGED
@@ -3,7 +3,7 @@ name: loki-mode
3
3
  description: Autonomous spec-driven build system with a built-in trust layer. It does not call work done until it is verified (RARV-C closure loop, 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.32.3
6
+ # Loki Mode v7.33.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.32.3 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
401
+ **v7.33.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 7.32.3
1
+ 7.33.0
@@ -1759,7 +1759,23 @@ ISSUES: CRITICAL:description (optional, one per line per issue)"
1759
1759
  claude)
1760
1760
  if command -v claude &>/dev/null; then
1761
1761
  local council_model="${PROVIDER_MODEL_FAST:-haiku}"
1762
- verdict=$(echo "$prompt" | claude --model "$council_model" -p 2>/dev/null | tail -20)
1762
+ # EMBED 2 + 3 (v7.33.0). Council member completion vote. The
1763
+ # $prompt is fully self-contained (evidence + instructions +
1764
+ # strict VOTE/REASON/ISSUES output format, piped via stdin) and
1765
+ # the verdict is captured. So --bare (cheap, no hooks/LSP/CLAUDE.
1766
+ # md/MCP) and --disallowedTools (a voting reviewer must never
1767
+ # mutate the tree) both apply. Gated + opt-out
1768
+ # LOKI_BARE_SUBCALLS=0 / LOKI_REVIEW_TOOL_GUARD=0. Helpers may be
1769
+ # out of scope when this file is sourced standalone, so each is
1770
+ # type-guarded (degrades to the prior bare invocation).
1771
+ local _cm_argv=("--model" "$council_model")
1772
+ if type loki_subcall_bare_enabled >/dev/null 2>&1 && loki_subcall_bare_enabled; then
1773
+ _cm_argv+=("--bare")
1774
+ fi
1775
+ if type loki_review_guard_enabled >/dev/null 2>&1 && loki_review_guard_enabled; then
1776
+ _cm_argv+=("--disallowedTools" "$(loki_review_guard_denylist)")
1777
+ fi
1778
+ verdict=$(echo "$prompt" | claude "${_cm_argv[@]}" -p 2>/dev/null | tail -20)
1763
1779
  fi
1764
1780
  ;;
1765
1781
  codex)
@@ -1842,7 +1858,19 @@ REASON: your reasoning"
1842
1858
  claude)
1843
1859
  if command -v claude &>/dev/null; then
1844
1860
  local council_model="${PROVIDER_MODEL_FAST:-haiku}"
1845
- verdict=$(echo "$prompt" | claude --model "$council_model" -p 2>/dev/null | tail -20)
1861
+ # EMBED 2 + 3 (v7.33.0). Contrarian (devil's-advocate) vote --
1862
+ # an adversarial reviewer. Self-contained $prompt via stdin,
1863
+ # verdict captured. --bare + --disallowedTools both apply (a
1864
+ # reviewer must never mutate the tree). Gated + opt-out
1865
+ # LOKI_BARE_SUBCALLS=0 / LOKI_REVIEW_TOOL_GUARD=0; type-guarded.
1866
+ local _co_argv=("--model" "$council_model")
1867
+ if type loki_subcall_bare_enabled >/dev/null 2>&1 && loki_subcall_bare_enabled; then
1868
+ _co_argv+=("--bare")
1869
+ fi
1870
+ if type loki_review_guard_enabled >/dev/null 2>&1 && loki_review_guard_enabled; then
1871
+ _co_argv+=("--disallowedTools" "$(loki_review_guard_denylist)")
1872
+ fi
1873
+ verdict=$(echo "$prompt" | claude "${_co_argv[@]}" -p 2>/dev/null | tail -20)
1846
1874
  fi
1847
1875
  ;;
1848
1876
  codex)
@@ -263,7 +263,20 @@ Respond ONLY with a valid JSON object. No markdown fencing."
263
263
  case "${PROVIDER_NAME:-claude}" in
264
264
  claude)
265
265
  if command -v claude &>/dev/null; then
266
- result=$(echo "$full_prompt" | claude --model haiku -p 2>/dev/null || echo '{"verdict":"REJECT","reasoning":"review execution failed","issues":[]}')
266
+ # EMBED 2 + 3 (v7.33.0). Council-v2 reviewer verdict. $full_prompt
267
+ # is self-contained (evidence + PRD + strict JSON output, via
268
+ # stdin) and the JSON result is captured. --bare + --disallowedTools
269
+ # both apply (a reviewer must never mutate the tree). Gated +
270
+ # opt-out LOKI_BARE_SUBCALLS=0 / LOKI_REVIEW_TOOL_GUARD=0;
271
+ # type-guarded for standalone sourcing.
272
+ local _c2_argv=("--model" "haiku")
273
+ if type loki_subcall_bare_enabled >/dev/null 2>&1 && loki_subcall_bare_enabled; then
274
+ _c2_argv+=("--bare")
275
+ fi
276
+ if type loki_review_guard_enabled >/dev/null 2>&1 && loki_review_guard_enabled; then
277
+ _c2_argv+=("--disallowedTools" "$(loki_review_guard_denylist)")
278
+ fi
279
+ result=$(echo "$full_prompt" | claude "${_c2_argv[@]}" -p 2>/dev/null || echo '{"verdict":"REJECT","reasoning":"review execution failed","issues":[]}')
267
280
  else
268
281
  result='{"verdict":"REJECT","reasoning":"reviewer CLI unavailable","issues":[]}'
269
282
  fi
package/autonomy/grill.sh CHANGED
@@ -38,6 +38,16 @@ GRILL_EXIT_ERROR=3
38
38
  GRILL_DIR_DEFAULT=".loki/grill"
39
39
  GRILL_REPORT_NAME="report.md"
40
40
 
41
+ # Source the claude-flags helper (idempotent, guarded) so the v7.33.0 embeds
42
+ # (--bare / --disallowedTools) are available in grill's standalone invocation
43
+ # path. Best-effort: if the file is missing the type-guards at the call site
44
+ # degrade to the prior bare invocation.
45
+ _grill_flags_helper="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib/claude-flags.sh"
46
+ if [ -f "$_grill_flags_helper" ]; then
47
+ # shellcheck disable=SC1090
48
+ . "$_grill_flags_helper"
49
+ fi
50
+
41
51
  _grill_log() { printf '[grill] %s\n' "$*" >&2; }
42
52
  _grill_err() { printf '[grill][error] %s\n' "$*" >&2; }
43
53
 
@@ -178,8 +188,24 @@ grill_invoke_provider() {
178
188
  local out
179
189
  # Single-shot, non-interactive. Same pattern as the in-loop
180
190
  # adversarial reviewer (run.sh:7807) and USAGE regen (run.sh:9832).
191
+ # EMBED 2 + 3 (v7.33.0). The devil's-advocate grill is a cheap
192
+ # self-contained adversarial subcall (the entire spec + interrogation
193
+ # instructions are in $prompt, piped via -p -; output captured). So:
194
+ # EMBED 2 (--bare): no hooks/LSP/CLAUDE.md/MCP needed; cheaper.
195
+ # Opt out LOKI_BARE_SUBCALLS=0.
196
+ # EMBED 3 (--disallowedTools): a grill agent interrogates a spec and
197
+ # must never mutate the tree (defense-in-depth). Deny Edit/Write/
198
+ # NotebookEdit + git mutations. Opt out LOKI_REVIEW_TOOL_GUARD=0.
199
+ # Type-guarded so an environment without the helper degrades cleanly.
200
+ local _gr_argv=("--dangerously-skip-permissions" "--model" "${LOKI_GRILL_MODEL:-sonnet}")
201
+ if type loki_subcall_bare_enabled >/dev/null 2>&1 && loki_subcall_bare_enabled; then
202
+ _gr_argv+=("--bare")
203
+ fi
204
+ if type loki_review_guard_enabled >/dev/null 2>&1 && loki_review_guard_enabled; then
205
+ _gr_argv+=("--disallowedTools" "$(loki_review_guard_denylist)")
206
+ fi
181
207
  out="$(printf '%s' "$prompt" \
182
- | _grill_with_timeout "${LOKI_GRILL_TIMEOUT:-180}" claude --dangerously-skip-permissions --model "${LOKI_GRILL_MODEL:-sonnet}" -p - 2>/dev/null)"
208
+ | _grill_with_timeout "${LOKI_GRILL_TIMEOUT:-180}" claude "${_gr_argv[@]}" -p - 2>/dev/null)"
183
209
  if [ -z "$out" ]; then
184
210
  _grill_err "provider returned no output (timeout or invocation error)"
185
211
  return $GRILL_EXIT_ERROR
@@ -130,3 +130,99 @@ loki_claude_flag_supported() {
130
130
  *) return 1 ;;
131
131
  esac
132
132
  }
133
+
134
+ # ---------- v7.33.0 cheap-subcall + reviewer-guard flag emitters ----------
135
+ # These centralize three Claude Code 2.1.170 embeds so every cheap NON-MAIN
136
+ # subcall site emits the same gated flags. Each emitter prints its flags to
137
+ # stdout (space-free per element via one-per-line is not needed; callers read
138
+ # them into an array with command substitution + word-splitting on the single
139
+ # token they emit, OR append the function output as additional argv elements).
140
+ # All are default-ON, opt-out via env, and gated on loki_claude_flag_supported
141
+ # so an older CLI degrades gracefully (emits nothing).
142
+
143
+ # EMBED 2 -- --bare (cheap NON-MAIN subcalls only). Minimal mode. Per
144
+ # `claude --help` it SKIPS hooks, LSP, plugin sync, attribution, auto-memory,
145
+ # background prefetches, keychain reads, and CLAUDE.md AUTO-discovery. It does
146
+ # NOT nullify explicit --mcp-config/--settings/--agents (the help lists those as
147
+ # the way to "explicitly provide context" UNDER --bare); what it drops is the
148
+ # IMPLICIT/auto-discovered context. Therefore --bare is ONLY safe on subcalls
149
+ # whose prompt is fully self-contained (the entire instruction set + context is
150
+ # passed via -p), and NEVER on the main RARV loop or on any call that relies on
151
+ # auto-discovered CLAUDE.md / hooks / auto-memory.
152
+ #
153
+ # AUTH GATE (critical): --bare sets CLAUDE_CODE_SIMPLE=1 and per `claude --help`
154
+ # reads Anthropic auth STRICTLY from ANTHROPIC_API_KEY or an apiKeyHelper via
155
+ # --settings -- "OAuth and keychain are never read". A subscription/OAuth user
156
+ # (no ANTHROPIC_API_KEY) gets "Not logged in" on every --bare subcall, which
157
+ # exits 0 with the error on stdout, so a council vote parses as the default
158
+ # REJECT and the loop silently corrupts. So --bare is enabled ONLY when an
159
+ # API-key auth path exists (ANTHROPIC_API_KEY set, or an apiKeyHelper configured
160
+ # in settings); otherwise it emits nothing and the subcall runs full-auth, the
161
+ # same as before this embed. Subscription users are thus unaffected by default.
162
+ # Default-ON (when auth-safe); opt out entirely with LOKI_BARE_SUBCALLS=0.
163
+ # Predicate so call sites can append "--bare" to their argv array uniformly:
164
+ # loki_subcall_bare_enabled && argv+=("--bare")
165
+ loki_subcall_bare_enabled() {
166
+ [ "${LOKI_BARE_SUBCALLS:-1}" = "0" ] && return 1
167
+ # API-key auth required (see AUTH GATE above). ANTHROPIC_API_KEY is the
168
+ # common case; apiKeyHelper covers the settings-configured helper. Anything
169
+ # else (OAuth/keychain subscription) must NOT use --bare. Trim the key so a
170
+ # whitespace-only value does not count as set (it would fail --bare auth).
171
+ local _key
172
+ _key="$(printf '%s' "${ANTHROPIC_API_KEY:-}" | tr -d '[:space:]')"
173
+ if [ -z "$_key" ] && ! _loki_apikey_helper_configured; then
174
+ return 1
175
+ fi
176
+ loki_claude_flag_supported "--bare"
177
+ }
178
+
179
+ # True when an apiKeyHelper is configured in any Claude settings source, which
180
+ # (unlike OAuth/keychain) IS honored under --bare. Best-effort, read-only.
181
+ _loki_apikey_helper_configured() {
182
+ local f
183
+ for f in "$HOME/.claude/settings.json" "$HOME/.config/claude/settings.json" \
184
+ "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json" \
185
+ "$PWD/.claude/settings.json" "$PWD/.claude/settings.local.json"; do
186
+ [ -f "$f" ] || continue
187
+ # Require "apiKeyHelper" : "<non-empty value>" (a real key:value pair),
188
+ # not the bare token in a comment/prose. Tolerates whitespace around the
189
+ # colon. Not a full JSON parse, but rejects the obvious false-positives.
190
+ grep -Eq '"apiKeyHelper"[[:space:]]*:[[:space:]]*"[^"]+"' "$f" 2>/dev/null && return 0
191
+ done
192
+ return 1
193
+ }
194
+
195
+ # EMBED 3 -- --disallowedTools (reviewer / adversarial subcalls). Motivation: a
196
+ # parallel agent once ran `git reset --hard` and wiped uncommitted work, so a
197
+ # reviewer/voter subcall should not casually mutate the tree. Per `claude --help`
198
+ # the value is a comma-or-space-separated list of tool names (e.g. "Bash(git *)
199
+ # Edit"); Bash(...) matching is command-PREFIX based (the `cmd:*` form).
200
+ #
201
+ # SCOPE / LIMITS (honest -- this is a denylist, NOT a sandbox):
202
+ # - Denies the direct file-mutation tools (Edit, Write, NotebookEdit).
203
+ # - Denies the common git MUTATION forms: the bare subcommand (`git reset:*`)
204
+ # AND the global-flag-prefixed evasions (`git -C:*`, `git --git-dir:*`,
205
+ # `git -c:*`) that slip a flag before the subcommand so the bare prefix
206
+ # does not match. Read-only git (diff/log/show/status) stays allowed.
207
+ # - It does NOT and cannot block every mutation path: a determined agent can
208
+ # still write via `Bash(echo > f)`, `sed -i`, `cp/mv/tee`, `python -c`, etc.
209
+ # This is a guardrail that raises the cost of the casual/common destructive
210
+ # command, not a guarantee the tree is immutable. The real safety net is
211
+ # that the integrator commits before any agent wave (see CLAUDE.md).
212
+ # The flag is variadic (<tools...>), so we emit ONE comma-separated token to
213
+ # avoid swallowing the following -p prompt as additional tool names.
214
+ # Default-ON; opt out with LOKI_REVIEW_TOOL_GUARD=0.
215
+ # Predicate + denylist so call sites append uniformly:
216
+ # if loki_review_guard_enabled; then
217
+ # argv+=("--disallowedTools" "$(loki_review_guard_denylist)")
218
+ # fi
219
+ loki_review_guard_enabled() {
220
+ [ "${LOKI_REVIEW_TOOL_GUARD:-1}" = "0" ] && return 1
221
+ loki_claude_flag_supported "--disallowedTools"
222
+ }
223
+ loki_review_guard_denylist() {
224
+ # Comma-separated single token. Bash(cmd:*) is command-prefix matching.
225
+ # The `git -C:*` / `git --git-dir:*` / `git -c:*` entries close the
226
+ # global-flag-before-subcommand evasion of the bare git-mutation rules.
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
+ }
package/autonomy/run.sh CHANGED
@@ -3248,7 +3248,18 @@ Output ONLY the resolved file content with no conflict markers. No explanations.
3248
3248
 
3249
3249
  case "${PROVIDER_NAME:-claude}" in
3250
3250
  claude)
3251
- resolution=$(claude --dangerously-skip-permissions -p "$conflict_prompt" --output-format text 2>/dev/null)
3251
+ # EMBED 2 (v7.33.0): --bare on this cheap NON-MAIN subcall.
3252
+ # Reasoning: $conflict_prompt is fully self-contained -- it
3253
+ # carries the complete instruction set AND the entire conflicted
3254
+ # file content inline, and the agent's output is captured to a
3255
+ # variable (the shell, not the agent, writes the resolved file).
3256
+ # It needs no hooks, LSP, CLAUDE.md auto-discovery, or MCP, so
3257
+ # --bare is safe and cheaper. Gated + opt-out LOKI_BARE_SUBCALLS=0.
3258
+ local _cr_argv=("--dangerously-skip-permissions")
3259
+ if type loki_subcall_bare_enabled >/dev/null 2>&1 && loki_subcall_bare_enabled; then
3260
+ _cr_argv+=("--bare")
3261
+ fi
3262
+ resolution=$(claude "${_cr_argv[@]}" -p "$conflict_prompt" --output-format text 2>/dev/null)
3252
3263
  ;;
3253
3264
  codex)
3254
3265
  resolution=$(codex exec --full-auto --skip-git-repo-check "$conflict_prompt" 2>/dev/null)
@@ -7803,7 +7814,31 @@ BUILD_PROMPT
7803
7814
  # Mythos 5 (Project Glasswing), not Fable. If a future change
7804
7815
  # adds --model here, the security-sentinel reviewer must be
7805
7816
  # pinned to opus, never fable.
7806
- claude --dangerously-skip-permissions -p "$prompt_text" \
7817
+ # EMBED 2 + 3 (v7.33.0). This is a 3-reviewer council
7818
+ # subcall. $prompt_text is fully self-contained (built above
7819
+ # into $review_prompt_file with the diff, changed files,
7820
+ # checks, and strict VERDICT/FINDINGS output format), output
7821
+ # is captured to $review_output, and it deliberately does NOT
7822
+ # pass --model or go through buildAutoFlags. So:
7823
+ # EMBED 2 (--bare): the prompt needs no hooks/LSP/CLAUDE.md/
7824
+ # MCP discovery, so --bare is safe and cheaper. Opt out
7825
+ # LOKI_BARE_SUBCALLS=0.
7826
+ # EMBED 3 (--disallowedTools): raise the cost of a reviewer
7827
+ # casually mutating the tree (a parallel agent once ran
7828
+ # `git reset --hard` and wiped uncommitted work). Deny
7829
+ # Edit/Write/NotebookEdit + git mutation forms (incl. the
7830
+ # git -C / --git-dir evasions); read-only git stays allowed.
7831
+ # Guardrail, not a sandbox -- echo>/sed -i/etc. remain; the
7832
+ # real net is commit-before-agent-wave. Opt out
7833
+ # LOKI_REVIEW_TOOL_GUARD=0. See loki_review_guard_denylist.
7834
+ local _rv_argv=("--dangerously-skip-permissions")
7835
+ if type loki_subcall_bare_enabled >/dev/null 2>&1 && loki_subcall_bare_enabled; then
7836
+ _rv_argv+=("--bare")
7837
+ fi
7838
+ if type loki_review_guard_enabled >/dev/null 2>&1 && loki_review_guard_enabled; then
7839
+ _rv_argv+=("--disallowedTools" "$(loki_review_guard_denylist)")
7840
+ fi
7841
+ claude "${_rv_argv[@]}" -p "$prompt_text" \
7807
7842
  --output-format text > "$review_output" 2>/dev/null
7808
7843
  ;;
7809
7844
  codex)
@@ -8016,7 +8051,25 @@ ADVERSARIAL_EOF
8016
8051
  case "${PROVIDER_NAME:-claude}" in
8017
8052
  claude)
8018
8053
  if command -v claude &>/dev/null; then
8019
- claude --dangerously-skip-permissions -p "$adversarial_prompt" \
8054
+ # EMBED 2 + 3 (v7.33.0). Adversarial probe subcall.
8055
+ # $adversarial_prompt is fully self-contained (instructions +
8056
+ # changed files + diff inlined via the heredoc above) and output
8057
+ # is captured to $result_file. So:
8058
+ # EMBED 2 (--bare): no hooks/LSP/CLAUDE.md/MCP needed; cheaper.
8059
+ # Opt out LOKI_BARE_SUBCALLS=0.
8060
+ # EMBED 3 (--disallowedTools): keep an adversarial agent from
8061
+ # casually mutating the tree. Deny Edit/Write/NotebookEdit +
8062
+ # git mutation forms (incl. git -C / --git-dir evasions);
8063
+ # read-only git stays allowed. Guardrail, not a sandbox.
8064
+ # Opt out LOKI_REVIEW_TOOL_GUARD=0.
8065
+ local _adv_argv=("--dangerously-skip-permissions")
8066
+ if type loki_subcall_bare_enabled >/dev/null 2>&1 && loki_subcall_bare_enabled; then
8067
+ _adv_argv+=("--bare")
8068
+ fi
8069
+ if type loki_review_guard_enabled >/dev/null 2>&1 && loki_review_guard_enabled; then
8070
+ _adv_argv+=("--disallowedTools" "$(loki_review_guard_denylist)")
8071
+ fi
8072
+ claude "${_adv_argv[@]}" -p "$adversarial_prompt" \
8020
8073
  --output-format text > "$result_file" 2>/dev/null || true
8021
8074
  fi
8022
8075
  ;;
@@ -10041,9 +10094,21 @@ ${_commits}"
10041
10094
 
10042
10095
  # Use haiku for cheap, fast generation. --dangerously-skip-permissions
10043
10096
  # because this is a one-shot non-interactive call.
10097
+ # EMBED 2 (v7.33.0): --bare on this cheap NON-MAIN haiku subcall. The
10098
+ # USAGE.md-regen prompt ($_ic_prompt, piped via -p -) is fully self-contained
10099
+ # (project tree + manifests + entrypoint contents + commits inlined) and the
10100
+ # output is captured, not written by the agent. No hooks/LSP/CLAUDE.md/MCP
10101
+ # needed, so --bare is safe and cheaper. Opt out LOKI_BARE_SUBCALLS=0.
10102
+ # Always at least --dangerously-skip-permissions, so the array is never
10103
+ # empty (empty "${arr[@]}" under set -u errors on bash 3.2, stock macOS).
10104
+ local _ic_argv=("--dangerously-skip-permissions")
10105
+ if type loki_subcall_bare_enabled >/dev/null 2>&1 && loki_subcall_bare_enabled; then
10106
+ _ic_argv+=("--bare")
10107
+ fi
10108
+ _ic_argv+=("--model" "haiku")
10044
10109
  local _ic_out
10045
10110
  _ic_out=$(printf '%s' "$_ic_prompt" \
10046
- | timeout 60 claude --dangerously-skip-permissions --model haiku -p - 2>/dev/null \
10111
+ | timeout 60 claude "${_ic_argv[@]}" -p - 2>/dev/null \
10047
10112
  | head -200)
10048
10113
  # Sanity check: response must look like Markdown (starts with # or ##).
10049
10114
  if [ -z "$_ic_out" ] || ! printf '%s' "$_ic_out" | head -1 | grep -qE '^#'; then
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "7.32.3"
10
+ __version__ = "7.33.0"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -2,7 +2,7 @@
2
2
 
3
3
  The flagship product of [Autonomi](https://www.autonomi.dev/). Loki Mode is a spec-driven autonomous builder with a built-in trust layer that takes any spec to a deployed product and verifies completion with evidence (quality gates plus a completion council), not just a "done" claim. Complete installation instructions for all platforms and use cases.
4
4
 
5
- **Version:** v7.32.3
5
+ **Version:** v7.33.0
6
6
 
7
7
  ---
8
8
 
@@ -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.32.3";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.33.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=E69BF9B144A89A3864756E2164756E21
792
+ //# debugId=27D659E047A7DB5564756E2164756E21
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '7.32.3'
60
+ __version__ = '7.33.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.32.3",
4
+ "version": "7.33.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",
@@ -190,6 +190,20 @@ _loki_build_claude_auto_flags() {
190
190
  for _mcp_path in $_mcp_argv; do
191
191
  _LOKI_CLAUDE_AUTO_FLAGS+=("$_mcp_path")
192
192
  done
193
+ # EMBED 1 (v7.33.0): --strict-mcp-config. ONLY emitted alongside an
194
+ # actual --mcp-config bundle (never bare). Per `claude --help` it
195
+ # makes the agent load servers ONLY from --mcp-config, ignoring ALL
196
+ # other MCP sources (auto-discovered project .mcp.json AND any
197
+ # settings-injected configs). Note the bundle already includes the
198
+ # user's ~/.claude/mcp.json overlay explicitly, so the common
199
+ # user-MCP case is preserved; what is dropped is any MCP config not
200
+ # in the explicit bundle, making the run reproducible.
201
+ # Default-ON; opt out with LOKI_STRICT_MCP=0. Gated on CLI support so
202
+ # an older claude degrades gracefully.
203
+ if [ "${LOKI_STRICT_MCP:-1}" != "0" ] \
204
+ && loki_claude_flag_supported "--strict-mcp-config"; then
205
+ _LOKI_CLAUDE_AUTO_FLAGS+=("--strict-mcp-config")
206
+ fi
193
207
  fi
194
208
  fi
195
209