loki-mode 7.82.0 → 7.83.1
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 +532 -18
- package/autonomy/run.sh +168 -18
- package/completions/_loki +21 -3
- package/completions/loki.bash +20 -5
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +2 -2
- package/loki-ts/dist/loki.js +7 -4
- 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.83.1
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -406,4 +406,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
406
406
|
|
|
407
407
|
---
|
|
408
408
|
|
|
409
|
-
**v7.
|
|
409
|
+
**v7.83.1 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.
|
|
1
|
+
7.83.1
|
package/autonomy/loki
CHANGED
|
@@ -265,6 +265,30 @@ _loki_new_session_exec() {
|
|
|
265
265
|
fi
|
|
266
266
|
}
|
|
267
267
|
|
|
268
|
+
# Actionable PRD-not-found exit. Both PRD-path existence guards in cmd_start route
|
|
269
|
+
# through here so they cannot drift: the user always learns the two recoveries
|
|
270
|
+
# (fix the path, or drop the arg to auto-generate from the codebase). If a sibling
|
|
271
|
+
# .md file looks like a near-miss of the bad path, surface it honestly as a hint.
|
|
272
|
+
# Prints to stderr and exits 1. Honest: only suggests a "did you mean" when a real
|
|
273
|
+
# .md sibling exists; never fabricates a path.
|
|
274
|
+
_prd_not_found_die() {
|
|
275
|
+
local bad_path="$1"
|
|
276
|
+
echo -e "${RED}Error: PRD file not found: $bad_path${NC}" >&2
|
|
277
|
+
echo -e "${YELLOW}Hint: provide a path to an existing .md/.json/.txt/.yaml/.yml file, or run 'loki start' with no args to analyze the current directory.${NC}" >&2
|
|
278
|
+
# Best-effort near-miss suggestion: if a .md file sits in the same directory
|
|
279
|
+
# as the bad path, point at it. Read-only; silent when nothing matches.
|
|
280
|
+
local dir base suggestion
|
|
281
|
+
dir="$(dirname "$bad_path" 2>/dev/null)" || dir="."
|
|
282
|
+
base="$(basename "$bad_path" 2>/dev/null)"
|
|
283
|
+
if [ -d "$dir" ] && [ -n "$base" ]; then
|
|
284
|
+
suggestion="$(ls -1 "$dir"/*.md 2>/dev/null | head -1)"
|
|
285
|
+
if [ -n "$suggestion" ]; then
|
|
286
|
+
echo -e "${YELLOW}Did you mean: $suggestion ?${NC}" >&2
|
|
287
|
+
fi
|
|
288
|
+
fi
|
|
289
|
+
exit 1
|
|
290
|
+
}
|
|
291
|
+
|
|
268
292
|
# Anonymous usage telemetry
|
|
269
293
|
PROJECT_DIR="$SKILL_DIR"
|
|
270
294
|
_TELEMETRY_SCRIPT="$SKILL_DIR/autonomy/telemetry.sh"
|
|
@@ -714,6 +738,7 @@ show_help() {
|
|
|
714
738
|
echo ""
|
|
715
739
|
echo "Session:"
|
|
716
740
|
echo " status [--json] Show current status (--json for machine-readable)"
|
|
741
|
+
echo " next Run the right next step for you (resume / ship / why)"
|
|
717
742
|
echo " stop Stop execution immediately"
|
|
718
743
|
echo " pause Pause after current session"
|
|
719
744
|
echo " resume Resume paused execution"
|
|
@@ -745,7 +770,7 @@ show_help() {
|
|
|
745
770
|
echo " version Show version"
|
|
746
771
|
echo " help Show this help ('loki help aliases' for old names)"
|
|
747
772
|
echo ""
|
|
748
|
-
echo "More commands (grill, spec, deploy, cleanup, init, watch, demo, web, api,"
|
|
773
|
+
echo "More commands (ship, grill, spec, deploy, cleanup, init, watch, demo, web, api,"
|
|
749
774
|
echo "logs, github, import, council, proof, audit, compliance, agent, template,"
|
|
750
775
|
echo "magic, docs, wiki, ci, test, bench, secrets, telemetry, crash, worktree,"
|
|
751
776
|
echo "failover, monitor, remote, ...) are dispatchable and documented via"
|
|
@@ -909,6 +934,7 @@ show_landing() {
|
|
|
909
934
|
echo -e "First time here? ${CYAN}loki doctor${NC} checks your setup (an AI provider CLI is required)."
|
|
910
935
|
echo ""
|
|
911
936
|
echo "Get started:"
|
|
937
|
+
echo -e " ${CYAN}loki quickstart${NC} Guided first build from your idea (no PRD needed)"
|
|
912
938
|
echo -e " ${CYAN}loki start ./prd.md${NC} Build from a spec (PRD file, GitHub issue, or no arg)"
|
|
913
939
|
echo -e " ${CYAN}loki demo${NC} Build a sample todo app end to end (real run)"
|
|
914
940
|
echo -e " ${CYAN}loki dashboard start${NC} Start the live run monitor (then: loki dashboard open)"
|
|
@@ -1686,9 +1712,7 @@ cmd_start() {
|
|
|
1686
1712
|
case "$prd_file" in
|
|
1687
1713
|
*.md|*.json|*.txt|*.yaml|*.yml)
|
|
1688
1714
|
if [ ! -f "$prd_file" ]; then
|
|
1689
|
-
|
|
1690
|
-
echo "Hint: provide a path to an existing .md/.json/.txt/.yaml/.yml file, or run 'loki start' with no args to analyze the current directory." >&2
|
|
1691
|
-
exit 1
|
|
1715
|
+
_prd_not_found_die "$prd_file"
|
|
1692
1716
|
fi
|
|
1693
1717
|
;;
|
|
1694
1718
|
esac
|
|
@@ -1711,8 +1735,7 @@ cmd_start() {
|
|
|
1711
1735
|
case "$prd_file" in
|
|
1712
1736
|
*.md|*.json|*.txt|*.yaml|*.yml)
|
|
1713
1737
|
if [ ! -f "$prd_file" ]; then
|
|
1714
|
-
|
|
1715
|
-
exit 1
|
|
1738
|
+
_prd_not_found_die "$prd_file"
|
|
1716
1739
|
fi
|
|
1717
1740
|
;;
|
|
1718
1741
|
esac
|
|
@@ -2013,7 +2036,13 @@ cmd_start() {
|
|
|
2013
2036
|
fi
|
|
2014
2037
|
if [ -n "$_existing_pid" ] && kill -0 "$_existing_pid" 2>/dev/null; then
|
|
2015
2038
|
echo -e "${RED}Error: another loki instance is running (pid $_existing_pid).${NC}" >&2
|
|
2016
|
-
|
|
2039
|
+
# 'loki stop' is folder-scoped by default (v7.7.30), so it only stops a
|
|
2040
|
+
# run in THIS directory. If the live instance is in another project,
|
|
2041
|
+
# 'loki stop' here reports "No active session" and the user is stuck in
|
|
2042
|
+
# a start-says-running / stop-says-clean loop. Point at the global stop
|
|
2043
|
+
# and at 'loki status' to find the live run.
|
|
2044
|
+
echo -e "${YELLOW}Run 'loki stop' here, or 'loki stop --all' if it is running in another project.${NC}" >&2
|
|
2045
|
+
echo -e "${YELLOW}See 'loki status' for the live run, then retry 'loki start'.${NC}" >&2
|
|
2017
2046
|
exit 1
|
|
2018
2047
|
fi
|
|
2019
2048
|
# PID is stale (file present but process gone). Log + remove + continue.
|
|
@@ -2954,6 +2983,386 @@ cmd_resume() {
|
|
|
2954
2983
|
fi
|
|
2955
2984
|
}
|
|
2956
2985
|
|
|
2986
|
+
# Resolve the terminal-run status from the same already-captured artifacts that
|
|
2987
|
+
# cmd_why reads (no new state, no spend). Echoes a single token: the resolved
|
|
2988
|
+
# status string (e.g. council_approved, paused, max_iterations_reached), or the
|
|
2989
|
+
# literal "none" when no run exists here yet. Used by cmd_next so its status->
|
|
2990
|
+
# action mapping reads exactly the same state cmd_why does (no drift).
|
|
2991
|
+
# Read-only; never fabricates.
|
|
2992
|
+
_loki_resolve_run_status() {
|
|
2993
|
+
local loki_dir="${LOKI_DIR:-.loki}"
|
|
2994
|
+
local state_file="$loki_dir/autonomy-state.json"
|
|
2995
|
+
if [ -n "${LOKI_SESSION_ID:-}" ] && [ -f "$loki_dir/sessions/${LOKI_SESSION_ID}/autonomy-state.json" ]; then
|
|
2996
|
+
state_file="$loki_dir/sessions/${LOKI_SESSION_ID}/autonomy-state.json"
|
|
2997
|
+
fi
|
|
2998
|
+
local completion_file="$loki_dir/state/completion.json"
|
|
2999
|
+
|
|
3000
|
+
if [ ! -f "$state_file" ] && [ ! -f "$completion_file" ]; then
|
|
3001
|
+
printf '%s\n' "none"
|
|
3002
|
+
return 0
|
|
3003
|
+
fi
|
|
3004
|
+
|
|
3005
|
+
_LOKI_NA_STATE="$state_file" _LOKI_NA_COMPLETION="$completion_file" python3 - <<'NASTATUS'
|
|
3006
|
+
import json, os
|
|
3007
|
+
def load(p):
|
|
3008
|
+
try:
|
|
3009
|
+
with open(p) as f: return json.load(f)
|
|
3010
|
+
except Exception: return {}
|
|
3011
|
+
state = load(os.environ.get("_LOKI_NA_STATE", ""))
|
|
3012
|
+
comp = load(os.environ.get("_LOKI_NA_COMPLETION", ""))
|
|
3013
|
+
# Mirror cmd_why's COMP_OUTCOME_ALIASES so a completion-only state (a rotated or
|
|
3014
|
+
# crashed run with no live status, only completion.json's outcome) resolves to
|
|
3015
|
+
# the canonical status loki next's action map keys on. Without this, an outcome
|
|
3016
|
+
# of "complete"/"max_iterations" drifted to the default "loki status" instead of
|
|
3017
|
+
# "loki ship"/"loki resume".
|
|
3018
|
+
COMP_OUTCOME_ALIASES = {
|
|
3019
|
+
"complete": "council_approved",
|
|
3020
|
+
"max_iterations": "max_iterations_reached",
|
|
3021
|
+
"intervention": "paused",
|
|
3022
|
+
}
|
|
3023
|
+
status = state.get("status")
|
|
3024
|
+
if not status:
|
|
3025
|
+
raw_outcome = comp.get("outcome") or ""
|
|
3026
|
+
status = COMP_OUTCOME_ALIASES.get(raw_outcome, raw_outcome) or "unknown"
|
|
3027
|
+
print(status)
|
|
3028
|
+
NASTATUS
|
|
3029
|
+
}
|
|
3030
|
+
|
|
3031
|
+
# Map a resolved status to the single concrete next-action SENTENCE (the same
|
|
3032
|
+
# wording cmd_why prints under "What to do"). Echoes the action string. Kept in
|
|
3033
|
+
# lockstep with cmd_why's GUIDE map so why and next agree.
|
|
3034
|
+
_loki_next_action() {
|
|
3035
|
+
local status="${1:-unknown}"
|
|
3036
|
+
case "$status" in
|
|
3037
|
+
council_approved)
|
|
3038
|
+
echo "Review the diff and open a PR (loki ship, or git push + gh pr create)." ;;
|
|
3039
|
+
council_force_approved)
|
|
3040
|
+
echo "Review the diff carefully before merging -- convergence was not unanimous." ;;
|
|
3041
|
+
completion_promise_fulfilled)
|
|
3042
|
+
echo "Verify the promised outcome, then review and PR (loki ship)." ;;
|
|
3043
|
+
max_iterations_reached)
|
|
3044
|
+
echo "Raise LOKI_MAX_ITERATIONS (or narrow the spec), then resume (loki resume)." ;;
|
|
3045
|
+
max_retries_exceeded)
|
|
3046
|
+
echo "Read .loki/logs for the recurring error, fix the root cause, then re-run (loki why)." ;;
|
|
3047
|
+
failed)
|
|
3048
|
+
echo "Read the failure (loki why), fix it, then re-run." ;;
|
|
3049
|
+
policy_blocked)
|
|
3050
|
+
echo "Review the blocking finding (loki why); address it or use the documented override." ;;
|
|
3051
|
+
budget_exceeded)
|
|
3052
|
+
echo "Raise LOKI_BUDGET_LIMIT or accept the partial result, then resume (loki resume)." ;;
|
|
3053
|
+
paused)
|
|
3054
|
+
echo "Resume with: loki resume." ;;
|
|
3055
|
+
interrupted)
|
|
3056
|
+
echo "Resume with: loki resume." ;;
|
|
3057
|
+
stopped)
|
|
3058
|
+
echo "Start a new build (loki start), or resume if you meant to continue." ;;
|
|
3059
|
+
force_stopped)
|
|
3060
|
+
echo "Start a new build, or investigate why a force-stop was needed (loki why)." ;;
|
|
3061
|
+
running)
|
|
3062
|
+
echo "A build looks active (or crashed mid-run); check loki status." ;;
|
|
3063
|
+
none)
|
|
3064
|
+
echo "Run a build first: loki start <spec>." ;;
|
|
3065
|
+
*)
|
|
3066
|
+
echo "Check loki status and loki why for detail." ;;
|
|
3067
|
+
esac
|
|
3068
|
+
}
|
|
3069
|
+
|
|
3070
|
+
# loki next -- one command that runs the right next step for you.
|
|
3071
|
+
# Reads the SAME already-captured state as cmd_why (no new state, no spend),
|
|
3072
|
+
# resolves the terminal status, and proposes the single mapped next step, then
|
|
3073
|
+
# runs it on confirmation. Default on a TTY is confirm-then-run; --yes runs it
|
|
3074
|
+
# without asking; --dry-run and any non-TTY context only PRINT (honest: never
|
|
3075
|
+
# auto-acts in CI / when piped). Strictly additive: reuses cmd_resume, cmd_ship,
|
|
3076
|
+
# cmd_why, and cmd_status; never touches the build path.
|
|
3077
|
+
cmd_next() {
|
|
3078
|
+
local assume_yes=false
|
|
3079
|
+
local dry_run=false
|
|
3080
|
+
case "${1:-}" in
|
|
3081
|
+
--help|-h|help)
|
|
3082
|
+
echo -e "${BOLD}Loki Mode -- run the right next step for you${NC}"
|
|
3083
|
+
echo ""
|
|
3084
|
+
echo "Usage: loki next [--yes] [--dry-run]"
|
|
3085
|
+
echo ""
|
|
3086
|
+
echo "Reads the last build's recorded outcome (the same state 'loki why'"
|
|
3087
|
+
echo "reads -- no new run, no cost) and carries you forward by running the"
|
|
3088
|
+
echo "single mapped next step: resume a paused/interrupted run, finish a"
|
|
3089
|
+
echo "council-approved build (loki ship), or route a failure to 'loki why'."
|
|
3090
|
+
echo ""
|
|
3091
|
+
echo "Default on a terminal: it shows the proposed step and asks before"
|
|
3092
|
+
echo "running it. Non-interactive (piped/CI) it only prints the step;"
|
|
3093
|
+
echo "it never acts on its own there."
|
|
3094
|
+
echo ""
|
|
3095
|
+
echo "Options:"
|
|
3096
|
+
echo " --yes Run the proposed step without asking"
|
|
3097
|
+
echo " --dry-run Print the proposed step only; do not run it"
|
|
3098
|
+
echo " --help, -h Show this help and exit"
|
|
3099
|
+
return 0
|
|
3100
|
+
;;
|
|
3101
|
+
--yes|-y) assume_yes=true ;;
|
|
3102
|
+
--dry-run) dry_run=true ;;
|
|
3103
|
+
"") : ;;
|
|
3104
|
+
*) echo -e "${RED}Unknown flag: $1${NC}" >&2; echo "Usage: loki next [--yes] [--dry-run]" >&2; return 1 ;;
|
|
3105
|
+
esac
|
|
3106
|
+
# Allow a second flag (e.g. --dry-run --yes order-agnostic).
|
|
3107
|
+
case "${2:-}" in
|
|
3108
|
+
--yes|-y) assume_yes=true ;;
|
|
3109
|
+
--dry-run) dry_run=true ;;
|
|
3110
|
+
esac
|
|
3111
|
+
|
|
3112
|
+
local status
|
|
3113
|
+
status="$(_loki_resolve_run_status)"
|
|
3114
|
+
if [ "$status" = "none" ]; then
|
|
3115
|
+
echo "loki next: no run found here yet." >&2
|
|
3116
|
+
echo "Run a build first: loki start <spec>" >&2
|
|
3117
|
+
return 1
|
|
3118
|
+
fi
|
|
3119
|
+
|
|
3120
|
+
local action_text
|
|
3121
|
+
action_text="$(_loki_next_action "$status")"
|
|
3122
|
+
|
|
3123
|
+
# Map the status to the concrete callable that carries the user forward.
|
|
3124
|
+
# action_cmd is a human label; the case below dispatches to the real function.
|
|
3125
|
+
local action_cmd=""
|
|
3126
|
+
case "$status" in
|
|
3127
|
+
paused|interrupted) action_cmd="loki resume" ;;
|
|
3128
|
+
council_approved|council_force_approved|completion_promise_fulfilled)
|
|
3129
|
+
action_cmd="loki ship" ;;
|
|
3130
|
+
max_iterations_reached|budget_exceeded) action_cmd="loki resume" ;;
|
|
3131
|
+
failed|max_retries_exceeded|policy_blocked|force_stopped)
|
|
3132
|
+
action_cmd="loki why" ;;
|
|
3133
|
+
*) action_cmd="loki status" ;;
|
|
3134
|
+
esac
|
|
3135
|
+
|
|
3136
|
+
echo -e "${BOLD}Loki: next${NC}"
|
|
3137
|
+
echo " Last outcome : $status"
|
|
3138
|
+
echo " Next step : $action_text"
|
|
3139
|
+
echo " Will run : $action_cmd"
|
|
3140
|
+
echo ""
|
|
3141
|
+
|
|
3142
|
+
# Honesty gate: only ACT on an interactive terminal with consent, or with
|
|
3143
|
+
# --yes. --dry-run and non-TTY always print-only.
|
|
3144
|
+
if [ "$dry_run" = true ]; then
|
|
3145
|
+
echo "(dry-run: not running. Run '$action_cmd' yourself, or 'loki next --yes'.)"
|
|
3146
|
+
return 0
|
|
3147
|
+
fi
|
|
3148
|
+
if [ "$assume_yes" != true ]; then
|
|
3149
|
+
if [ ! -t 0 ] || [ ! -t 1 ]; then
|
|
3150
|
+
echo "(non-interactive: not running. Run '$action_cmd', or 'loki next --yes'.)"
|
|
3151
|
+
return 0
|
|
3152
|
+
fi
|
|
3153
|
+
local reply=""
|
|
3154
|
+
printf "Run '%s' now? [y/N] " "$action_cmd"
|
|
3155
|
+
read -r reply || reply=""
|
|
3156
|
+
case "$reply" in
|
|
3157
|
+
y|Y|yes|YES) : ;;
|
|
3158
|
+
*) echo "Skipped. Run '$action_cmd' when ready."; return 0 ;;
|
|
3159
|
+
esac
|
|
3160
|
+
fi
|
|
3161
|
+
|
|
3162
|
+
# Dispatch to the real command. For budget/iteration caps, print the env-var
|
|
3163
|
+
# guidance first (the resume alone will not lift the cap), then resume.
|
|
3164
|
+
case "$status" in
|
|
3165
|
+
max_iterations_reached)
|
|
3166
|
+
echo "Note: raise the cap first if you want more iterations, e.g.:"
|
|
3167
|
+
echo " LOKI_MAX_ITERATIONS=<n> loki resume"
|
|
3168
|
+
echo ""
|
|
3169
|
+
;;
|
|
3170
|
+
budget_exceeded)
|
|
3171
|
+
echo "Note: raise the budget first if you want to continue, e.g.:"
|
|
3172
|
+
echo " LOKI_BUDGET_LIMIT=<usd> loki resume"
|
|
3173
|
+
echo ""
|
|
3174
|
+
;;
|
|
3175
|
+
esac
|
|
3176
|
+
case "$action_cmd" in
|
|
3177
|
+
"loki resume") cmd_resume ;;
|
|
3178
|
+
"loki ship") cmd_ship ;;
|
|
3179
|
+
"loki why") cmd_why ;;
|
|
3180
|
+
*) cmd_status ;;
|
|
3181
|
+
esac
|
|
3182
|
+
return $?
|
|
3183
|
+
}
|
|
3184
|
+
|
|
3185
|
+
# loki ship -- the build is done, now get it out (review + PR advice in one flow).
|
|
3186
|
+
# Composes existing surfaces only: optionally opens the running app (--preview),
|
|
3187
|
+
# runs the SAME diff quality gates as cmd_review, and on a clean-enough result
|
|
3188
|
+
# prints the exact branch-aware PR command via the shared print_pr_advice (the
|
|
3189
|
+
# same advisory cmd_deploy uses). PRINT-ONLY for shipping: it NEVER pushes and
|
|
3190
|
+
# NEVER deploys; it prints (and clipboards, as the advisory already does) the
|
|
3191
|
+
# git push + PR command for YOU to run. If review finds HIGH/CRITICAL it stops
|
|
3192
|
+
# and points at the findings instead of advising a PR.
|
|
3193
|
+
cmd_ship() {
|
|
3194
|
+
local do_preview=false
|
|
3195
|
+
local review_args=()
|
|
3196
|
+
# Tracks whether the caller passed an explicit scope flag (--staged, --since,
|
|
3197
|
+
# ...). Passthrough flags like --yes/--severity do NOT set this, so they do
|
|
3198
|
+
# not suppress the clean-loki-branch auto-scoping below.
|
|
3199
|
+
local ship_explicit_scope=false
|
|
3200
|
+
while [ $# -gt 0 ]; do
|
|
3201
|
+
case "${1:-}" in
|
|
3202
|
+
--help|-h|help)
|
|
3203
|
+
echo -e "${BOLD}Loki Mode -- finish the build: review, then PR advice (print-only)${NC}"
|
|
3204
|
+
echo ""
|
|
3205
|
+
echo "Usage: loki ship [--preview] [--yes] [review options]"
|
|
3206
|
+
echo ""
|
|
3207
|
+
echo "Runs the natural finish line in one command:"
|
|
3208
|
+
echo " 1. (with --preview) opens the app Loki built and started locally"
|
|
3209
|
+
echo " 2. runs 'loki review' on your uncommitted/branch diff (the same"
|
|
3210
|
+
echo " quality gates)"
|
|
3211
|
+
echo " 3. on a clean-enough result, PRINTS the exact branch-aware"
|
|
3212
|
+
echo " git push + pull-request command for YOU to run"
|
|
3213
|
+
echo ""
|
|
3214
|
+
echo "It is advisory only for shipping: it NEVER runs 'git push', NEVER"
|
|
3215
|
+
echo "creates a PR, and NEVER deploys. You run the printed command."
|
|
3216
|
+
echo "If review finds HIGH or CRITICAL findings it stops and points you"
|
|
3217
|
+
echo "at the findings instead of advising a PR (exit code 1)."
|
|
3218
|
+
echo ""
|
|
3219
|
+
echo "Options:"
|
|
3220
|
+
echo " --preview Open the running local app first (loki preview)"
|
|
3221
|
+
echo " --yes, -y Pass through to review (skip its confirmation prompt)"
|
|
3222
|
+
echo " --help, -h Show this help and exit"
|
|
3223
|
+
echo ""
|
|
3224
|
+
echo "Any other options are passed through to 'loki review'"
|
|
3225
|
+
echo "(e.g. --staged, --since <commit>, --severity high)."
|
|
3226
|
+
return 0
|
|
3227
|
+
;;
|
|
3228
|
+
--preview) do_preview=true ;;
|
|
3229
|
+
# Only a known set of NON-scope passthrough flags are exempt from
|
|
3230
|
+
# "explicit scope". Everything else the caller passes -- an explicit
|
|
3231
|
+
# scope flag (--staged/--since/...), a positional <file-or-dir>, or any
|
|
3232
|
+
# other review flag that narrows WHAT is reviewed -- counts as explicit
|
|
3233
|
+
# scope, so we do NOT override it with the clean-branch auto-scoping
|
|
3234
|
+
# below. This is the safe default: passthrough-that-changes-scope must
|
|
3235
|
+
# win over our auto-scope. The exempt list is only flags that never
|
|
3236
|
+
# change scope: --yes/-y (skip prompt), --severity (threshold).
|
|
3237
|
+
# (Without this, `loki ship somefile.py` on a clean loki branch would
|
|
3238
|
+
# get --since base injected and review the whole range, not the file.)
|
|
3239
|
+
--yes|-y|--severity=*)
|
|
3240
|
+
review_args+=("$1") ;;
|
|
3241
|
+
# --severity in its DOCUMENTED two-token form (`--severity high`) takes
|
|
3242
|
+
# a VALUE in the next arg. Consume both here so the value does not fall
|
|
3243
|
+
# to the catch-all and wrongly mark explicit scope (which would skip the
|
|
3244
|
+
# clean-branch auto-scoping and re-introduce the empty-diff false-clean
|
|
3245
|
+
# bug). --severity sets a threshold, not a scope, so neither token is
|
|
3246
|
+
# explicit scope.
|
|
3247
|
+
--severity)
|
|
3248
|
+
review_args+=("$1")
|
|
3249
|
+
if [ "$#" -ge 2 ] && [ "${2#-}" = "$2" ]; then
|
|
3250
|
+
review_args+=("$2"); shift
|
|
3251
|
+
fi
|
|
3252
|
+
;;
|
|
3253
|
+
*) review_args+=("$1"); ship_explicit_scope=true ;;
|
|
3254
|
+
esac
|
|
3255
|
+
shift
|
|
3256
|
+
done
|
|
3257
|
+
|
|
3258
|
+
# 1. Optional preview of the running local app. Only when an app-runner state
|
|
3259
|
+
# file exists, so we never imply an app is running when it is not.
|
|
3260
|
+
if [ "$do_preview" = true ]; then
|
|
3261
|
+
local loki_dir="${LOKI_DIR:-.loki}"
|
|
3262
|
+
if [ -f "$loki_dir/state/app-runner.json" ] || [ -f "$loki_dir/app-runner.json" ]; then
|
|
3263
|
+
echo -e "${BOLD}Opening the running app...${NC}"
|
|
3264
|
+
cmd_preview || true
|
|
3265
|
+
echo ""
|
|
3266
|
+
else
|
|
3267
|
+
echo "(no running app detected; skipping preview. Start one with loki start.)"
|
|
3268
|
+
echo ""
|
|
3269
|
+
fi
|
|
3270
|
+
fi
|
|
3271
|
+
|
|
3272
|
+
# 2. Run the SAME diff quality gates as cmd_review. Capture its documented
|
|
3273
|
+
# exit code (0 clean, 1 HIGH, 2 CRITICAL). set -e safe: capture via || rc=$?.
|
|
3274
|
+
#
|
|
3275
|
+
# Scope correction: cmd_review defaults to the UNCOMMITTED diff, but run.sh
|
|
3276
|
+
# auto-commits the session's work to the loki/session-* branch, so by the
|
|
3277
|
+
# time `loki ship` runs the tree is usually CLEAN -- which would make review
|
|
3278
|
+
# see an empty diff and falsely report "clean" before advising a PR (the
|
|
3279
|
+
# safety gate would be a no-op exactly when it matters). When the caller
|
|
3280
|
+
# gave no explicit scope AND we are on a loki/* branch with a clean tree,
|
|
3281
|
+
# default the scope to the branch-vs-base RANGE (the work being shipped),
|
|
3282
|
+
# resolved the same way the PR advice below resolves it.
|
|
3283
|
+
local _ship_dir="."
|
|
3284
|
+
if [ "$ship_explicit_scope" = false ]; then
|
|
3285
|
+
local _ship_head="" _ship_base="" _ship_dirty=""
|
|
3286
|
+
_ship_head="$(git -C "$_ship_dir" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")"
|
|
3287
|
+
# Untracked files (e.g. a gitignored .loki/) do not count as "dirty" --
|
|
3288
|
+
# what matters is whether the shippable TRACKED work is already committed.
|
|
3289
|
+
_ship_dirty="$(git -C "$_ship_dir" status --porcelain --untracked-files=no 2>/dev/null)"
|
|
3290
|
+
case "$_ship_head" in
|
|
3291
|
+
loki/*)
|
|
3292
|
+
if [ -z "$_ship_dirty" ]; then
|
|
3293
|
+
if [ -s "$_ship_dir/.loki/state/base-branch.txt" ]; then
|
|
3294
|
+
_ship_base="$(head -n1 "$_ship_dir/.loki/state/base-branch.txt" 2>/dev/null || echo "")"
|
|
3295
|
+
fi
|
|
3296
|
+
if [ -z "$_ship_base" ]; then
|
|
3297
|
+
local _ship_oh=""
|
|
3298
|
+
_ship_oh="$(git -C "$_ship_dir" symbolic-ref refs/remotes/origin/HEAD 2>/dev/null || echo "")"
|
|
3299
|
+
[ -n "$_ship_oh" ] && _ship_base="${_ship_oh##*/}"
|
|
3300
|
+
fi
|
|
3301
|
+
if [ -n "$_ship_base" ] && git -C "$_ship_dir" rev-parse --verify --quiet "$_ship_base" >/dev/null 2>&1; then
|
|
3302
|
+
review_args+=("--since" "$_ship_base")
|
|
3303
|
+
echo -e "${DIM}Clean working tree on $_ship_head; reviewing the branch range vs $_ship_base.${NC}"
|
|
3304
|
+
fi
|
|
3305
|
+
fi
|
|
3306
|
+
;;
|
|
3307
|
+
esac
|
|
3308
|
+
fi
|
|
3309
|
+
echo -e "${BOLD}Running quality gates on the diff (loki review)...${NC}"
|
|
3310
|
+
echo ""
|
|
3311
|
+
local rc=0
|
|
3312
|
+
cmd_review "${review_args[@]}" || rc=$?
|
|
3313
|
+
|
|
3314
|
+
if [ "$rc" -ge 1 ]; then
|
|
3315
|
+
echo ""
|
|
3316
|
+
if [ "$rc" -ge 2 ]; then
|
|
3317
|
+
echo -e "${RED}Review found CRITICAL findings. Not advising a PR yet.${NC}" >&2
|
|
3318
|
+
else
|
|
3319
|
+
echo -e "${YELLOW}Review found HIGH findings. Not advising a PR yet.${NC}" >&2
|
|
3320
|
+
fi
|
|
3321
|
+
echo "Fix the findings above (or run 'loki review' for the full report), then 'loki ship' again." >&2
|
|
3322
|
+
return 1
|
|
3323
|
+
fi
|
|
3324
|
+
|
|
3325
|
+
# 3. Clean enough: print the branch-aware PR command (the SAME advisory
|
|
3326
|
+
# cmd_deploy uses). Derive base/head exactly as cmd_deploy does.
|
|
3327
|
+
echo ""
|
|
3328
|
+
echo -e "${GREEN}Review is clean (no HIGH or CRITICAL findings).${NC}"
|
|
3329
|
+
echo ""
|
|
3330
|
+
local dir="."
|
|
3331
|
+
local head="" base=""
|
|
3332
|
+
head="$(git -C "$dir" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")"
|
|
3333
|
+
[ -n "$head" ] || head="HEAD"
|
|
3334
|
+
case "$head" in
|
|
3335
|
+
loki/*)
|
|
3336
|
+
if [ -s "$dir/.loki/state/base-branch.txt" ]; then
|
|
3337
|
+
base="$(head -n1 "$dir/.loki/state/base-branch.txt" 2>/dev/null || echo "")"
|
|
3338
|
+
fi
|
|
3339
|
+
;;
|
|
3340
|
+
esac
|
|
3341
|
+
if [ -z "$base" ]; then
|
|
3342
|
+
local origin_head=""
|
|
3343
|
+
origin_head="$(git -C "$dir" symbolic-ref refs/remotes/origin/HEAD 2>/dev/null || echo "")"
|
|
3344
|
+
if [ -n "$origin_head" ]; then
|
|
3345
|
+
base="${origin_head##*/}"
|
|
3346
|
+
fi
|
|
3347
|
+
fi
|
|
3348
|
+
|
|
3349
|
+
if declare -F print_pr_advice >/dev/null 2>&1; then
|
|
3350
|
+
if [ -n "$base" ]; then
|
|
3351
|
+
print_pr_advice "$base" "$head" "$dir"
|
|
3352
|
+
else
|
|
3353
|
+
echo "Could not determine the PR base branch automatically; set it manually:"
|
|
3354
|
+
print_pr_advice "manually-set-base" "$head" "$dir"
|
|
3355
|
+
fi
|
|
3356
|
+
else
|
|
3357
|
+
echo "To open a pull request:"
|
|
3358
|
+
echo " git push -u origin ${head}"
|
|
3359
|
+
echo " Open a pull request for ${head} (set the base branch manually)."
|
|
3360
|
+
fi
|
|
3361
|
+
echo ""
|
|
3362
|
+
echo -e "${DIM}Loki does not push or deploy for you; run the command above when ready.${NC}"
|
|
3363
|
+
return 0
|
|
3364
|
+
}
|
|
3365
|
+
|
|
2957
3366
|
# loki why -- actionable failure/outcome diagnosis (B5).
|
|
2958
3367
|
# Reads the already-captured run artifacts (no new state): the terminal run state
|
|
2959
3368
|
# (.loki/<autonomy-state>.json: status, lastExitCode, iterationCount), the durable
|
|
@@ -3284,6 +3693,43 @@ cmd_status() {
|
|
|
3284
3693
|
fi
|
|
3285
3694
|
fi
|
|
3286
3695
|
|
|
3696
|
+
# v7.82: surface the single most valuable, most shareable fact -- where the
|
|
3697
|
+
# built app is running -- when the app runner reports a live app. Display-only
|
|
3698
|
+
# read of .loki/app-runner/state.json (same shape build_completion_summary
|
|
3699
|
+
# reads); silent when no app is running. The --json path (cmd_status_json) is
|
|
3700
|
+
# untouched, so machine output stays byte-identical.
|
|
3701
|
+
if [ -f "$LOKI_DIR/app-runner/state.json" ]; then
|
|
3702
|
+
local app_url
|
|
3703
|
+
app_url=$(_LOKI_APP_STATE="$LOKI_DIR/app-runner/state.json" python3 -c "
|
|
3704
|
+
import json, os
|
|
3705
|
+
try:
|
|
3706
|
+
d = json.load(open(os.environ['_LOKI_APP_STATE']))
|
|
3707
|
+
print(d.get('url', '') if d.get('status') == 'running' else '')
|
|
3708
|
+
except Exception:
|
|
3709
|
+
print('')" 2>/dev/null)
|
|
3710
|
+
if [ -n "$app_url" ]; then
|
|
3711
|
+
echo ""
|
|
3712
|
+
echo -e "${GREEN}App running:${NC} ${BOLD}${app_url}${NC} ${DIM}- open it to try it${NC}"
|
|
3713
|
+
fi
|
|
3714
|
+
fi
|
|
3715
|
+
|
|
3716
|
+
# v7.82: completion-aware one-liner. When a finished run left a completion
|
|
3717
|
+
# record, show its outcome and point at \`loki why\` for the full story. Reads
|
|
3718
|
+
# the persisted completion.json (no recompute); silent when absent.
|
|
3719
|
+
if [ -f "$LOKI_DIR/state/completion.json" ]; then
|
|
3720
|
+
local last_outcome
|
|
3721
|
+
last_outcome=$(_LOKI_CJ="$LOKI_DIR/state/completion.json" python3 -c "
|
|
3722
|
+
import json, os
|
|
3723
|
+
try:
|
|
3724
|
+
d = json.load(open(os.environ['_LOKI_CJ']))
|
|
3725
|
+
print(d.get('outcome', '') or '')
|
|
3726
|
+
except Exception:
|
|
3727
|
+
print('')" 2>/dev/null)
|
|
3728
|
+
if [ -n "$last_outcome" ]; then
|
|
3729
|
+
echo -e "${DIM}Last run: ${last_outcome} - run 'loki why' for details${NC}"
|
|
3730
|
+
fi
|
|
3731
|
+
fi
|
|
3732
|
+
|
|
3287
3733
|
echo ""
|
|
3288
3734
|
echo -e "${DIM} Tip: loki analyze context show - detailed token breakdown${NC}"
|
|
3289
3735
|
echo -e "${DIM} Tip: loki analyze code overview - codebase intelligence${NC}"
|
|
@@ -4866,7 +5312,7 @@ cmd_welcome_terminal() {
|
|
|
4866
5312
|
echo -e " ${CYAN}Cross-project memory${NC} Lessons compound, so agents stop repeating mistakes."
|
|
4867
5313
|
echo -e " ${CYAN}Provider-agnostic${NC} Claude, Codex, Cline, or Aider. Your keys, your infra."
|
|
4868
5314
|
echo ""
|
|
4869
|
-
echo -e " Quick start: ${BOLD}loki start ./prd.md${NC}"
|
|
5315
|
+
echo -e " Quick start: ${BOLD}loki quickstart${NC} (from your idea) or ${BOLD}loki start ./prd.md${NC} (from a spec)"
|
|
4870
5316
|
echo -e " Docs: ${BOLD}https://www.autonomi.dev/docs${NC}"
|
|
4871
5317
|
echo ""
|
|
4872
5318
|
if _loki_welcome_analytics_on; then
|
|
@@ -9524,11 +9970,17 @@ cmd_doctor() {
|
|
|
9524
9970
|
return 1
|
|
9525
9971
|
elif [ "$warn_count" -gt 0 ]; then
|
|
9526
9972
|
echo -e "${YELLOW}All required checks passed with some warnings.${NC}"
|
|
9527
|
-
return 0
|
|
9528
9973
|
else
|
|
9529
9974
|
echo -e "${GREEN}All checks passed. System is ready for Loki Mode.${NC}"
|
|
9530
|
-
return 0
|
|
9531
9975
|
fi
|
|
9976
|
+
# Setup verified (no required check failed): hand the user straight to a
|
|
9977
|
+
# first build with a copy-paste command, so they never dead-end here. Gated
|
|
9978
|
+
# on fail_count==0 above, so a failing setup is never told to build. The
|
|
9979
|
+
# --json path returns long before this code, so machine output is untouched.
|
|
9980
|
+
echo ""
|
|
9981
|
+
echo "Next: loki quickstart (guided first build from your idea, no PRD needed)"
|
|
9982
|
+
echo " or loki demo (builds a sample todo app end to end) or loki start ./prd.md"
|
|
9983
|
+
return 0
|
|
9532
9984
|
}
|
|
9533
9985
|
|
|
9534
9986
|
# JSON output for loki doctor --json
|
|
@@ -15750,6 +16202,9 @@ main() {
|
|
|
15750
16202
|
why)
|
|
15751
16203
|
cmd_why "$@"
|
|
15752
16204
|
;;
|
|
16205
|
+
next)
|
|
16206
|
+
cmd_next "$@"
|
|
16207
|
+
;;
|
|
15753
16208
|
stats)
|
|
15754
16209
|
# CLI consolidation (Phase A): 'stats' is a deprecated alias of
|
|
15755
16210
|
# 'report session'. On the Bun route this arm is never reached
|
|
@@ -15773,6 +16228,9 @@ main() {
|
|
|
15773
16228
|
deploy)
|
|
15774
16229
|
cmd_deploy "$@"
|
|
15775
16230
|
;;
|
|
16231
|
+
ship)
|
|
16232
|
+
cmd_ship "$@"
|
|
16233
|
+
;;
|
|
15776
16234
|
open)
|
|
15777
16235
|
# CLI consolidation (Phase A): 'open' is a deprecated alias of 'preview'.
|
|
15778
16236
|
_deprecated_alias open preview "$@"
|
|
@@ -23474,12 +23932,15 @@ _code_diff() {
|
|
|
23474
23932
|
# Output shell completion scripts
|
|
23475
23933
|
cmd_completions() {
|
|
23476
23934
|
local shell="${1:-bash}"
|
|
23935
|
+
# Consume the subcommand so the install arm can read an optional shell
|
|
23936
|
+
# override as its own positional ($1). bash/zsh raw-output arms ignore $@.
|
|
23937
|
+
shift 2>/dev/null || true
|
|
23477
23938
|
local skill_dir
|
|
23478
|
-
|
|
23939
|
+
|
|
23479
23940
|
# Find the skill directory (where autonomy/loki is located)
|
|
23480
23941
|
skill_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
23481
23942
|
local completions_dir="$skill_dir/completions"
|
|
23482
|
-
|
|
23943
|
+
|
|
23483
23944
|
case "$shell" in
|
|
23484
23945
|
bash)
|
|
23485
23946
|
if [ -f "$completions_dir/loki.bash" ]; then
|
|
@@ -23497,24 +23958,77 @@ cmd_completions() {
|
|
|
23497
23958
|
exit 1
|
|
23498
23959
|
fi
|
|
23499
23960
|
;;
|
|
23961
|
+
install)
|
|
23962
|
+
# One-command install: detect the current shell, append a single
|
|
23963
|
+
# guarded line to the right rc file exactly once (idempotent via a
|
|
23964
|
+
# sentinel comment), then tell the user how to activate it. This
|
|
23965
|
+
# replaces the error-prone manual recipe with something that just
|
|
23966
|
+
# works after one command.
|
|
23967
|
+
local detected target_shell rc_file
|
|
23968
|
+
detected="$(basename "${SHELL:-}" 2>/dev/null)"
|
|
23969
|
+
# Allow an explicit override: loki completions install bash|zsh
|
|
23970
|
+
target_shell="${1:-$detected}"
|
|
23971
|
+
case "$target_shell" in
|
|
23972
|
+
bash)
|
|
23973
|
+
rc_file="${HOME}/.bashrc"
|
|
23974
|
+
;;
|
|
23975
|
+
zsh)
|
|
23976
|
+
rc_file="${HOME}/.zshrc"
|
|
23977
|
+
;;
|
|
23978
|
+
*)
|
|
23979
|
+
echo -e "${RED}Could not detect a supported shell (got: ${target_shell:-unknown}).${NC}" >&2
|
|
23980
|
+
echo "Specify one explicitly: loki completions install bash | loki completions install zsh" >&2
|
|
23981
|
+
exit 1
|
|
23982
|
+
;;
|
|
23983
|
+
esac
|
|
23984
|
+
local sentinel="# loki-mode completions"
|
|
23985
|
+
if [ -f "$rc_file" ] && grep -qF "$sentinel" "$rc_file" 2>/dev/null; then
|
|
23986
|
+
echo "Loki completions already installed in ${rc_file} (no change)."
|
|
23987
|
+
echo "Activate now with: source ${rc_file}"
|
|
23988
|
+
return 0
|
|
23989
|
+
fi
|
|
23990
|
+
# Append the sentinel plus a single eval line that re-loads the
|
|
23991
|
+
# completion script at shell start. eval (not redirect) is the
|
|
23992
|
+
# correct persistent form: it sources the script into the
|
|
23993
|
+
# interactive shell each time the rc file runs.
|
|
23994
|
+
{
|
|
23995
|
+
printf '\n%s\n' "$sentinel"
|
|
23996
|
+
printf '%s\n' "eval \"\$(loki completions ${target_shell})\""
|
|
23997
|
+
} >> "$rc_file" || {
|
|
23998
|
+
echo -e "${RED}Failed to write to ${rc_file}.${NC}" >&2
|
|
23999
|
+
exit 1
|
|
24000
|
+
}
|
|
24001
|
+
echo "Installed Loki ${target_shell} completions into ${rc_file}."
|
|
24002
|
+
echo "Activate now with: source ${rc_file} (or restart your shell)"
|
|
24003
|
+
return 0
|
|
24004
|
+
;;
|
|
23500
24005
|
*)
|
|
23501
24006
|
echo -e "${BOLD}Loki Shell Completions${NC}"
|
|
23502
24007
|
echo ""
|
|
23503
24008
|
echo "Output shell completion scripts for bash or zsh."
|
|
23504
24009
|
echo ""
|
|
23505
24010
|
echo "Usage: loki completions <shell>"
|
|
24011
|
+
echo " loki completions install [bash|zsh]"
|
|
23506
24012
|
echo ""
|
|
23507
24013
|
echo "Shells:"
|
|
23508
|
-
echo " bash
|
|
23509
|
-
echo " zsh
|
|
24014
|
+
echo " bash Bash completion script"
|
|
24015
|
+
echo " zsh Zsh completion script"
|
|
24016
|
+
echo " install Auto-install completions into your shell rc file"
|
|
23510
24017
|
echo ""
|
|
23511
24018
|
echo "Installation:"
|
|
23512
24019
|
echo ""
|
|
23513
|
-
echo "
|
|
23514
|
-
echo "
|
|
24020
|
+
echo " One command (recommended):"
|
|
24021
|
+
echo " loki completions install"
|
|
24022
|
+
echo ""
|
|
24023
|
+
echo " Manual (persistent), Bash:"
|
|
24024
|
+
echo " echo 'eval \"\$(loki completions bash)\"' >> ~/.bashrc"
|
|
24025
|
+
echo " Manual (persistent), Zsh:"
|
|
24026
|
+
echo " echo 'eval \"\$(loki completions zsh)\"' >> ~/.zshrc"
|
|
23515
24027
|
echo ""
|
|
23516
|
-
echo "
|
|
23517
|
-
echo "
|
|
24028
|
+
echo " Session only (current shell), Bash:"
|
|
24029
|
+
echo " eval \"\$(loki completions bash)\""
|
|
24030
|
+
echo " Session only (current shell), Zsh:"
|
|
24031
|
+
echo " eval \"\$(loki completions zsh)\""
|
|
23518
24032
|
echo ""
|
|
23519
24033
|
exit 1
|
|
23520
24034
|
;;
|
package/autonomy/run.sh
CHANGED
|
@@ -2674,34 +2674,35 @@ except Exception:
|
|
|
2674
2674
|
fi
|
|
2675
2675
|
|
|
2676
2676
|
# ---- Durable human-readable file: .loki/COMPLETION.txt --------------------
|
|
2677
|
+
# Presentation-only receipt: a single fixed-width label column, the live-app
|
|
2678
|
+
# URL elevated right under the outcome headline, and a consistent rule line.
|
|
2679
|
+
# Stays pure ASCII (no emoji, no dashes, no color codes) so it pastes cleanly
|
|
2680
|
+
# into a PR description or a chat. Same facts and values as before.
|
|
2677
2681
|
{
|
|
2678
2682
|
echo "Loki Mode run summary"
|
|
2679
2683
|
echo "====================="
|
|
2680
2684
|
echo ""
|
|
2681
|
-
|
|
2682
|
-
echo "Branch: $branch"
|
|
2683
|
-
echo "Files changed: $files_changed (+$insertions / -$deletions)"
|
|
2684
|
-
echo "Finished: $ts"
|
|
2685
|
-
echo ""
|
|
2686
|
-
if [ -n "$delegate_branch" ]; then
|
|
2687
|
-
echo "Delegate branch: $delegate_branch"
|
|
2688
|
-
fi
|
|
2689
|
-
if [ -n "$pr_url" ]; then
|
|
2690
|
-
echo "Pull request: $pr_url"
|
|
2691
|
-
elif [ "$outcome" = "complete" ]; then
|
|
2692
|
-
echo "Pull request: not opened (set LOKI_DELEGATE_PR=1 to open one)"
|
|
2693
|
-
fi
|
|
2694
|
-
echo ""
|
|
2685
|
+
printf '%-14s %s\n' "Outcome:" "$outcome_label"
|
|
2695
2686
|
if [ -n "$live_app_url" ]; then
|
|
2696
2687
|
# Compute the dashboard scheme the same way start_dashboard does
|
|
2697
2688
|
# (url_scheme is local to that function, not visible here).
|
|
2698
2689
|
local _dash_scheme="http"
|
|
2699
2690
|
[ -n "${LOKI_TLS_CERT:-}" ] && [ -n "${LOKI_TLS_KEY:-}" ] && _dash_scheme="https"
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2691
|
+
printf '%-14s %s\n' "Live app:" "$live_app_url (served locally on this machine)"
|
|
2692
|
+
printf '%-14s %s\n' "Dashboard:" "${_dash_scheme}://127.0.0.1:${DASHBOARD_PORT:-57374}/ (App Runner -> Live App)"
|
|
2693
|
+
fi
|
|
2694
|
+
printf '%-14s %s\n' "Branch:" "$branch"
|
|
2695
|
+
printf '%-14s %s\n' "Files:" "$files_changed (+$insertions / -$deletions)"
|
|
2696
|
+
printf '%-14s %s\n' "Finished:" "$ts"
|
|
2697
|
+
if [ -n "$delegate_branch" ]; then
|
|
2698
|
+
printf '%-14s %s\n' "Delegate:" "$delegate_branch"
|
|
2699
|
+
fi
|
|
2700
|
+
if [ -n "$pr_url" ]; then
|
|
2701
|
+
printf '%-14s %s\n' "Pull request:" "$pr_url"
|
|
2702
|
+
elif [ "$outcome" = "complete" ]; then
|
|
2703
|
+
printf '%-14s %s\n' "Pull request:" "not opened (set LOKI_DELEGATE_PR=1 to open one)"
|
|
2703
2704
|
fi
|
|
2704
|
-
|
|
2705
|
+
printf '%-14s %s\n' "Tasks:" "pending=$pending in_progress=$in_progress completed=$completed failed=$failed"
|
|
2705
2706
|
echo ""
|
|
2706
2707
|
if [ -n "$evidence_inconclusive_line" ]; then
|
|
2707
2708
|
echo "$evidence_inconclusive_line"
|
|
@@ -2901,10 +2902,130 @@ emit_completion_summary() {
|
|
|
2901
2902
|
local outcome="${1:-complete}"
|
|
2902
2903
|
local urgency="${2:-normal}"
|
|
2903
2904
|
build_completion_summary "$outcome"
|
|
2905
|
+
# Render the screenshot-worthy completion card inline on a foreground TTY run,
|
|
2906
|
+
# AFTER the durable files are written. The card re-reads the persisted
|
|
2907
|
+
# completion.json (never recomputes) so it can never diverge from the file.
|
|
2908
|
+
print_completion_card
|
|
2904
2909
|
send_notification "${_LOKI_SUMMARY_TITLE:-Run finished}" "${_LOKI_SUMMARY_BODY:-}" "$urgency"
|
|
2905
2910
|
return 0
|
|
2906
2911
|
}
|
|
2907
2912
|
|
|
2913
|
+
#===============================================================================
|
|
2914
|
+
# print_completion_card (visible-delight completion card)
|
|
2915
|
+
#
|
|
2916
|
+
# Display-only. Renders a boxed summary card to the interactive TTY at the close
|
|
2917
|
+
# of a foreground run: outcome, branch, files changed (+ins/-del), the live-app
|
|
2918
|
+
# URL with a "try it" line when an app is running, the copy-pasteable review
|
|
2919
|
+
# command, and the recorded-assumptions count. This is the single most
|
|
2920
|
+
# screenshot-worthy moment of a run, which was previously only written to a file.
|
|
2921
|
+
#
|
|
2922
|
+
# It reads ONLY from the already-persisted .loki/state/completion.json (written
|
|
2923
|
+
# by build_completion_summary just before this is called) and the app-runner
|
|
2924
|
+
# state, so the card and the durable file are guaranteed identical. Nothing is
|
|
2925
|
+
# computed or written here.
|
|
2926
|
+
#
|
|
2927
|
+
# Gate (same shape as the HUD at the colors block): interactive stdout, not
|
|
2928
|
+
# --bg, and not opted out via LOKI_COMPLETION_CARD=0. Off-TTY / --bg / --json
|
|
2929
|
+
# paths emit nothing, so machine output stays byte-identical. Wrapped so any
|
|
2930
|
+
# internal failure still returns 0 and can never abort the completion path.
|
|
2931
|
+
#===============================================================================
|
|
2932
|
+
print_completion_card() {
|
|
2933
|
+
# Gate first: emit nothing unless interactive TTY, not background, not opted out.
|
|
2934
|
+
if ! { [ -t 1 ] && [ "${BACKGROUND_MODE:-false}" != "true" ] && [ "${LOKI_COMPLETION_CARD:-1}" != "0" ]; }; then
|
|
2935
|
+
return 0
|
|
2936
|
+
fi
|
|
2937
|
+
|
|
2938
|
+
local loki_dir="${TARGET_DIR:-.}/.loki"
|
|
2939
|
+
local _cj="$loki_dir/state/completion.json"
|
|
2940
|
+
[ -f "$_cj" ] || return 0
|
|
2941
|
+
|
|
2942
|
+
# Pull the fields we render straight from the persisted record. The python3
|
|
2943
|
+
# call emits one field per line in a fixed order; we read them line by line
|
|
2944
|
+
# so empty fields (e.g. no pr_url) keep their positions (a single delimiter
|
|
2945
|
+
# split would collapse adjacent empties). Any failure leaves the card
|
|
2946
|
+
# unrendered. Trailing-newline guard: NUL-free, fields are single-line.
|
|
2947
|
+
local _fields
|
|
2948
|
+
_fields="$(python3 -c "
|
|
2949
|
+
import json,sys
|
|
2950
|
+
try:
|
|
2951
|
+
d=json.load(open(sys.argv[1]))
|
|
2952
|
+
except Exception:
|
|
2953
|
+
sys.exit(0)
|
|
2954
|
+
def g(k):
|
|
2955
|
+
v=d.get(k,'')
|
|
2956
|
+
return '' if v is None else str(v).replace('\n',' ')
|
|
2957
|
+
for k in ['outcome','branch','files_changed','insertions','deletions',
|
|
2958
|
+
'review_cmd','pr_url','assumptions_total','assumptions_high']:
|
|
2959
|
+
print(g(k))
|
|
2960
|
+
" "$_cj" 2>/dev/null)" || return 0
|
|
2961
|
+
[ -z "$_fields" ] && return 0
|
|
2962
|
+
|
|
2963
|
+
local _outcome _branch _files _ins _del _review _pr _atotal _ahigh
|
|
2964
|
+
{
|
|
2965
|
+
IFS= read -r _outcome
|
|
2966
|
+
IFS= read -r _branch
|
|
2967
|
+
IFS= read -r _files
|
|
2968
|
+
IFS= read -r _ins
|
|
2969
|
+
IFS= read -r _del
|
|
2970
|
+
IFS= read -r _review
|
|
2971
|
+
IFS= read -r _pr
|
|
2972
|
+
IFS= read -r _atotal
|
|
2973
|
+
IFS= read -r _ahigh
|
|
2974
|
+
} <<EOF
|
|
2975
|
+
$_fields
|
|
2976
|
+
EOF
|
|
2977
|
+
|
|
2978
|
+
# Human outcome label (mirror build_completion_summary's mapping).
|
|
2979
|
+
local _label
|
|
2980
|
+
case "$_outcome" in
|
|
2981
|
+
complete) _label="Completed" ;;
|
|
2982
|
+
max_iterations) _label="Max iterations" ;;
|
|
2983
|
+
stopped) _label="Stopped" ;;
|
|
2984
|
+
force_stopped) _label="Stopped (not verified-complete)" ;;
|
|
2985
|
+
failed) _label="Failed" ;;
|
|
2986
|
+
intervention) _label="Needs input" ;;
|
|
2987
|
+
*) _label="$_outcome" ;;
|
|
2988
|
+
esac
|
|
2989
|
+
|
|
2990
|
+
# Live app URL (best-effort), same read as build_completion_summary.
|
|
2991
|
+
local _url="" _app_state="$loki_dir/app-runner/state.json"
|
|
2992
|
+
if [ -f "$_app_state" ]; then
|
|
2993
|
+
_url="$(python3 -c "import json,sys
|
|
2994
|
+
try:
|
|
2995
|
+
d=json.load(open(sys.argv[1]))
|
|
2996
|
+
print(d.get('url','') if d.get('status')=='running' else '')
|
|
2997
|
+
except Exception:
|
|
2998
|
+
print('')" "$_app_state" 2>/dev/null)" || _url=""
|
|
2999
|
+
fi
|
|
3000
|
+
|
|
3001
|
+
# Box width matches log_header (66 inner columns). Render lines that fit;
|
|
3002
|
+
# this is decoration, so over-wide content is simply not boxed-truncated
|
|
3003
|
+
# (the durable file carries the full text).
|
|
3004
|
+
echo ""
|
|
3005
|
+
echo -e "${GREEN}+================================================================+${NC}"
|
|
3006
|
+
echo -e "${GREEN}|${NC} ${BOLD}Loki Mode: ${_label}${NC}"
|
|
3007
|
+
echo -e "${GREEN}|${NC}"
|
|
3008
|
+
if [ -n "$_url" ]; then
|
|
3009
|
+
echo -e "${GREEN}|${NC} ${BOLD}${CYAN}Your app is live at ${_url}${NC} ${DIM}- open it to try it${NC}"
|
|
3010
|
+
echo -e "${GREEN}|${NC}"
|
|
3011
|
+
fi
|
|
3012
|
+
echo -e "${GREEN}|${NC} Branch: ${BOLD}${_branch}${NC}"
|
|
3013
|
+
echo -e "${GREEN}|${NC} Files: ${BOLD}${_files}${NC} changed ${GREEN}+${_ins}${NC} / ${RED}-${_del}${NC}"
|
|
3014
|
+
case "$_atotal" in ''|0) : ;; *)
|
|
3015
|
+
echo -e "${GREEN}|${NC} Spec assumptions recorded: ${BOLD}${_atotal}${NC} (${_ahigh} high) ${DIM}see .loki/assumptions/ledger.md${NC}"
|
|
3016
|
+
;;
|
|
3017
|
+
esac
|
|
3018
|
+
if [ -n "$_pr" ]; then
|
|
3019
|
+
echo -e "${GREEN}|${NC} Pull request: ${_pr}"
|
|
3020
|
+
fi
|
|
3021
|
+
echo -e "${GREEN}|${NC}"
|
|
3022
|
+
echo -e "${GREEN}|${NC} ${DIM}Review the work:${NC}"
|
|
3023
|
+
echo -e "${GREEN}|${NC} ${_review}"
|
|
3024
|
+
echo -e "${GREEN}+================================================================+${NC}"
|
|
3025
|
+
echo ""
|
|
3026
|
+
return 0
|
|
3027
|
+
}
|
|
3028
|
+
|
|
2908
3029
|
#===============================================================================
|
|
2909
3030
|
# on_run_complete (Slice 3: opt-in local git output on success)
|
|
2910
3031
|
#
|
|
@@ -16593,6 +16714,35 @@ main() {
|
|
|
16593
16714
|
exit 1
|
|
16594
16715
|
fi
|
|
16595
16716
|
|
|
16717
|
+
# v7.82: one-line "Building:" headline under the start banner so the opening
|
|
16718
|
+
# frame reflects the user's own intent (their PRD / brief / this codebase),
|
|
16719
|
+
# not a generic banner. Display-only, derived from already-resolved values:
|
|
16720
|
+
# PRD basename for a file, the recorded brief text for a brief run, or fixed
|
|
16721
|
+
# text for a no-arg in-repo run. Truncated to one tidy line. Gated to an
|
|
16722
|
+
# interactive TTY, not --bg, and opt-out via LOKI_START_HEADLINE=0, so the
|
|
16723
|
+
# off-TTY / background path is byte-identical (no output). Best-effort; a
|
|
16724
|
+
# failure here must never affect parsing or the build flow.
|
|
16725
|
+
if [ -t 1 ] && [ "${BACKGROUND_MODE:-false}" != "true" ] && [ "${LOKI_START_HEADLINE:-1}" != "0" ]; then
|
|
16726
|
+
local _headline=""
|
|
16727
|
+
if [ -n "$PRD_PATH" ]; then
|
|
16728
|
+
_headline="$(basename "$PRD_PATH" 2>/dev/null || echo "$PRD_PATH")"
|
|
16729
|
+
elif [ -f ".loki/state/brief.txt" ]; then
|
|
16730
|
+
# Recorded one-line brief (written by cmd_start). Collapse to a single
|
|
16731
|
+
# line and truncate to ~60 chars so the banner stays clean.
|
|
16732
|
+
local _brief
|
|
16733
|
+
_brief="$(tr '\n' ' ' < .loki/state/brief.txt 2>/dev/null | sed 's/ */ /g; s/^ //; s/ $//')"
|
|
16734
|
+
if [ -n "$_brief" ]; then
|
|
16735
|
+
if [ "${#_brief}" -gt 60 ]; then
|
|
16736
|
+
_brief="${_brief:0:57}..."
|
|
16737
|
+
fi
|
|
16738
|
+
_headline="\"$_brief\""
|
|
16739
|
+
fi
|
|
16740
|
+
fi
|
|
16741
|
+
[ -z "$_headline" ] && _headline="analyzing this codebase"
|
|
16742
|
+
echo -e " ${BOLD}${CYAN}Building: ${_headline}${NC}"
|
|
16743
|
+
echo ""
|
|
16744
|
+
fi
|
|
16745
|
+
|
|
16596
16746
|
# Handle background mode
|
|
16597
16747
|
if [ "$BACKGROUND_MODE" = "true" ]; then
|
|
16598
16748
|
# Initialize .loki directory first
|
package/completions/_loki
CHANGED
|
@@ -11,7 +11,7 @@ function _loki {
|
|
|
11
11
|
case $state in
|
|
12
12
|
(args)
|
|
13
13
|
case $line[1] in
|
|
14
|
-
start)
|
|
14
|
+
start|quick)
|
|
15
15
|
_loki_start
|
|
16
16
|
;;
|
|
17
17
|
council)
|
|
@@ -101,7 +101,7 @@ function _loki {
|
|
|
101
101
|
'1:subcommand:(list show open share)'
|
|
102
102
|
;;
|
|
103
103
|
completions)
|
|
104
|
-
_arguments '1:shell:(bash zsh)'
|
|
104
|
+
_arguments '1:shell:(bash zsh install)'
|
|
105
105
|
;;
|
|
106
106
|
monitor)
|
|
107
107
|
_directories
|
|
@@ -122,6 +122,7 @@ function _loki_commands {
|
|
|
122
122
|
commands=(
|
|
123
123
|
'start:Start Loki Mode'
|
|
124
124
|
'quick:Quick single-task mode'
|
|
125
|
+
'quickstart:Guided first build from your idea'
|
|
125
126
|
'monitor:Monitor Docker Compose services with auto-fix'
|
|
126
127
|
'demo:Interactive 60-second demo'
|
|
127
128
|
'init:Interactive PRD builder'
|
|
@@ -129,11 +130,16 @@ function _loki_commands {
|
|
|
129
130
|
'pause:Pause execution'
|
|
130
131
|
'resume:Resume execution'
|
|
131
132
|
'status:Show status'
|
|
133
|
+
'next:Run the right next step for the current build'
|
|
134
|
+
'ship:Finish the build (gates + PR advice) in one command'
|
|
135
|
+
'why:Explain why the last run did what it did'
|
|
132
136
|
'dashboard:Dashboard commands'
|
|
137
|
+
'web:Start the web app surface'
|
|
133
138
|
'logs:View session logs'
|
|
134
139
|
'serve:Start API server'
|
|
135
140
|
'api:API server commands'
|
|
136
141
|
'sandbox:Docker sandbox commands'
|
|
142
|
+
'docker:Run Loki in a Docker container'
|
|
137
143
|
'notify:Notification commands'
|
|
138
144
|
'import:Import GitHub issues'
|
|
139
145
|
'issue:GitHub issue commands'
|
|
@@ -153,6 +159,8 @@ function _loki_commands {
|
|
|
153
159
|
'metrics:Session productivity report'
|
|
154
160
|
'share:Share session report as GitHub Gist'
|
|
155
161
|
'proof:Inspect/share proof-of-run artifacts'
|
|
162
|
+
'preview:Preview the locally-running app'
|
|
163
|
+
'deploy:Deploy the built product (CI/CD-aware)'
|
|
156
164
|
'context:Context window management'
|
|
157
165
|
'ctx:Context window management (alias)'
|
|
158
166
|
'code:Codebase intelligence'
|
|
@@ -234,7 +242,17 @@ function _loki_start {
|
|
|
234
242
|
'-y[Skip confirmation prompts]' \
|
|
235
243
|
'--budget[Cost budget limit in USD]:amount:' \
|
|
236
244
|
'--max-iterations[Max iterations]:number:' \
|
|
237
|
-
'*:
|
|
245
|
+
'*:spec file:_loki_spec_files'
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
# Bias spec completion toward the file shapes Loki accepts (.md/.json/.txt/
|
|
249
|
+
# .yaml/.yml), while still offering directories so the user can navigate into a
|
|
250
|
+
# subfolder. Mirrors the bash completion. Makes the most-common command
|
|
251
|
+
# tab-driven and typo-proof, cutting "PRD file not found" mistakes.
|
|
252
|
+
function _loki_spec_files {
|
|
253
|
+
_alternative \
|
|
254
|
+
'specs:spec file:_files -g "*.(md|json|txt|yaml|yml)"' \
|
|
255
|
+
'dirs:directory:_files -/'
|
|
238
256
|
}
|
|
239
257
|
|
|
240
258
|
function _loki_council {
|
package/completions/loki.bash
CHANGED
|
@@ -5,7 +5,7 @@ _loki_completion() {
|
|
|
5
5
|
_init_completion || return
|
|
6
6
|
|
|
7
7
|
# Main subcommands (must match autonomy/loki main case statement)
|
|
8
|
-
local main_commands="start quick monitor demo init stop pause resume status dashboard web serve api sandbox notify import github issue config provider reset memory compound checkpoint council dogfood projects enterprise secrets doctor watchdog audit metrics syslog onboard share proof explain plan report cost kpis stats test ci watch telemetry agent context ctx code run export review optimize heal modernize migrate cluster worktree wt trigger failover remote deploy docker mcp magic assets analyze compliance crash open otel preview quickstart rc rollback self-update sentrux setup-skill spec state template trust trust-metrics ultracode update verify voice why wiki bench cleanup logs grill docs cp version completions help"
|
|
8
|
+
local main_commands="start quick monitor demo init stop pause resume status next ship dashboard web serve api sandbox notify import github issue config provider reset memory compound checkpoint council dogfood projects enterprise secrets doctor watchdog audit metrics syslog onboard share proof explain plan report cost kpis stats test ci watch telemetry agent context ctx code run export review optimize heal modernize migrate cluster worktree wt trigger failover remote deploy docker mcp magic assets analyze compliance crash open otel preview quickstart rc rollback self-update sentrux setup-skill spec state template trust trust-metrics ultracode update verify voice why wiki bench cleanup logs grill docs cp version completions help"
|
|
9
9
|
|
|
10
10
|
# 1. If we are on the first argument (subcommand)
|
|
11
11
|
if [[ $cword -eq 1 ]]; then
|
|
@@ -15,7 +15,7 @@ _loki_completion() {
|
|
|
15
15
|
|
|
16
16
|
# 2. Handle subcommands and their specific flags/args
|
|
17
17
|
case "${words[1]}" in
|
|
18
|
-
start)
|
|
18
|
+
start|quick)
|
|
19
19
|
# If the previous word was --provider, show provider names
|
|
20
20
|
if [[ "$prev" == "--provider" ]]; then
|
|
21
21
|
# Active providers (loki rejects gemini since v7.5.18; cline/aider
|
|
@@ -32,8 +32,23 @@ _loki_completion() {
|
|
|
32
32
|
return 0
|
|
33
33
|
fi
|
|
34
34
|
|
|
35
|
-
#
|
|
36
|
-
|
|
35
|
+
# Bias toward the spec-shaped files Loki actually accepts
|
|
36
|
+
# (.md/.json/.txt/.yaml/.yml), plus directories so the user can still
|
|
37
|
+
# navigate into a subfolder. This makes the most-common command
|
|
38
|
+
# tab-driven and typo-proof, cutting "PRD file not found" mistakes.
|
|
39
|
+
# If the spec-filtered set is empty (no specs here), fall back to
|
|
40
|
+
# generic file completion so nothing the user could type is lost.
|
|
41
|
+
local _specs _dirs _eg_was_set=0
|
|
42
|
+
shopt -q extglob && _eg_was_set=1
|
|
43
|
+
shopt -s extglob
|
|
44
|
+
_specs=$(compgen -f -X '!*.@(md|json|txt|yaml|yml)' -- "$cur")
|
|
45
|
+
[[ "$_eg_was_set" -eq 0 ]] && shopt -u extglob
|
|
46
|
+
_dirs=$(compgen -d -- "$cur")
|
|
47
|
+
if [[ -n "$_specs" || -n "$_dirs" ]]; then
|
|
48
|
+
COMPREPLY=( $_specs $_dirs )
|
|
49
|
+
else
|
|
50
|
+
COMPREPLY=( $(compgen -f -- "$cur") )
|
|
51
|
+
fi
|
|
37
52
|
;;
|
|
38
53
|
|
|
39
54
|
council)
|
|
@@ -198,7 +213,7 @@ _loki_completion() {
|
|
|
198
213
|
;;
|
|
199
214
|
|
|
200
215
|
completions)
|
|
201
|
-
COMPREPLY=( $(compgen -W "bash zsh" -- "$cur") )
|
|
216
|
+
COMPREPLY=( $(compgen -W "bash zsh install" -- "$cur") )
|
|
202
217
|
;;
|
|
203
218
|
|
|
204
219
|
context|ctx)
|
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.83.1
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -395,7 +395,7 @@ provider works inside the container. Provide auth with your Anthropic API key:
|
|
|
395
395
|
# Run Loki Mode in Docker (Claude provider, API-key auth)
|
|
396
396
|
docker run --rm -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
|
|
397
397
|
-v $(pwd):/workspace -w /workspace \
|
|
398
|
-
asklokesh/loki-mode:7.
|
|
398
|
+
asklokesh/loki-mode:7.83.1 start ./my-spec.md
|
|
399
399
|
```
|
|
400
400
|
|
|
401
401
|
##### docker compose + .env (no host install)
|
package/loki-ts/dist/loki.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var QQ=Object.defineProperty;var ZQ=($)=>$;function zQ($,Q){this[$]=ZQ.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)QQ($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:zQ.bind(Q,Z)})};var L=($,Q)=>()=>($&&(Q=$($=0)),Q);var q$=import.meta.require;var h1={};b(h1,{lokiDir:()=>P,homeLokiDir:()=>i$,findRepoRootForVersion:()=>t$,REPO_ROOT:()=>g});import{resolve as a,dirname as r$}from"path";import{fileURLToPath as XQ}from"url";import{existsSync as R$}from"fs";import{homedir as KQ}from"os";function qQ(){let $=b1;for(let Q=0;Q<6;Q++){if(R$(a($,"VERSION"))&&R$(a($,"autonomy/run.sh")))return $;let Z=r$($);if(Z===$)break;$=Z}return a(b1,"..","..","..")}function t$($){let Q=$;for(let Z=0;Z<6;Z++){if(R$(a(Q,"VERSION"))&&R$(a(Q,"autonomy/run.sh")))return Q;let z=r$(Q);if(z===Q)break;Q=z}return a($,"..","..","..")}function P(){return process.env.LOKI_DIR??a(process.cwd(),".loki")}function i$(){return a(KQ(),".loki")}var b1,g;var C=L(()=>{b1=r$(XQ(import.meta.url));g=qQ()});import{readFileSync as VQ}from"fs";import{resolve as JQ,dirname as UQ}from"path";import{fileURLToPath as WQ}from"url";function E$(){if(Q$!==null)return Q$;let $="7.
|
|
2
|
+
var QQ=Object.defineProperty;var ZQ=($)=>$;function zQ($,Q){this[$]=ZQ.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)QQ($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:zQ.bind(Q,Z)})};var L=($,Q)=>()=>($&&(Q=$($=0)),Q);var q$=import.meta.require;var h1={};b(h1,{lokiDir:()=>P,homeLokiDir:()=>i$,findRepoRootForVersion:()=>t$,REPO_ROOT:()=>g});import{resolve as a,dirname as r$}from"path";import{fileURLToPath as XQ}from"url";import{existsSync as R$}from"fs";import{homedir as KQ}from"os";function qQ(){let $=b1;for(let Q=0;Q<6;Q++){if(R$(a($,"VERSION"))&&R$(a($,"autonomy/run.sh")))return $;let Z=r$($);if(Z===$)break;$=Z}return a(b1,"..","..","..")}function t$($){let Q=$;for(let Z=0;Z<6;Z++){if(R$(a(Q,"VERSION"))&&R$(a(Q,"autonomy/run.sh")))return Q;let z=r$(Q);if(z===Q)break;Q=z}return a($,"..","..","..")}function P(){return process.env.LOKI_DIR??a(process.cwd(),".loki")}function i$(){return a(KQ(),".loki")}var b1,g;var C=L(()=>{b1=r$(XQ(import.meta.url));g=qQ()});import{readFileSync as VQ}from"fs";import{resolve as JQ,dirname as UQ}from"path";import{fileURLToPath as WQ}from"url";function E$(){if(Q$!==null)return Q$;let $="7.83.1";if(typeof $==="string"&&$.length>0)return Q$=$,Q$;try{let Q=UQ(WQ(import.meta.url)),Z=t$(Q);Q$=VQ(JQ(Z,"VERSION"),"utf-8").trim()}catch{Q$="unknown"}return Q$}var Q$=null;var e$=L(()=>{C()});var g1={};b(g1,{runOrThrow:()=>HQ,run:()=>F,readStreamCapped:()=>m1,commandVersion:()=>BQ,commandExists:()=>f,ShellError:()=>$1,MAX_STDOUT_BYTES:()=>v1});async function m1($,Q=v1){let Z=$.getReader(),z=new TextDecoder,X="",q=0;try{while(q<Q){let{done:K,value:U}=await Z.read();if(K)break;if(!U)continue;if(q+=U.byteLength,q>Q){let J=U.byteLength-(q-Q);X+=z.decode(U.subarray(0,J),{stream:!0});break}X+=z.decode(U,{stream:!0})}X+=z.decode()}finally{try{await Z.cancel()}catch{}Z.releaseLock()}return X}async function F($,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,U]=await Promise.all([m1(Z.stdout),new Response(Z.stderr).text(),Z.exited]);return{stdout:q,stderr:K,exitCode:U}}finally{if(z)clearTimeout(z);if(X)clearTimeout(X)}}async function HQ($,Q={}){let Z=await F($,Q);if(Z.exitCode!==0)throw new $1(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function f($){let Q=GQ($),Z=await F(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function GQ($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function BQ($,Q="--version"){if(!await f($))return null;let z=await F([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var v1=16777216,$1;var d=L(()=>{$1=class $1 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 s($){return YQ?"":$}var YQ,O,S,_,_Z,I,k,h,V;var c=L(()=>{YQ=(process.env.NO_COLOR??"").length>0;O=s("\x1B[0;31m"),S=s("\x1B[0;32m"),_=s("\x1B[1;33m"),_Z=s("\x1B[0;34m"),I=s("\x1B[0;36m"),k=s("\x1B[1m"),h=s("\x1B[2m"),V=s("\x1B[0m")});import{existsSync as jQ}from"fs";async function Z$(){if(Y$!==void 0)return Y$;let $="/opt/homebrew/bin/python3.12";if(jQ($))return Y$=$,$;let Q=await f("python3.12");if(Q)return Y$=Q,Q;let Z=await f("python3");return Y$=Z,Z}async function z$($,Q={}){let Z=await Z$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return F([Z,"-c",$],Q)}var Y$;var V$=L(()=>{d()});var X0={};b(X0,{runStatus:()=>oQ});import{existsSync as y,readFileSync as U$,readdirSync as r1,statSync as t1}from"fs";import{resolve as D,basename as vQ}from"path";import{homedir as mQ}from"os";function i1($){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 e1($,Q,Z){if(Q===0)return null;let z=Math.trunc($*100/Q),X=Math.trunc($*N$/Q);if(X>N$)X=N$;let q=N$-X,K=S;if(z>=80)K=O;else if(z>=50)K=_;let U="=".repeat(Math.max(0,X))+" ".repeat(Math.max(0,q)),J=i1($),W=i1(Q);return` ${k}${Z}${V} ${K}[${U}]${V} ${z}% (${J} / ${W})`}async function fQ(){if(await f("jq"))return!0;return process.stdout.write(`${O}Error: jq is required but not installed.${V}
|
|
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)
|
|
@@ -410,8 +410,11 @@ Start a session with: loki start <prd>`}}let X=tQ(z);return{exitCode:0,stdout:Q?
|
|
|
410
410
|
|
|
411
411
|
`),$.fail>0)return process.stdout.write(`${O}Some required prerequisites are missing.${V}
|
|
412
412
|
`),process.stdout.write(`Install missing dependencies and run 'loki doctor' again.
|
|
413
|
-
`),1;if($.warn>0)
|
|
414
|
-
`)
|
|
413
|
+
`),1;if($.warn>0)process.stdout.write(`${_}All required checks passed with some warnings.${V}
|
|
414
|
+
`);else process.stdout.write(`${S}All checks passed. System is ready for Loki Mode.${V}
|
|
415
|
+
`);return process.stdout.write(`
|
|
416
|
+
`),process.stdout.write(`Next: loki quickstart (guided first build from your idea, no PRD needed)
|
|
417
|
+
`),process.stdout.write(` or loki demo (builds a sample todo app end to end) or loki start ./prd.md
|
|
415
418
|
`),0}async function Y3($){let Q=!1;for(let Z of $)if(Z==="--json")Q=!0;else if(Z==="--help"||Z==="-h")return G3(),0;else return process.stderr.write(`${O}Unknown option: ${Z}${V}
|
|
416
419
|
`),process.stderr.write(`Usage: loki doctor [--json]
|
|
417
420
|
`),1;if(Q){let Z=await _0();return process.stdout.write(JSON.stringify(Z,null,2)+`
|
|
@@ -793,4 +796,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
|
|
|
793
796
|
`),2}default:return process.stderr.write(`Unknown command: ${Q}
|
|
794
797
|
`),process.stderr.write($Q),2}}s1();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var qZ=await KZ(Bun.argv.slice(2));process.exit(qZ);
|
|
795
798
|
|
|
796
|
-
//# debugId=
|
|
799
|
+
//# debugId=8AD63677BE89399E64756E2164756E21
|
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.83.1",
|
|
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.83.1",
|
|
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",
|