loki-mode 8.66.0 → 8.67.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 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.66.0
6
+ # Loki Mode v8.67.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.66.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
472
+ **v8.67.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 8.66.0
1
+ 8.67.0
package/autonomy/loki CHANGED
@@ -11282,6 +11282,18 @@ cmd_doctor() {
11282
11282
  fi
11283
11283
  echo ""
11284
11284
 
11285
+ # Provider Availability. Which provider a build would ACTUALLY pick, which
11286
+ # is decided by providers/loader.sh auto_detect_provider, not by PATH order
11287
+ # and not by the section above. Rendered by the shared helper in
11288
+ # provider-offer.sh so the Bun route prints the identical bytes (doctor
11289
+ # stdout is byte-compared -- tests/test-doctor-blocker-parity.sh). Skipped
11290
+ # silently, with no error, when loader.sh is missing or unsourceable.
11291
+ if declare -f render_provider_availability >/dev/null 2>&1; then
11292
+ if render_provider_availability; then
11293
+ echo ""
11294
+ fi
11295
+ fi
11296
+
11285
11297
  echo -e "${CYAN}API Keys:${NC}"
11286
11298
  # Note: CLI tools use their own login sessions outside Docker/K8s.
11287
11299
  # This section helps first-time users verify they can authenticate.
@@ -11763,6 +11775,16 @@ else:
11763
11775
  cmd_doctor_json() {
11764
11776
  local _loki_version
11765
11777
  _loki_version=$(get_version)
11778
+ # Provider availability, computed in bash and handed to the python program
11779
+ # as an env var (same pattern as LOKI_VERSION). Computed here rather than
11780
+ # inside the program because the answer comes from sourcing loader.sh.
11781
+ # Empty when loader.sh is unavailable, and the key is then null: the same
11782
+ # graceful skip the text section performs.
11783
+ local _provider_availability=""
11784
+ if declare -f provider_availability_json >/dev/null 2>&1; then
11785
+ _provider_availability="$(provider_availability_json 2>/dev/null || true)"
11786
+ fi
11787
+ LOKI_PROVIDER_AVAILABILITY="$_provider_availability" \
11766
11788
  LOKI_VERSION="$_loki_version" \
11767
11789
  LOKI_CATALOG_PATH="${LOKI_MODEL_CATALOG:-${_LOKI_SCRIPT_DIR}/../providers/model_catalog.json}" \
11768
11790
  python3 -c "
@@ -11966,9 +11988,20 @@ except Exception:
11966
11988
  model_catalog = {'status': 'warn', 'updated': None, 'age_days': None,
11967
11989
  'detail': 'Catalog unreadable or missing an ISO \"updated\" date -- cannot determine age'}
11968
11990
 
11991
+ # Provider availability, handed in from bash (see cmd_doctor_json). Null when
11992
+ # providers/loader.sh could not be sourced, mirroring the text sections skip.
11993
+ # CAUTION: this block lives inside a DOUBLE-QUOTED python3 -c program, so no
11994
+ # apostrophes and no double quotes anywhere here, comments included.
11995
+ try:
11996
+ _pa_raw = os.environ.get('LOKI_PROVIDER_AVAILABILITY', '').strip()
11997
+ provider_availability = json.loads(_pa_raw) if _pa_raw else None
11998
+ except Exception:
11999
+ provider_availability = None
12000
+
11969
12001
  result = {
11970
12002
  'loki_mode_version': os.environ.get('LOKI_VERSION', 'unknown'),
11971
12003
  'checks': checks,
12004
+ 'provider_availability': provider_availability,
11972
12005
  'disk': {
11973
12006
  'available_gb': disk_gb,
11974
12007
  '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
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "8.66.0"
10
+ __version__ = "8.67.0"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try: