loki-mode 7.91.1 → 7.93.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/loki +154 -27
- package/autonomy/provider-offer.sh +8 -1
- package/autonomy/run.sh +314 -2
- package/dashboard/__init__.py +1 -1
- package/dashboard/server.py +179 -20
- package/dashboard/static/index.html +423 -104
- package/docs/INSTALLATION.md +2 -2
- package/loki-ts/dist/loki.js +5 -5
- 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.93.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.93.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.
|
|
1
|
+
7.93.0
|
package/autonomy/loki
CHANGED
|
@@ -313,6 +313,112 @@ get_version() {
|
|
|
313
313
|
fi
|
|
314
314
|
}
|
|
315
315
|
|
|
316
|
+
# Stale-install nudge (bash route; mirrors loki-ts/src/util/update_check.ts).
|
|
317
|
+
# Best-effort, fail-silent, and adds zero latency on the hot path:
|
|
318
|
+
# - skipped entirely on non-TTY / CI / LOKI_NO_UPDATE_CHECK=1;
|
|
319
|
+
# - result cached in ~/.loki/cache/update-check-bash.json for >= 24h, so the
|
|
320
|
+
# network is hit at most once a day (separate file from the Bun cache, which
|
|
321
|
+
# stores millisecond timestamps; this file stores Unix seconds);
|
|
322
|
+
# - the registry fetch has a hard 1.5s timeout and is fully fail-silent;
|
|
323
|
+
# - the hint goes to STDERR so stdout stays byte-identical for scripts and the
|
|
324
|
+
# bash<->Bun parity tests.
|
|
325
|
+
# We never fabricate a version: the hint prints only when a real check (or a
|
|
326
|
+
# fresh cache from one) reports a strictly newer stable semver.
|
|
327
|
+
_loki_update_cache_path() {
|
|
328
|
+
echo "$HOME/.loki/cache/update-check-bash.json"
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
# _loki_semver_newer <latest> <current> -> 0 (true) iff latest is strictly newer.
|
|
332
|
+
# Rejects anything that is not a plain MAJOR.MINOR.PATCH (e.g. "unknown",
|
|
333
|
+
# prereleases): on any unparseable input it returns 1 (never nudge).
|
|
334
|
+
_loki_semver_newer() {
|
|
335
|
+
local latest="$1" current="$2"
|
|
336
|
+
case "$latest" in
|
|
337
|
+
[0-9]*.[0-9]*.[0-9]*) ;;
|
|
338
|
+
*) return 1 ;;
|
|
339
|
+
esac
|
|
340
|
+
case "$current" in
|
|
341
|
+
[0-9]*.[0-9]*.[0-9]*) ;;
|
|
342
|
+
*) return 1 ;;
|
|
343
|
+
esac
|
|
344
|
+
# Reject prerelease / build suffixes (anything past the third number).
|
|
345
|
+
case "$latest" in *[!0-9.]*) return 1 ;; esac
|
|
346
|
+
case "$current" in *[!0-9.]*) return 1 ;; esac
|
|
347
|
+
local l_maj l_min l_pat c_maj c_min c_pat
|
|
348
|
+
IFS='.' read -r l_maj l_min l_pat _ <<EOF
|
|
349
|
+
$latest
|
|
350
|
+
EOF
|
|
351
|
+
IFS='.' read -r c_maj c_min c_pat _ <<EOF
|
|
352
|
+
$current
|
|
353
|
+
EOF
|
|
354
|
+
# Default any empty component to 0 (defensive).
|
|
355
|
+
l_maj=${l_maj:-0}; l_min=${l_min:-0}; l_pat=${l_pat:-0}
|
|
356
|
+
c_maj=${c_maj:-0}; c_min=${c_min:-0}; c_pat=${c_pat:-0}
|
|
357
|
+
if [ "$l_maj" -ne "$c_maj" ]; then [ "$l_maj" -gt "$c_maj" ]; return; fi
|
|
358
|
+
if [ "$l_min" -ne "$c_min" ]; then [ "$l_min" -gt "$c_min" ]; return; fi
|
|
359
|
+
[ "$l_pat" -gt "$c_pat" ]
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
# maybe_print_update_hint: print the one-line update nudge to stderr if (and
|
|
363
|
+
# only if) a successful, non-fabricated check reports a strictly newer release.
|
|
364
|
+
# Never blocks, never slows the command, never touches stdout. Always returns 0.
|
|
365
|
+
maybe_print_update_hint() {
|
|
366
|
+
# Opt-out / non-interactive / CI: do nothing, touch neither disk nor network.
|
|
367
|
+
[ "${LOKI_NO_UPDATE_CHECK:-}" = "1" ] && return 0
|
|
368
|
+
[ -n "${CI:-}" ] && return 0
|
|
369
|
+
[ ! -t 1 ] && return 0
|
|
370
|
+
|
|
371
|
+
local current
|
|
372
|
+
current=$(get_version)
|
|
373
|
+
case "$current" in
|
|
374
|
+
[0-9]*.[0-9]*.[0-9]*) ;;
|
|
375
|
+
*) return 0 ;; # "unknown" or non-semver -> never nudge
|
|
376
|
+
esac
|
|
377
|
+
|
|
378
|
+
local cache_file latest=""
|
|
379
|
+
cache_file=$(_loki_update_cache_path)
|
|
380
|
+
|
|
381
|
+
# Fast path: a fresh (< 24h) cache means zero network.
|
|
382
|
+
if [ -f "$cache_file" ]; then
|
|
383
|
+
local checked_at now age
|
|
384
|
+
checked_at=$(sed -n 's/.*"checkedAt"[[:space:]]*:[[:space:]]*\([0-9]\{1,\}\).*/\1/p' "$cache_file" 2>/dev/null | head -1)
|
|
385
|
+
latest=$(sed -n 's/.*"latest"[[:space:]]*:[[:space:]]*"\([0-9][0-9.]*\)".*/\1/p' "$cache_file" 2>/dev/null | head -1)
|
|
386
|
+
if [ -n "$checked_at" ] && [ -n "$latest" ]; then
|
|
387
|
+
now=$(date +%s 2>/dev/null || echo 0)
|
|
388
|
+
age=$(( now - checked_at ))
|
|
389
|
+
if [ "$age" -ge 0 ] && [ "$age" -lt 86400 ]; then
|
|
390
|
+
if _loki_semver_newer "$latest" "$current"; then
|
|
391
|
+
printf 'A newer loki-mode is available (%s). Update: bun install -g loki-mode\n' "$latest" >&2
|
|
392
|
+
fi
|
|
393
|
+
return 0
|
|
394
|
+
fi
|
|
395
|
+
fi
|
|
396
|
+
latest="" # stale cache -> fall through to a fresh fetch
|
|
397
|
+
fi
|
|
398
|
+
|
|
399
|
+
# Cache miss/stale: query the registry. Hard timeout, fail-silent.
|
|
400
|
+
if command -v curl >/dev/null 2>&1; then
|
|
401
|
+
latest=$(curl -fsS --max-time 1.5 "https://registry.npmjs.org/loki-mode/latest" 2>/dev/null \
|
|
402
|
+
| sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([0-9][0-9.]*\)".*/\1/p' | head -1)
|
|
403
|
+
fi
|
|
404
|
+
# No usable result -> print nothing, write nothing (never fabricate).
|
|
405
|
+
case "$latest" in
|
|
406
|
+
[0-9]*.[0-9]*.[0-9]*) ;;
|
|
407
|
+
*) return 0 ;;
|
|
408
|
+
esac
|
|
409
|
+
|
|
410
|
+
# Persist the fresh result (best-effort; a non-writable HOME must not break).
|
|
411
|
+
local now2
|
|
412
|
+
now2=$(date +%s 2>/dev/null || echo 0)
|
|
413
|
+
( mkdir -p "$(dirname "$cache_file")" 2>/dev/null \
|
|
414
|
+
&& printf '{"checkedAt":%s,"latest":"%s"}\n' "$now2" "$latest" > "$cache_file" 2>/dev/null ) || true
|
|
415
|
+
|
|
416
|
+
if _loki_semver_newer "$latest" "$current"; then
|
|
417
|
+
printf 'A newer loki-mode is available (%s). Update: bun install -g loki-mode\n' "$latest" >&2
|
|
418
|
+
fi
|
|
419
|
+
return 0
|
|
420
|
+
}
|
|
421
|
+
|
|
316
422
|
# Ensure dashboard Python venv with all deps installed.
|
|
317
423
|
# Uses ~/.loki/dashboard-venv (persistent, writable, survives npm/brew upgrades).
|
|
318
424
|
# Sets DASHBOARD_PYTHON to the venv python3 path on success.
|
|
@@ -717,11 +823,11 @@ show_help() {
|
|
|
717
823
|
echo ""
|
|
718
824
|
echo "Usage: loki <command> [options]"
|
|
719
825
|
echo ""
|
|
720
|
-
echo "New here? Try
|
|
826
|
+
echo "New here? Try these in order:"
|
|
721
827
|
echo " loki doctor Check your setup is ready (a few seconds)"
|
|
722
|
-
echo " loki
|
|
723
|
-
echo " loki demo Build a sample todo app end to end (real run)"
|
|
828
|
+
echo " loki quickstart Guided first build from your idea (no PRD needed)"
|
|
724
829
|
echo " loki start ./prd.md Build from a spec (PRD file, GitHub issue, or no arg)"
|
|
830
|
+
echo " quickstart = guided first build; quick = one small task (3 iters max)."
|
|
725
831
|
echo " Docs: https://github.com/asklokesh/loki-mode | Report a problem: loki crash"
|
|
726
832
|
echo ""
|
|
727
833
|
# CLI consolidation (Phase A): the front page presents ~17 canonical
|
|
@@ -778,11 +884,11 @@ show_help() {
|
|
|
778
884
|
echo " version Show version"
|
|
779
885
|
echo " help Show this help ('loki help aliases' for old names)"
|
|
780
886
|
echo ""
|
|
781
|
-
echo "More commands (
|
|
782
|
-
echo "
|
|
783
|
-
echo "
|
|
784
|
-
echo "
|
|
785
|
-
echo "'loki <
|
|
887
|
+
echo "More commands (the most useful; many more are dispatchable):"
|
|
888
|
+
echo " Advanced: ship, deploy, init, web"
|
|
889
|
+
echo " Cloud + CI: github, import, ci"
|
|
890
|
+
echo " Debugging: logs, crash, cleanup"
|
|
891
|
+
echo "...run 'loki help <name>' for any command."
|
|
786
892
|
echo ""
|
|
787
893
|
echo "Aliases (deprecated): older command names still work; they print a"
|
|
788
894
|
echo "one-line pointer to stderr and never alter --json output. See the full"
|
|
@@ -804,19 +910,8 @@ show_help() {
|
|
|
804
910
|
echo " --bmad-project PATH Use BMAD Method project artifacts as input"
|
|
805
911
|
echo " --openspec PATH Use OpenSpec change directory as input"
|
|
806
912
|
echo ""
|
|
807
|
-
echo "
|
|
808
|
-
echo "
|
|
809
|
-
echo " --no-start Generate PRD but don't start execution"
|
|
810
|
-
echo " --output FILE Save PRD to custom path"
|
|
811
|
-
echo " --provider NAME AI provider: claude (default), codex, cline, aider"
|
|
812
|
-
echo " --parallel Enable parallel mode with git worktrees"
|
|
813
|
-
echo " --budget USD Set cost budget limit"
|
|
814
|
-
echo ""
|
|
815
|
-
echo "Progressive Isolation (for 'run'):"
|
|
816
|
-
echo " --worktree, -w Git worktree isolation (separate branch)"
|
|
817
|
-
echo " --pr Auto-create PR after completion (implies --worktree)"
|
|
818
|
-
echo " --ship Auto-merge after PR (implies --pr)"
|
|
819
|
-
echo " --detach, -d Run in background (implies --worktree)"
|
|
913
|
+
echo "Deprecated alias 'run': 'loki run <issue-ref>' still works as an alias"
|
|
914
|
+
echo "for 'loki start'. See its options with 'loki run --help'."
|
|
820
915
|
echo ""
|
|
821
916
|
echo "Examples:"
|
|
822
917
|
echo ""
|
|
@@ -941,11 +1036,11 @@ show_landing() {
|
|
|
941
1036
|
echo ""
|
|
942
1037
|
echo -e "First time here? ${CYAN}loki doctor${NC} checks your setup (an AI provider CLI is required)."
|
|
943
1038
|
echo ""
|
|
944
|
-
echo "Get started:"
|
|
1039
|
+
echo "Get started (in order):"
|
|
1040
|
+
echo -e " ${CYAN}loki doctor${NC} Check your setup is ready (a few seconds)"
|
|
945
1041
|
echo -e " ${CYAN}loki quickstart${NC} Guided first build from your idea (no PRD needed)"
|
|
946
1042
|
echo -e " ${CYAN}loki start ./prd.md${NC} Build from a spec (PRD file, GitHub issue, or no arg)"
|
|
947
|
-
echo
|
|
948
|
-
echo -e " ${CYAN}loki dashboard start${NC} Start the live run monitor (then: loki dashboard open)"
|
|
1043
|
+
echo " quickstart = guided first build; quick = one small task (3 iters max)."
|
|
949
1044
|
echo ""
|
|
950
1045
|
echo -e "Need help? ${CYAN}loki help${NC} lists every command."
|
|
951
1046
|
echo "Tip: preview cost and scope before building: loki plan <your-prd.md>"
|
|
@@ -3408,7 +3503,8 @@ cmd_why() {
|
|
|
3408
3503
|
fi
|
|
3409
3504
|
|
|
3410
3505
|
if [ "$as_json" = "1" ]; then
|
|
3411
|
-
_LOKI_WHY_STATE="$state_file" _LOKI_WHY_COMPLETION="$completion_file"
|
|
3506
|
+
_LOKI_WHY_STATE="$state_file" _LOKI_WHY_COMPLETION="$completion_file" \
|
|
3507
|
+
_LOKI_WHY_LAST_ERROR="$loki_dir/state/LAST_ERROR.json" python3 - <<'WHYJSON'
|
|
3412
3508
|
import json, os
|
|
3413
3509
|
def load(p):
|
|
3414
3510
|
try:
|
|
@@ -3416,7 +3512,8 @@ def load(p):
|
|
|
3416
3512
|
except Exception: return {}
|
|
3417
3513
|
state = load(os.environ.get("_LOKI_WHY_STATE", ""))
|
|
3418
3514
|
comp = load(os.environ.get("_LOKI_WHY_COMPLETION", ""))
|
|
3419
|
-
|
|
3515
|
+
last_error = load(os.environ.get("_LOKI_WHY_LAST_ERROR", ""))
|
|
3516
|
+
print(json.dumps({"state": state, "completion": comp, "last_error": last_error}, indent=2))
|
|
3420
3517
|
WHYJSON
|
|
3421
3518
|
return 0
|
|
3422
3519
|
fi
|
|
@@ -3428,6 +3525,7 @@ WHYJSON
|
|
|
3428
3525
|
_why_head_sha="$(git rev-parse HEAD 2>/dev/null || echo "")"
|
|
3429
3526
|
_LOKI_WHY_STATE="$state_file" _LOKI_WHY_COMPLETION="$completion_file" \
|
|
3430
3527
|
_LOKI_WHY_HEAD_SHA="$_why_head_sha" \
|
|
3528
|
+
_LOKI_WHY_LAST_ERROR="$loki_dir/state/LAST_ERROR.json" \
|
|
3431
3529
|
_LOKI_WHY_HANDOFFS="$loki_dir/memory/handoffs" python3 - <<'WHYTXT'
|
|
3432
3530
|
import json, os, glob
|
|
3433
3531
|
def load(p):
|
|
@@ -3519,6 +3617,28 @@ print()
|
|
|
3519
3617
|
print(f" What happened: {meaning}")
|
|
3520
3618
|
print(f" What to do : {action}")
|
|
3521
3619
|
|
|
3620
|
+
# Surface the structured per-iteration error record, if one exists at
|
|
3621
|
+
# .loki/state/LAST_ERROR.json (the run.sh writer that produces it ships in the
|
|
3622
|
+
# SAME v7.92.0 release; on an older engine the file is simply absent and this
|
|
3623
|
+
# block stays dormant - never fabricated). run.sh clears it at the start of every
|
|
3624
|
+
# fresh run, so a present record belongs to the CURRENT run. We still only show it
|
|
3625
|
+
# when THIS run's outcome is itself a failure - never beside a success verdict
|
|
3626
|
+
# (that would be a fake-green-adjacent lie) - using the same SUCCESS set below.
|
|
3627
|
+
SUCCESS_STATUSES = {"council_approved", "completion_promise_fulfilled", "complete", "completed"}
|
|
3628
|
+
le = {}
|
|
3629
|
+
try:
|
|
3630
|
+
with open(os.environ.get("_LOKI_WHY_LAST_ERROR", "")) as f:
|
|
3631
|
+
le = json.load(f)
|
|
3632
|
+
except Exception:
|
|
3633
|
+
le = {}
|
|
3634
|
+
if isinstance(le, dict) and le.get("error_class") and status not in SUCCESS_STATUSES:
|
|
3635
|
+
print()
|
|
3636
|
+
_it = le.get("iteration")
|
|
3637
|
+
_it_s = f" (iteration {_it})" if _it not in (None, "") else ""
|
|
3638
|
+
print(f" Last error : {le.get('error_class')}{_it_s}")
|
|
3639
|
+
if le.get("brief"):
|
|
3640
|
+
print(f" {le.get('brief')}")
|
|
3641
|
+
|
|
3522
3642
|
# Surface the latest structured handoff (already-captured context), honestly.
|
|
3523
3643
|
hd = sorted(glob.glob(os.path.join(os.environ.get("_LOKI_WHY_HANDOFFS",""), "*.md")))
|
|
3524
3644
|
if hd:
|
|
@@ -10104,6 +10224,9 @@ except Exception:
|
|
|
10104
10224
|
echo ""
|
|
10105
10225
|
echo "Next: loki quickstart (guided first build from your idea, no PRD needed)"
|
|
10106
10226
|
echo " or loki demo (builds a sample todo app end to end) or loki start ./prd.md"
|
|
10227
|
+
# Best-effort stale-install nudge (stderr only; never blocks; off on
|
|
10228
|
+
# non-TTY/CI). Reached only on the human-readable path (--json returns above).
|
|
10229
|
+
maybe_print_update_hint
|
|
10107
10230
|
return 0
|
|
10108
10231
|
}
|
|
10109
10232
|
|
|
@@ -10476,6 +10599,9 @@ cmd_version() {
|
|
|
10476
10599
|
;;
|
|
10477
10600
|
esac
|
|
10478
10601
|
echo "Loki Mode v$(get_version)"
|
|
10602
|
+
# Best-effort stale-install nudge (stderr only; never blocks; off on
|
|
10603
|
+
# non-TTY/CI). Mirrors the Bun route's update check for parity.
|
|
10604
|
+
maybe_print_update_hint
|
|
10479
10605
|
}
|
|
10480
10606
|
|
|
10481
10607
|
# Secrets / credential management
|
|
@@ -16704,7 +16830,8 @@ main() {
|
|
|
16704
16830
|
if _suggestion=$(_suggest_command "$command"); then
|
|
16705
16831
|
echo "Did you mean 'loki ${_suggestion}'?" >&2
|
|
16706
16832
|
fi
|
|
16707
|
-
|
|
16833
|
+
# Always point to the full command list, even with no near-miss match.
|
|
16834
|
+
echo "Run 'loki help' to see every command." >&2
|
|
16708
16835
|
exit 1
|
|
16709
16836
|
;;
|
|
16710
16837
|
esac
|
|
@@ -69,8 +69,15 @@ _po_non_interactive() {
|
|
|
69
69
|
# successful install. Inherited stdio; Loki never handles credentials.
|
|
70
70
|
_po_run_login() {
|
|
71
71
|
# claude must actually be on PATH for login to make sense.
|
|
72
|
+
# If the install succeeded but the binary is not yet resolvable, npm's
|
|
73
|
+
# global bin is almost certainly not on PATH. Print the exact copy-paste
|
|
74
|
+
# fix. We print the literal $(npm config get prefix) form (single-quoted,
|
|
75
|
+
# NOT executed here) so the user runs it in their own shell and so this
|
|
76
|
+
# post-install path makes no extra npm invocation.
|
|
72
77
|
if ! command -v claude >/dev/null 2>&1; then
|
|
73
|
-
printf "%sInstalled, but 'claude' is not on your PATH yet.
|
|
78
|
+
printf "%sInstalled, but 'claude' is not on your PATH yet. Add npm's global bin to your shell:%s\n" "$_PO_YELLOW" "$_PO_NC"
|
|
79
|
+
printf ' export PATH="$(npm config get prefix)/bin:$PATH"\n'
|
|
80
|
+
printf "Then restart your shell (or source your rc) and run: loki doctor\n"
|
|
74
81
|
return 0
|
|
75
82
|
fi
|
|
76
83
|
|