loki-mode 7.20.0 → 7.22.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/project-graph.sh +30 -4
- package/autonomy/run.sh +359 -8
- package/dashboard/__init__.py +1 -1
- 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/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.
|
|
6
|
+
# Loki Mode v7.22.0
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -383,4 +383,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
383
383
|
|
|
384
384
|
---
|
|
385
385
|
|
|
386
|
-
**v7.
|
|
386
|
+
**v7.22.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.
|
|
1
|
+
7.22.0
|
|
@@ -557,6 +557,32 @@ _lpg_find_git_root() {
|
|
|
557
557
|
# Layer order: parent -> members -> subdir(root-to-leaf, deepest last)
|
|
558
558
|
# -> scope. All paths are deduped via a tracked set of absolute paths.
|
|
559
559
|
# Honors __LPG_TOTAL_CAP across all layers (stops at layer boundary).
|
|
560
|
+
# _lpg_memory_file <dir>
|
|
561
|
+
#
|
|
562
|
+
# Resolve the per-directory memory/conventions file for the layered doc walker.
|
|
563
|
+
# Prefers AGENTS.md (the agents.md standard: plain Markdown, nearest-file-wins,
|
|
564
|
+
# read natively by Claude Code/Codex/etc.) and falls back to CLAUDE.md only when
|
|
565
|
+
# AGENTS.md is absent in that directory. The two are never merged. The reader
|
|
566
|
+
# (_lpg_read_layer / _append_layer) is filename-agnostic, so this is the single
|
|
567
|
+
# point that decides which file each layer site reads.
|
|
568
|
+
_lpg_memory_file() {
|
|
569
|
+
local dir="$1"
|
|
570
|
+
if [ -f "$dir/AGENTS.md" ]; then
|
|
571
|
+
printf '%s/AGENTS.md' "$dir"
|
|
572
|
+
else
|
|
573
|
+
printf '%s/CLAUDE.md' "$dir"
|
|
574
|
+
fi
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
# load_app_graph_context -- emit the layered conventions/doc context (parent,
|
|
578
|
+
# member, subdir, and scope layers) as <!-- LOKI_LAYER --> blocks.
|
|
579
|
+
#
|
|
580
|
+
# NOTE (route asymmetry, conscious + pre-existing): the TS route has NO port of
|
|
581
|
+
# this function. loki-ts/src/project_graph.ts is a membership graph, not a
|
|
582
|
+
# doc-layer text emitter, so this layered AGENTS.md/CLAUDE.md walker is BASH-ONLY
|
|
583
|
+
# by design (the layered-CLAUDE.md doc walker was never ported to TS). The
|
|
584
|
+
# AGENTS.md precedence added here therefore lives only in bash; do not add a TS
|
|
585
|
+
# walker to "fix" the asymmetry.
|
|
560
586
|
load_app_graph_context() {
|
|
561
587
|
local root="${LOKI_PROJECT_GRAPH_ROOT:-}"
|
|
562
588
|
|
|
@@ -634,7 +660,7 @@ load_app_graph_context() {
|
|
|
634
660
|
|
|
635
661
|
# Parent layer first.
|
|
636
662
|
if [ -n "$root" ]; then
|
|
637
|
-
_append_layer parent "$root
|
|
663
|
+
_append_layer parent "$(_lpg_memory_file "$root")" || { printf '%s' "$out"; return 0; }
|
|
638
664
|
fi
|
|
639
665
|
|
|
640
666
|
# Member layers (skip the scope member -- we add it as scope below).
|
|
@@ -644,7 +670,7 @@ load_app_graph_context() {
|
|
|
644
670
|
if [ "$m" = "$target_dir" ]; then
|
|
645
671
|
continue
|
|
646
672
|
fi
|
|
647
|
-
_append_layer member "$m
|
|
673
|
+
_append_layer member "$(_lpg_memory_file "$m")" || { printf '%s' "$out"; return 0; }
|
|
648
674
|
done
|
|
649
675
|
|
|
650
676
|
# Subdir layers: ancestors of target_dir up to (and including) git_root,
|
|
@@ -674,12 +700,12 @@ load_app_graph_context() {
|
|
|
674
700
|
# subdir_chain is leaf-to-root order; reverse so we emit root-to-leaf.
|
|
675
701
|
local i count=${#subdir_chain[@]}
|
|
676
702
|
for (( i = count - 1; i >= 0; i-- )); do
|
|
677
|
-
_append_layer subdir "${subdir_chain[$i]}
|
|
703
|
+
_append_layer subdir "$(_lpg_memory_file "${subdir_chain[$i]}")" || { printf '%s' "$out"; return 0; }
|
|
678
704
|
done
|
|
679
705
|
fi
|
|
680
706
|
|
|
681
707
|
# Scope layer (target dir).
|
|
682
|
-
_append_layer scope "$target_dir
|
|
708
|
+
_append_layer scope "$(_lpg_memory_file "$target_dir")" || { printf '%s' "$out"; return 0; }
|
|
683
709
|
|
|
684
710
|
printf '%s' "$out"
|
|
685
711
|
}
|
package/autonomy/run.sh
CHANGED
|
@@ -2355,6 +2355,15 @@ notify_all_complete() {
|
|
|
2355
2355
|
|
|
2356
2356
|
notify_intervention_needed() {
|
|
2357
2357
|
local reason="$1"
|
|
2358
|
+
# Delegate-then-notify: this helper ONLY fires the (gated) desktop ping. It
|
|
2359
|
+
# deliberately does NOT write the durable COMPLETION.txt / completion.json
|
|
2360
|
+
# record. Reason: notify_intervention_needed is also called from NON-terminal
|
|
2361
|
+
# sites (the perpetual-mode PAUSE auto-clear branch, uncertainty escalation)
|
|
2362
|
+
# where the run keeps going. Writing a "Needs input" durable file there would
|
|
2363
|
+
# falsely tell a detached user the run is done / blocked when it is not. The
|
|
2364
|
+
# durable intervention write now lives only at the genuinely blocking pause
|
|
2365
|
+
# sites (immediately before handle_pause), so the durable state matches the
|
|
2366
|
+
# actual run state.
|
|
2358
2367
|
send_notification "Intervention Needed" "$reason" "critical"
|
|
2359
2368
|
}
|
|
2360
2369
|
|
|
@@ -2363,6 +2372,265 @@ notify_rate_limit() {
|
|
|
2363
2372
|
send_notification "Rate Limited" "Waiting ${wait_time}s before retry" "normal"
|
|
2364
2373
|
}
|
|
2365
2374
|
|
|
2375
|
+
#===============================================================================
|
|
2376
|
+
# Delegate-then-notify: completion summary (Release 2, "delegate then notify")
|
|
2377
|
+
#
|
|
2378
|
+
# build_completion_summary <outcome> writes two durable files that survive a
|
|
2379
|
+
# detached (--bg) run where the terminal is gone and a bell would be useless:
|
|
2380
|
+
# .loki/COMPLETION.txt human plain text (no emojis, no dashes)
|
|
2381
|
+
# .loki/state/completion.json machine-readable record of the same facts
|
|
2382
|
+
# It also exports two strings for send_notification to consume:
|
|
2383
|
+
# _LOKI_SUMMARY_TITLE short notification subtitle
|
|
2384
|
+
# _LOKI_SUMMARY_BODY short notification body (outcome + branch + file count)
|
|
2385
|
+
#
|
|
2386
|
+
# All git reads are best-effort and non-fatal. The diff window is the run-start
|
|
2387
|
+
# SHA captured once at runner init (_LOKI_RUN_START_SHA); we REUSE it and never
|
|
2388
|
+
# recapture, so the reported diff matches the evidence gate's window exactly.
|
|
2389
|
+
#
|
|
2390
|
+
# This function NEVER sends a notification and NEVER gates on
|
|
2391
|
+
# NOTIFICATIONS_ENABLED: the files are state, not a notification, and must be
|
|
2392
|
+
# written even when desktop notifications are disabled. emit_completion_summary
|
|
2393
|
+
# below is the wrapper that writes the files AND (gated) fires the desktop ping.
|
|
2394
|
+
#===============================================================================
|
|
2395
|
+
build_completion_summary() {
|
|
2396
|
+
local outcome="${1:-complete}"
|
|
2397
|
+
local loki_dir="${TARGET_DIR:-.}/.loki"
|
|
2398
|
+
mkdir -p "$loki_dir/state" 2>/dev/null || true
|
|
2399
|
+
|
|
2400
|
+
# Human-readable outcome label and notification title.
|
|
2401
|
+
local outcome_label notify_title
|
|
2402
|
+
case "$outcome" in
|
|
2403
|
+
complete) outcome_label="Completed"; notify_title="Run complete" ;;
|
|
2404
|
+
max_iterations) outcome_label="Max iterations"; notify_title="Run stopped (max iterations)" ;;
|
|
2405
|
+
stopped) outcome_label="Stopped"; notify_title="Run stopped" ;;
|
|
2406
|
+
failed) outcome_label="Failed"; notify_title="Run failed" ;;
|
|
2407
|
+
intervention) outcome_label="Needs input"; notify_title="Input needed" ;;
|
|
2408
|
+
*) outcome_label="$outcome"; notify_title="Run finished" ;;
|
|
2409
|
+
esac
|
|
2410
|
+
|
|
2411
|
+
# Branch + diff stats vs the run-start SHA (best-effort; non-git or empty
|
|
2412
|
+
# baseline yields empty values, which we render as "unknown"/"0").
|
|
2413
|
+
local start_sha="${_LOKI_RUN_START_SHA:-}"
|
|
2414
|
+
local branch="" head_sha="" diff_stat="" files_changed=0 insertions=0 deletions=0 review_cmd=""
|
|
2415
|
+
branch="$( (cd "${TARGET_DIR:-.}" && git rev-parse --abbrev-ref HEAD) 2>/dev/null || true )"
|
|
2416
|
+
[ -z "$branch" ] && branch="unknown"
|
|
2417
|
+
head_sha="$( (cd "${TARGET_DIR:-.}" && git rev-parse HEAD) 2>/dev/null || true )"
|
|
2418
|
+
|
|
2419
|
+
if [ -n "$start_sha" ]; then
|
|
2420
|
+
diff_stat="$( (cd "${TARGET_DIR:-.}" && git diff --stat "${start_sha}..HEAD") 2>/dev/null || true )"
|
|
2421
|
+
# Parse the git diff --shortstat tail for counts (locale-stable enough
|
|
2422
|
+
# for our display; failures leave the zeros in place).
|
|
2423
|
+
local shortstat
|
|
2424
|
+
shortstat="$( (cd "${TARGET_DIR:-.}" && git diff --shortstat "${start_sha}..HEAD") 2>/dev/null || true )"
|
|
2425
|
+
if [ -n "$shortstat" ]; then
|
|
2426
|
+
files_changed="$(printf '%s\n' "$shortstat" | grep -oE '[0-9]+ file' | grep -oE '[0-9]+' | head -1)"
|
|
2427
|
+
insertions="$(printf '%s\n' "$shortstat" | grep -oE '[0-9]+ insertion' | grep -oE '[0-9]+' | head -1)"
|
|
2428
|
+
deletions="$(printf '%s\n' "$shortstat" | grep -oE '[0-9]+ deletion' | grep -oE '[0-9]+' | head -1)"
|
|
2429
|
+
fi
|
|
2430
|
+
review_cmd="git diff ${start_sha}..HEAD"
|
|
2431
|
+
else
|
|
2432
|
+
review_cmd="git diff HEAD"
|
|
2433
|
+
fi
|
|
2434
|
+
[ -z "$files_changed" ] && files_changed=0
|
|
2435
|
+
[ -z "$insertions" ] && insertions=0
|
|
2436
|
+
[ -z "$deletions" ] && deletions=0
|
|
2437
|
+
|
|
2438
|
+
# Task counts: reuse the SAME queue reads as update_status_file.
|
|
2439
|
+
local pending=0 in_progress=0 completed=0 failed=0
|
|
2440
|
+
[ -f "$loki_dir/queue/pending.json" ] && pending=$(python3 -c "import json; print(len(json.load(open('$loki_dir/queue/pending.json'))))" 2>/dev/null || echo "0")
|
|
2441
|
+
[ -f "$loki_dir/queue/in-progress.json" ] && in_progress=$(python3 -c "import json; print(len(json.load(open('$loki_dir/queue/in-progress.json'))))" 2>/dev/null || echo "0")
|
|
2442
|
+
[ -f "$loki_dir/queue/completed.json" ] && completed=$(python3 -c "import json; print(len(json.load(open('$loki_dir/queue/completed.json'))))" 2>/dev/null || echo "0")
|
|
2443
|
+
[ -f "$loki_dir/queue/failed.json" ] && failed=$(python3 -c "import json; print(len(json.load(open('$loki_dir/queue/failed.json'))))" 2>/dev/null || echo "0")
|
|
2444
|
+
|
|
2445
|
+
# Optional delegate-mode extras populated by Slice 3 (branch isolation / PR).
|
|
2446
|
+
local delegate_branch="${_LOKI_DELEGATE_BRANCH_NAME:-}"
|
|
2447
|
+
local pr_url="${_LOKI_DELEGATE_PR_URL:-}"
|
|
2448
|
+
|
|
2449
|
+
local ts
|
|
2450
|
+
ts="$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date)"
|
|
2451
|
+
|
|
2452
|
+
# ---- Durable human-readable file: .loki/COMPLETION.txt --------------------
|
|
2453
|
+
{
|
|
2454
|
+
echo "Loki Mode run summary"
|
|
2455
|
+
echo "====================="
|
|
2456
|
+
echo ""
|
|
2457
|
+
echo "Outcome: $outcome_label"
|
|
2458
|
+
echo "Branch: $branch"
|
|
2459
|
+
echo "Files changed: $files_changed (+$insertions / -$deletions)"
|
|
2460
|
+
echo "Finished: $ts"
|
|
2461
|
+
echo ""
|
|
2462
|
+
if [ -n "$delegate_branch" ]; then
|
|
2463
|
+
echo "Delegate branch: $delegate_branch"
|
|
2464
|
+
fi
|
|
2465
|
+
if [ -n "$pr_url" ]; then
|
|
2466
|
+
echo "Pull request: $pr_url"
|
|
2467
|
+
elif [ "$outcome" = "complete" ]; then
|
|
2468
|
+
echo "Pull request: not opened (set LOKI_DELEGATE_PR=1 to open one)"
|
|
2469
|
+
fi
|
|
2470
|
+
echo ""
|
|
2471
|
+
echo "Tasks: pending=$pending in_progress=$in_progress completed=$completed failed=$failed"
|
|
2472
|
+
echo ""
|
|
2473
|
+
echo "Review the work:"
|
|
2474
|
+
echo " $review_cmd"
|
|
2475
|
+
echo ""
|
|
2476
|
+
if [ -n "$diff_stat" ]; then
|
|
2477
|
+
echo "Diff stat:"
|
|
2478
|
+
echo "$diff_stat"
|
|
2479
|
+
else
|
|
2480
|
+
echo "Diff stat: (no changes detected vs run start, or git unavailable)"
|
|
2481
|
+
fi
|
|
2482
|
+
} > "$loki_dir/COMPLETION.txt" 2>/dev/null || true
|
|
2483
|
+
|
|
2484
|
+
# ---- Durable machine-readable file: .loki/state/completion.json -----------
|
|
2485
|
+
_LOKI_CS_OUTCOME="$outcome" \
|
|
2486
|
+
_LOKI_CS_BRANCH="$branch" \
|
|
2487
|
+
_LOKI_CS_START_SHA="$start_sha" \
|
|
2488
|
+
_LOKI_CS_HEAD_SHA="$head_sha" \
|
|
2489
|
+
_LOKI_CS_FILES="$files_changed" \
|
|
2490
|
+
_LOKI_CS_INS="$insertions" \
|
|
2491
|
+
_LOKI_CS_DEL="$deletions" \
|
|
2492
|
+
_LOKI_CS_REVIEW="$review_cmd" \
|
|
2493
|
+
_LOKI_CS_DELEGATE_BRANCH="$delegate_branch" \
|
|
2494
|
+
_LOKI_CS_PR_URL="$pr_url" \
|
|
2495
|
+
_LOKI_CS_TS="$ts" \
|
|
2496
|
+
_LOKI_CS_OUT_FILE="$loki_dir/state/completion.json" \
|
|
2497
|
+
python3 -c "
|
|
2498
|
+
import json, os, tempfile
|
|
2499
|
+
out = os.environ['_LOKI_CS_OUT_FILE']
|
|
2500
|
+
def i(v):
|
|
2501
|
+
try: return int(v)
|
|
2502
|
+
except (TypeError, ValueError): return 0
|
|
2503
|
+
rec = {
|
|
2504
|
+
'outcome': os.environ.get('_LOKI_CS_OUTCOME', ''),
|
|
2505
|
+
'branch': os.environ.get('_LOKI_CS_BRANCH', ''),
|
|
2506
|
+
'start_sha': os.environ.get('_LOKI_CS_START_SHA', ''),
|
|
2507
|
+
'head_sha': os.environ.get('_LOKI_CS_HEAD_SHA', ''),
|
|
2508
|
+
'files_changed': i(os.environ.get('_LOKI_CS_FILES')),
|
|
2509
|
+
'insertions': i(os.environ.get('_LOKI_CS_INS')),
|
|
2510
|
+
'deletions': i(os.environ.get('_LOKI_CS_DEL')),
|
|
2511
|
+
'review_cmd': os.environ.get('_LOKI_CS_REVIEW', ''),
|
|
2512
|
+
'delegate_branch': os.environ.get('_LOKI_CS_DELEGATE_BRANCH', ''),
|
|
2513
|
+
'pr_url': os.environ.get('_LOKI_CS_PR_URL', ''),
|
|
2514
|
+
'timestamp': os.environ.get('_LOKI_CS_TS', ''),
|
|
2515
|
+
}
|
|
2516
|
+
d = os.path.dirname(out)
|
|
2517
|
+
fd, tmp = tempfile.mkstemp(dir=d, suffix='.json')
|
|
2518
|
+
with os.fdopen(fd, 'w') as f:
|
|
2519
|
+
json.dump(rec, f, indent=2)
|
|
2520
|
+
os.replace(tmp, out)
|
|
2521
|
+
" 2>/dev/null || true
|
|
2522
|
+
|
|
2523
|
+
# ---- Short strings for the desktop notification --------------------------
|
|
2524
|
+
# Desktop body stays terse; full detail lives in COMPLETION.txt.
|
|
2525
|
+
_LOKI_SUMMARY_TITLE="$notify_title"
|
|
2526
|
+
_LOKI_SUMMARY_BODY="${outcome_label} on ${branch}: ${files_changed} files changed"
|
|
2527
|
+
if [ -n "$pr_url" ]; then
|
|
2528
|
+
_LOKI_SUMMARY_BODY="${_LOKI_SUMMARY_BODY}. PR: ${pr_url}"
|
|
2529
|
+
fi
|
|
2530
|
+
export _LOKI_SUMMARY_TITLE _LOKI_SUMMARY_BODY
|
|
2531
|
+
return 0
|
|
2532
|
+
}
|
|
2533
|
+
|
|
2534
|
+
#===============================================================================
|
|
2535
|
+
# emit_completion_summary <outcome> [urgency]
|
|
2536
|
+
#
|
|
2537
|
+
# The single entry point every terminal state calls. It ALWAYS writes the
|
|
2538
|
+
# durable summary files (state, not a notification) and then fires ONE desktop
|
|
2539
|
+
# notification gated by the existing LOKI_NOTIFICATIONS flag (send_notification
|
|
2540
|
+
# already short-circuits when disabled, so the gate is implicit but explicit
|
|
2541
|
+
# here for clarity). Centralizing this keeps the success-only PR side effect
|
|
2542
|
+
# (Slice 3) in one place and prevents duplicate notifications.
|
|
2543
|
+
#===============================================================================
|
|
2544
|
+
emit_completion_summary() {
|
|
2545
|
+
local outcome="${1:-complete}"
|
|
2546
|
+
local urgency="${2:-normal}"
|
|
2547
|
+
build_completion_summary "$outcome"
|
|
2548
|
+
send_notification "${_LOKI_SUMMARY_TITLE:-Run finished}" "${_LOKI_SUMMARY_BODY:-}" "$urgency"
|
|
2549
|
+
return 0
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2552
|
+
#===============================================================================
|
|
2553
|
+
# on_run_complete (Slice 3: opt-in local git output on success)
|
|
2554
|
+
#
|
|
2555
|
+
# Called from every SUCCESS exit BEFORE emit_completion_summary so the PR url it
|
|
2556
|
+
# discovers is folded into the summary. Default behavior is a no-op: it only
|
|
2557
|
+
# acts when LOKI_DELEGATE_PR=1.
|
|
2558
|
+
#
|
|
2559
|
+
# LOKI_DELEGATE_PR=1 opens a LOCAL pull request from the user's machine, only if:
|
|
2560
|
+
# - this is a GitHub repo (gh + a github.com remote), AND
|
|
2561
|
+
# - `gh auth status` succeeds, AND
|
|
2562
|
+
# - the current branch is not main/master (never PR a default branch to itself)
|
|
2563
|
+
# It mirrors the proven pattern at autonomy/loki:5524-5527: push the branch,
|
|
2564
|
+
# then `gh pr create --head <branch>`. NO auto-merge. Every call is best-effort
|
|
2565
|
+
# (`|| true`); failures never block completion. This is a single sanctioned
|
|
2566
|
+
# local network call, never CI.
|
|
2567
|
+
#
|
|
2568
|
+
# Reconciliation with the existing GITHUB_PR path (run.sh create_github_pr,
|
|
2569
|
+
# invoked after run_autonomous returns when LOKI_GITHUB_PR=true): if GITHUB_PR
|
|
2570
|
+
# is already true we DEFER to that path and do nothing here, so a user who set
|
|
2571
|
+
# both knobs never gets a double PR.
|
|
2572
|
+
#===============================================================================
|
|
2573
|
+
on_run_complete() {
|
|
2574
|
+
# Default OFF.
|
|
2575
|
+
if [ "${LOKI_DELEGATE_PR:-0}" != "1" ]; then
|
|
2576
|
+
return 0
|
|
2577
|
+
fi
|
|
2578
|
+
# Defer to the existing dedicated PR path to avoid a double PR.
|
|
2579
|
+
if [ "${GITHUB_PR:-false}" = "true" ]; then
|
|
2580
|
+
return 0
|
|
2581
|
+
fi
|
|
2582
|
+
# Network-call timeout guard: a stalled network / auth prompt would
|
|
2583
|
+
# otherwise hang the completion path indefinitely in --bg. Run each network
|
|
2584
|
+
# call through `timeout 30` when available; fall back to the bare call if
|
|
2585
|
+
# timeout is not installed (a local wrapper keeps this set -u safe on bash
|
|
2586
|
+
# 3.2, where an empty array expansion would error). Keeps every existing
|
|
2587
|
+
# `|| true` non-fatal behavior.
|
|
2588
|
+
_loki_net() {
|
|
2589
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
2590
|
+
timeout 30 "$@"
|
|
2591
|
+
else
|
|
2592
|
+
"$@"
|
|
2593
|
+
fi
|
|
2594
|
+
}
|
|
2595
|
+
# Require gh + auth.
|
|
2596
|
+
if ! command -v gh >/dev/null 2>&1; then
|
|
2597
|
+
return 0
|
|
2598
|
+
fi
|
|
2599
|
+
if ! (cd "${TARGET_DIR:-.}" && _loki_net gh auth status) >/dev/null 2>&1; then
|
|
2600
|
+
return 0
|
|
2601
|
+
fi
|
|
2602
|
+
# Require a GitHub remote (skip silently on non-GitHub repos).
|
|
2603
|
+
local remote_url
|
|
2604
|
+
remote_url="$( (cd "${TARGET_DIR:-.}" && git config --get remote.origin.url) 2>/dev/null || true )"
|
|
2605
|
+
case "$remote_url" in
|
|
2606
|
+
*github.com*) : ;;
|
|
2607
|
+
*) return 0 ;;
|
|
2608
|
+
esac
|
|
2609
|
+
# Resolve current branch; never PR a default branch to itself.
|
|
2610
|
+
local branch
|
|
2611
|
+
branch="$( (cd "${TARGET_DIR:-.}" && git rev-parse --abbrev-ref HEAD) 2>/dev/null || true )"
|
|
2612
|
+
case "$branch" in
|
|
2613
|
+
""|main|master|HEAD) return 0 ;;
|
|
2614
|
+
esac
|
|
2615
|
+
log_info "LOKI_DELEGATE_PR=1: opening a local pull request for branch '$branch'..."
|
|
2616
|
+
# Push, then create. Non-interactive (no tty in --bg). Best-effort, each
|
|
2617
|
+
# network call bounded by the timeout guard above.
|
|
2618
|
+
(cd "${TARGET_DIR:-.}" && _loki_net git push -u origin "$branch") >/dev/null 2>&1 || true
|
|
2619
|
+
local pr_title
|
|
2620
|
+
pr_title="Loki Mode: ${branch}"
|
|
2621
|
+
local pr_url=""
|
|
2622
|
+
pr_url="$( (cd "${TARGET_DIR:-.}" && _loki_net gh pr create --title "$pr_title" --body "Opened by Loki Mode (delegate mode). Review locally before merge." --head "$branch") 2>/dev/null || true )"
|
|
2623
|
+
if [ -n "$pr_url" ]; then
|
|
2624
|
+
# Export so build_completion_summary folds the url into the summary.
|
|
2625
|
+
_LOKI_DELEGATE_PR_URL="$pr_url"
|
|
2626
|
+
export _LOKI_DELEGATE_PR_URL
|
|
2627
|
+
log_info "Pull request opened: $pr_url"
|
|
2628
|
+
else
|
|
2629
|
+
log_warn "LOKI_DELEGATE_PR=1: gh pr create did not return a URL (a PR may already exist for this branch)."
|
|
2630
|
+
fi
|
|
2631
|
+
return 0
|
|
2632
|
+
}
|
|
2633
|
+
|
|
2366
2634
|
#===============================================================================
|
|
2367
2635
|
# Parallel Workflow Functions (Git Worktrees)
|
|
2368
2636
|
#===============================================================================
|
|
@@ -10068,6 +10336,14 @@ build_prompt() {
|
|
|
10068
10336
|
# is the single most leveraged grounding primitive per OpenCode research.
|
|
10069
10337
|
local lsp_grounding_instruction="LSP_GROUNDING: When the loki-mode-lsp-proxy MCP server is available, prefer LSP tools for symbol verification BEFORE writing code that references those symbols. Workflow: (1) Need to call \`foo.bar()\` you have not already read? -> mcp__loki-mode-lsp-proxy__lsp_check_exists with symbol='bar' (sub-200ms when cached). If exists:false, do NOT write the call -- use mcp__loki-mode-lsp-proxy__lsp_workspace_symbols with the concept name to find the real symbol, or use Read to see the actual API. (2) Just edited a file? -> mcp__loki-mode-lsp-proxy__lsp_get_diagnostics on that file to see new errors before the next iteration. (3) Need to jump to a definition by name (no file:line known)? -> mcp__loki-mode-lsp-proxy__lsp_find_definition_by_name. Skip these tools silently when the server is not available -- check the tool list, do not retry on errors. Goal: eliminate hallucinated API calls before they ship."
|
|
10070
10338
|
|
|
10339
|
+
# AGENTS.md instruction (agents.md standard: plain Markdown at repo root,
|
|
10340
|
+
# nearest-file-wins, read natively by Claude Code/Codex/etc.). Loki prefers
|
|
10341
|
+
# AGENTS.md and falls back to CLAUDE.md only when AGENTS.md is absent; the
|
|
10342
|
+
# two are never merged. This string MUST stay byte-identical to
|
|
10343
|
+
# AGENTS_MD_INSTRUCTION in loki-ts/src/runner/build_prompt.ts (parity-locked,
|
|
10344
|
+
# same precedent as AUTONOMY_OVERRIDE_TEXT in providers/claude_flags.ts).
|
|
10345
|
+
local agents_md_instruction="Project conventions: read AGENTS.md in the repository root for build, test, and style conventions. If AGENTS.md is absent, read CLAUDE.md instead. The nearest such file to the code you are editing takes precedence."
|
|
10346
|
+
|
|
10071
10347
|
# Load existing context if resuming
|
|
10072
10348
|
local context_injection=""
|
|
10073
10349
|
if [ $retry -gt 0 ]; then
|
|
@@ -10397,15 +10673,15 @@ except Exception:
|
|
|
10397
10673
|
else
|
|
10398
10674
|
if [ $retry -eq 0 ]; then
|
|
10399
10675
|
if [ -n "$prd" ]; then
|
|
10400
|
-
echo "Loki Mode with PRD at $prd. $update_instruction $human_directive $gate_failure_context $queue_tasks $bmad_context $openspec_context $mirofish_context $magic_context $checklist_status $app_runner_info $playwright_info $memory_context_section $rarv_instruction $memory_instruction $usage_doc_instruction $lsp_grounding_instruction $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
10676
|
+
echo "Loki Mode with PRD at $prd. $update_instruction $human_directive $gate_failure_context $queue_tasks $bmad_context $openspec_context $mirofish_context $magic_context $checklist_status $app_runner_info $playwright_info $memory_context_section $rarv_instruction $memory_instruction $usage_doc_instruction $lsp_grounding_instruction $agents_md_instruction $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
10401
10677
|
else
|
|
10402
|
-
echo "Loki Mode. $human_directive $gate_failure_context $queue_tasks $bmad_context $openspec_context $mirofish_context $magic_context $checklist_status $app_runner_info $playwright_info $memory_context_section $analysis_instruction $rarv_instruction $memory_instruction $usage_doc_instruction $lsp_grounding_instruction $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
10678
|
+
echo "Loki Mode. $human_directive $gate_failure_context $queue_tasks $bmad_context $openspec_context $mirofish_context $magic_context $checklist_status $app_runner_info $playwright_info $memory_context_section $analysis_instruction $rarv_instruction $memory_instruction $usage_doc_instruction $lsp_grounding_instruction $agents_md_instruction $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
10403
10679
|
fi
|
|
10404
10680
|
else
|
|
10405
10681
|
if [ -n "$prd" ]; then
|
|
10406
|
-
echo "Loki Mode - Resume iteration #$iteration (retry #$retry). PRD: $prd. $human_directive $gate_failure_context $queue_tasks $bmad_context $openspec_context $mirofish_context $magic_context $checklist_status $app_runner_info $playwright_info $memory_context_section $rarv_instruction $memory_instruction $usage_doc_instruction $lsp_grounding_instruction $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
10682
|
+
echo "Loki Mode - Resume iteration #$iteration (retry #$retry). PRD: $prd. $human_directive $gate_failure_context $queue_tasks $bmad_context $openspec_context $mirofish_context $magic_context $checklist_status $app_runner_info $playwright_info $memory_context_section $rarv_instruction $memory_instruction $usage_doc_instruction $lsp_grounding_instruction $agents_md_instruction $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
10407
10683
|
else
|
|
10408
|
-
echo "Loki Mode - Resume iteration #$iteration (retry #$retry). $human_directive $gate_failure_context $queue_tasks $bmad_context $openspec_context $mirofish_context $magic_context $checklist_status $app_runner_info $playwright_info $memory_context_section Use .loki/generated-prd.md if exists. $rarv_instruction $memory_instruction $usage_doc_instruction $lsp_grounding_instruction $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
10684
|
+
echo "Loki Mode - Resume iteration #$iteration (retry #$retry). $human_directive $gate_failure_context $queue_tasks $bmad_context $openspec_context $mirofish_context $magic_context $checklist_status $app_runner_info $playwright_info $memory_context_section Use .loki/generated-prd.md if exists. $rarv_instruction $memory_instruction $usage_doc_instruction $lsp_grounding_instruction $agents_md_instruction $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
10409
10685
|
fi
|
|
10410
10686
|
fi
|
|
10411
10687
|
fi
|
|
@@ -10448,6 +10724,7 @@ except Exception:
|
|
|
10448
10724
|
fi
|
|
10449
10725
|
printf '%s\n' "$usage_doc_instruction"
|
|
10450
10726
|
printf '%s\n' "$lsp_grounding_instruction"
|
|
10727
|
+
printf '%s\n' "$agents_md_instruction"
|
|
10451
10728
|
printf '</loki_system>\n'
|
|
10452
10729
|
printf '[CACHE_BREAKPOINT]\n'
|
|
10453
10730
|
|
|
@@ -10480,6 +10757,7 @@ except Exception:
|
|
|
10480
10757
|
printf '%s\n' "$memory_instruction"
|
|
10481
10758
|
printf '%s\n' "$usage_doc_instruction"
|
|
10482
10759
|
printf '%s\n' "$lsp_grounding_instruction"
|
|
10760
|
+
printf '%s\n' "$agents_md_instruction"
|
|
10483
10761
|
# For codebase-analysis mode (no PRD), analysis_instruction is part of the
|
|
10484
10762
|
# static prefix so it remains cache-stable.
|
|
10485
10763
|
if [ -z "$prd" ]; then
|
|
@@ -11463,6 +11741,28 @@ run_autonomous() {
|
|
|
11463
11741
|
# file, which the gate treats as inconclusive (pass-through).
|
|
11464
11742
|
local _start_sha_file=".loki/state/start-sha"
|
|
11465
11743
|
mkdir -p ".loki/state"
|
|
11744
|
+
|
|
11745
|
+
# Delegate-then-notify (Slice 3): LOKI_DELEGATE_BRANCH=1 (default OFF)
|
|
11746
|
+
# isolates this run's work on a fresh branch loki/delegate-<timestamp> so the
|
|
11747
|
+
# user's working branch stays clean. Created IN-PROCESS (plain git, no
|
|
11748
|
+
# detached child) only on a genuine fresh run (ITERATION_COUNT==0) so a
|
|
11749
|
+
# resume does not spawn a new branch each time. Best-effort: a non-git repo,
|
|
11750
|
+
# dirty tree that blocks checkout, or any git failure leaves the run on the
|
|
11751
|
+
# current branch (default behavior preserved). Done BEFORE the start-sha
|
|
11752
|
+
# capture so the diff window baselines to the new branch HEAD.
|
|
11753
|
+
if [ "${LOKI_DELEGATE_BRANCH:-0}" = "1" ] && [ "${ITERATION_COUNT:-0}" -eq 0 ]; then
|
|
11754
|
+
if (cd "${TARGET_DIR:-.}" && git rev-parse --git-dir) >/dev/null 2>&1; then
|
|
11755
|
+
local _delegate_branch="loki/delegate-$(date +%Y%m%d-%H%M%S)"
|
|
11756
|
+
if (cd "${TARGET_DIR:-.}" && git checkout -b "$_delegate_branch") >/dev/null 2>&1; then
|
|
11757
|
+
_LOKI_DELEGATE_BRANCH_NAME="$_delegate_branch"
|
|
11758
|
+
export _LOKI_DELEGATE_BRANCH_NAME
|
|
11759
|
+
log_info "LOKI_DELEGATE_BRANCH=1: isolated work on new branch '$_delegate_branch'"
|
|
11760
|
+
else
|
|
11761
|
+
log_warn "LOKI_DELEGATE_BRANCH=1: could not create branch (dirty tree or git error); continuing on current branch."
|
|
11762
|
+
fi
|
|
11763
|
+
fi
|
|
11764
|
+
fi
|
|
11765
|
+
|
|
11466
11766
|
if [ "${ITERATION_COUNT:-0}" -eq 0 ] || [ ! -s "$_start_sha_file" ]; then
|
|
11467
11767
|
(cd "${TARGET_DIR:-.}" && git rev-parse HEAD 2>/dev/null) > "$_start_sha_file" 2>/dev/null || true
|
|
11468
11768
|
fi
|
|
@@ -11546,6 +11846,13 @@ except Exception as exc:
|
|
|
11546
11846
|
# Check max iterations before starting
|
|
11547
11847
|
if check_max_iterations; then
|
|
11548
11848
|
log_error "Max iterations already reached. Reset with: rm .loki/autonomy-state.json"
|
|
11849
|
+
# Delegate-then-notify: terminal state. Mirror the in-loop max-iterations
|
|
11850
|
+
# site so a detached (--bg) run still writes COMPLETION.txt + fires the
|
|
11851
|
+
# ping on this pre-loop exit. _LOKI_RUN_START_SHA is already exported
|
|
11852
|
+
# above (runner init), so the diff window is correct. This return is
|
|
11853
|
+
# mutually exclusive with the in-loop site (it returns before the loop),
|
|
11854
|
+
# so there is no double-emit.
|
|
11855
|
+
emit_completion_summary max_iterations
|
|
11549
11856
|
return 1
|
|
11550
11857
|
fi
|
|
11551
11858
|
|
|
@@ -11573,6 +11880,9 @@ except Exception as exc:
|
|
|
11573
11880
|
# Check max iterations
|
|
11574
11881
|
if check_max_iterations; then
|
|
11575
11882
|
save_state $retry "max_iterations_reached" 0
|
|
11883
|
+
# Delegate-then-notify: terminal state, write summary + ping so a
|
|
11884
|
+
# detached run tells the user it stopped at the iteration cap.
|
|
11885
|
+
emit_completion_summary max_iterations
|
|
11576
11886
|
return 0
|
|
11577
11887
|
fi
|
|
11578
11888
|
|
|
@@ -12469,7 +12779,11 @@ if __name__ == "__main__":
|
|
|
12469
12779
|
log_info "Council voted to stop (convergence detected + requirements verified)"
|
|
12470
12780
|
log_info "Running memory consolidation..."
|
|
12471
12781
|
run_memory_consolidation
|
|
12472
|
-
|
|
12782
|
+
# Delegate-then-notify: optional local PR on success, then the
|
|
12783
|
+
# durable summary + desktop ping. on_run_complete is idempotent
|
|
12784
|
+
# and only opens a PR when LOKI_DELEGATE_PR=1 (default OFF).
|
|
12785
|
+
on_run_complete
|
|
12786
|
+
emit_completion_summary complete
|
|
12473
12787
|
save_state $retry "council_approved" 0
|
|
12474
12788
|
rm -f "$iter_output" 2>/dev/null
|
|
12475
12789
|
return 0
|
|
@@ -12532,7 +12846,10 @@ if __name__ == "__main__":
|
|
|
12532
12846
|
# Run memory consolidation on successful completion
|
|
12533
12847
|
log_info "Running memory consolidation..."
|
|
12534
12848
|
run_memory_consolidation
|
|
12535
|
-
|
|
12849
|
+
# Delegate-then-notify: optional local PR on success, then the
|
|
12850
|
+
# durable summary + desktop ping (see on_run_complete).
|
|
12851
|
+
on_run_complete
|
|
12852
|
+
emit_completion_summary complete
|
|
12536
12853
|
save_state $retry "completion_promise_fulfilled" 0
|
|
12537
12854
|
rm -f "$iter_output" 2>/dev/null
|
|
12538
12855
|
return 0
|
|
@@ -12626,6 +12943,9 @@ if __name__ == "__main__":
|
|
|
12626
12943
|
|
|
12627
12944
|
log_error "Max retries ($MAX_RETRIES) exceeded"
|
|
12628
12945
|
save_state $retry "failed" 1
|
|
12946
|
+
# Delegate-then-notify: terminal failure. critical urgency so the desktop
|
|
12947
|
+
# ping is louder; the summary file records where the partial work landed.
|
|
12948
|
+
emit_completion_summary failed critical
|
|
12629
12949
|
return 1
|
|
12630
12950
|
}
|
|
12631
12951
|
|
|
@@ -12768,11 +13088,19 @@ check_human_intervention() {
|
|
|
12768
13088
|
log_warn "PAUSE file created by budget limit - NOT auto-clearing in perpetual mode"
|
|
12769
13089
|
log_warn "Budget limit reached. Remove .loki/signals/BUDGET_EXCEEDED and .loki/PAUSE to continue."
|
|
12770
13090
|
notify_intervention_needed "Budget limit reached - execution paused" 2>/dev/null || true
|
|
13091
|
+
# Genuinely blocking pause: write the durable intervention record
|
|
13092
|
+
# now (state-only; the ping above already fired). This is the
|
|
13093
|
+
# correct site for the durable file because the run actually halts
|
|
13094
|
+
# here until the operator clears the budget signal.
|
|
13095
|
+
build_completion_summary intervention 2>/dev/null || true
|
|
12771
13096
|
local pause_result
|
|
12772
13097
|
handle_pause
|
|
12773
13098
|
pause_result=$?
|
|
12774
13099
|
rm -f "$loki_dir/PAUSE"
|
|
12775
13100
|
if [ "$pause_result" -eq 1 ]; then
|
|
13101
|
+
# STOP requested DURING the pause: relabel the durable record
|
|
13102
|
+
# as stopped (state-only; the user typed STOP and is aware).
|
|
13103
|
+
build_completion_summary stopped 2>/dev/null || true
|
|
12776
13104
|
return 2
|
|
12777
13105
|
fi
|
|
12778
13106
|
return 1
|
|
@@ -12786,12 +13114,17 @@ check_human_intervention() {
|
|
|
12786
13114
|
fi
|
|
12787
13115
|
log_warn "PAUSE file detected - pausing execution"
|
|
12788
13116
|
notify_intervention_needed "Execution paused via PAUSE file"
|
|
13117
|
+
# Genuinely blocking pause: write the durable intervention record now
|
|
13118
|
+
# (state-only; the ping above already fired).
|
|
13119
|
+
build_completion_summary intervention 2>/dev/null || true
|
|
12789
13120
|
local pause_result
|
|
12790
13121
|
handle_pause
|
|
12791
13122
|
pause_result=$?
|
|
12792
13123
|
rm -f "$loki_dir/PAUSE"
|
|
12793
13124
|
if [ "$pause_result" -eq 1 ]; then
|
|
12794
|
-
# STOP was requested during pause
|
|
13125
|
+
# STOP was requested during pause: relabel the durable record as
|
|
13126
|
+
# stopped (state-only; the user typed STOP and is aware).
|
|
13127
|
+
build_completion_summary stopped 2>/dev/null || true
|
|
12795
13128
|
return 2
|
|
12796
13129
|
fi
|
|
12797
13130
|
return 1
|
|
@@ -12804,11 +13137,16 @@ check_human_intervention() {
|
|
|
12804
13137
|
rm -f "$loki_dir/PAUSE_AT_CHECKPOINT"
|
|
12805
13138
|
notify_intervention_needed "Execution paused at checkpoint"
|
|
12806
13139
|
touch "$loki_dir/PAUSE"
|
|
13140
|
+
# Genuinely blocking pause: write the durable intervention record now
|
|
13141
|
+
# (state-only; the ping above already fired).
|
|
13142
|
+
build_completion_summary intervention 2>/dev/null || true
|
|
12807
13143
|
local pause_result
|
|
12808
13144
|
handle_pause
|
|
12809
13145
|
pause_result=$?
|
|
12810
13146
|
rm -f "$loki_dir/PAUSE"
|
|
12811
13147
|
if [ "$pause_result" -eq 1 ]; then
|
|
13148
|
+
# STOP requested during pause: relabel as stopped (state-only).
|
|
13149
|
+
build_completion_summary stopped 2>/dev/null || true
|
|
12812
13150
|
return 2
|
|
12813
13151
|
fi
|
|
12814
13152
|
return 1
|
|
@@ -12876,7 +13214,11 @@ check_human_intervention() {
|
|
|
12876
13214
|
fi
|
|
12877
13215
|
log_info "Running memory consolidation..."
|
|
12878
13216
|
run_memory_consolidation
|
|
12879
|
-
|
|
13217
|
+
# Delegate-then-notify: force-review approval is a real completion
|
|
13218
|
+
# (returns 2, which the run loop maps to a clean return 0). Treat it
|
|
13219
|
+
# like the other success exits: optional local PR + summary + ping.
|
|
13220
|
+
on_run_complete
|
|
13221
|
+
emit_completion_summary complete
|
|
12880
13222
|
save_state ${RETRY_COUNT:-0} "council_force_approved" 0
|
|
12881
13223
|
return 2 # Stop
|
|
12882
13224
|
fi
|
|
@@ -12887,6 +13229,12 @@ check_human_intervention() {
|
|
|
12887
13229
|
if [ -f "$loki_dir/STOP" ]; then
|
|
12888
13230
|
log_warn "STOP file detected - stopping execution"
|
|
12889
13231
|
rm -f "$loki_dir/STOP"
|
|
13232
|
+
# Delegate-then-notify: an explicit STOP file is a deliberate stop, but
|
|
13233
|
+
# a detached (--bg) user still benefits from a summary of partial work.
|
|
13234
|
+
# NOTE: the SIGTERM/`loki stop` group-kill path (cleanup handler near the
|
|
13235
|
+
# end of this file) is intentionally NOT notified: that user is at a
|
|
13236
|
+
# terminal issuing the stop and is already aware.
|
|
13237
|
+
emit_completion_summary stopped
|
|
12890
13238
|
return 2
|
|
12891
13239
|
fi
|
|
12892
13240
|
|
|
@@ -13354,6 +13702,9 @@ main() {
|
|
|
13354
13702
|
echo -e " ${DIM}Logs:${NC} tail -f $log_file"
|
|
13355
13703
|
echo -e " ${DIM}Status:${NC} cat .loki/STATUS.txt"
|
|
13356
13704
|
echo ""
|
|
13705
|
+
echo -e "${GREEN}You will be notified when done (or if input is needed).${NC}"
|
|
13706
|
+
echo -e " ${DIM}Summary on completion:${NC} cat .loki/COMPLETION.txt"
|
|
13707
|
+
echo ""
|
|
13357
13708
|
|
|
13358
13709
|
exit 0
|
|
13359
13710
|
fi
|
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.22.0
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
package/loki-ts/dist/loki.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var f8=Object.defineProperty;var u8=($)=>$;function c8($,Q){this[$]=u8.bind(null,Q)}var g=($,Q)=>{for(var Z in Q)f8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:c8.bind(Q,Z)})};var k=($,Q)=>()=>($&&(Q=$($=0)),Q);var X1=import.meta.require;var F$={};g(F$,{lokiDir:()=>P,homeLokiDir:()=>o1,findRepoRootForVersion:()=>d1,REPO_ROOT:()=>f});import{resolve as n,dirname as l1}from"path";import{fileURLToPath as p8}from"url";import{existsSync as L1}from"fs";import{homedir as l8}from"os";function d8(){let $=j$;for(let Q=0;Q<6;Q++){if(L1(n($,"VERSION"))&&L1(n($,"autonomy/run.sh")))return $;let Z=l1($);if(Z===$)break;$=Z}return n(j$,"..","..","..")}function d1($){let Q=$;for(let Z=0;Z<6;Z++){if(L1(n(Q,"VERSION"))&&L1(n(Q,"autonomy/run.sh")))return Q;let z=l1(Q);if(z===Q)break;Q=z}return n($,"..","..","..")}function P(){return process.env.LOKI_DIR??n(process.cwd(),".loki")}function o1(){return n(l8(),".loki")}var j$,f;var y=k(()=>{j$=l1(p8(import.meta.url));f=d8()});import{readFileSync as o8}from"fs";import{resolve as n8,dirname as a8}from"path";import{fileURLToPath as s8}from"url";function k1(){if($1!==null)return $1;let $="7.
|
|
2
|
+
var f8=Object.defineProperty;var u8=($)=>$;function c8($,Q){this[$]=u8.bind(null,Q)}var g=($,Q)=>{for(var Z in Q)f8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:c8.bind(Q,Z)})};var k=($,Q)=>()=>($&&(Q=$($=0)),Q);var X1=import.meta.require;var F$={};g(F$,{lokiDir:()=>P,homeLokiDir:()=>o1,findRepoRootForVersion:()=>d1,REPO_ROOT:()=>f});import{resolve as n,dirname as l1}from"path";import{fileURLToPath as p8}from"url";import{existsSync as L1}from"fs";import{homedir as l8}from"os";function d8(){let $=j$;for(let Q=0;Q<6;Q++){if(L1(n($,"VERSION"))&&L1(n($,"autonomy/run.sh")))return $;let Z=l1($);if(Z===$)break;$=Z}return n(j$,"..","..","..")}function d1($){let Q=$;for(let Z=0;Z<6;Z++){if(L1(n(Q,"VERSION"))&&L1(n(Q,"autonomy/run.sh")))return Q;let z=l1(Q);if(z===Q)break;Q=z}return n($,"..","..","..")}function P(){return process.env.LOKI_DIR??n(process.cwd(),".loki")}function o1(){return n(l8(),".loki")}var j$,f;var y=k(()=>{j$=l1(p8(import.meta.url));f=d8()});import{readFileSync as o8}from"fs";import{resolve as n8,dirname as a8}from"path";import{fileURLToPath as s8}from"url";function k1(){if($1!==null)return $1;let $="7.22.0";if(typeof $==="string"&&$.length>0)return $1=$,$1;try{let Q=a8(s8(import.meta.url)),Z=d1(Q);$1=o8(n8(Z,"VERSION"),"utf-8").trim()}catch{$1="unknown"}return $1}var $1=null;var n1=k(()=>{y()});var E$={};g(E$,{runOrThrow:()=>t8,run:()=>j,commandVersion:()=>i8,commandExists:()=>v,ShellError:()=>a1});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,K;if(Q.timeoutMs&&Q.timeoutMs>0)z=setTimeout(()=>{try{Z.kill("SIGTERM")}catch{}K=setTimeout(()=>{try{Z.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[H,X,q]=await Promise.all([new Response(Z.stdout).text(),new Response(Z.stderr).text(),Z.exited]);return{stdout:H,stderr:X,exitCode:q}}finally{if(z)clearTimeout(z);if(K)clearTimeout(K)}}async function t8($,Q={}){let Z=await j($,Q);if(Z.exitCode!==0)throw new a1(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function v($){let Q=r8($),Z=await j(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function r8($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function i8($,Q="--version"){if(!await v($))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 a1;var d=k(()=>{a1=class a1 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 e8?"":$}var e8,T,N,_,KZ,A,R,h,J;var c=k(()=>{e8=(process.env.NO_COLOR??"").length>0;T=a("\x1B[0;31m"),N=a("\x1B[0;32m"),_=a("\x1B[1;33m"),KZ=a("\x1B[0;34m"),A=a("\x1B[0;36m"),R=a("\x1B[1m"),h=a("\x1B[2m"),J=a("\x1B[0m")});import{existsSync as U7}from"fs";async function Q1(){if(B1!==void 0)return B1;let $="/opt/homebrew/bin/python3.12";if(U7($))return B1=$,$;let Q=await v("python3.12");if(Q)return B1=Q,Q;let Z=await v("python3");return B1=Z,Z}async function Z1($,Q={}){let Z=await Q1();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return j([Z,"-c",$],Q)}var B1;var H1=k(()=>{d()});var d$={};g(d$,{runStatus:()=>N7});import{existsSync as b,readFileSync as q1,readdirSync as v$,statSync as f$}from"fs";import{resolve as D,basename as P7}from"path";import{homedir as L7}from"os";async function j7(){if(await v("jq"))return!0;return process.stdout.write(`${T}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)
|
|
@@ -787,4 +787,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
|
|
|
787
787
|
`),2}default:return process.stderr.write(`Unknown command: ${Q}
|
|
788
788
|
`),process.stderr.write(v8),2}}g$();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var l3=await p3(Bun.argv.slice(2));process.exit(l3);
|
|
789
789
|
|
|
790
|
-
//# debugId=
|
|
790
|
+
//# debugId=BA6A18624E28A6CC64756E2164756E21
|
package/mcp/__init__.py
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loki-mode",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.22.0",
|
|
4
4
|
"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).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|