loki-mode 8.66.0 → 8.68.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/loki +121 -3
- package/autonomy/provider-offer.sh +123 -0
- package/dashboard/__init__.py +1 -1
- package/loki-ts/dist/loki.js +342 -341
- 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 v8.
|
|
6
|
+
# Loki Mode v8.68.0
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -469,4 +469,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
469
469
|
|
|
470
470
|
---
|
|
471
471
|
|
|
472
|
-
**v8.
|
|
472
|
+
**v8.68.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
8.
|
|
1
|
+
8.68.0
|
package/autonomy/loki
CHANGED
|
@@ -906,7 +906,7 @@ show_help() {
|
|
|
906
906
|
echo " next Run the right next step for you (resume / ship / why)"
|
|
907
907
|
echo " stop Stop execution immediately"
|
|
908
908
|
echo " pause Pause after current session"
|
|
909
|
-
echo " resume Resume paused
|
|
909
|
+
echo " resume Resume a paused or interrupted run (after Ctrl-C or a crash)"
|
|
910
910
|
echo " steer \"<note>\" Nudge a running build (needs LOKI_PROMPT_INJECTION=1)"
|
|
911
911
|
echo ""
|
|
912
912
|
echo "Verify / trust:"
|
|
@@ -3605,18 +3605,88 @@ cmd_steer() {
|
|
|
3605
3605
|
return 0
|
|
3606
3606
|
}
|
|
3607
3607
|
|
|
3608
|
+
# Print the one copy-pasteable command that gets an INTERRUPTED run going again,
|
|
3609
|
+
# naming what was found (iteration reached, when it stopped) so the user can tell
|
|
3610
|
+
# it is their run and not a stale directory. Returns 0 when a hint was printed,
|
|
3611
|
+
# 1 when there is nothing resumable (caller then prints its own message).
|
|
3612
|
+
#
|
|
3613
|
+
# Keyed on status == "interrupted" ONLY, deliberately. run.sh's load_state
|
|
3614
|
+
# preserves iterationCount for exactly paused|interrupted|budget_exceeded|
|
|
3615
|
+
# stopped and RESETS it to 0 for everything else -- including "running", which
|
|
3616
|
+
# is what an uncatchable SIGKILL leaves behind. Printing "resume from iteration
|
|
3617
|
+
# 7" on a "running" record would promise progress that load_state is about to
|
|
3618
|
+
# throw away, on a dev box without LOKI_DURABLE_STATE=1. A hint pointing at
|
|
3619
|
+
# nothing is worse than silence, so the narrow key is the point, not an
|
|
3620
|
+
# oversight. budget_exceeded / max_iterations_reached already get their own
|
|
3621
|
+
# tailored guidance from _loki_next_action (raise the cap FIRST, then resume);
|
|
3622
|
+
# duplicating them here would contradict it.
|
|
3623
|
+
#
|
|
3624
|
+
# Read-only: reads state, writes nothing, spends nothing.
|
|
3625
|
+
_loki_print_interrupted_resume_hint() {
|
|
3626
|
+
local status
|
|
3627
|
+
status="$(_loki_resolve_run_status 2>/dev/null)" || return 1
|
|
3628
|
+
[ "$status" = "interrupted" ] || return 1
|
|
3629
|
+
|
|
3630
|
+
local state_file="$LOKI_DIR/autonomy-state.json"
|
|
3631
|
+
if [ -n "${LOKI_SESSION_ID:-}" ] && [ -f "$LOKI_DIR/sessions/${LOKI_SESSION_ID}/autonomy-state.json" ]; then
|
|
3632
|
+
state_file="$LOKI_DIR/sessions/${LOKI_SESSION_ID}/autonomy-state.json"
|
|
3633
|
+
fi
|
|
3634
|
+
[ -f "$state_file" ] || return 1
|
|
3635
|
+
|
|
3636
|
+
# iteration, lastRun and prdPath in one read. Tab-separated so a spec path
|
|
3637
|
+
# containing spaces survives; python3 is already a hard dep of this file.
|
|
3638
|
+
local fields iteration last_run prd_path
|
|
3639
|
+
fields="$(_LOKI_RH_STATE="$state_file" python3 -c "
|
|
3640
|
+
import json, os
|
|
3641
|
+
try:
|
|
3642
|
+
d = json.load(open(os.environ['_LOKI_RH_STATE']))
|
|
3643
|
+
except Exception:
|
|
3644
|
+
d = {}
|
|
3645
|
+
# save_state() writes "iteration" (autonomy/run.sh:6707). Reading only
|
|
3646
|
+
# "iterationCount" made every REAL interrupted run report iteration 0 -- the
|
|
3647
|
+
# one number that makes this message reassuring rather than alarming. The
|
|
3648
|
+
# fixture that first exercised this used the other spelling, so the test
|
|
3649
|
+
# passed while the shipped path was wrong. Accept both, real key first.
|
|
3650
|
+
print('\t'.join([
|
|
3651
|
+
str(d.get('iteration', d.get('iterationCount', 0))),
|
|
3652
|
+
str(d.get('lastRun', '') or 'unknown'),
|
|
3653
|
+
str(d.get('prdPath', '') or ''),
|
|
3654
|
+
]))" 2>/dev/null)" || return 1
|
|
3655
|
+
IFS=$'\t' read -r iteration last_run prd_path <<< "$fields"
|
|
3656
|
+
|
|
3657
|
+
# The spec path is what makes the command RESUME this run rather than start a
|
|
3658
|
+
# different one, so only interpolate it when it still exists on disk. A stale
|
|
3659
|
+
# path would produce a command that fails on paste.
|
|
3660
|
+
local resume_cmd="loki start"
|
|
3661
|
+
if [ -n "$prd_path" ] && [ -f "$prd_path" ]; then
|
|
3662
|
+
resume_cmd="loki start \"$prd_path\""
|
|
3663
|
+
fi
|
|
3664
|
+
|
|
3665
|
+
echo -e "${YELLOW}Interrupted run found.${NC} Stopped at iteration ${BOLD}${iteration}${NC} (last activity: ${last_run})."
|
|
3666
|
+
echo "Saved progress is intact. Resume it with:"
|
|
3667
|
+
echo ""
|
|
3668
|
+
echo -e " ${BOLD}${resume_cmd}${NC}"
|
|
3669
|
+
echo ""
|
|
3670
|
+
echo -e "${DIM}It picks up from iteration ${iteration}; verification re-runs, so nothing inherits a stale PASS.${NC}"
|
|
3671
|
+
return 0
|
|
3672
|
+
}
|
|
3673
|
+
|
|
3608
3674
|
# Resume paused execution
|
|
3609
3675
|
cmd_resume() {
|
|
3610
3676
|
# v7.6.2 B-13 fix: --help must print help, not act on session state.
|
|
3611
3677
|
case "${1:-}" in
|
|
3612
3678
|
--help|-h|help)
|
|
3613
|
-
echo -e "${BOLD}Loki Mode -- resume paused session${NC}"
|
|
3679
|
+
echo -e "${BOLD}Loki Mode -- resume a paused or interrupted session${NC}"
|
|
3614
3680
|
echo ""
|
|
3615
3681
|
echo "Usage: loki resume [options]"
|
|
3616
3682
|
echo ""
|
|
3617
3683
|
echo "Clears the paused flag and lets the autonomous runner continue"
|
|
3618
3684
|
echo "from where 'loki pause' stopped it."
|
|
3619
3685
|
echo ""
|
|
3686
|
+
echo "If the run was interrupted instead (Ctrl-C, crash, closed laptop)"
|
|
3687
|
+
echo "there is no flag to clear, so this prints the iteration it reached"
|
|
3688
|
+
echo "and the exact command to continue from that saved state."
|
|
3689
|
+
echo ""
|
|
3620
3690
|
echo "Options:"
|
|
3621
3691
|
echo " --help, -h Show this help and exit"
|
|
3622
3692
|
echo ""
|
|
@@ -3638,8 +3708,17 @@ cmd_resume() {
|
|
|
3638
3708
|
local session_running=false
|
|
3639
3709
|
is_session_running && session_running=true
|
|
3640
3710
|
|
|
3641
|
-
# If nothing is paused/stopped and no session is running,
|
|
3711
|
+
# If nothing is paused/stopped and no session is running, the run may still
|
|
3712
|
+
# be RESUMABLE: a Ctrl-C / crash / closed laptop leaves no PAUSE or STOP
|
|
3713
|
+
# file, only autonomy-state.json with status "interrupted" and the iteration
|
|
3714
|
+
# it reached. This branch used to print "No session to resume" and send the
|
|
3715
|
+
# user to `loki start`, hiding real saved progress -- and because
|
|
3716
|
+
# `loki next` maps interrupted -> cmd_resume, the one command whose job is
|
|
3717
|
+
# "do the right next thing" hit the same dead end. Surface it instead.
|
|
3642
3718
|
if ! $has_pause_signal && ! $has_stop_signal && ! $session_running; then
|
|
3719
|
+
if _loki_print_interrupted_resume_hint; then
|
|
3720
|
+
return 0
|
|
3721
|
+
fi
|
|
3643
3722
|
echo -e "${YELLOW}No session to resume.${NC}"
|
|
3644
3723
|
echo "Start a session with: loki start"
|
|
3645
3724
|
exit 0
|
|
@@ -4528,6 +4607,12 @@ cmd_status() {
|
|
|
4528
4607
|
echo -e "${RED}Status: STOPPED${NC}"
|
|
4529
4608
|
echo -e "${DIM} Clear with: loki resume${NC}"
|
|
4530
4609
|
echo ""
|
|
4610
|
+
elif _loki_print_interrupted_resume_hint; then
|
|
4611
|
+
# An interrupted run (Ctrl-C / crash / closed laptop) leaves no signal
|
|
4612
|
+
# file, so the two branches above miss it entirely. Same hint cmd_resume
|
|
4613
|
+
# prints. elif, not a separate block: it is mutually exclusive with an
|
|
4614
|
+
# explicit PAUSE/STOP, and must not stack onto the "Last run:" line.
|
|
4615
|
+
echo ""
|
|
4531
4616
|
fi
|
|
4532
4617
|
|
|
4533
4618
|
# Check status file
|
|
@@ -11282,6 +11367,18 @@ cmd_doctor() {
|
|
|
11282
11367
|
fi
|
|
11283
11368
|
echo ""
|
|
11284
11369
|
|
|
11370
|
+
# Provider Availability. Which provider a build would ACTUALLY pick, which
|
|
11371
|
+
# is decided by providers/loader.sh auto_detect_provider, not by PATH order
|
|
11372
|
+
# and not by the section above. Rendered by the shared helper in
|
|
11373
|
+
# provider-offer.sh so the Bun route prints the identical bytes (doctor
|
|
11374
|
+
# stdout is byte-compared -- tests/test-doctor-blocker-parity.sh). Skipped
|
|
11375
|
+
# silently, with no error, when loader.sh is missing or unsourceable.
|
|
11376
|
+
if declare -f render_provider_availability >/dev/null 2>&1; then
|
|
11377
|
+
if render_provider_availability; then
|
|
11378
|
+
echo ""
|
|
11379
|
+
fi
|
|
11380
|
+
fi
|
|
11381
|
+
|
|
11285
11382
|
echo -e "${CYAN}API Keys:${NC}"
|
|
11286
11383
|
# Note: CLI tools use their own login sessions outside Docker/K8s.
|
|
11287
11384
|
# This section helps first-time users verify they can authenticate.
|
|
@@ -11763,6 +11860,16 @@ else:
|
|
|
11763
11860
|
cmd_doctor_json() {
|
|
11764
11861
|
local _loki_version
|
|
11765
11862
|
_loki_version=$(get_version)
|
|
11863
|
+
# Provider availability, computed in bash and handed to the python program
|
|
11864
|
+
# as an env var (same pattern as LOKI_VERSION). Computed here rather than
|
|
11865
|
+
# inside the program because the answer comes from sourcing loader.sh.
|
|
11866
|
+
# Empty when loader.sh is unavailable, and the key is then null: the same
|
|
11867
|
+
# graceful skip the text section performs.
|
|
11868
|
+
local _provider_availability=""
|
|
11869
|
+
if declare -f provider_availability_json >/dev/null 2>&1; then
|
|
11870
|
+
_provider_availability="$(provider_availability_json 2>/dev/null || true)"
|
|
11871
|
+
fi
|
|
11872
|
+
LOKI_PROVIDER_AVAILABILITY="$_provider_availability" \
|
|
11766
11873
|
LOKI_VERSION="$_loki_version" \
|
|
11767
11874
|
LOKI_CATALOG_PATH="${LOKI_MODEL_CATALOG:-${_LOKI_SCRIPT_DIR}/../providers/model_catalog.json}" \
|
|
11768
11875
|
python3 -c "
|
|
@@ -11966,9 +12073,20 @@ except Exception:
|
|
|
11966
12073
|
model_catalog = {'status': 'warn', 'updated': None, 'age_days': None,
|
|
11967
12074
|
'detail': 'Catalog unreadable or missing an ISO \"updated\" date -- cannot determine age'}
|
|
11968
12075
|
|
|
12076
|
+
# Provider availability, handed in from bash (see cmd_doctor_json). Null when
|
|
12077
|
+
# providers/loader.sh could not be sourced, mirroring the text sections skip.
|
|
12078
|
+
# CAUTION: this block lives inside a DOUBLE-QUOTED python3 -c program, so no
|
|
12079
|
+
# apostrophes and no double quotes anywhere here, comments included.
|
|
12080
|
+
try:
|
|
12081
|
+
_pa_raw = os.environ.get('LOKI_PROVIDER_AVAILABILITY', '').strip()
|
|
12082
|
+
provider_availability = json.loads(_pa_raw) if _pa_raw else None
|
|
12083
|
+
except Exception:
|
|
12084
|
+
provider_availability = None
|
|
12085
|
+
|
|
11969
12086
|
result = {
|
|
11970
12087
|
'loki_mode_version': os.environ.get('LOKI_VERSION', 'unknown'),
|
|
11971
12088
|
'checks': checks,
|
|
12089
|
+
'provider_availability': provider_availability,
|
|
11972
12090
|
'disk': {
|
|
11973
12091
|
'available_gb': disk_gb,
|
|
11974
12092
|
'status': disk_status
|
|
@@ -35,6 +35,22 @@ else
|
|
|
35
35
|
_PO_NC=$'\033[0m'
|
|
36
36
|
fi
|
|
37
37
|
|
|
38
|
+
# Section colors for render_provider_availability. Deliberately gated on
|
|
39
|
+
# NO_COLOR ALONE, unlike the _PO_* set above which also blanks on a non-TTY.
|
|
40
|
+
# The section is printed inside `loki doctor` next to blocks colored by loki own
|
|
41
|
+
# CYAN/GREEN/NC, and those are blanked on NO_COLOR only (autonomy/loki:37). A
|
|
42
|
+
# TTY test here would strip color from this one section whenever doctor stdout
|
|
43
|
+
# is piped while every neighbour kept its escapes.
|
|
44
|
+
if [ -n "${NO_COLOR:-}" ]; then
|
|
45
|
+
_PO_S_CYAN=''; _PO_S_GREEN=''; _PO_S_YELLOW=''; _PO_S_DIM=''; _PO_S_NC=''
|
|
46
|
+
else
|
|
47
|
+
_PO_S_CYAN=$'\033[0;36m'
|
|
48
|
+
_PO_S_GREEN=$'\033[0;32m'
|
|
49
|
+
_PO_S_YELLOW=$'\033[1;33m'
|
|
50
|
+
_PO_S_DIM=$'\033[2m'
|
|
51
|
+
_PO_S_NC=$'\033[0m'
|
|
52
|
+
fi
|
|
53
|
+
|
|
38
54
|
# The one canonical install command. Quoted everywhere; never re-derived.
|
|
39
55
|
_PO_INSTALL_CMD="npm install -g @anthropic-ai/claude-code"
|
|
40
56
|
|
|
@@ -351,6 +367,109 @@ provider_offer_gate() {
|
|
|
351
367
|
return 2
|
|
352
368
|
}
|
|
353
369
|
|
|
370
|
+
# render_provider_availability: print the "Provider Availability" doctor section.
|
|
371
|
+
#
|
|
372
|
+
# WHY IT LIVES HERE. `loki doctor` stdout is compared byte for byte between the
|
|
373
|
+
# bash route and the Bun route (tests/test-doctor-blocker-parity.sh, and the
|
|
374
|
+
# bun-parity workflow). Two independent renderers drift; one shared renderer
|
|
375
|
+
# cannot. This is the same parity-by-construction seam the install offer and the
|
|
376
|
+
# detect-sdk probe already use, so doctor.ts calls this instead of reimplementing
|
|
377
|
+
# the priority list in TypeScript.
|
|
378
|
+
#
|
|
379
|
+
# WHY IT DOES NOT REUSE THE EXISTING "AI Providers" BLOCK. That block answers a
|
|
380
|
+
# different question -- is each CLI on PATH, and how do I install it. This one
|
|
381
|
+
# answers "which provider would a build actually pick", which is a property of
|
|
382
|
+
# providers/loader.sh auto_detect_provider, not of PATH. It also covers opencode,
|
|
383
|
+
# which the older block never listed.
|
|
384
|
+
#
|
|
385
|
+
# THE ORDER IS NOT SUPPORTED_PROVIDERS. auto_detect_provider walks
|
|
386
|
+
# claude cline codex aider opencode; SUPPORTED_PROVIDERS is declared
|
|
387
|
+
# claude codex cline aider opencode -- codex and cline are SWAPPED. Marking the
|
|
388
|
+
# first installed entry of SUPPORTED_PROVIDERS would name codex on a machine
|
|
389
|
+
# where a build would really pick cline. So the selected provider is always
|
|
390
|
+
# whatever auto_detect_provider returns, never re-derived here.
|
|
391
|
+
#
|
|
392
|
+
# DEGRADES SILENTLY. Requirement: if loader.sh cannot be sourced, the section is
|
|
393
|
+
# skipped rather than erroring. Everything runs inside one subshell, so a missing
|
|
394
|
+
# or broken loader returns non-zero with nothing printed and doctor continues.
|
|
395
|
+
render_provider_availability() {
|
|
396
|
+
# Path derived from THIS FILE, never from SKILL_DIR or _LOKI_SCRIPT_DIR:
|
|
397
|
+
# those belong to autonomy/loki and are unset when doctor.ts spawns this
|
|
398
|
+
# script standalone, which would blank the section on the Bun route only.
|
|
399
|
+
local _po_here
|
|
400
|
+
_po_here="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." 2>/dev/null && pwd)" || return 1
|
|
401
|
+
local _loader="$_po_here/providers/loader.sh"
|
|
402
|
+
[ -f "$_loader" ] || return 1
|
|
403
|
+
|
|
404
|
+
(
|
|
405
|
+
# shellcheck source=/dev/null
|
|
406
|
+
source "$_loader" >/dev/null 2>&1 || exit 1
|
|
407
|
+
declare -f auto_detect_provider >/dev/null 2>&1 || exit 1
|
|
408
|
+
[ "${#SUPPORTED_PROVIDERS[@]}" -gt 0 ] 2>/dev/null || exit 1
|
|
409
|
+
|
|
410
|
+
local _selected
|
|
411
|
+
_selected="$(auto_detect_provider 2>/dev/null)"
|
|
412
|
+
|
|
413
|
+
printf '%s\n' "${_PO_S_CYAN}Provider Availability:${_PO_S_NC}"
|
|
414
|
+
local _p
|
|
415
|
+
for _p in "${SUPPORTED_PROVIDERS[@]}"; do
|
|
416
|
+
if check_provider_installed "$_p" >/dev/null 2>&1; then
|
|
417
|
+
if [ "$_p" = "$_selected" ]; then
|
|
418
|
+
printf '%s\n' " ${_PO_S_GREEN}PASS${_PO_S_NC} $_p - installed ${_PO_S_DIM}(auto-selected)${_PO_S_NC}"
|
|
419
|
+
else
|
|
420
|
+
printf '%s\n' " ${_PO_S_GREEN}PASS${_PO_S_NC} $_p - installed"
|
|
421
|
+
fi
|
|
422
|
+
else
|
|
423
|
+
printf '%s\n' " ${_PO_S_YELLOW}WARN${_PO_S_NC} $_p - not installed"
|
|
424
|
+
fi
|
|
425
|
+
done
|
|
426
|
+
|
|
427
|
+
if [ -n "$_selected" ]; then
|
|
428
|
+
printf '%s\n' " ${_PO_S_DIM}Auto-selected provider: ${_selected} (override with LOKI_PROVIDER)${_PO_S_NC}"
|
|
429
|
+
else
|
|
430
|
+
printf '%s\n' " ${_PO_S_DIM}Auto-selected provider: none (no supported CLI installed)${_PO_S_NC}"
|
|
431
|
+
fi
|
|
432
|
+
) || return 1
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
# provider_availability_json: the same data as one JSON object, for doctor --json.
|
|
436
|
+
# Separate from the renderer so neither format has to parse the other. Same
|
|
437
|
+
# degrade rule: no loader means no output and a non-zero status.
|
|
438
|
+
provider_availability_json() {
|
|
439
|
+
local _po_here
|
|
440
|
+
_po_here="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." 2>/dev/null && pwd)" || return 1
|
|
441
|
+
local _loader="$_po_here/providers/loader.sh"
|
|
442
|
+
[ -f "$_loader" ] || return 1
|
|
443
|
+
|
|
444
|
+
(
|
|
445
|
+
# shellcheck source=/dev/null
|
|
446
|
+
source "$_loader" >/dev/null 2>&1 || exit 1
|
|
447
|
+
declare -f auto_detect_provider >/dev/null 2>&1 || exit 1
|
|
448
|
+
[ "${#SUPPORTED_PROVIDERS[@]}" -gt 0 ] 2>/dev/null || exit 1
|
|
449
|
+
|
|
450
|
+
local _selected _p _rows=""
|
|
451
|
+
_selected="$(auto_detect_provider 2>/dev/null)"
|
|
452
|
+
for _p in "${SUPPORTED_PROVIDERS[@]}"; do
|
|
453
|
+
if check_provider_installed "$_p" >/dev/null 2>&1; then
|
|
454
|
+
_rows="${_rows}${_p}=true"$'\n'
|
|
455
|
+
else
|
|
456
|
+
_rows="${_rows}${_p}=false"$'\n'
|
|
457
|
+
fi
|
|
458
|
+
done
|
|
459
|
+
_PA_SELECTED="$_selected" _PA_ROWS="$_rows" python3 -c '
|
|
460
|
+
import json, os
|
|
461
|
+
rows = []
|
|
462
|
+
for line in os.environ.get("_PA_ROWS", "").splitlines():
|
|
463
|
+
if not line:
|
|
464
|
+
continue
|
|
465
|
+
name, _, installed = line.partition("=")
|
|
466
|
+
rows.append({"name": name, "installed": installed == "true"})
|
|
467
|
+
selected = os.environ.get("_PA_SELECTED", "") or None
|
|
468
|
+
print(json.dumps({"selected": selected, "providers": rows}, indent=2))
|
|
469
|
+
' 2>/dev/null || exit 1
|
|
470
|
+
) || return 1
|
|
471
|
+
}
|
|
472
|
+
|
|
354
473
|
# Executed directly (doctor.ts child_process bridge, or manual): run the offer.
|
|
355
474
|
# When sourced by autonomy/loki, this block does not run.
|
|
356
475
|
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
|
|
@@ -362,6 +481,10 @@ if [ "${BASH_SOURCE[0]}" = "$0" ]; then
|
|
|
362
481
|
# (loki-ts/src/commands/doctor.ts). Prints nothing, so it cannot perturb
|
|
363
482
|
# the parity-captured stdout.
|
|
364
483
|
detect-sdk) detect_bundled_sdk_provider ;;
|
|
484
|
+
# Doctor "Provider Availability" section + its --json payload. Both
|
|
485
|
+
# routes call these so the two renderings cannot drift apart.
|
|
486
|
+
providers) render_provider_availability ;;
|
|
487
|
+
providers-json) provider_availability_json ;;
|
|
365
488
|
*) offer_provider_install report ;;
|
|
366
489
|
esac
|
|
367
490
|
fi
|