loki-mode 7.126.0 → 7.127.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 +2 -2
- package/VERSION +1 -1
- package/autonomy/lib/cockpit-render.sh +171 -4
- package/autonomy/loki +222 -14
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +2 -2
- package/loki-ts/dist/cockpit.js +11 -8
- 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/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, 8 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.
|
|
6
|
+
# Loki Mode v7.127.0
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -408,4 +408,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
408
408
|
|
|
409
409
|
---
|
|
410
410
|
|
|
411
|
-
**v7.
|
|
411
|
+
**v7.127.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.
|
|
1
|
+
7.127.0
|
|
@@ -70,14 +70,33 @@ def project_state(path):
|
|
|
70
70
|
gs = str(g.get("status", "")).lower()
|
|
71
71
|
status = {"pass": "pass", "fail": "fail", "failed": "fail",
|
|
72
72
|
"skipped": "skip", "skip": "skip"}.get(gs, "pending")
|
|
73
|
-
|
|
73
|
+
# runner/scanner names the tool; summary is the one-line evidence.
|
|
74
|
+
runner = g.get("runner") or g.get("scanner") or ""
|
|
75
|
+
gates.append({
|
|
76
|
+
"name": g.get("gate", "gate"),
|
|
77
|
+
"status": status,
|
|
78
|
+
"runner": str(runner)[:24],
|
|
79
|
+
"evidence": str(g.get("summary", ""))[:80],
|
|
80
|
+
})
|
|
74
81
|
|
|
75
|
-
#
|
|
82
|
+
# Default model tier per reviewer (matches the standing council roster).
|
|
83
|
+
REVIEWER_TIER = {
|
|
84
|
+
"architecture-strategist": "opus",
|
|
85
|
+
"maintainer-mergeability": "opus",
|
|
86
|
+
"security-sentinel": "opus",
|
|
87
|
+
"test-coverage-auditor": "sonnet",
|
|
88
|
+
"performance-oracle": "opus",
|
|
89
|
+
"dependency-analyst": "sonnet",
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# Council votes: .loki/council/*.json, each with a vote-ish field. Skips the
|
|
93
|
+
# aggregate state.json (no per-reviewer vote) so only real reviewer entries
|
|
94
|
+
# populate the strip.
|
|
76
95
|
council = []
|
|
77
96
|
cdir = os.path.join(loki, "council")
|
|
78
97
|
try:
|
|
79
98
|
for fn in sorted(os.listdir(cdir)):
|
|
80
|
-
if not fn.endswith(".json"):
|
|
99
|
+
if not fn.endswith(".json") or fn == "state.json":
|
|
81
100
|
continue
|
|
82
101
|
cj = read_json(os.path.join(cdir, fn))
|
|
83
102
|
raw = str(cj.get("vote") or cj.get("verdict") or "").lower()
|
|
@@ -89,7 +108,9 @@ def project_state(path):
|
|
|
89
108
|
elif "concern" in raw:
|
|
90
109
|
vote = "concern"
|
|
91
110
|
reviewer = cj.get("reviewer") or cj.get("name") or os.path.splitext(fn)[0]
|
|
92
|
-
|
|
111
|
+
reviewer = str(reviewer)
|
|
112
|
+
tier = cj.get("tier") or cj.get("model_tier") or REVIEWER_TIER.get(reviewer, "")
|
|
113
|
+
council.append({"reviewer": reviewer[:24], "vote": vote, "tier": str(tier)[:8]})
|
|
93
114
|
except Exception:
|
|
94
115
|
pass
|
|
95
116
|
|
|
@@ -117,6 +138,7 @@ except Exception:
|
|
|
117
138
|
for r in runs:
|
|
118
139
|
fleet.append({
|
|
119
140
|
"name": r.get("name") or os.path.basename(r.get("path", "") or "project"),
|
|
141
|
+
"path": r.get("path", "") or "",
|
|
120
142
|
"phase": r.get("phase", "") or "",
|
|
121
143
|
"iteration": r.get("iteration", 0) or 0,
|
|
122
144
|
"status": r.get("status", "") or "",
|
|
@@ -189,3 +211,148 @@ cockpit_render() {
|
|
|
189
211
|
# Gather -> render. Preserve the TS exit code (0 image / 3 fallback).
|
|
190
212
|
cockpit_gather_state "$skill_dir" "$focus_repo" | bun "$entry" "${args[@]}"
|
|
191
213
|
}
|
|
214
|
+
|
|
215
|
+
#===============================================================================
|
|
216
|
+
# E3: keyboard interactivity for `loki cockpit --follow`.
|
|
217
|
+
# These live here (sourced by cmd_cockpit) so they can be extracted + unit
|
|
218
|
+
# tested without pulling in the whole autonomy/loki CLI.
|
|
219
|
+
#===============================================================================
|
|
220
|
+
|
|
221
|
+
# Pure key -> action mapping. Takes a normalized key token, echoes one action
|
|
222
|
+
# word. Side-effect-free so it is trivially unit-testable.
|
|
223
|
+
# TAB / RIGHT / DOWN -> next (cycle focus forward across the fleet)
|
|
224
|
+
# LEFT / UP -> prev (cycle focus backward)
|
|
225
|
+
# e -> explain (print verify evidence for the focused run)
|
|
226
|
+
# s -> steer (print how to steer the focused run)
|
|
227
|
+
# q / ESC / Ctrl-C -> quit
|
|
228
|
+
# anything else -> noop
|
|
229
|
+
cockpit_handle_key() {
|
|
230
|
+
case "$1" in
|
|
231
|
+
TAB|RIGHT|DOWN) echo "next" ;;
|
|
232
|
+
LEFT|UP) echo "prev" ;;
|
|
233
|
+
e|E) echo "explain" ;;
|
|
234
|
+
s|S) echo "steer" ;;
|
|
235
|
+
q|Q|ESC|$'\x03') echo "quit" ;;
|
|
236
|
+
*) echo "noop" ;;
|
|
237
|
+
esac
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
# Read one keypress (non-blocking, no redraw stall) and normalize it to a token
|
|
241
|
+
# cockpit_handle_key understands. Echoes the token, or nothing if no key was
|
|
242
|
+
# pressed within the timeout. Arrow keys arrive as ESC [ A/B/C/D; a lone ESC (no
|
|
243
|
+
# following bytes) normalizes to ESC (quit). Only usable on a TTY.
|
|
244
|
+
# $1 = timeout seconds (fractional ok; feeds `read -t`)
|
|
245
|
+
cockpit_read_key() {
|
|
246
|
+
local timeout="${1:-2}"
|
|
247
|
+
local k rest
|
|
248
|
+
IFS= read -rsn1 -t "$timeout" k 2>/dev/null || return 1
|
|
249
|
+
case "$k" in
|
|
250
|
+
$'\t') echo "TAB"; return 0 ;;
|
|
251
|
+
$'\x03') echo "ESC"; return 0 ;; # Ctrl-C byte if trapped as data
|
|
252
|
+
$'\x1b')
|
|
253
|
+
# Possible escape sequence: grab up to 2 more bytes without blocking.
|
|
254
|
+
IFS= read -rsn2 -t 0.01 rest 2>/dev/null || rest=""
|
|
255
|
+
case "$rest" in
|
|
256
|
+
'[A') echo "UP" ;;
|
|
257
|
+
'[B') echo "DOWN" ;;
|
|
258
|
+
'[C') echo "RIGHT" ;;
|
|
259
|
+
'[D') echo "LEFT" ;;
|
|
260
|
+
*) echo "ESC" ;;
|
|
261
|
+
esac
|
|
262
|
+
return 0 ;;
|
|
263
|
+
'') return 1 ;; # timed out, no key
|
|
264
|
+
*) echo "$k"; return 0 ;;
|
|
265
|
+
esac
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
# Echo the fleet repo paths (one per line) so the follow loop can cycle focus.
|
|
269
|
+
# Empty output = no fleet (cycling is a no-op). Best-effort, never errors.
|
|
270
|
+
cockpit_fleet_paths() {
|
|
271
|
+
local skill_dir="$1"
|
|
272
|
+
local state_json
|
|
273
|
+
state_json="$(cockpit_gather_state "$skill_dir" "" 2>/dev/null || true)"
|
|
274
|
+
[ -z "$state_json" ] && return 0
|
|
275
|
+
command -v python3 >/dev/null 2>&1 || return 0
|
|
276
|
+
LOKI_CK_STATE="$state_json" python3 - <<'PYPATHS' 2>/dev/null || true
|
|
277
|
+
import json, os
|
|
278
|
+
s = json.loads(os.environ.get("LOKI_CK_STATE") or "{}")
|
|
279
|
+
for r in s.get("fleet") or []:
|
|
280
|
+
p = r.get("path") or ""
|
|
281
|
+
if p:
|
|
282
|
+
print(p)
|
|
283
|
+
PYPATHS
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
# Given the current focus path, a direction (next|prev), and a newline list of
|
|
287
|
+
# fleet paths, echo the path to focus after cycling. Wraps around. If focus is
|
|
288
|
+
# not in the list, next -> first entry, prev -> last entry. Empty list echoes the
|
|
289
|
+
# current focus unchanged. Pure, unit-testable.
|
|
290
|
+
# $1 = current focus path (may be empty) $2 = next|prev $3 = newline paths
|
|
291
|
+
cockpit_cycle_focus() {
|
|
292
|
+
local cur="$1" dir="$2" paths="$3"
|
|
293
|
+
[ -z "$paths" ] && { echo "$cur"; return 0; }
|
|
294
|
+
local -a arr=()
|
|
295
|
+
local line
|
|
296
|
+
while IFS= read -r line; do
|
|
297
|
+
[ -n "$line" ] && arr+=("$line")
|
|
298
|
+
done <<< "$paths"
|
|
299
|
+
local n="${#arr[@]}"
|
|
300
|
+
[ "$n" -eq 0 ] && { echo "$cur"; return 0; }
|
|
301
|
+
# Find current index.
|
|
302
|
+
local idx=-1 i
|
|
303
|
+
for i in "${!arr[@]}"; do
|
|
304
|
+
if [ "${arr[$i]}" = "$cur" ]; then idx="$i"; break; fi
|
|
305
|
+
done
|
|
306
|
+
local target
|
|
307
|
+
if [ "$idx" -lt 0 ]; then
|
|
308
|
+
# Not in list: next -> first, prev -> last.
|
|
309
|
+
if [ "$dir" = "prev" ]; then target=$((n - 1)); else target=0; fi
|
|
310
|
+
elif [ "$dir" = "prev" ]; then
|
|
311
|
+
target=$(( (idx - 1 + n) % n ))
|
|
312
|
+
else
|
|
313
|
+
target=$(( (idx + 1) % n ))
|
|
314
|
+
fi
|
|
315
|
+
echo "${arr[$target]}"
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
# Print the verify evidence ("explain") for the focused run. Reads the run's
|
|
319
|
+
# .loki/verify/evidence.json directly. Never fabricates: says so if absent.
|
|
320
|
+
cockpit_explain_focus() {
|
|
321
|
+
local focus_repo="$1"
|
|
322
|
+
local repo="${focus_repo:-$(pwd)}"
|
|
323
|
+
local ev="$repo/.loki/verify/evidence.json"
|
|
324
|
+
printf '\n'
|
|
325
|
+
printf 'verify --explain (%s)\n' "$repo"
|
|
326
|
+
if [ ! -f "$ev" ] || ! command -v python3 >/dev/null 2>&1; then
|
|
327
|
+
echo " (no evidence.json for this run yet)"
|
|
328
|
+
return 0
|
|
329
|
+
fi
|
|
330
|
+
LOKI_CK_EV="$ev" python3 - <<'PYEV' 2>/dev/null || echo " (could not read evidence)"
|
|
331
|
+
import json, os
|
|
332
|
+
try:
|
|
333
|
+
ev = json.load(open(os.environ["LOKI_CK_EV"]))
|
|
334
|
+
except Exception:
|
|
335
|
+
print(" (could not read evidence)"); raise SystemExit(0)
|
|
336
|
+
print(f" Verdict {str(ev.get('verdict','?')).upper()}")
|
|
337
|
+
checks = ev.get("deterministic_gates") or ev.get("checks") or ev.get("gates") or []
|
|
338
|
+
for c in checks[:12]:
|
|
339
|
+
name = c.get("gate") or c.get("name") or c.get("id") or "?"
|
|
340
|
+
status = c.get("status") or c.get("result") or "?"
|
|
341
|
+
print(f" {name:<28} {status}")
|
|
342
|
+
reason = ev.get("reason") or ev.get("summary")
|
|
343
|
+
if reason:
|
|
344
|
+
print(f" Reason {reason}")
|
|
345
|
+
PYEV
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
# Print a steering hint for the focused run. Steering is opt-in prompt injection;
|
|
349
|
+
# this only tells the operator how, it does not inject anything.
|
|
350
|
+
cockpit_steer_hint() {
|
|
351
|
+
local focus_repo="$1"
|
|
352
|
+
local repo="${focus_repo:-$(pwd)}"
|
|
353
|
+
printf '\n'
|
|
354
|
+
echo "Steer this run"
|
|
355
|
+
echo " Drop a note the next iteration will read:"
|
|
356
|
+
echo " echo 'your guidance' > $repo/.loki/steering.md"
|
|
357
|
+
echo " Or open the dashboard chat: loki dashboard"
|
|
358
|
+
}
|
package/autonomy/loki
CHANGED
|
@@ -1230,6 +1230,31 @@ _loki_brand_banner() {
|
|
|
1230
1230
|
fi
|
|
1231
1231
|
}
|
|
1232
1232
|
|
|
1233
|
+
# E4: a 3-line "what Loki will do" preview from REAL signals only. Line 1 ties
|
|
1234
|
+
# the detected tier to its phase count (simple=3, standard=6, complex=8 -- the
|
|
1235
|
+
# same mapping run.sh uses; auto/unknown stays honest as "auto-detecting"). Line
|
|
1236
|
+
# 2 names the spec, or admits "exploring the codebase" when there is none. Line 3
|
|
1237
|
+
# is the fixed RARV-C closure promise. Never fabricates. Prints 3 lines to stdout.
|
|
1238
|
+
# Args: $1=spec label $2=tier
|
|
1239
|
+
_loki_plan_preview() {
|
|
1240
|
+
local spec="$1" tier="$2"
|
|
1241
|
+
local phases shape
|
|
1242
|
+
case "$(printf '%s' "$tier" | tr '[:upper:]' '[:lower:]')" in
|
|
1243
|
+
simple) phases="3 phases"; shape="a focused build" ;;
|
|
1244
|
+
standard) phases="6 phases"; shape="a full SDLC pass" ;;
|
|
1245
|
+
complex) phases="8 phases"; shape="a deep multi-phase build" ;;
|
|
1246
|
+
*) phases="phase count auto-detecting"; shape="scope from your spec" ;;
|
|
1247
|
+
esac
|
|
1248
|
+
printf ' plan %s -- %s\n' "$phases" "$shape"
|
|
1249
|
+
case "$spec" in
|
|
1250
|
+
""|*"no PRD"*|*"codebase"*)
|
|
1251
|
+
printf ' from exploring the codebase (no spec file)\n' ;;
|
|
1252
|
+
*)
|
|
1253
|
+
printf ' from %s\n' "$spec" ;;
|
|
1254
|
+
esac
|
|
1255
|
+
printf ' loop Reason - Act - Reflect - Verify - Close, until verified\n'
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1233
1258
|
# Print the rich handoff card and resolve the user's choice. Echoes exactly one
|
|
1234
1259
|
# decision word on stdout:
|
|
1235
1260
|
# launch-bg detach only
|
|
@@ -1284,8 +1309,16 @@ _loki_start_handoff() {
|
|
|
1284
1309
|
echo -e " ${DIM}log${NC} ${LOKI_DIR:-.loki}/logs/background-*.log"
|
|
1285
1310
|
echo -e " ${DIM}dashboard${NC} ${dash_url}"
|
|
1286
1311
|
echo ""
|
|
1312
|
+
# E4 plan preview: 3 lines of real intent before backgrounding.
|
|
1313
|
+
_loki_plan_preview "$spec" "$tier"
|
|
1314
|
+
echo ""
|
|
1287
1315
|
echo -e " ${BOLD}Both${NC}[default] ${BOLD}D${NC}ashboard ${BOLD}W${NC}atch De${BOLD}t${NC}ach ${BOLD}S${NC}top"
|
|
1288
1316
|
echo -e " ${DIM}opening your cockpit + dashboard in 8s -- press a key to choose${NC}"
|
|
1317
|
+
# First-run delight: only when this project has never chosen a view, teach
|
|
1318
|
+
# the cockpit once. Subsequent runs (config file exists) stay lean.
|
|
1319
|
+
if [ ! -f "$cfg_file" ]; then
|
|
1320
|
+
echo -e " ${DIM}first build here -- 'loki cockpit' reopens this live view any time${NC}"
|
|
1321
|
+
fi
|
|
1289
1322
|
} >&2
|
|
1290
1323
|
|
|
1291
1324
|
local reply=""
|
|
@@ -8746,7 +8779,7 @@ cmd_watch() {
|
|
|
8746
8779
|
}
|
|
8747
8780
|
|
|
8748
8781
|
#===============================================================================
|
|
8749
|
-
# loki cockpit - Multi-repo live status as a terminal inline image (v7.
|
|
8782
|
+
# loki cockpit - Multi-repo live status as a terminal inline image (v7.127.0)
|
|
8750
8783
|
#
|
|
8751
8784
|
# Renders the Autonomi cockpit frame directly in supported terminals (iTerm2 /
|
|
8752
8785
|
# WezTerm / ghostty via the iTerm2 protocol; Kitty via its graphics protocol).
|
|
@@ -8782,10 +8815,13 @@ if council:
|
|
|
8782
8815
|
print(" Council " + " ".join(f"{c.get('reviewer','?')}:{c.get('vote','?')}" for c in council[:6]))
|
|
8783
8816
|
fleet = s.get('fleet') or []
|
|
8784
8817
|
if fleet:
|
|
8818
|
+
cap = 8 # cap so a huge registry (e.g. 171 runs) never floods the summary
|
|
8785
8819
|
print(f" Fleet ({len(fleet)}):")
|
|
8786
|
-
for r in fleet[:
|
|
8820
|
+
for r in fleet[:cap]:
|
|
8787
8821
|
mark = "*" if r.get("running") else " "
|
|
8788
8822
|
print(f" {mark} {r.get('name','?'):<28} iter {r.get('iteration',0)} {r.get('phase') or r.get('status') or ''}")
|
|
8823
|
+
if len(fleet) > cap:
|
|
8824
|
+
print(f" + {len(fleet) - cap} more")
|
|
8789
8825
|
PYTXT
|
|
8790
8826
|
}
|
|
8791
8827
|
|
|
@@ -8800,7 +8836,7 @@ cmd_cockpit() {
|
|
|
8800
8836
|
while [[ $# -gt 0 ]]; do
|
|
8801
8837
|
case "$1" in
|
|
8802
8838
|
--help|-h)
|
|
8803
|
-
echo -e "${BOLD}loki cockpit${NC} - Live multi-repo status as a terminal inline image (v7.
|
|
8839
|
+
echo -e "${BOLD}loki cockpit${NC} - Live multi-repo status as a terminal inline image (v7.127.0)"
|
|
8804
8840
|
echo ""
|
|
8805
8841
|
echo "Usage: loki cockpit [options]"
|
|
8806
8842
|
echo ""
|
|
@@ -8887,14 +8923,44 @@ cmd_cockpit() {
|
|
|
8887
8923
|
return 3
|
|
8888
8924
|
}
|
|
8889
8925
|
|
|
8926
|
+
# E3 footer hints. _cockpit_hint is the persistent key legend under a frame;
|
|
8927
|
+
# _cockpit_hint_key is shown after an explain/steer panel to say how to return.
|
|
8928
|
+
_cockpit_hint() {
|
|
8929
|
+
printf '\n'
|
|
8930
|
+
echo -e " ${DIM}Tab/arrows focus e explain s steer q quit${NC}"
|
|
8931
|
+
}
|
|
8932
|
+
_cockpit_hint_key() {
|
|
8933
|
+
printf '\n'
|
|
8934
|
+
echo -e " ${DIM}press any key to return to the live view${NC}"
|
|
8935
|
+
}
|
|
8936
|
+
|
|
8937
|
+
# Max mtime (whole seconds) across the focus run's state files -- the honest
|
|
8938
|
+
# "something changed" signal for event-driven redraw. Prints empty when stat
|
|
8939
|
+
# is unavailable so the caller falls back to a blind interval. Both BSD stat
|
|
8940
|
+
# (-f %m, macOS) and GNU stat (-c %Y, Linux) are probed; nothing else is
|
|
8941
|
+
# installed. ponytail: whole-second granularity is enough for a status frame.
|
|
8942
|
+
_cockpit_mtime() {
|
|
8943
|
+
local repo="$1" loki base m best=""
|
|
8944
|
+
[ -n "$repo" ] || repo="$(pwd)"
|
|
8945
|
+
loki="$repo/.loki"
|
|
8946
|
+
for base in autonomy-state.json events.jsonl verify/evidence.json council; do
|
|
8947
|
+
local p="$loki/$base"
|
|
8948
|
+
[ -e "$p" ] || continue
|
|
8949
|
+
m="$(stat -f '%m' "$p" 2>/dev/null || stat -c '%Y' "$p" 2>/dev/null || true)"
|
|
8950
|
+
[ -n "$m" ] || continue
|
|
8951
|
+
if [ -z "$best" ] || { [ "$m" -gt "$best" ] 2>/dev/null; }; then best="$m"; fi
|
|
8952
|
+
done
|
|
8953
|
+
printf '%s' "$best"
|
|
8954
|
+
}
|
|
8955
|
+
|
|
8890
8956
|
if [ "$run_once" = "1" ]; then
|
|
8891
8957
|
_cockpit_frame
|
|
8892
8958
|
return 0
|
|
8893
8959
|
fi
|
|
8894
8960
|
|
|
8895
|
-
# --follow (default): re-render until interrupted.
|
|
8896
|
-
#
|
|
8897
|
-
#
|
|
8961
|
+
# --follow (default): re-render on state change until interrupted. Render the
|
|
8962
|
+
# FIRST frame BEFORE the alt-screen so a fallback terminal keeps its text
|
|
8963
|
+
# summary in the scrollback (looping text is noise -> we stop there).
|
|
8898
8964
|
local frame_rc=0
|
|
8899
8965
|
_cockpit_frame || frame_rc=$?
|
|
8900
8966
|
if [ "$frame_rc" != "0" ]; then
|
|
@@ -8902,13 +8968,107 @@ cmd_cockpit() {
|
|
|
8902
8968
|
# were already printed once. Point the user at --follow-capable UIs.
|
|
8903
8969
|
return 0
|
|
8904
8970
|
fi
|
|
8905
|
-
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
8971
|
+
|
|
8972
|
+
# Image path: enter the alternate screen buffer so redraws never scroll the
|
|
8973
|
+
# user's scrollback, and restore the terminal cleanly on any exit. The traps
|
|
8974
|
+
# cover Ctrl-C (INT), kill (TERM) and normal return (EXIT). The loop runs in a
|
|
8975
|
+
# subshell so the EXIT trap is scoped to the follow loop and cannot leak into
|
|
8976
|
+
# the caller's shell.
|
|
8977
|
+
#
|
|
8978
|
+
# E3 keyboard interactivity: on a TTY we put the terminal in raw single-key
|
|
8979
|
+
# mode so Tab/arrows/e/s/q are read without Enter, and the wait between frames
|
|
8980
|
+
# becomes a key poll (cockpit_read_key) instead of a blind sleep -- so a key
|
|
8981
|
+
# acts instantly instead of after the cadence. Restore stty + cursor on any
|
|
8982
|
+
# exit. If stdin is not a TTY, interactivity is skipped and the loop still
|
|
8983
|
+
# renders + refreshes exactly as before.
|
|
8984
|
+
local interactive=0
|
|
8985
|
+
if [ -t 0 ] && [ -t 1 ] && command -v stty >/dev/null 2>&1; then
|
|
8986
|
+
interactive=1
|
|
8987
|
+
fi
|
|
8988
|
+
(
|
|
8989
|
+
local _saved_stty=""
|
|
8990
|
+
[ "$interactive" = "1" ] && _saved_stty="$(stty -g 2>/dev/null || true)"
|
|
8991
|
+
_cockpit_restore() {
|
|
8992
|
+
[ -n "$_saved_stty" ] && stty "$_saved_stty" 2>/dev/null || true
|
|
8993
|
+
printf '\033[?25h\033[?1049l' # show cursor, leave alt-screen
|
|
8994
|
+
}
|
|
8995
|
+
trap '_cockpit_restore; exit 0' INT TERM
|
|
8996
|
+
trap '_cockpit_restore' EXIT
|
|
8997
|
+
[ "$interactive" = "1" ] && stty -echo -icanon min 0 time 0 2>/dev/null || true
|
|
8998
|
+
printf '\033[?1049h\033[?25l' # enter alt-screen, hide cursor
|
|
8999
|
+
printf '\033[H\033[2J' # home + clear the alt buffer once
|
|
9000
|
+
_cockpit_frame
|
|
9001
|
+
[ "$interactive" = "1" ] && _cockpit_hint
|
|
9002
|
+
|
|
9003
|
+
# Cadence bounds (seconds): poll every min_cadence for a state change
|
|
9004
|
+
# (anti-thrash upper bound on redraw rate); force a refresh at least every
|
|
9005
|
+
# max_idle so a stalled run still shows a live freshness clock. --interval
|
|
9006
|
+
# sets max_idle (and the blind fallback period when stat is missing).
|
|
9007
|
+
local min_cadence=1
|
|
9008
|
+
local max_idle="$interval"; [ "$max_idle" -ge 2 ] 2>/dev/null || max_idle=2
|
|
9009
|
+
local last_mtime; last_mtime="$(_cockpit_mtime "$focus_repo")"
|
|
9010
|
+
local idle=0
|
|
9011
|
+
while :; do
|
|
9012
|
+
# Wait one cadence step -- via a key poll when interactive (returns
|
|
9013
|
+
# early on a keypress), otherwise a plain sleep. On any wait failure
|
|
9014
|
+
# (interrupted) break out and let the trap restore the terminal.
|
|
9015
|
+
local key=""
|
|
9016
|
+
if [ "$interactive" = "1" ]; then
|
|
9017
|
+
key="$(cockpit_read_key "$min_cadence" 2>/dev/null || true)"
|
|
9018
|
+
else
|
|
9019
|
+
sleep "$min_cadence" 2>/dev/null || break
|
|
9020
|
+
fi
|
|
9021
|
+
idle=$((idle + min_cadence))
|
|
9022
|
+
|
|
9023
|
+
# Dispatch a keypress (no-op if none / not interactive).
|
|
9024
|
+
if [ -n "$key" ]; then
|
|
9025
|
+
local action; action="$(cockpit_handle_key "$key")"
|
|
9026
|
+
case "$action" in
|
|
9027
|
+
quit) break ;;
|
|
9028
|
+
next|prev)
|
|
9029
|
+
local paths; paths="$(cockpit_fleet_paths "$SKILL_DIR")"
|
|
9030
|
+
focus_repo="$(cockpit_cycle_focus "$focus_repo" "$action" "$paths")"
|
|
9031
|
+
last_mtime="$(_cockpit_mtime "$focus_repo")"
|
|
9032
|
+
idle=0
|
|
9033
|
+
printf '\033[H\033[2J'
|
|
9034
|
+
_cockpit_frame || break
|
|
9035
|
+
_cockpit_hint
|
|
9036
|
+
continue ;;
|
|
9037
|
+
explain)
|
|
9038
|
+
printf '\033[H\033[2J'
|
|
9039
|
+
_cockpit_frame
|
|
9040
|
+
cockpit_explain_focus "$focus_repo"
|
|
9041
|
+
_cockpit_hint_key
|
|
9042
|
+
continue ;;
|
|
9043
|
+
steer)
|
|
9044
|
+
printf '\033[H\033[2J'
|
|
9045
|
+
_cockpit_frame
|
|
9046
|
+
cockpit_steer_hint "$focus_repo"
|
|
9047
|
+
_cockpit_hint_key
|
|
9048
|
+
continue ;;
|
|
9049
|
+
esac
|
|
9050
|
+
fi
|
|
9051
|
+
|
|
9052
|
+
local cur; cur="$(_cockpit_mtime "$focus_repo")"
|
|
9053
|
+
local changed=0
|
|
9054
|
+
if [ -n "$cur" ]; then
|
|
9055
|
+
# Event-driven: redraw when a watched file advanced, or after
|
|
9056
|
+
# max_idle seconds with no change (keeps the freshness clock live).
|
|
9057
|
+
if [ "$cur" != "$last_mtime" ] || [ "$idle" -ge "$max_idle" ]; then
|
|
9058
|
+
changed=1; last_mtime="$cur"
|
|
9059
|
+
fi
|
|
9060
|
+
else
|
|
9061
|
+
# stat unavailable -> blind interval fallback.
|
|
9062
|
+
[ "$idle" -ge "$max_idle" ] && changed=1
|
|
9063
|
+
fi
|
|
9064
|
+
if [ "$changed" = "1" ]; then
|
|
9065
|
+
idle=0
|
|
9066
|
+
printf '\033[H\033[2J' # home + clear alt buffer, then redraw
|
|
9067
|
+
_cockpit_frame || break
|
|
9068
|
+
[ "$interactive" = "1" ] && _cockpit_hint
|
|
9069
|
+
fi
|
|
9070
|
+
done
|
|
9071
|
+
)
|
|
8912
9072
|
return 0
|
|
8913
9073
|
}
|
|
8914
9074
|
|
|
@@ -10175,7 +10335,7 @@ cmd_doctor() {
|
|
|
10175
10335
|
echo " --json Output machine-readable JSON"
|
|
10176
10336
|
echo ""
|
|
10177
10337
|
echo "Checks: node, python3, jq, git, curl, bash version,"
|
|
10178
|
-
echo " claude/codex CLIs, and
|
|
10338
|
+
echo " claude/codex CLIs, disk space, and cockpit render capability."
|
|
10179
10339
|
return 0
|
|
10180
10340
|
;;
|
|
10181
10341
|
*)
|
|
@@ -10627,6 +10787,54 @@ except Exception:
|
|
|
10627
10787
|
fi
|
|
10628
10788
|
echo ""
|
|
10629
10789
|
|
|
10790
|
+
# Cockpit capability (E5): report what `loki cockpit` will actually do in
|
|
10791
|
+
# this terminal. Informational only (does NOT touch pass/warn/fail counts,
|
|
10792
|
+
# like Runtime route above), so the bun-parity matrix stays reconcilable.
|
|
10793
|
+
# The protocol + resvg + path come from a bun probe that reuses the real
|
|
10794
|
+
# detection code (loki-ts) so this report can never drift from behavior;
|
|
10795
|
+
# with no bun we honestly report the text-only path.
|
|
10796
|
+
echo -e "${CYAN}Cockpit:${NC}"
|
|
10797
|
+
# Truecolor: purely from COLORTERM, same signal the renderer relies on.
|
|
10798
|
+
case "${COLORTERM:-}" in
|
|
10799
|
+
truecolor|24bit)
|
|
10800
|
+
echo -e " ${GREEN}PASS${NC} Truecolor: yes (COLORTERM=${COLORTERM})" ;;
|
|
10801
|
+
*)
|
|
10802
|
+
echo -e " ${DIM} -- ${NC} Truecolor: not signalled (COLORTERM unset) -- colors may be approximate" ;;
|
|
10803
|
+
esac
|
|
10804
|
+
local _ck_ts_dir="${_LOKI_SCRIPT_DIR}/../loki-ts"
|
|
10805
|
+
local _ck_entry="$_ck_ts_dir/dist/cockpit.js"
|
|
10806
|
+
[ -f "$_ck_entry" ] || _ck_entry="$_ck_ts_dir/src/cockpit/cli.ts"
|
|
10807
|
+
if command -v bun >/dev/null 2>&1 && [ -f "$_ck_entry" ]; then
|
|
10808
|
+
echo -e " ${GREEN}PASS${NC} Bun present -- cockpit renderer available"
|
|
10809
|
+
local _ck_probe _ck_proto _ck_resvg _ck_path
|
|
10810
|
+
_ck_probe="$(bun "$_ck_entry" --probe 2>/dev/null || true)"
|
|
10811
|
+
_ck_proto="$(printf '%s\n' "$_ck_probe" | sed -n 's/^protocol=//p' | head -1)"
|
|
10812
|
+
_ck_resvg="$(printf '%s\n' "$_ck_probe" | sed -n 's/^resvg=//p' | head -1)"
|
|
10813
|
+
_ck_path="$(printf '%s\n' "$_ck_probe" | sed -n 's/^path=//p' | head -1)"
|
|
10814
|
+
[ -n "$_ck_proto" ] || _ck_proto="none"
|
|
10815
|
+
[ -n "$_ck_resvg" ] || _ck_resvg="no"
|
|
10816
|
+
[ -n "$_ck_path" ] || _ck_path="text+dashboard"
|
|
10817
|
+
if [ "$_ck_proto" = "none" ]; then
|
|
10818
|
+
echo -e " ${DIM} -- ${NC} Inline-image protocol: none (this terminal has no iTerm2/Kitty graphics)"
|
|
10819
|
+
else
|
|
10820
|
+
echo -e " ${GREEN}PASS${NC} Inline-image protocol: ${_ck_proto}"
|
|
10821
|
+
fi
|
|
10822
|
+
if [ "$_ck_resvg" = "yes" ]; then
|
|
10823
|
+
echo -e " ${GREEN}PASS${NC} @resvg/resvg-js: installed (SVG can rasterize to PNG)"
|
|
10824
|
+
else
|
|
10825
|
+
echo -e " ${DIM} -- ${NC} @resvg/resvg-js: not installed (optional; cockpit uses text+dashboard)"
|
|
10826
|
+
fi
|
|
10827
|
+
if [ "$_ck_path" = "image" ]; then
|
|
10828
|
+
echo -e " ${GREEN}PASS${NC} Render path: inline image ('loki cockpit' draws the frame in-terminal)"
|
|
10829
|
+
else
|
|
10830
|
+
echo -e " ${DIM} -- ${NC} Render path: text + browser dashboard ('loki cockpit' prints a summary; open 'loki dashboard')"
|
|
10831
|
+
fi
|
|
10832
|
+
else
|
|
10833
|
+
echo -e " ${YELLOW}WARN${NC} Bun not found -- 'loki cockpit' uses the text summary + browser dashboard (install bun for the inline-image frame)"
|
|
10834
|
+
echo -e " ${DIM} -- ${NC} Render path: text + browser dashboard"
|
|
10835
|
+
fi
|
|
10836
|
+
echo ""
|
|
10837
|
+
|
|
10630
10838
|
# Summary
|
|
10631
10839
|
echo -e "${BOLD}Summary:${NC} ${GREEN}$pass_count passed${NC}, ${RED}$fail_count failed${NC}, ${YELLOW}$warn_count warnings${NC}"
|
|
10632
10840
|
echo ""
|
package/dashboard/__init__.py
CHANGED
package/docs/INSTALLATION.md
CHANGED
|
@@ -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.
|
|
5
|
+
**Version:** v7.127.0
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -396,7 +396,7 @@ provider works inside the container. Provide auth with your Anthropic API key:
|
|
|
396
396
|
# Run Loki Mode in Docker (Claude provider, API-key auth)
|
|
397
397
|
docker run --rm -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
|
|
398
398
|
-v $(pwd):/workspace -w /workspace \
|
|
399
|
-
asklokesh/loki-mode:7.
|
|
399
|
+
asklokesh/loki-mode:7.127.0 start ./my-spec.md
|
|
400
400
|
```
|
|
401
401
|
|
|
402
402
|
##### docker compose + .env (no host install)
|
package/loki-ts/dist/cockpit.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var
|
|
2
|
+
var B=import.meta.require;function j(D){return String(D??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function n(D,L,Q){let N=Q/512;return`<g transform="translate(${D},${L}) scale(${N})">
|
|
3
3
|
<rect x="0" y="0" width="512" height="512" rx="118" fill="#553de9"/>
|
|
4
4
|
<path d="M152 405 L242 120" stroke="#FFFEFB" stroke-width="28" stroke-linecap="round" fill="none"/>
|
|
5
5
|
<path d="M360 405 L270 120" stroke="#FFFEFB" stroke-width="28" stroke-linecap="round" fill="none"/>
|
|
6
6
|
<path d="M242 120 Q256 86 270 120" stroke="#FFFEFB" stroke-width="28" stroke-linecap="round" fill="none"/>
|
|
7
7
|
<circle cx="256" cy="295" r="20" fill="#1FC5A8"/>
|
|
8
|
-
</g>`}function
|
|
9
|
-
<text x="${D}" y="${L+24}" font-family="'JetBrains Mono','SF Mono',ui-monospace,monospace" font-size="20" font-weight="600" fill="${
|
|
10
|
-
<rect x="0" y="0" width="900" height="
|
|
8
|
+
</g>`}function g(D){switch(D){case"verified":return"#1f8a52";case"failed":return"#b23a3a";case"working":case"pending":return"#9a6a12";default:return"#6b6675"}}function y(D){switch(D){case"pass":return"#1f8a52";case"fail":return"#b23a3a";case"pending":return"#9a6a12";default:return"#6b6675"}}function d(D){switch(D){case"approve":return"#1f8a52";case"reject":return"#b23a3a";case"concern":return"#9a6a12";default:return"#6b6675"}}function C(D,L,Q,N,J="#201515"){return`<text x="${D}" y="${L}" font-family="'Inter','Helvetica Neue',Arial,sans-serif" font-size="12" fill="#6b6675" letter-spacing="0.5">${j(Q.toUpperCase())}</text>
|
|
9
|
+
<text x="${D}" y="${L+24}" font-family="'JetBrains Mono','SF Mono',ui-monospace,monospace" font-size="20" font-weight="600" fill="${J}">${j(N)}</text>`}var c=["Reason","Act","Reflect","Verify"];function r(D){let L=(D||"").toLowerCase();if(/verif|complet|done|ship|deploy/.test(L))return 3;if(/reflect|review|council|critique|assess/.test(L))return 2;if(/reason|plan|design|architect|spec|analy/.test(L))return 0;return 1}function h(D){let N=D.budgetLimitUsd&&D.budgetLimitUsd>0?`$${D.budgetUsd.toFixed(2)} / $${D.budgetLimitUsd.toFixed(2)}`:`$${D.budgetUsd.toFixed(2)}`,J="";J+=n(32,32,44),J+=`<text x="92" y="54" font-family="'Fraunces','Georgia',serif" font-size="24" font-weight="600" fill="#201515">Autonomi</text>`,J+=`<text x="92" y="74" font-family="'Inter','Helvetica Neue',Arial,sans-serif" font-size="13" fill="#6b6675">Loki Cockpit</text>`;let X=D.freshness?`updated ${D.freshness}`:"live";J+=`<text x="868" y="54" text-anchor="end" font-family="'JetBrains Mono','SF Mono',ui-monospace,monospace" font-size="12" fill="#6b6675">${j(X)}</text>`,J+=`<text x="868" y="74" text-anchor="end" font-family="'JetBrains Mono','SF Mono',ui-monospace,monospace" font-size="13" fill="${g(D.verdict)}" font-weight="600">${j(D.verdict.toUpperCase())}</text>`;let $=96,M=168;J+=`<rect x="32" y="${$}" width="836" height="${M}" rx="16" fill="#ffffff" stroke="#e2e0e8"/>`,J+=`<text x="56" y="${$+34}" font-family="'Fraunces','Georgia',serif" font-size="20" font-weight="600" fill="#201515">${j(D.run)}</text>`,J+=`<text x="56" y="${$+54}" font-family="'Inter','Helvetica Neue',Arial,sans-serif" font-size="13" fill="#6b6675">phase ${j(D.phase)}</text>`;let E=$+96,P=(U)=>56+U*168;J+=C(P(0),E,"iteration",String(D.iteration)),J+=C(P(1),E,"tier",D.tier),J+=C(P(2),E,"provider",D.provider),J+=C(P(3),E,"budget",N);let H=$+M+40;J+=`<text x="32" y="${H}" font-family="'Inter','Helvetica Neue',Arial,sans-serif" font-size="13" font-weight="600" fill="#6b6675" letter-spacing="0.5">RARV LOOP</text>`;let S=r(D.phase),F=H+14,R=200;c.forEach((U,G)=>{let Z=32+G*(R+12),q=G<S,V=G===S;J+=`<rect x="${Z}" y="${F}" width="${R}" height="40" rx="10" fill="${V?"#553de9":"#ffffff"}" fill-opacity="${V?"0.10":"1"}" stroke="${V?"#553de9":q?"#1f8a52":"#6b6675"}" stroke-width="${V?2:1}"/>`;let f=q?"check":V?"dot":"wait";if(f==="check")J+=`<path d="M${Z+16} ${F+20} l6 6 l10 -12" stroke="#1f8a52" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>`;else if(f==="dot")J+=`<circle cx="${Z+22}" cy="${F+20}" r="5" fill="#553de9"/>`;else J+=`<circle cx="${Z+22}" cy="${F+20}" r="5" fill="none" stroke="#6b6675" stroke-width="1.5"/>`;J+=`<text x="${Z+40}" y="${F+25}" font-family="'Inter','Helvetica Neue',Arial,sans-serif" font-size="14" font-weight="${V?600:500}" fill="${V?"#553de9":q?"#201515":"#6b6675"}">${U}</text>`});let Y=F+40+40;J+=`<text x="32" y="${Y}" font-family="'Inter','Helvetica Neue',Arial,sans-serif" font-size="13" font-weight="600" fill="#6b6675" letter-spacing="0.5">QUALITY GATES</text>`;let x=D.gates.length?D.gates:[{name:"no gates yet",status:"pending"}],T=30,W=Y+12;J+=`<rect x="32" y="${W}" width="836" height="${x.slice(0,8).length*T+8}" rx="12" fill="#ffffff" stroke="#e2e0e8"/>`,W+=8;for(let U of x.slice(0,8)){let G=y(U.status),Z=W+T/2;if(J+=`<circle cx="50" cy="${Z}" r="4" fill="${G}"/>`,J+=`<text x="66" y="${Z+4}" font-family="'JetBrains Mono','SF Mono',ui-monospace,monospace" font-size="12" fill="#201515">${j(U.name)}</text>`,U.runner)J+=`<text x="242" y="${Z+4}" font-family="'Inter','Helvetica Neue',Arial,sans-serif" font-size="11" fill="#6b6675">${j(U.runner)}</text>`;if(U.evidence){let q=U.evidence.length>52?`${U.evidence.slice(0,49)}...`:U.evidence;J+=`<text x="332" y="${Z+4}" font-family="'Inter','Helvetica Neue',Arial,sans-serif" font-size="11" fill="#6b6675">${j(q)}</text>`}J+=`<text x="852" y="${Z+4}" text-anchor="end" font-family="'JetBrains Mono','SF Mono',ui-monospace,monospace" font-size="11" font-weight="700" fill="${G}">${j(U.status.toUpperCase())}</text>`,W+=T}let k=W+40;J+=`<text x="32" y="${k}" font-family="'Inter','Helvetica Neue',Arial,sans-serif" font-size="13" font-weight="600" fill="#6b6675" letter-spacing="0.5">COMPLETION COUNCIL</text>`;let m=D.council.length?D.council:[{reviewer:"pending",vote:"pending"}],A=k+12;J+=`<rect x="32" y="${A}" width="836" height="${m.slice(0,6).length*T+8}" rx="12" fill="#ffffff" stroke="#e2e0e8"/>`,A+=8;for(let U of m.slice(0,6)){let G=d(U.vote),Z=A+T/2;if(J+=`<circle cx="50" cy="${Z}" r="4" fill="${G}"/>`,J+=`<text x="66" y="${Z+4}" font-family="'Inter','Helvetica Neue',Arial,sans-serif" font-size="12.5" font-weight="500" fill="#201515">${j(U.reviewer)}</text>`,U.tier)J+=`<text x="292" y="${Z+4}" font-family="'JetBrains Mono','SF Mono',ui-monospace,monospace" font-size="11" fill="#6b6675">${j(U.tier)}</text>`;J+=`<text x="852" y="${Z+4}" text-anchor="end" font-family="'JetBrains Mono','SF Mono',ui-monospace,monospace" font-size="11" font-weight="700" fill="${G}">${j(U.vote.toUpperCase())}</text>`,A+=T}let w=A+46;J+=`<text x="32" y="${w}" font-family="'Inter','Helvetica Neue',Arial,sans-serif" font-size="13" font-weight="600" fill="#6b6675" letter-spacing="0.5">FLEET (${D.fleet.length})</text>`;let K=w+20,_=D.fleet.slice(0,4);for(let U of _){let G=U.running?"#1f8a52":"#6b6675";J+=`<circle cx="38" cy="${K-4}" r="5" fill="${G}"/>`,J+=`<text x="52" y="${K}" font-family="'JetBrains Mono','SF Mono',ui-monospace,monospace" font-size="13" fill="#201515">${j(U.name)}</text>`;let Z=`iter ${U.iteration??0} ${j(U.phase||U.status||"")}`;J+=`<text x="868" y="${K}" text-anchor="end" font-family="'JetBrains Mono','SF Mono',ui-monospace,monospace" font-size="12" fill="#6b6675">${Z}</text>`,K+=26}if(D.fleet.length>_.length)J+=`<text x="52" y="${K}" font-family="'Inter','Helvetica Neue',Arial,sans-serif" font-size="12" fill="#6b6675">+ ${D.fleet.length-_.length} more</text>`,K+=26;let z=Math.round(K+32);return`<svg xmlns="http://www.w3.org/2000/svg" width="900" height="${z}" viewBox="0 0 900 ${z}">
|
|
10
|
+
<rect x="0" y="0" width="900" height="${z}" fill="#f1f2f6"/>
|
|
11
11
|
${J}
|
|
12
|
-
</svg>`}var
|
|
13
|
-
`),
|
|
14
|
-
`),
|
|
12
|
+
</svg>`}var O;function u(){if(O!==void 0)return O;try{let D=globalThis.require??B;D.resolve("@resvg/resvg-js"),O=D("@resvg/resvg-js")}catch{O=null}return O}function b(){return u()!==null}function p(D,L=900){let Q=u();if(!Q||typeof Q.Resvg!=="function")return{png:null,available:!1,reason:"raster unavailable (@resvg/resvg-js not installed)"};try{let J=new Q.Resvg(D,{fitTo:{mode:"width",value:L}}).render().asPng();return{png:Uint8Array.from(J),available:!0}}catch(N){return{png:null,available:!1,reason:`raster failed: ${N.message}`}}}function l(D){return Buffer.from(D).toString("base64")}function a(D){let L=l(D);return`\x1B]1337;File=${`inline=1;size=${D.length};width=auto;height=auto;preserveAspectRatio=1`}:${L}\x07`}function s(D,L=4096){let Q=l(D);if(Q.length===0)return"\x1B_Ga=T,f=100,m=0;\x1B\\";let N=[];for(let X=0;X<Q.length;X+=L)N.push(Q.slice(X,X+L));let J="";for(let X=0;X<N.length;X++){let M=X===N.length-1?0:1,E=X===0?`a=T,f=100,m=${M}`:`m=${M}`;J+=`\x1B_G${E};${N[X]}\x1B\\`}return J}function i(D,L){return D==="kitty"?s(L):a(L)}var o=new Set(["iterm.app","wezterm","ghostty"]);function I(D=process.env){let L=(D.LOKI_COCKPIT_PROTOCOL||"").trim().toLowerCase();if(L==="iterm2"||L==="kitty"||L==="none")return L;let Q=(D.KITTY_WINDOW_ID||"").trim(),N=(D.TERM||"").trim().toLowerCase();if(Q.length>0||N==="xterm-kitty")return"kitty";let J=(D.TERM_PROGRAM||"").trim().toLowerCase();if(o.has(J))return"iterm2";return"none"}function v(D,L={}){let Q=h(D);if(L.forceText)return{kind:"fallback",protocol:"none",reason:"--no-image (text/browser fallback)",svg:Q};let N;if(L.protocol&&L.protocol!=="auto")N=L.protocol;else N=I(L.env);if(N==="none")return{kind:"fallback",protocol:N,reason:"no inline-image terminal detected",svg:Q};let J=p(Q);if(!J.available||!J.png)return{kind:"fallback",protocol:N,reason:J.reason||"rasterization unavailable",svg:Q};let X=i(N,J.png);return{kind:"image",protocol:N,data:X,svg:Q}}async function e(){let D=[];for await(let L of process.stdin)D.push(L);return Buffer.concat(D).toString("utf-8")}function t(D){let L="auto",Q=!1,N;for(let J=0;J<D.length;J++){let X=D[J];if(X==="--protocol"){let $=(D[++J]||"auto").toLowerCase();L=$==="iterm2"||$==="kitty"||$==="none"?$:"auto"}else if(X==="--no-image")Q=!0;else if(X==="--svg-out")N=D[++J]}return{protocol:L,noImage:Q,svgOut:N}}function DD(){let D=I(),L=b(),Q=D!=="none"&&L?"image":"text+dashboard";return process.stdout.write(`protocol=${D}
|
|
13
|
+
`),process.stdout.write(`resvg=${L?"yes":"no"}
|
|
14
|
+
`),process.stdout.write(`path=${Q}
|
|
15
|
+
`),0}async function JD(D=process.argv.slice(2)){if(D.includes("--probe"))return DD();let{protocol:L,noImage:Q,svgOut:N}=t(D),J;try{let $=await e();J=JSON.parse($)}catch($){return process.stderr.write(`FALLBACK could not parse cockpit state: ${$.message}
|
|
16
|
+
`),3}let X=v(J,{protocol:L,forceText:Q});if(N)try{let{writeFileSync:$}=await import("fs");$(N,X.svg)}catch{}if(X.kind==="image"&&X.data)return process.stdout.write(X.data),0;return process.stderr.write(`FALLBACK ${X.reason||"image unavailable"}
|
|
17
|
+
`),3}if(import.meta.main)JD().then((D)=>process.exit(D));export{JD as main};
|
|
15
18
|
|
|
16
|
-
//# debugId=
|
|
19
|
+
//# debugId=43ABF0138FF2304964756E2164756E21
|
package/loki-ts/dist/loki.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var q8=Object.defineProperty;var J8=($)=>$;function V8($,Q){this[$]=J8.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)q8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:V8.bind(Q,Z)})};var P=($,Q)=>()=>($&&(Q=$($=0)),Q);var J$=import.meta.require;var m1={};b(m1,{lokiDir:()=>j,homeLokiDir:()=>T$,findRepoRootForVersion:()=>$1,REPO_ROOT:()=>h});import{resolve as t,dirname as e$}from"path";import{fileURLToPath as W8}from"url";import{existsSync as N$}from"fs";import{homedir as H8}from"os";function G8(){let $=v1;for(let Q=0;Q<6;Q++){if(N$(t($,"VERSION"))&&N$(t($,"autonomy/run.sh")))return $;let Z=e$($);if(Z===$)break;$=Z}return t(v1,"..","..","..")}function $1($){let Q=$;for(let Z=0;Z<6;Z++){if(N$(t(Q,"VERSION"))&&N$(t(Q,"autonomy/run.sh")))return Q;let z=e$(Q);if(z===Q)break;Q=z}return t($,"..","..","..")}function j(){return process.env.LOKI_DIR??t(process.cwd(),".loki")}function T$(){return t(H8(),".loki")}var v1,h;var C=P(()=>{v1=e$(W8(import.meta.url));h=G8()});import{readFileSync as U8}from"fs";import{resolve as Y8,dirname as B8}from"path";import{fileURLToPath as M8}from"url";function S$(){if(Q$!==null)return Q$;let $="7.
|
|
2
|
+
var q8=Object.defineProperty;var J8=($)=>$;function V8($,Q){this[$]=J8.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)q8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:V8.bind(Q,Z)})};var P=($,Q)=>()=>($&&(Q=$($=0)),Q);var J$=import.meta.require;var m1={};b(m1,{lokiDir:()=>j,homeLokiDir:()=>T$,findRepoRootForVersion:()=>$1,REPO_ROOT:()=>h});import{resolve as t,dirname as e$}from"path";import{fileURLToPath as W8}from"url";import{existsSync as N$}from"fs";import{homedir as H8}from"os";function G8(){let $=v1;for(let Q=0;Q<6;Q++){if(N$(t($,"VERSION"))&&N$(t($,"autonomy/run.sh")))return $;let Z=e$($);if(Z===$)break;$=Z}return t(v1,"..","..","..")}function $1($){let Q=$;for(let Z=0;Z<6;Z++){if(N$(t(Q,"VERSION"))&&N$(t(Q,"autonomy/run.sh")))return Q;let z=e$(Q);if(z===Q)break;Q=z}return t($,"..","..","..")}function j(){return process.env.LOKI_DIR??t(process.cwd(),".loki")}function T$(){return t(H8(),".loki")}var v1,h;var C=P(()=>{v1=e$(W8(import.meta.url));h=G8()});import{readFileSync as U8}from"fs";import{resolve as Y8,dirname as B8}from"path";import{fileURLToPath as M8}from"url";function S$(){if(Q$!==null)return Q$;let $="7.127.0";if(typeof $==="string"&&$.length>0)return Q$=$,Q$;try{let Q=B8(M8(import.meta.url)),Z=$1(Q);Q$=U8(Y8(Z,"VERSION"),"utf-8").trim()}catch{Q$="unknown"}return Q$}var Q$=null;var Q1=P(()=>{C()});var p1={};b(p1,{runOrThrow:()=>S8,run:()=>R,readStreamCapped:()=>c1,commandVersion:()=>C8,commandExists:()=>u,ShellError:()=>Z1,MAX_STDOUT_BYTES:()=>u1});async function c1($,Q=u1){let Z=$.getReader(),z=new TextDecoder,X="",q=0;try{while(q<Q){let{done:K,value:W}=await Z.read();if(K)break;if(!W)continue;if(q+=W.byteLength,q>Q){let V=W.byteLength-(q-Q);X+=z.decode(W.subarray(0,V),{stream:!0});break}X+=z.decode(W,{stream:!0})}X+=z.decode()}finally{try{await Z.cancel()}catch{}Z.releaseLock()}return X}async function R($,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[q,K,W]=await Promise.all([c1(Z.stdout),new Response(Z.stderr).text(),Z.exited]);return{stdout:q,stderr:K,exitCode:W}}finally{if(z)clearTimeout(z);if(X)clearTimeout(X)}}async function S8($,Q={}){let Z=await R($,Q);if(Z.exitCode!==0)throw new Z1(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function u($){let Q=D8($),Z=await R(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function D8($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function C8($,Q="--version"){if(!await u($))return null;let z=await R([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var u1=16777216,Z1;var o=P(()=>{Z1=class Z1 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 r($){return b8?"":$}var b8,M,S,_,tZ,L,k,y,J;var p=P(()=>{b8=(process.env.NO_COLOR??"").length>0;M=r("\x1B[0;31m"),S=r("\x1B[0;32m"),_=r("\x1B[1;33m"),tZ=r("\x1B[0;34m"),L=r("\x1B[0;36m"),k=r("\x1B[1m"),y=r("\x1B[2m"),J=r("\x1B[0m")});import{existsSync as l8}from"fs";async function Z$(){if(A$!==void 0)return A$;let $="/opt/homebrew/bin/python3.12";if(l8($))return A$=$,$;let Q=await u("python3.12");if(Q)return A$=Q,Q;let Z=await u("python3");return A$=Z,Z}async function z$($,Q={}){let Z=await Z$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return R([Z,"-c",$],Q)}var A$;var V$=P(()=>{o()});var V0={};b(V0,{runStatus:()=>U3});import{existsSync as v,readFileSync as H$,readdirSync as $0,statSync as Q0}from"fs";import{resolve as D,basename as z3}from"path";import{homedir as X3}from"os";function Z0($){let Q=Math.trunc($);if(Q>=1e6)return`${(Math.trunc(Q/1e6*10)/10).toFixed(1)}M`;if(Q>=1000)return`${(Math.trunc(Q/1000*10)/10).toFixed(1)}K`;return String(Q)}function z0($,Q,Z){if(Q===0)return null;let z=Math.trunc($*100/Q),X=Math.trunc($*C$/Q);if(X>C$)X=C$;let q=C$-X,K=S;if(z>=80)K=M;else if(z>=50)K=_;let W="=".repeat(Math.max(0,X))+" ".repeat(Math.max(0,q)),V=Z0($),H=Z0(Q);return` ${k}${Z}${J} ${K}[${W}]${J} ${z}% (${V} / ${H})`}async function q3(){if(await u("jq"))return!0;return process.stdout.write(`${M}Error: jq is required but not installed.${J}
|
|
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)
|
|
@@ -814,4 +814,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
|
|
|
814
814
|
`),2}default:return process.stderr.write(`Unknown command: ${Q}
|
|
815
815
|
`),process.stderr.write(K8),2}}e1();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var SZ=await NZ(Bun.argv.slice(2));process.exit(SZ);
|
|
816
816
|
|
|
817
|
-
//# debugId=
|
|
817
|
+
//# debugId=3F5CF6688FCD67CA64756E2164756E21
|
package/mcp/__init__.py
CHANGED
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.
|
|
4
|
+
"version": "7.127.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 8 quality gates. Provider-agnostic (Claude Code, OpenAI Codex, Cline, Aider).",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"agent",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "loki-mode",
|
|
4
4
|
"displayName": "Loki Mode",
|
|
5
|
-
"version": "7.
|
|
5
|
+
"version": "7.127.0",
|
|
6
6
|
"description": "Autonomous spec-to-product build system with a built-in trust layer (RARV-C closure loop, 8 quality gates, completion council). Ships Loki's spec-hardening, drift-detection, and deterministic PR verification commands plus the Loki MCP server.",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Autonomi",
|