loki-mode 7.41.0 → 7.41.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/app-runner.sh +138 -3
- package/autonomy/lib/claude-flags.sh +56 -3
- package/autonomy/loki +70 -6
- package/autonomy/run.sh +343 -10
- package/dashboard/__init__.py +1 -1
- package/dashboard/server.py +95 -2
- package/dashboard/static/index.html +58 -32
- package/docs/INSTALLATION.md +1 -1
- package/loki-ts/dist/loki.js +2 -2
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/skills/quality-gates.md +39 -2
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.41.
|
|
6
|
+
# Loki Mode v7.41.2
|
|
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.41.
|
|
401
|
+
**v7.41.2 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.41.
|
|
1
|
+
7.41.2
|
package/autonomy/app-runner.sh
CHANGED
|
@@ -136,6 +136,121 @@ HEALTH_EOF
|
|
|
136
136
|
mv "$tmp_file" "$_APP_RUNNER_DIR/health.json"
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
# Re-derive a detection.json field (type/command) so we can rewrite it after a
|
|
140
|
+
# port reconcile without threading those values through globals. Echoes the raw
|
|
141
|
+
# string value (empty on miss). Mirrors the grep-based read style used by
|
|
142
|
+
# app_runner_status.
|
|
143
|
+
_read_detection_field() {
|
|
144
|
+
local field="$1"
|
|
145
|
+
[ -f "$_APP_RUNNER_DIR/detection.json" ] || return 0
|
|
146
|
+
grep -o "\"${field}\": *\"[^\"]*\"" "$_APP_RUNNER_DIR/detection.json" 2>/dev/null \
|
|
147
|
+
| head -1 | sed 's/.*"\([^"]*\)"$/\1/'
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
# Rewrite detection.json with the reconciled port, preserving type/command.
|
|
151
|
+
_rewrite_detection_port() {
|
|
152
|
+
local d_type d_command
|
|
153
|
+
d_type=$(_read_detection_field "type")
|
|
154
|
+
d_command=$(_read_detection_field "command")
|
|
155
|
+
[ -n "$d_type" ] || return 0
|
|
156
|
+
_write_detection "$d_type" "$d_command"
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
# Fix #2 (finding #597): reconcile the recorded port with the port the app
|
|
160
|
+
# ACTUALLY bound, using the listen line in app.log as the source of truth. This
|
|
161
|
+
# corrects the dashboard Live Preview even when the app ignores PORT and picks
|
|
162
|
+
# its own port. Bounded poll: returns as soon as a listen line is found, and
|
|
163
|
+
# never runs for docker (compose URLs come from published-port mapping) or when
|
|
164
|
+
# no port was recorded. Default window LOKI_APP_PORT_RECONCILE_SECS (default 12)
|
|
165
|
+
# at 0.5s intervals. On no match within the window the recorded port is kept (no
|
|
166
|
+
# regression). Stdout: nothing; mutates _APP_RUNNER_PORT / _APP_RUNNER_URL and
|
|
167
|
+
# rewrites state.json + detection.json only when the real port differs.
|
|
168
|
+
_app_runner_reconcile_port() {
|
|
169
|
+
[ "$_APP_RUNNER_IS_DOCKER" != true ] || return 0
|
|
170
|
+
[ -n "$_APP_RUNNER_PORT" ] && [ "$_APP_RUNNER_PORT" -gt 0 ] 2>/dev/null || return 0
|
|
171
|
+
local log_file="$_APP_RUNNER_DIR/app.log"
|
|
172
|
+
|
|
173
|
+
# Fast path: if the recorded port already serves HTTP, the app honored our
|
|
174
|
+
# chosen port (fix #1 worked) or otherwise bound it -- nothing to reconcile,
|
|
175
|
+
# and we avoid the poll latency entirely. Covers quiet-but-serving apps that
|
|
176
|
+
# never log a recognizable listen line.
|
|
177
|
+
if command -v curl >/dev/null 2>&1 && \
|
|
178
|
+
curl -sf -o /dev/null -m 2 "http://localhost:${_APP_RUNNER_PORT}/" 2>/dev/null; then
|
|
179
|
+
return 0
|
|
180
|
+
fi
|
|
181
|
+
|
|
182
|
+
local max_secs="${LOKI_APP_PORT_RECONCILE_SECS:-12}"
|
|
183
|
+
[[ "$max_secs" =~ ^[0-9]+$ ]] || max_secs=12
|
|
184
|
+
local max_iter=$(( max_secs * 2 ))
|
|
185
|
+
[ "$max_iter" -gt 0 ] || max_iter=1
|
|
186
|
+
|
|
187
|
+
local real_port="" iter=0
|
|
188
|
+
while [ "$iter" -lt "$max_iter" ]; do
|
|
189
|
+
if [ -f "$log_file" ]; then
|
|
190
|
+
real_port=$(_parse_listen_port "$log_file")
|
|
191
|
+
[ -n "$real_port" ] && break
|
|
192
|
+
fi
|
|
193
|
+
# Stop early if the process already died (failed start): nothing to wait for.
|
|
194
|
+
if [ -n "$_APP_RUNNER_PID" ] && ! kill -0 "$_APP_RUNNER_PID" 2>/dev/null; then
|
|
195
|
+
break
|
|
196
|
+
fi
|
|
197
|
+
sleep 0.5
|
|
198
|
+
iter=$(( iter + 1 ))
|
|
199
|
+
done
|
|
200
|
+
|
|
201
|
+
[ -n "$real_port" ] || return 0
|
|
202
|
+
if [ "$real_port" != "$_APP_RUNNER_PORT" ]; then
|
|
203
|
+
log_info "App Runner: reconciled port $_APP_RUNNER_PORT -> $real_port (from app.log listen line)"
|
|
204
|
+
_APP_RUNNER_PORT="$real_port"
|
|
205
|
+
_APP_RUNNER_URL="http://localhost:${real_port}"
|
|
206
|
+
_rewrite_detection_port
|
|
207
|
+
fi
|
|
208
|
+
return 0
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
# Parse the actual bound port from an app log file. Scans known listen-line
|
|
212
|
+
# shapes in priority order and returns the LAST (most recent) plausible port,
|
|
213
|
+
# tolerating ANSI color codes that dev servers emit. Validates 1-65535. Echoes
|
|
214
|
+
# the port or nothing.
|
|
215
|
+
_parse_listen_port() {
|
|
216
|
+
local file="$1"
|
|
217
|
+
[ -f "$file" ] || return 0
|
|
218
|
+
# Strip ANSI SGR sequences (\e[...m) so color-wrapped URLs still match.
|
|
219
|
+
local clean
|
|
220
|
+
clean=$(sed -E $'s/\x1b\\[[0-9;]*m//g' "$file" 2>/dev/null) || clean=$(cat "$file" 2>/dev/null)
|
|
221
|
+
[ -n "$clean" ] || return 0
|
|
222
|
+
|
|
223
|
+
local candidate=""
|
|
224
|
+
# 1) Explicit URL with a port: http://host:PORT (most reliable).
|
|
225
|
+
candidate=$(printf '%s\n' "$clean" \
|
|
226
|
+
| grep -oiE 'https?://[a-z0-9.\-]+:[0-9]{1,5}' \
|
|
227
|
+
| grep -oE ':[0-9]{1,5}' | tr -d ':' | tail -1)
|
|
228
|
+
# 2) A number anchored to the literal word "port": "port 8080", "port=3000",
|
|
229
|
+
# "port: 5000". This runs BEFORE the bare host:port scan so a clock-style
|
|
230
|
+
# timestamp on the same line (e.g. "12:30:45 ... port 8080") cannot win.
|
|
231
|
+
if [ -z "$candidate" ]; then
|
|
232
|
+
candidate=$(printf '%s\n' "$clean" \
|
|
233
|
+
| grep -ioE 'port[ =:]+[0-9]{1,5}' \
|
|
234
|
+
| grep -oE '[0-9]{1,5}' | tail -1)
|
|
235
|
+
fi
|
|
236
|
+
# 3) Keyword listen lines with a real host token before the colon:
|
|
237
|
+
# "localhost:5173", "0.0.0.0:8080", "127.0.0.1:3000". Requiring a letter
|
|
238
|
+
# or a dot immediately left of the colon excludes "HH:MM" timestamps,
|
|
239
|
+
# which have a digit there.
|
|
240
|
+
if [ -z "$candidate" ]; then
|
|
241
|
+
candidate=$(printf '%s\n' "$clean" \
|
|
242
|
+
| grep -iE 'listen|running on|ready|started|serving|server' \
|
|
243
|
+
| grep -oiE '[a-z.][a-z0-9.\-]*:[0-9]{1,5}' \
|
|
244
|
+
| grep -oE ':[0-9]{1,5}' | tr -d ':' | tail -1)
|
|
245
|
+
fi
|
|
246
|
+
|
|
247
|
+
[ -n "$candidate" ] || return 0
|
|
248
|
+
# Validate range 1-65535.
|
|
249
|
+
if [ "$candidate" -ge 1 ] 2>/dev/null && [ "$candidate" -le 65535 ] 2>/dev/null; then
|
|
250
|
+
printf '%s\n' "$candidate"
|
|
251
|
+
fi
|
|
252
|
+
}
|
|
253
|
+
|
|
139
254
|
# Rotate app.log if it exceeds max lines
|
|
140
255
|
_rotate_app_log() {
|
|
141
256
|
local log_file="$_APP_RUNNER_DIR/app.log"
|
|
@@ -606,6 +721,21 @@ app_runner_start() {
|
|
|
606
721
|
log_step "App Runner: starting application ($_APP_RUNNER_METHOD on port $_APP_RUNNER_PORT)..."
|
|
607
722
|
_rotate_app_log
|
|
608
723
|
|
|
724
|
+
# Fix #1 (finding #597): pass Loki's chosen port to the app via the env so the
|
|
725
|
+
# app honors it instead of binding its own default (e.g. a Node app reading
|
|
726
|
+
# `process.env.PORT || 4000` would otherwise bind 4000 while Loki recorded the
|
|
727
|
+
# guessed 3000, leaving the dashboard Live Preview pointed at a dead port).
|
|
728
|
+
# We export PORT plus the common ecosystem aliases. An app that ignores these
|
|
729
|
+
# vars is unaffected; an ignored env var is harmless by definition. We do NOT
|
|
730
|
+
# set HOST/BIND -- changing the bind address can break apps. For docker (which
|
|
731
|
+
# gets its port via published-port mapping, not the child env) this is a no-op
|
|
732
|
+
# at the binary boundary, so we only export for the direct-exec path.
|
|
733
|
+
local _port_env_prefix=""
|
|
734
|
+
if [ "$_APP_RUNNER_IS_DOCKER" != true ] && \
|
|
735
|
+
[ -n "$_APP_RUNNER_PORT" ] && [ "$_APP_RUNNER_PORT" -gt 0 ] 2>/dev/null; then
|
|
736
|
+
_port_env_prefix="export PORT=$_APP_RUNNER_PORT HTTP_PORT=$_APP_RUNNER_PORT SERVER_PORT=$_APP_RUNNER_PORT APP_PORT=$_APP_RUNNER_PORT; "
|
|
737
|
+
fi
|
|
738
|
+
|
|
609
739
|
# Start the process in a new process group
|
|
610
740
|
if command -v setsid >/dev/null 2>&1; then
|
|
611
741
|
_APP_RUNNER_HAS_SETSID=true
|
|
@@ -615,7 +745,7 @@ app_runner_start() {
|
|
|
615
745
|
# Note: $_APP_RUNNER_METHOD has passed _validate_app_command (whitelist).
|
|
616
746
|
# The `--` after `bash -lc` prevents flag injection if the assembled
|
|
617
747
|
# script string ever begins with a `-`.
|
|
618
|
-
(cd "$dir" && setsid bash -lc -- 'echo $$ > "'"$_pgid_file"'"; exec '"$_APP_RUNNER_METHOD" >> "$_APP_RUNNER_DIR/app.log" 2>&1) &
|
|
748
|
+
(cd "$dir" && setsid bash -lc -- "$_port_env_prefix"'echo $$ > "'"$_pgid_file"'"; exec '"$_APP_RUNNER_METHOD" >> "$_APP_RUNNER_DIR/app.log" 2>&1) &
|
|
619
749
|
local _subshell_pid=$!
|
|
620
750
|
# Wait briefly for the pgid file to appear, then read the real PGID
|
|
621
751
|
local _pgid_wait=0
|
|
@@ -633,7 +763,7 @@ app_runner_start() {
|
|
|
633
763
|
_APP_RUNNER_HAS_SETSID=false
|
|
634
764
|
# Note: $_APP_RUNNER_METHOD has passed _validate_app_command (whitelist).
|
|
635
765
|
# The `--` after `bash -lc` prevents flag injection.
|
|
636
|
-
(cd "$dir" && bash -lc -- "$_APP_RUNNER_METHOD" >> "$_APP_RUNNER_DIR/app.log" 2>&1) &
|
|
766
|
+
(cd "$dir" && bash -lc -- "${_port_env_prefix}exec $_APP_RUNNER_METHOD" >> "$_APP_RUNNER_DIR/app.log" 2>&1) &
|
|
637
767
|
_APP_RUNNER_PID=$!
|
|
638
768
|
fi
|
|
639
769
|
# Register with central PID registry if available
|
|
@@ -675,8 +805,13 @@ app_runner_start() {
|
|
|
675
805
|
return 1
|
|
676
806
|
fi
|
|
677
807
|
elif kill -0 "$_APP_RUNNER_PID" 2>/dev/null; then
|
|
808
|
+
# Reconcile recorded port with the port the app actually bound (finding
|
|
809
|
+
# #597), so state.json / detection.json / the preview URL point at the
|
|
810
|
+
# live port even when the app ignored PORT. Mutates globals before the
|
|
811
|
+
# state write below. Bounded; no-op when the app honored the chosen port.
|
|
812
|
+
_app_runner_reconcile_port
|
|
678
813
|
_write_app_state "running"
|
|
679
|
-
log_info "App Runner: application started (PID: $_APP_RUNNER_PID)"
|
|
814
|
+
log_info "App Runner: application started (PID: $_APP_RUNNER_PID) on port $_APP_RUNNER_PORT"
|
|
680
815
|
return 0
|
|
681
816
|
else
|
|
682
817
|
log_error "App Runner: application failed to start"
|
|
@@ -527,8 +527,22 @@ LOKI_CAVEMAN_VERSION="${LOKI_CAVEMAN_VERSION:-1.9.0}"
|
|
|
527
527
|
|
|
528
528
|
# The compression level for free-form activation. Maps directly to caveman's
|
|
529
529
|
# CAVEMAN_DEFAULT_MODE values: lite | full | ultra | wenyan | wenyan-lite |
|
|
530
|
-
# wenyan-full | wenyan-ultra.
|
|
531
|
-
#
|
|
530
|
+
# wenyan-full | wenyan-ultra. Never "off" here -- "off" is the suppression value,
|
|
531
|
+
# not an activation level.
|
|
532
|
+
#
|
|
533
|
+
# v7.x #593 -- INTELLIGENT AUTO-SELECTION (no new user knob): when the user does
|
|
534
|
+
# NOT set LOKI_CAVEMAN_LEVEL explicitly, the level is INFERRED per-invocation from
|
|
535
|
+
# the run's existing RARV-tier signal (see _loki_caveman_infer_level). When the
|
|
536
|
+
# user DOES set it, that value overrides the inference entirely (opt-out escape
|
|
537
|
+
# hatch). Capture set-ness BEFORE the ":-full" default clobbers it -- once the
|
|
538
|
+
# default fills the var, "user set full" and "defaulted to full" are
|
|
539
|
+
# indistinguishable, so the inference would silently never fire. ${var+set} is
|
|
540
|
+
# non-empty only when the var was genuinely set (even to empty). The "full"
|
|
541
|
+
# default is kept so the public var still reads "full" for external readers and
|
|
542
|
+
# so a child re-source re-derives USERSET correctly (unexported default).
|
|
543
|
+
if [ -z "${LOKI_CAVEMAN_LEVEL_USERSET+x}" ]; then
|
|
544
|
+
LOKI_CAVEMAN_LEVEL_USERSET="${LOKI_CAVEMAN_LEVEL+set}"
|
|
545
|
+
fi
|
|
532
546
|
LOKI_CAVEMAN_LEVEL="${LOKI_CAVEMAN_LEVEL:-full}"
|
|
533
547
|
|
|
534
548
|
# ---------- DEFAULT-SUPPRESS: off by construction, tree-wide ----------
|
|
@@ -563,6 +577,35 @@ fi
|
|
|
563
577
|
export LOKI_CAVEMAN_USER_MODE
|
|
564
578
|
export CAVEMAN_DEFAULT_MODE=off
|
|
565
579
|
|
|
580
|
+
# ---------- v7.x #593 intelligent compression-level inference ----------
|
|
581
|
+
# Infer the caveman compression level from the run's existing RARV-tier signal,
|
|
582
|
+
# so the level is DECIDED by inspecting the work rather than asked of the user.
|
|
583
|
+
# No new user input is introduced: the tier already drives effort/model selection
|
|
584
|
+
# (loki_effort_for_tier, get_rarv_tier). On the bash route the tier is read from
|
|
585
|
+
# LOKI_CURRENT_TIER (exported by run_autonomous each iteration); the TS mirror
|
|
586
|
+
# (cavemanActivateEnv) receives the same tier vocabulary (planning|development|
|
|
587
|
+
# fast) via call.tier, so both routes infer identically from the same signal.
|
|
588
|
+
#
|
|
589
|
+
# INFERENCE RULE (deterministic, conservative-for-accuracy):
|
|
590
|
+
# planning (Reason phase -- architecture / design / nuanced reasoning) -> lite
|
|
591
|
+
# development (Act / Reflect -- implementation, the prior default) -> full
|
|
592
|
+
# fast (Verify phase -- testing / validation, more routine) -> full
|
|
593
|
+
# unknown / empty tier -> full
|
|
594
|
+
# The auto ceiling is "full": inference NEVER selects ultra. ultra is reachable
|
|
595
|
+
# only via an explicit LOKI_CAVEMAN_LEVEL override (the opt-out escape hatch), so
|
|
596
|
+
# the autonomous path can never compress hard enough to lose technical nuance.
|
|
597
|
+
# "lite" on planning protects the highest-nuance output (architecture/design);
|
|
598
|
+
# everything else stays at the established "full" default. When the tier is
|
|
599
|
+
# unknown we pick the SAFER (established) "full", never something more aggressive.
|
|
600
|
+
# shellcheck disable=SC2120 # $1 is optional; callers rely on the global fallback
|
|
601
|
+
_loki_caveman_infer_level() {
|
|
602
|
+
local tier="${1:-${LOKI_CURRENT_TIER:-}}"
|
|
603
|
+
case "$tier" in
|
|
604
|
+
planning) printf '%s' "lite" ;;
|
|
605
|
+
*) printf '%s' "full" ;;
|
|
606
|
+
esac
|
|
607
|
+
}
|
|
608
|
+
|
|
566
609
|
# Rank a caveman mode by compression aggressiveness for the no-raise comparison.
|
|
567
610
|
# Higher number = more aggressive (drops more nuance). "off" is the floor; unknown
|
|
568
611
|
# or empty modes rank as -1 (treated as "no opinion", so they never win a min()).
|
|
@@ -656,7 +699,17 @@ loki_caveman_enabled() {
|
|
|
656
699
|
loki_caveman_activate_env() {
|
|
657
700
|
loki_caveman_supported || return 0
|
|
658
701
|
loki_caveman_enabled || return 0
|
|
659
|
-
|
|
702
|
+
# #593: the level is the EXPLICIT user value when LOKI_CAVEMAN_LEVEL was set
|
|
703
|
+
# (override / opt-out escape hatch), else the INFERRED level from the RARV
|
|
704
|
+
# tier. The no-raise guard below then runs unchanged on this base, so an
|
|
705
|
+
# explicit level is still lowered toward a user's lower global mode exactly
|
|
706
|
+
# as before -- "override" means override the inference, not the no-raise.
|
|
707
|
+
local level
|
|
708
|
+
if [ -n "${LOKI_CAVEMAN_LEVEL_USERSET:-}" ]; then
|
|
709
|
+
level="${LOKI_CAVEMAN_LEVEL:-full}"
|
|
710
|
+
else
|
|
711
|
+
level="$(_loki_caveman_infer_level)"
|
|
712
|
+
fi
|
|
660
713
|
# Respect (never exceed) a user's explicitly-lower global level. A user who
|
|
661
714
|
# globally set CAVEMAN_DEFAULT_MODE=off opted OUT of compression entirely;
|
|
662
715
|
# honor that by activating nothing (empty -> bare claude invocation).
|
package/autonomy/loki
CHANGED
|
@@ -77,6 +77,35 @@ if [ -f "$_LOKI_SCRIPT_DIR/provider-offer.sh" ]; then
|
|
|
77
77
|
source "$_LOKI_SCRIPT_DIR/provider-offer.sh"
|
|
78
78
|
fi
|
|
79
79
|
|
|
80
|
+
# caveman ACTIVATION level for FREE-FORM (narration) claude subcalls in this CLI
|
|
81
|
+
# (migration phase exec, migration docs-gen, heal execution). Prints the level to
|
|
82
|
+
# pass as CAVEMAN_DEFAULT_MODE when activation is warranted, else nothing. autonomy/
|
|
83
|
+
# loki does NOT source claude-flags.sh globally, so this lazily sources it on demand
|
|
84
|
+
# (same pattern as the ultrareview/workflows gates and autonomy/lib/voter-agents.sh).
|
|
85
|
+
# The gate (provider==claude, claude on PATH, opt-in, no legacy-completion-match,
|
|
86
|
+
# no-raise of a user's lower global level) lives entirely in loki_caveman_activate_env;
|
|
87
|
+
# this is just the lazy-source wrapper. Degrades to empty (bare invocation, byte-
|
|
88
|
+
# identical to pre-caveman) when the lib is missing or the helper is unavailable.
|
|
89
|
+
# NEVER use this for captured-output / parsed subcalls -- those HARD-SUPPRESS with
|
|
90
|
+
# CAVEMAN_DEFAULT_MODE=off (see _docs_invoke_provider and run.sh:3286).
|
|
91
|
+
_loki_caveman_narration_level() {
|
|
92
|
+
if ! declare -F loki_caveman_activate_env >/dev/null 2>&1; then
|
|
93
|
+
local _cm_lib=""
|
|
94
|
+
if [ -f "${_LOKI_SCRIPT_DIR}/lib/claude-flags.sh" ]; then
|
|
95
|
+
_cm_lib="${_LOKI_SCRIPT_DIR}/lib/claude-flags.sh"
|
|
96
|
+
elif [ -f "$(dirname "$0")/lib/claude-flags.sh" ]; then
|
|
97
|
+
_cm_lib="$(dirname "$0")/lib/claude-flags.sh"
|
|
98
|
+
fi
|
|
99
|
+
if [ -n "$_cm_lib" ]; then
|
|
100
|
+
# shellcheck source=autonomy/lib/claude-flags.sh
|
|
101
|
+
. "$_cm_lib" 2>/dev/null || true
|
|
102
|
+
fi
|
|
103
|
+
fi
|
|
104
|
+
if declare -F loki_caveman_activate_env >/dev/null 2>&1; then
|
|
105
|
+
loki_caveman_activate_env
|
|
106
|
+
fi
|
|
107
|
+
}
|
|
108
|
+
|
|
80
109
|
# Quickstart guided interview (v7.29.0): provides cmd_quickstart and its
|
|
81
110
|
# deterministic offline template matcher. Functions-only file (no top-level
|
|
82
111
|
# command), sourced here so the dispatch case can call cmd_quickstart. Composes
|
|
@@ -11499,9 +11528,19 @@ Tasks:
|
|
|
11499
11528
|
local phase_exit=0
|
|
11500
11529
|
if [ -n "$phase_prompt" ]; then
|
|
11501
11530
|
local provider_name="${LOKI_PROVIDER:-claude}"
|
|
11531
|
+
# caveman ACTIVATION (free-form narration): the phase exec writes its
|
|
11532
|
+
# deliverables via tools; stdout is piped to python and only DISPLAYED,
|
|
11533
|
+
# never captured/parsed for a verdict. So compress the narration tokens
|
|
11534
|
+
# at the configured level when warranted (mirrors run.sh:13167 main loop).
|
|
11535
|
+
# An EMPTY CAVEMAN_DEFAULT_MODE is NOT inert (caveman falls back to the
|
|
11536
|
+
# user global default), so when activation is not warranted we must NOT
|
|
11537
|
+
# set the var at all (bare branch), keeping the call byte-identical to
|
|
11538
|
+
# pre-caveman. No-op when caveman is absent.
|
|
11539
|
+
local _loki_mig_cm=""
|
|
11540
|
+
_loki_mig_cm="$(_loki_caveman_narration_level)"
|
|
11502
11541
|
case "$provider_name" in
|
|
11503
11542
|
claude)
|
|
11504
|
-
{ (cd "$codebase_path" && claude --dangerously-skip-permissions -p "$phase_prompt" --output-format stream-json --verbose 2>&1) | \
|
|
11543
|
+
{ (cd "$codebase_path" && if [ -n "$_loki_mig_cm" ]; then CAVEMAN_DEFAULT_MODE="$_loki_mig_cm" claude --dangerously-skip-permissions -p "$phase_prompt" --output-format stream-json --verbose 2>&1; else claude --dangerously-skip-permissions -p "$phase_prompt" --output-format stream-json --verbose 2>&1; fi) | \
|
|
11505
11544
|
while IFS= read -r line; do
|
|
11506
11545
|
# Extract text from stream-json
|
|
11507
11546
|
if echo "$line" | python3 -c "
|
|
@@ -11667,9 +11706,15 @@ IMPORTANT RULES:
|
|
|
11667
11706
|
|
|
11668
11707
|
local doc_exit=0
|
|
11669
11708
|
local provider_name="${LOKI_PROVIDER:-claude}"
|
|
11709
|
+
# caveman ACTIVATION (free-form narration): docs-gen writes the docs via
|
|
11710
|
+
# tools; stdout is piped to python and only DISPLAYED, never captured/parsed.
|
|
11711
|
+
# Compress narration at the configured level when warranted; empty -> bare
|
|
11712
|
+
# branch, byte-identical to pre-caveman (empty mode is NOT inert). No-op absent.
|
|
11713
|
+
local _loki_doc_cm=""
|
|
11714
|
+
_loki_doc_cm="$(_loki_caveman_narration_level)"
|
|
11670
11715
|
case "$provider_name" in
|
|
11671
11716
|
claude)
|
|
11672
|
-
{ (cd "$codebase_path" && claude --dangerously-skip-permissions -p "$doc_prompt" --output-format stream-json --verbose 2>&1) | \
|
|
11717
|
+
{ (cd "$codebase_path" && if [ -n "$_loki_doc_cm" ]; then CAVEMAN_DEFAULT_MODE="$_loki_doc_cm" claude --dangerously-skip-permissions -p "$doc_prompt" --output-format stream-json --verbose 2>&1; else claude --dangerously-skip-permissions -p "$doc_prompt" --output-format stream-json --verbose 2>&1; fi) | \
|
|
11673
11718
|
while IFS= read -r line; do
|
|
11674
11719
|
if echo "$line" | python3 -c "
|
|
11675
11720
|
import sys, json
|
|
@@ -12290,10 +12335,17 @@ Begin healing now. Follow the RARV cycle for each action."
|
|
|
12290
12335
|
|
|
12291
12336
|
# Execute with the appropriate provider
|
|
12292
12337
|
local heal_exit=0
|
|
12338
|
+
# caveman ACTIVATION (free-form narration): heal execution writes its artifacts
|
|
12339
|
+
# (.loki/healing/*, characterization tests) via tools; stdout is piped to python
|
|
12340
|
+
# and only DISPLAYED, never captured/parsed for a verdict. Compress narration at
|
|
12341
|
+
# the configured level when warranted; empty -> bare branch, byte-identical to
|
|
12342
|
+
# pre-caveman (empty mode is NOT inert). No-op when caveman is absent.
|
|
12343
|
+
local _loki_heal_cm=""
|
|
12344
|
+
_loki_heal_cm="$(_loki_caveman_narration_level)"
|
|
12293
12345
|
case "$provider" in
|
|
12294
12346
|
claude)
|
|
12295
12347
|
local run_args=(--dangerously-skip-permissions -p "$heal_prompt" --output-format stream-json --verbose)
|
|
12296
|
-
(cd "$codebase_path" && claude "${run_args[@]}" 2>&1) | \
|
|
12348
|
+
(cd "$codebase_path" && if [ -n "$_loki_heal_cm" ]; then CAVEMAN_DEFAULT_MODE="$_loki_heal_cm" claude "${run_args[@]}" 2>&1; else claude "${run_args[@]}" 2>&1; fi) | \
|
|
12297
12349
|
while IFS= read -r line; do
|
|
12298
12350
|
if echo "$line" | python3 -c "
|
|
12299
12351
|
import sys, json
|
|
@@ -13719,8 +13771,11 @@ tokens_per_tier = {
|
|
|
13719
13771
|
# row from a stale \$0.25/\$1.25 (Haiku 3.5) to the published Claude Haiku 4.5
|
|
13720
13772
|
# price of \$1/\$5 (anthropic.com/news/claude-haiku-4-5), so all four rows now
|
|
13721
13773
|
# match the three canonical model-keyed tables (run.sh pricing.json, run.sh
|
|
13722
|
-
# check_budget_limit, dashboard _DEFAULT_PRICING)
|
|
13723
|
-
#
|
|
13774
|
+
# check_budget_limit, dashboard _DEFAULT_PRICING). The Fable row (\$10/\$50) is
|
|
13775
|
+
# kept as the reference price, but since v7.39.1 fable is unavailable at the API
|
|
13776
|
+
# and a fable pin RESOLVES to Opus everywhere -- so a fable-pinned run both
|
|
13777
|
+
# dispatches AND quotes Opus (\$5/\$25), matching the runner. The Fable price row
|
|
13778
|
+
# is only reachable as an explicit advisory reference, not via a session pin.
|
|
13724
13779
|
pricing = {
|
|
13725
13780
|
'Fable': {'input': 10.00, 'output': 50.00},
|
|
13726
13781
|
'Opus': {'input': 5.00, 'output': 25.00},
|
|
@@ -23691,7 +23746,16 @@ _docs_invoke_provider() {
|
|
|
23691
23746
|
|
|
23692
23747
|
case "$provider" in
|
|
23693
23748
|
claude)
|
|
23694
|
-
|
|
23749
|
+
# caveman HARD-SUPPRESS (captured deliverable): the claude stdout IS
|
|
23750
|
+
# the generated doc -- it is captured here and written verbatim to a
|
|
23751
|
+
# markdown file by both callers (loki:23869, loki:24239). A globally
|
|
23752
|
+
# active caveman would compress this stdout, corrupting the generated
|
|
23753
|
+
# CLAUDE.md / DECISIONS.md / README into caveman-speak. So disable
|
|
23754
|
+
# caveman unconditionally on this site (mirrors run.sh:3286, the
|
|
23755
|
+
# structurally identical captured `claude -p` resolution subcall).
|
|
23756
|
+
# env-prefix form because of the optional $t_prefix timeout wrapper.
|
|
23757
|
+
# No-op when caveman is absent.
|
|
23758
|
+
result=$($t_prefix env CAVEMAN_DEFAULT_MODE=off claude -p "$prompt" 2>/dev/null) || exit_code=$?
|
|
23695
23759
|
;;
|
|
23696
23760
|
codex)
|
|
23697
23761
|
result=$($t_prefix codex exec --full-auto "$prompt" 2>/dev/null) || exit_code=$?
|