loki-mode 7.30.0 → 7.32.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/README.md +4 -2
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/context-tracker.py +8 -0
- package/autonomy/loki +799 -123
- package/autonomy/mcp-launch.sh +149 -36
- package/autonomy/run.sh +168 -4
- package/bin/loki +71 -0
- package/dashboard/__init__.py +1 -1
- package/dashboard/server.py +326 -1
- package/dashboard/static/index.html +105 -39
- package/docs/INSTALLATION.md +1 -1
- package/docs/competitive/replit-lovable-analysis.md +1 -1
- package/loki-ts/data/model-pricing.json +1 -0
- package/loki-ts/dist/loki.js +233 -231
- package/mcp/__init__.py +1 -1
- package/mcp/_sdk_loader.py +157 -0
- package/mcp/lsp_proxy.py +61 -61
- package/mcp/server.py +35 -129
- package/package.json +1 -1
- package/providers/claude.sh +76 -19
- package/providers/model_catalog.json +9 -0
- package/skills/model-selection.md +49 -1
package/autonomy/loki
CHANGED
|
@@ -132,8 +132,12 @@ find_skill_dir() {
|
|
|
132
132
|
return 1
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
# Use || true to prevent set -e from exiting before error message
|
|
136
|
-
SKILL_DIR
|
|
135
|
+
# Use || true to prevent set -e from exiting before error message.
|
|
136
|
+
# Honor a pre-set SKILL_DIR (test harnesses point it at a fixture); fall back to
|
|
137
|
+
# auto-detection when unset. No production code exports SKILL_DIR into a child
|
|
138
|
+
# loki invocation (children receive LOKI_SKILL_DIR instead), so this only changes
|
|
139
|
+
# behavior when a caller deliberately exports SKILL_DIR.
|
|
140
|
+
SKILL_DIR="${SKILL_DIR:-$(find_skill_dir)}" || true
|
|
137
141
|
if [ -z "$SKILL_DIR" ]; then
|
|
138
142
|
# Check if installation exists but is incomplete (missing run.sh)
|
|
139
143
|
_check_dir1="$HOME/.claude/skills/loki-mode"
|
|
@@ -324,6 +328,105 @@ emit_event() {
|
|
|
324
328
|
fi
|
|
325
329
|
}
|
|
326
330
|
|
|
331
|
+
# --- CLI consolidation (Phase A): deprecated-alias back-compat contract -------
|
|
332
|
+
# An old command token continues to dispatch and forward 1:1 to its canonical
|
|
333
|
+
# command. It prints exactly ONE pointer line to STDERR and never touches
|
|
334
|
+
# machine-readable stdout. The deprecation line is suppressed whenever an
|
|
335
|
+
# explicit machine-output flag (--json, -q, --quiet) is present in the args, so
|
|
336
|
+
# callers that capture combined 2>&1 (and JSON consumers) see a clean stream.
|
|
337
|
+
# It is ALSO suppressed for the positional machine-output formats of forwarded
|
|
338
|
+
# commands (json|csv|timeline as the first arg, e.g. `loki export json`), since
|
|
339
|
+
# those formats emit a pure machine-readable stream that a combined 2>&1 capture
|
|
340
|
+
# would otherwise dirty with the note line. The human-readable `markdown` format
|
|
341
|
+
# is intentionally NOT suppressed.
|
|
342
|
+
# Non-TTY stdout is intentionally NOT a suppression trigger (some callers pipe
|
|
343
|
+
# stdout but read stderr).
|
|
344
|
+
#
|
|
345
|
+
# Usage:
|
|
346
|
+
# _deprecated_alias <old> <new> "$@" # emit pointer + telemetry (suppressed
|
|
347
|
+
# # under --json/-q/--quiet); then the
|
|
348
|
+
# # caller forwards to the canonical cmd.
|
|
349
|
+
# Reference implementation: the run->start deprecation (cmd_run / v6.84.0).
|
|
350
|
+
_deprecated_alias_should_suppress() {
|
|
351
|
+
# Returns 0 (suppress) when a machine-output flag is present anywhere in
|
|
352
|
+
# "$@", OR when the FIRST arg is a positional machine-output format
|
|
353
|
+
# (json|csv|timeline) used by forwarded commands like `loki export json`.
|
|
354
|
+
# The positional check is first-arg-only so we never suppress on an
|
|
355
|
+
# incidental later token; markdown (human-readable) is deliberately excluded.
|
|
356
|
+
case "${1:-}" in
|
|
357
|
+
json|csv|timeline) return 0 ;;
|
|
358
|
+
esac
|
|
359
|
+
local a
|
|
360
|
+
for a in "$@"; do
|
|
361
|
+
case "$a" in
|
|
362
|
+
--json|-q|--quiet) return 0 ;;
|
|
363
|
+
esac
|
|
364
|
+
done
|
|
365
|
+
return 1
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
_deprecated_alias() {
|
|
369
|
+
local old="$1"; shift
|
|
370
|
+
local new="$1"; shift
|
|
371
|
+
# "$@" is now the user-supplied argv for the alias invocation.
|
|
372
|
+
if _deprecated_alias_should_suppress "$@"; then
|
|
373
|
+
return 0
|
|
374
|
+
fi
|
|
375
|
+
echo "note: 'loki ${old}' is now 'loki ${new}'. The old form still works." >&2
|
|
376
|
+
# Reuse the existing cli_command_deprecated telemetry event (from=/to=).
|
|
377
|
+
# IMPORTANT: a forwarding alias must add no side effect the canonical command
|
|
378
|
+
# lacks. events/emit.sh creates "$LOKI_DIR/events/pending" as a side effect,
|
|
379
|
+
# which would make a no-session directory suddenly contain .loki and flip the
|
|
380
|
+
# exit code of downstream guards (e.g. cmd_share's "No .loki/ directory"
|
|
381
|
+
# check). It also removes a bash-vs-bun race for read-only reporters
|
|
382
|
+
# (cmd_stats sees .loki appear vs Bun seeing it absent). So we only emit
|
|
383
|
+
# telemetry when .loki already exists; the stderr pointer line is always
|
|
384
|
+
# printed (modulo the machine-output suppression above). The matching Bun
|
|
385
|
+
# helper (loki-ts/src/util/deprecated_alias.ts) emits no telemetry, so the
|
|
386
|
+
# two routes are side-effect consistent.
|
|
387
|
+
if [ -d "${LOKI_DIR:-.loki}" ]; then
|
|
388
|
+
emit_event cli_command_deprecated cli alias \
|
|
389
|
+
"from=${old}" \
|
|
390
|
+
"to=${new}" \
|
|
391
|
+
"argv=${*:-}"
|
|
392
|
+
fi
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
# CLI consolidation (Phase B): kpis is a Bun-only command (Phase K, v7.5.28+).
|
|
396
|
+
# It ships in the Bun runtime (loki-ts) and reuses the canonical cost arithmetic
|
|
397
|
+
# in loki-ts/src/runner/budget.ts (PRICING map + calculateCostFromRecords, the
|
|
398
|
+
# cost single-source-of-truth). It is deliberately NOT reimplemented in bash:
|
|
399
|
+
# a bash port would duplicate that arithmetic and risk drift. On the bash route
|
|
400
|
+
# (LOKI_LEGACY_BASH=1, or a machine without Bun) both the canonical
|
|
401
|
+
# `report kpis` and the deprecated `kpis` alias print this honest requirement
|
|
402
|
+
# message rather than the generic "Unknown command" (which would contradict
|
|
403
|
+
# `loki help` listing kpis). --json yields a structured {"available": false}
|
|
404
|
+
# payload so a machine consumer is not handed prose. Exits 1.
|
|
405
|
+
# Usage: _kpis_bash_unavailable "$@" (the alias arm prints the pointer first)
|
|
406
|
+
# --json is honored FLAG-ANYWHERE (not just as $1): a machine consumer that
|
|
407
|
+
# passes the suppression flags first -- e.g. `kpis --quiet --json` or `report
|
|
408
|
+
# kpis -q --json` -- must still get the structured {"available": false} payload
|
|
409
|
+
# on stdout, not prose. This mirrors _deprecated_alias_should_suppress, which
|
|
410
|
+
# also scans all of "$@". Exit 1 either way (the bash route never produces KPIs).
|
|
411
|
+
# Copy points at the CANONICAL `loki report kpis` (the deprecated `loki kpis`
|
|
412
|
+
# alias prints its own pointer first, at the alias arm).
|
|
413
|
+
_kpis_bash_unavailable() {
|
|
414
|
+
local _ka _want_json=""
|
|
415
|
+
for _ka in "$@"; do
|
|
416
|
+
if [ "$_ka" = "--json" ]; then
|
|
417
|
+
_want_json=1
|
|
418
|
+
break
|
|
419
|
+
fi
|
|
420
|
+
done
|
|
421
|
+
if [ -n "$_want_json" ]; then
|
|
422
|
+
printf '{"available": false, "reason": "loki report kpis requires the Bun runtime; it is not implemented on the bash (LOKI_LEGACY_BASH) route"}\n'
|
|
423
|
+
else
|
|
424
|
+
echo "loki report kpis requires the Bun runtime and is not available on the bash route." >&2
|
|
425
|
+
echo "Remove LOKI_LEGACY_BASH=1 (or install Bun: https://bun.sh) to use 'loki report kpis'." >&2
|
|
426
|
+
fi
|
|
427
|
+
exit 1
|
|
428
|
+
}
|
|
429
|
+
|
|
327
430
|
# Emit learning signal (non-blocking) - SYN-018
|
|
328
431
|
# Usage: emit_learning_signal <signal_type> [options]
|
|
329
432
|
# Signal types: user_preference, error_pattern, success_pattern, tool_efficiency, workflow_pattern
|
|
@@ -523,6 +626,12 @@ PYEOF
|
|
|
523
626
|
# Show help
|
|
524
627
|
show_help() {
|
|
525
628
|
local version=$(get_version)
|
|
629
|
+
# Strip color when stdout is not a TTY (piped/redirected) so captured help
|
|
630
|
+
# output is clean, matching the bare-loki welcome path. Local overrides only.
|
|
631
|
+
local BOLD="$BOLD" NC="$NC"
|
|
632
|
+
if [ ! -t 1 ]; then
|
|
633
|
+
BOLD=''; NC=''
|
|
634
|
+
fi
|
|
526
635
|
echo -e "${BOLD}Loki Mode v$version${NC}"
|
|
527
636
|
echo ""
|
|
528
637
|
echo "Usage: loki <command> [options]"
|
|
@@ -534,82 +643,63 @@ show_help() {
|
|
|
534
643
|
echo " loki start ./prd.md Build from a spec (PRD file, GitHub issue, or no arg)"
|
|
535
644
|
echo " Docs: https://github.com/asklokesh/loki-mode | Report a problem: loki crash"
|
|
536
645
|
echo ""
|
|
646
|
+
# CLI consolidation (Phase A): the front page presents ~17 canonical
|
|
647
|
+
# workflow-oriented entries grouped by section. Deprecated alias names
|
|
648
|
+
# (stats, metrics, cost, trust-metrics, serve, open, otel, cp, wt, rc, ...)
|
|
649
|
+
# do NOT appear here; they live in the collapsed footer below and in
|
|
650
|
+
# `loki help aliases`. The full long-tail surface is still dispatchable and
|
|
651
|
+
# documented per-command via `loki <command> --help`.
|
|
537
652
|
echo "Commands:"
|
|
538
|
-
echo "
|
|
539
|
-
echo "
|
|
540
|
-
echo "
|
|
541
|
-
echo "
|
|
542
|
-
echo "
|
|
653
|
+
echo ""
|
|
654
|
+
echo "Build:"
|
|
655
|
+
echo " start [SPEC] Start a build (PRD file, GitHub issue, or no arg)"
|
|
656
|
+
echo " plan <PRD-file> Dry-run analysis: complexity, cost, execution plan"
|
|
657
|
+
echo " grill [SPEC] Devil's-advocate spec interrogation before you build"
|
|
658
|
+
echo " spec [cmd] Living spec: lock|status|sync (drift detection)"
|
|
543
659
|
echo " quickstart [idea] Guided first build: setup, idea, template, plan, go"
|
|
544
|
-
echo "
|
|
545
|
-
echo "
|
|
546
|
-
echo "
|
|
547
|
-
echo " export <format> Export session data: json|markdown|csv|timeline (v6.0.0)"
|
|
660
|
+
echo ""
|
|
661
|
+
echo "Session:"
|
|
662
|
+
echo " status [--json] Show current status (--json for machine-readable)"
|
|
548
663
|
echo " stop Stop execution immediately"
|
|
549
|
-
echo " cleanup Kill orphaned processes from crashed sessions"
|
|
550
664
|
echo " pause Pause after current session"
|
|
551
665
|
echo " resume Resume paused execution"
|
|
552
|
-
echo "
|
|
553
|
-
echo "
|
|
554
|
-
echo "
|
|
555
|
-
echo "
|
|
556
|
-
echo "
|
|
557
|
-
echo "
|
|
558
|
-
echo "
|
|
559
|
-
echo "
|
|
560
|
-
echo "
|
|
561
|
-
echo "
|
|
562
|
-
echo "
|
|
563
|
-
echo "
|
|
564
|
-
echo "
|
|
565
|
-
echo "
|
|
566
|
-
echo "
|
|
567
|
-
echo "
|
|
568
|
-
echo "
|
|
569
|
-
echo "
|
|
570
|
-
echo "
|
|
571
|
-
echo "
|
|
572
|
-
echo "
|
|
573
|
-
echo "
|
|
574
|
-
echo "
|
|
575
|
-
echo " heal <path> Legacy system healing (archaeology, stabilize, modernize)"
|
|
576
|
-
echo " verify [base] Deterministic PR verification (Autonomi Verify MVP; CI-gate exit codes)"
|
|
577
|
-
echo " spec [cmd] Living spec: keep the spec true (lock|status|sync; drift detection, CI-gate exit codes)"
|
|
578
|
-
echo " grill [spec] Interrogate a spec with the hardest questions before you build it (Devil's Advocate)"
|
|
579
|
-
echo " review [opts] Standalone code review with quality gates (diff, staged, PR, files)"
|
|
580
|
-
echo " optimize Optimize prompts based on session history"
|
|
581
|
-
echo " enterprise Enterprise feature management (tokens, OIDC)"
|
|
582
|
-
echo " metrics [opts] Session productivity report (--json, --last N, --save, --share)"
|
|
583
|
-
echo " cost [opts] Transparent cost view: per-run/project spend + budget (--json, --last N)"
|
|
584
|
-
echo " trust [--json] Visible trust trajectory: council/gate pass-rate + interventions over runs [R4]"
|
|
585
|
-
echo " trust-metrics Trust-layer metrics: evidence-block rate, gate distribution, council split, cost/verified (--json)"
|
|
586
|
-
echo " dogfood Show self-development statistics"
|
|
587
|
-
echo " secrets [cmd] API key status and validation (status|validate)"
|
|
588
|
-
echo " reset [target] Reset session state (all|retries|failed)"
|
|
666
|
+
echo " cleanup Kill orphaned processes from crashed sessions"
|
|
667
|
+
echo ""
|
|
668
|
+
echo "Verify / trust:"
|
|
669
|
+
echo " verify [base] Deterministic PR verification (CI-gate exit codes)"
|
|
670
|
+
echo " review [opts] Standalone code review with quality gates"
|
|
671
|
+
echo " trust [cmd] Visible trust trajectory; 'trust detail' for metrics"
|
|
672
|
+
echo ""
|
|
673
|
+
echo "Observe:"
|
|
674
|
+
echo " dashboard [cmd] Operations UI server (start|stop|status|url|open)"
|
|
675
|
+
echo " mcp Launch the MCP server (Model Context Protocol, stdio)"
|
|
676
|
+
echo ""
|
|
677
|
+
echo "Report:"
|
|
678
|
+
echo " report [cmd] Reporting: session|metrics|cost|export|share|dogfood"
|
|
679
|
+
echo ""
|
|
680
|
+
echo "Knowledge:"
|
|
681
|
+
echo " analyze [cmd] Codebase intelligence: explain|onboard|code|context"
|
|
682
|
+
echo " memory [cmd] Cross-project learnings (list|show|search|stats|compound)"
|
|
683
|
+
echo ""
|
|
684
|
+
echo "Modernize:"
|
|
685
|
+
echo " modernize [cmd] Legacy modernization: heal|migrate"
|
|
686
|
+
echo ""
|
|
687
|
+
echo "Config:"
|
|
688
|
+
echo " config [cmd] Manage configuration + provider (show|set|get|provider)"
|
|
589
689
|
echo " doctor [--json] Check system prerequisites and skill symlinks"
|
|
590
|
-
echo "
|
|
591
|
-
echo " self-update Upgrade loki via current manager (use --to bun|npm|brew to switch)"
|
|
592
|
-
echo " watchdog [cmd] Process health monitoring (status|help)"
|
|
593
|
-
echo " worktree [cmd] Parallel worktree management (list|merge|clean|status)"
|
|
594
|
-
echo " agent [cmd] Agent type dispatch (list|info|run|start|review)"
|
|
595
|
-
echo " remote [PRD] Start remote session (connect from phone/browser, Claude Pro/Max)"
|
|
596
|
-
echo " trigger Event-driven autonomous execution (schedules, webhooks)"
|
|
597
|
-
echo " failover [cmd] Cross-provider auto-failover (status|--enable|--test|--chain)"
|
|
598
|
-
echo " onboard [path] Analyze a repo and generate CLAUDE.md (structure, conventions, commands)"
|
|
599
|
-
echo ' explain [path] Analyze any codebase and explain its architecture in plain English'
|
|
600
|
-
echo " docs [cmd] Generate, update, check project documentation"
|
|
601
|
-
echo " magic [cmd] Spec-driven component generation (MagicModules + MoMoA)"
|
|
602
|
-
echo " plan <PRD> Dry-run PRD analysis: complexity, cost, and execution plan"
|
|
603
|
-
echo " ci [opts] CI/CD quality gate integration (--pr, --report, --github-comment)"
|
|
604
|
-
echo " test [opts] AI-powered test generation (--file, --dir, --changed, --dry-run)"
|
|
605
|
-
echo " context [cmd] Context window management (show|files|tools|add|clear)"
|
|
606
|
-
echo " code [cmd] Codebase intelligence (overview|symbols|deps|hotspots|diff|search)"
|
|
607
|
-
echo " report [opts] Session report generator (--format text|markdown|html, --output)"
|
|
608
|
-
echo " share [opts] Share session report as GitHub Gist (--private, --format)"
|
|
609
|
-
echo " proof [cmd] Inspect/share proof-of-run artifacts (list|show|open|share)"
|
|
610
|
-
echo " bench [cmd] Head-to-head benchmark harness (run|vs|list|verify|report)"
|
|
690
|
+
echo ""
|
|
611
691
|
echo " version Show version"
|
|
612
|
-
echo " help Show this help"
|
|
692
|
+
echo " help Show this help ('loki help aliases' for old names)"
|
|
693
|
+
echo ""
|
|
694
|
+
echo "More commands (init, watch, demo, web, api, logs, github,"
|
|
695
|
+
echo "import, council, proof, audit, agent, template, magic, docs, wiki, ci,"
|
|
696
|
+
echo "test, bench, secrets, telemetry, crash, worktree, failover, monitor,"
|
|
697
|
+
echo "remote, ...) are dispatchable and documented via"
|
|
698
|
+
echo "'loki <command> --help'."
|
|
699
|
+
echo ""
|
|
700
|
+
echo "Aliases (deprecated): older command names still work; they print a"
|
|
701
|
+
echo "one-line pointer to stderr and never alter --json output. See the full"
|
|
702
|
+
echo "list with 'loki help aliases'."
|
|
613
703
|
echo ""
|
|
614
704
|
echo "Options for 'start':"
|
|
615
705
|
echo " --provider NAME AI provider: claude (default), codex, cline, aider"
|
|
@@ -655,11 +745,11 @@ show_help() {
|
|
|
655
745
|
echo ""
|
|
656
746
|
echo " # Session ops + observability"
|
|
657
747
|
echo " loki status [--json] # Current status"
|
|
658
|
-
echo " loki
|
|
659
|
-
echo " loki kpis [--json]
|
|
748
|
+
echo " loki report session --efficiency # Token + cost stats"
|
|
749
|
+
echo " loki report kpis [--json] # Accuracy + efficiency KPI snapshot"
|
|
660
750
|
echo " loki doctor [--json] # System prereq + skill symlinks"
|
|
661
751
|
echo " loki logs # Tail recent log output"
|
|
662
|
-
echo " loki export json|markdown|csv|timeline # Export session"
|
|
752
|
+
echo " loki report export json|markdown|csv|timeline # Export session"
|
|
663
753
|
echo " loki assets export team.tgz # Export shareable team assets (redacted)"
|
|
664
754
|
echo " loki cleanup # Kill orphaned processes"
|
|
665
755
|
echo ""
|
|
@@ -694,6 +784,56 @@ show_help() {
|
|
|
694
784
|
echo " See: $RUN_SH (header comments) for full list."
|
|
695
785
|
}
|
|
696
786
|
|
|
787
|
+
# CLI consolidation (Phase A): `loki help aliases` subview. Lists every
|
|
788
|
+
# deprecated alias and its canonical command for users with scripts. The old
|
|
789
|
+
# forms continue to work (they print a one-line stderr pointer and never alter
|
|
790
|
+
# --json output); they are supported through at least two MAJOR versions and
|
|
791
|
+
# considered for removal no earlier than vN+2.0.0.
|
|
792
|
+
show_help_aliases() {
|
|
793
|
+
# Strip color when stdout is not a TTY (piped/redirected), matching the
|
|
794
|
+
# bare-loki welcome path and show_help. Local overrides only.
|
|
795
|
+
local BOLD="$BOLD" NC="$NC"
|
|
796
|
+
if [ ! -t 1 ]; then
|
|
797
|
+
BOLD=''; NC=''
|
|
798
|
+
fi
|
|
799
|
+
echo -e "${BOLD}Loki Mode -- deprecated command aliases${NC}"
|
|
800
|
+
echo ""
|
|
801
|
+
echo "These older command names still work. Each forwards 1:1 to its"
|
|
802
|
+
echo "canonical command, prints exactly one pointer line to stderr, and"
|
|
803
|
+
echo "never alters --json output (the pointer is suppressed under --json,"
|
|
804
|
+
echo "-q, and --quiet). They are supported through at least two MAJOR"
|
|
805
|
+
echo "versions; removal no earlier than vN+2.0.0."
|
|
806
|
+
echo ""
|
|
807
|
+
echo " Old command Canonical command"
|
|
808
|
+
echo " ----------- -----------------"
|
|
809
|
+
echo " run <issue> start <issue>"
|
|
810
|
+
echo " serve api start"
|
|
811
|
+
echo " open preview"
|
|
812
|
+
echo " otel telemetry"
|
|
813
|
+
echo " cp checkpoint"
|
|
814
|
+
echo " wt worktree"
|
|
815
|
+
echo " rc remote"
|
|
816
|
+
echo " stats report session"
|
|
817
|
+
echo " metrics report metrics"
|
|
818
|
+
echo " cost report cost"
|
|
819
|
+
echo " export report export"
|
|
820
|
+
echo " share report share"
|
|
821
|
+
echo " dogfood report dogfood"
|
|
822
|
+
echo " kpis report kpis"
|
|
823
|
+
echo " trust-metrics trust detail"
|
|
824
|
+
echo " compound memory compound"
|
|
825
|
+
echo " explain analyze explain"
|
|
826
|
+
echo " onboard analyze onboard"
|
|
827
|
+
echo " code analyze code"
|
|
828
|
+
echo " context analyze context"
|
|
829
|
+
echo " ctx analyze context"
|
|
830
|
+
echo " heal modernize heal"
|
|
831
|
+
echo " migrate modernize migrate"
|
|
832
|
+
echo ""
|
|
833
|
+
echo "More groupings (ui, new, admin) land in later Phase B slices."
|
|
834
|
+
echo "See internal/CLI-CONSOLIDATION-DESIGN.md for the full migration plan."
|
|
835
|
+
}
|
|
836
|
+
|
|
697
837
|
# Tight newcomer landing for the bare `loki` invocation (no args).
|
|
698
838
|
# v7.28.x UX: the full 70-command table lives in `loki help`; a no-args run
|
|
699
839
|
# now shows only a calm ~12-line orientation so the "what do I do first"
|
|
@@ -5349,17 +5489,28 @@ cmd_run() {
|
|
|
5349
5489
|
break
|
|
5350
5490
|
fi
|
|
5351
5491
|
done
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5492
|
+
# CLI consolidation (Phase A): align the run->start notice with the
|
|
5493
|
+
# standardized one-line alias contract (stderr-only, suppressed under
|
|
5494
|
+
# --json/-q/--quiet, contains "is now 'loki start'"). The word
|
|
5495
|
+
# "deprecated" is kept so existing run/start unification tests still
|
|
5496
|
+
# match. Telemetry reuses the cli_command_deprecated event.
|
|
5497
|
+
if [ "$_show_warn" = "true" ] && ! _deprecated_alias_should_suppress "$@"; then
|
|
5498
|
+
echo "note: 'loki run' is now 'loki start <issue-ref>' (run is deprecated). The old form still works." >&2
|
|
5356
5499
|
# Emit telemetry event so we can measure adoption of the unified command.
|
|
5357
5500
|
# Signature: emit_event <type> <source> <action> [key=value ...]
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5501
|
+
# No-side-effect alias contract (matches _deprecated_alias at :371):
|
|
5502
|
+
# events/emit.sh creates "$LOKI_DIR/events/pending" as a side effect,
|
|
5503
|
+
# which would make a clean directory suddenly contain .loki and flip
|
|
5504
|
+
# downstream guards (cmd_share's "No .loki/" check, bash-vs-bun read
|
|
5505
|
+
# divergence). Only emit when .loki already exists; the stderr pointer
|
|
5506
|
+
# is always printed (modulo machine-output suppression above).
|
|
5507
|
+
if [ -d "${LOKI_DIR:-.loki}" ]; then
|
|
5508
|
+
emit_event cli_command_deprecated cli run_to_start \
|
|
5509
|
+
"from=run" \
|
|
5510
|
+
"to=start" \
|
|
5511
|
+
"version=6.84.0" \
|
|
5512
|
+
"argv=${*:-}"
|
|
5513
|
+
fi
|
|
5363
5514
|
fi
|
|
5364
5515
|
fi
|
|
5365
5516
|
|
|
@@ -10476,17 +10627,29 @@ cmd_dogfood() {
|
|
|
10476
10627
|
return 0
|
|
10477
10628
|
fi
|
|
10478
10629
|
|
|
10479
|
-
local stats_script="$SKILL_DIR/scripts/dogfood-stats.sh"
|
|
10480
|
-
if [ ! -f "$stats_script" ]; then
|
|
10481
|
-
echo -e "${RED}Error: dogfood-stats.sh not found${NC}"
|
|
10482
|
-
exit 1
|
|
10483
|
-
fi
|
|
10484
|
-
|
|
10485
10630
|
local json_flag=""
|
|
10486
10631
|
if [[ "${1:-}" == "--json" ]]; then
|
|
10487
10632
|
json_flag="--json"
|
|
10488
10633
|
fi
|
|
10489
10634
|
|
|
10635
|
+
# Honest degradation: scripts/dogfood-stats.sh is a development-only helper
|
|
10636
|
+
# that is NOT shipped in the npm tarball (package.json `files` omits
|
|
10637
|
+
# scripts/). On a packaged install it is absent, so report that plainly
|
|
10638
|
+
# rather than a bare "not found" that reads like a bug. Message goes to
|
|
10639
|
+
# STDERR so a --json caller's stdout stays clean; --json gets a structured
|
|
10640
|
+
# degraded payload on stdout so consumers do not choke on prose.
|
|
10641
|
+
local stats_script="$SKILL_DIR/scripts/dogfood-stats.sh"
|
|
10642
|
+
if [ ! -f "$stats_script" ]; then
|
|
10643
|
+
if [ -n "$json_flag" ]; then
|
|
10644
|
+
printf '{"available": false, "reason": "dogfood-stats.sh is a dev-only helper not shipped in the published package"}\n'
|
|
10645
|
+
else
|
|
10646
|
+
echo "loki report dogfood: self-development stats are unavailable in this install." >&2
|
|
10647
|
+
echo "The dogfood-stats.sh helper is a development-only script and is not" >&2
|
|
10648
|
+
echo "shipped in the published package. Run it from a loki-mode source checkout." >&2
|
|
10649
|
+
fi
|
|
10650
|
+
return 0
|
|
10651
|
+
fi
|
|
10652
|
+
|
|
10490
10653
|
bash "$stats_script" $json_flag
|
|
10491
10654
|
}
|
|
10492
10655
|
|
|
@@ -11725,6 +11888,37 @@ cmd_heal_help() {
|
|
|
11725
11888
|
echo " loki heal --friction-map ./legacy-app # View friction map"
|
|
11726
11889
|
}
|
|
11727
11890
|
|
|
11891
|
+
# CLI consolidation (Phase B, design item 6): the 'modernize' noun groups the
|
|
11892
|
+
# legacy-modernization surface (heal/migrate) under one entry. It is a THIN
|
|
11893
|
+
# dispatcher: each arm forwards verbatim to the existing cmd_* handler (no
|
|
11894
|
+
# handler logic moves; heal and migrate keep their separate phase vocabularies
|
|
11895
|
+
# and state). The two top-level tokens remain deprecated forwarding aliases.
|
|
11896
|
+
cmd_modernize() {
|
|
11897
|
+
local subcommand="${1:-help}"
|
|
11898
|
+
case "$subcommand" in
|
|
11899
|
+
heal) shift; cmd_heal "$@"; return $? ;;
|
|
11900
|
+
migrate) shift; cmd_migrate "$@"; return $? ;;
|
|
11901
|
+
--help|-h|help)
|
|
11902
|
+
echo -e "${BOLD}loki modernize${NC} - Legacy system modernization"
|
|
11903
|
+
echo ""
|
|
11904
|
+
echo "Usage: loki modernize <subcommand> [args]"
|
|
11905
|
+
echo ""
|
|
11906
|
+
echo "Subcommands (grouped modernization surface):"
|
|
11907
|
+
echo " heal <path> Legacy system healing in phases (was: loki heal)"
|
|
11908
|
+
echo " migrate <path> Codebase migration in phases (was: loki migrate)"
|
|
11909
|
+
echo ""
|
|
11910
|
+
echo "Run 'loki modernize <subcommand> --help' for each. The old"
|
|
11911
|
+
echo "top-level names still work; see 'loki help aliases'."
|
|
11912
|
+
return 0
|
|
11913
|
+
;;
|
|
11914
|
+
*)
|
|
11915
|
+
echo -e "${RED}Unknown modernize command: $subcommand${NC}" >&2
|
|
11916
|
+
echo "Run 'loki modernize --help' for usage." >&2
|
|
11917
|
+
return 1
|
|
11918
|
+
;;
|
|
11919
|
+
esac
|
|
11920
|
+
}
|
|
11921
|
+
|
|
11728
11922
|
cmd_heal() {
|
|
11729
11923
|
local codebase_path=""
|
|
11730
11924
|
local phase="${LOKI_HEAL_PHASE:-archaeology}"
|
|
@@ -13061,6 +13255,200 @@ show_verbose = sys.argv[3] == 'true'
|
|
|
13061
13255
|
session_model_env = (os.environ.get('LOKI_SESSION_MODEL', 'sonnet') or 'sonnet').strip().lower()
|
|
13062
13256
|
legacy_tier_switching = (os.environ.get('LOKI_LEGACY_TIER_SWITCHING', 'false') or 'false').strip().lower() == 'true'
|
|
13063
13257
|
|
|
13258
|
+
# Model-honesty contract: the estimator must quote the model the runner will
|
|
13259
|
+
# actually invoke, and name the lever that selected it. The runner honors only
|
|
13260
|
+
# two session-level levers (LOKI_MODEL was estimator-only and is removed):
|
|
13261
|
+
# 1. A pending mid-flight override file (.loki/state/model-override) -- the
|
|
13262
|
+
# most specific, just-requested intent; it wins.
|
|
13263
|
+
# 2. LOKI_SESSION_MODEL (the session pin the runner reads at run.sh:12309).
|
|
13264
|
+
# Canonical normalization is shared with run.sh + the dashboard: trim +
|
|
13265
|
+
# lowercase + EXACT allowlist match. A value with interior whitespace (fab le)
|
|
13266
|
+
# is rejected everywhere, so the three readers agree.
|
|
13267
|
+
def _loki_norm_alias(raw):
|
|
13268
|
+
raw = (raw or '').strip().lower()
|
|
13269
|
+
return raw if raw in ('haiku', 'sonnet', 'opus', 'fable') else ''
|
|
13270
|
+
|
|
13271
|
+
# Session-pin normalization is BROADER than the override-file allowlist above.
|
|
13272
|
+
# run.sh's session-pin case (run.sh:12331) accepts the four model aliases AND
|
|
13273
|
+
# the three raw tier names (planning|development|fast) -- documented at
|
|
13274
|
+
# skills/model-selection.md:8 -- mapping each through resolve_model_for_tier.
|
|
13275
|
+
# The OVERRIDE file path (_loki_norm_alias) stays narrow because that value is
|
|
13276
|
+
# fed straight to 'claude --model', where tier names are not valid. The session
|
|
13277
|
+
# pin is a tier route, so tier names ARE valid pins. This normalizer mirrors
|
|
13278
|
+
# run.sh's trim+lowercase (interior whitespace preserved, so 'fab le' stays junk
|
|
13279
|
+
# and falls through to the default tier exactly like the runner's '*' arm).
|
|
13280
|
+
def _loki_norm_session_pin(raw):
|
|
13281
|
+
raw = (raw or '').strip().lower()
|
|
13282
|
+
return raw if raw in (
|
|
13283
|
+
'haiku', 'sonnet', 'opus', 'fable',
|
|
13284
|
+
'planning', 'development', 'fast',
|
|
13285
|
+
) else ''
|
|
13286
|
+
|
|
13287
|
+
# session_model_env starts from LOKI_SESSION_MODEL; track the lever that set it
|
|
13288
|
+
# so the provenance line names the real source (never a false attribution).
|
|
13289
|
+
_session_model_source = 'LOKI_SESSION_MODEL'
|
|
13290
|
+
session_model_env = _loki_norm_session_pin(os.environ.get('LOKI_SESSION_MODEL', '')) or 'sonnet'
|
|
13291
|
+
|
|
13292
|
+
_fable_override = ''
|
|
13293
|
+
try:
|
|
13294
|
+
_ov_path = os.path.join('.loki', 'state', 'model-override')
|
|
13295
|
+
if os.path.isfile(_ov_path):
|
|
13296
|
+
with open(_ov_path) as _ovf:
|
|
13297
|
+
_fable_override = _loki_norm_alias(_ovf.read())
|
|
13298
|
+
except Exception:
|
|
13299
|
+
_fable_override = ''
|
|
13300
|
+
if _fable_override:
|
|
13301
|
+
session_model_env = _fable_override
|
|
13302
|
+
_session_model_source = '.loki/state/model-override'
|
|
13303
|
+
|
|
13304
|
+
# Architect opt-in (LOKI_FABLE_ARCHITECT=1): the runner routes ONLY the first
|
|
13305
|
+
# (architecture) iteration to fable. Disclose that here so the quote is not
|
|
13306
|
+
# blind to the lever (it would otherwise under-quote by one fable iteration).
|
|
13307
|
+
# Suppressed when an explicit planning-model override or a fable session/override
|
|
13308
|
+
# already applies. The maxTier clamp below still caps the disclosed cost.
|
|
13309
|
+
_fable_architect = (
|
|
13310
|
+
(os.environ.get('LOKI_FABLE_ARCHITECT', '0') or '0').strip() == '1'
|
|
13311
|
+
and not os.environ.get('LOKI_CLAUDE_MODEL_PLANNING')
|
|
13312
|
+
and not os.environ.get('LOKI_MODEL_PLANNING')
|
|
13313
|
+
and session_model_env != 'fable'
|
|
13314
|
+
and not (os.environ.get('LOKI_LEGACY_TIER_SWITCHING', 'false') or 'false').strip().lower() == 'true'
|
|
13315
|
+
)
|
|
13316
|
+
|
|
13317
|
+
# Apply the SAME LOKI_MAX_TIER ceiling the runner applies, so the quoted model
|
|
13318
|
+
# (and the architect disclosure) never exceed the operator cost cap.
|
|
13319
|
+
#
|
|
13320
|
+
# SYNC: This resolves the clamp result through the SAME provider config the runner
|
|
13321
|
+
# uses, a byte-faithful port of providers/claude.sh loki_apply_max_tier_clamp plus
|
|
13322
|
+
# the PROVIDER_MODEL_FAST / PROVIDER_MODEL_DEVELOPMENT resolution chains
|
|
13323
|
+
# (claude.sh:55-67, :318). The same port also lives in the dashboard
|
|
13324
|
+
# (dashboard/server.py _provider_model_fast / _provider_model_development /
|
|
13325
|
+
# _clamp_to_max_tier). All three readers MUST agree; the agreement is locked by
|
|
13326
|
+
# the parity test in tests/test-model-override.sh (resolver parity matrix) and the
|
|
13327
|
+
# cross-route tests in test-plan-command.sh. If you change resolution here, change
|
|
13328
|
+
# it in claude.sh AND dashboard/server.py, and re-run those tests. The 'or' chains
|
|
13329
|
+
# mirror bash ':-' empty-string-fallthrough; allow_haiku uses an exact 'true'
|
|
13330
|
+
# match to mirror bash [ \"\$x\" = \"true\" ]. PROVIDER_MODEL_DEVELOPMENT is opus
|
|
13331
|
+
# by default; PROVIDER_MODEL_FAST is sonnet by default (haiku when ALLOW_HAIKU).
|
|
13332
|
+
_allow_haiku = (os.environ.get('LOKI_ALLOW_HAIKU', 'false') or 'false') == 'true'
|
|
13333
|
+
def _provider_model_fast():
|
|
13334
|
+
return (os.environ.get('LOKI_CLAUDE_MODEL_FAST')
|
|
13335
|
+
or os.environ.get('LOKI_MODEL_FAST')
|
|
13336
|
+
or ('haiku' if _allow_haiku else 'sonnet'))
|
|
13337
|
+
def _provider_model_development():
|
|
13338
|
+
return (os.environ.get('LOKI_CLAUDE_MODEL_DEVELOPMENT')
|
|
13339
|
+
or os.environ.get('LOKI_MODEL_DEVELOPMENT')
|
|
13340
|
+
or ('sonnet' if _allow_haiku else 'opus'))
|
|
13341
|
+
def _provider_model_planning():
|
|
13342
|
+
# claude.sh:65 -> LOKI_CLAUDE_MODEL_PLANNING > LOKI_MODEL_PLANNING > opus.
|
|
13343
|
+
# CLAUDE_DEFAULT_PLANNING is always opus (LOKI_ALLOW_HAIKU only lowers the
|
|
13344
|
+
# development and fast defaults, not planning).
|
|
13345
|
+
return (os.environ.get('LOKI_CLAUDE_MODEL_PLANNING')
|
|
13346
|
+
or os.environ.get('LOKI_MODEL_PLANNING')
|
|
13347
|
+
or 'opus')
|
|
13348
|
+
_max_tier = (os.environ.get('LOKI_MAX_TIER', '') or '').strip().lower()
|
|
13349
|
+
def _loki_clamp_alias(alias):
|
|
13350
|
+
if not _max_tier:
|
|
13351
|
+
return alias
|
|
13352
|
+
if _max_tier == 'haiku':
|
|
13353
|
+
return _provider_model_fast()
|
|
13354
|
+
if _max_tier == 'sonnet':
|
|
13355
|
+
# alias passed as both model and tier (override-path convention): the
|
|
13356
|
+
# runner's sonnet arm reduces to 'downgrade iff alias==fable'.
|
|
13357
|
+
return _provider_model_development() if alias == 'fable' else alias
|
|
13358
|
+
if _max_tier == 'opus':
|
|
13359
|
+
return 'opus' if alias == 'fable' else alias
|
|
13360
|
+
return alias
|
|
13361
|
+
|
|
13362
|
+
# Session-pin tier route (the runner's NO-OVERRIDE path). When there is no
|
|
13363
|
+
# mid-flight override file, the runner does NOT feed the session alias straight
|
|
13364
|
+
# to --model. It maps the alias to an abstract TIER (run.sh:12331 -- opus->
|
|
13365
|
+
# planning, sonnet->development, haiku->fast, fable->fable) and resolves that
|
|
13366
|
+
# tier through resolve_model_for_tier (claude.sh:353), which reads
|
|
13367
|
+
# PROVIDER_MODEL_PLANNING/DEVELOPMENT/FAST and then applies
|
|
13368
|
+
# loki_apply_max_tier_clamp(model, REAL_tier). This DIFFERS from the override-
|
|
13369
|
+
# path clamp _loki_clamp_alias above: a 'sonnet' SESSION pin dispatches OPUS
|
|
13370
|
+
# (development tier -> PROVIDER_MODEL_DEVELOPMENT=opus on stock config), whereas
|
|
13371
|
+
# a 'sonnet' OVERRIDE file dispatches sonnet (alias fed straight to --model).
|
|
13372
|
+
# Quoting the alias name on the default session path was the task-568 gap (it
|
|
13373
|
+
# understated cost ~1.7x: Sonnet quote vs Opus dispatch). The estimator must
|
|
13374
|
+
# quote the model the runner actually dispatches.
|
|
13375
|
+
#
|
|
13376
|
+
# SYNC: byte-faithful with run.sh's session-pin case + claude.sh
|
|
13377
|
+
# resolve_model_for_tier + loki_apply_max_tier_clamp. The same resolver lives in
|
|
13378
|
+
# the dashboard (dashboard/server.py _resolve_session_pin). Locked by the
|
|
13379
|
+
# session-pin parity matrix and the no-cap stock cross-route cases in
|
|
13380
|
+
# tests/test-model-override.sh.
|
|
13381
|
+
def _resolve_session_pin(alias):
|
|
13382
|
+
# alias -> abstract tier (run.sh:12331). Unknown -> development (the runner's
|
|
13383
|
+
# '*' arm passes CURRENT_TIER through to resolve_model_for_tier, whose '*'
|
|
13384
|
+
# default is PROVIDER_MODEL_DEVELOPMENT, i.e. the development tier).
|
|
13385
|
+
_pin_tier = {
|
|
13386
|
+
'opus': 'planning',
|
|
13387
|
+
'sonnet': 'development',
|
|
13388
|
+
'haiku': 'fast',
|
|
13389
|
+
'fable': 'fable',
|
|
13390
|
+
# Raw tier-name pins (run.sh:12336 passthrough arm) map to their own
|
|
13391
|
+
# tier, NOT through the alias table. So pin=fast -> fast tier ->
|
|
13392
|
+
# PROVIDER_MODEL_FAST (sonnet stock / haiku ALLOW_HAIKU), matching the
|
|
13393
|
+
# runner's dispatch instead of collapsing onto development.
|
|
13394
|
+
'planning': 'planning',
|
|
13395
|
+
'development': 'development',
|
|
13396
|
+
'fast': 'fast',
|
|
13397
|
+
}.get(alias, 'development')
|
|
13398
|
+
# tier -> model (claude.sh resolve_model_for_tier explicit arms).
|
|
13399
|
+
if _pin_tier == 'planning':
|
|
13400
|
+
_m = _provider_model_planning()
|
|
13401
|
+
elif _pin_tier == 'fast':
|
|
13402
|
+
_m = _provider_model_fast()
|
|
13403
|
+
elif _pin_tier == 'fable':
|
|
13404
|
+
_m = 'fable'
|
|
13405
|
+
else: # development (and the '*' fallthrough)
|
|
13406
|
+
_m = _provider_model_development()
|
|
13407
|
+
# Apply the shared LOKI_MAX_TIER clamp with the REAL tier and resolved model
|
|
13408
|
+
# (loki_apply_max_tier_clamp(model, tier)), NOT the alias==alias override
|
|
13409
|
+
# convention. This is the one place where the two paths legitimately diverge
|
|
13410
|
+
# (e.g. opus pin + sonnet cap + ALLOW_HAIKU: tier route -> sonnet, because the
|
|
13411
|
+
# planning tier downgrades to PROVIDER_MODEL_DEVELOPMENT=sonnet).
|
|
13412
|
+
if not _max_tier:
|
|
13413
|
+
return _m
|
|
13414
|
+
if _max_tier == 'haiku':
|
|
13415
|
+
return _provider_model_fast()
|
|
13416
|
+
if _max_tier == 'sonnet':
|
|
13417
|
+
if _pin_tier in ('planning', 'fable') or _m == 'fable':
|
|
13418
|
+
return _provider_model_development()
|
|
13419
|
+
return _m
|
|
13420
|
+
if _max_tier == 'opus':
|
|
13421
|
+
return 'opus' if _m == 'fable' else _m
|
|
13422
|
+
return _m
|
|
13423
|
+
# Resolve the model the runner will ACTUALLY dispatch for the pinned session,
|
|
13424
|
+
# choosing the correct route:
|
|
13425
|
+
# - OVERRIDE file present: the runner feeds the alias straight to --model via
|
|
13426
|
+
# loki_apply_max_tier_clamp(alias, alias). Use _loki_clamp_alias (the
|
|
13427
|
+
# override-path clamp). A 'sonnet' override dispatches sonnet.
|
|
13428
|
+
# - No override (session pin): the runner maps alias -> tier ->
|
|
13429
|
+
# resolve_model_for_tier -> clamp(model, real_tier). Use _resolve_session_pin.
|
|
13430
|
+
# A 'sonnet' session pin dispatches OPUS (development tier).
|
|
13431
|
+
# This is the task-568 fix: price the actually-dispatched model on BOTH routes.
|
|
13432
|
+
_is_override = (_session_model_source == '.loki/state/model-override')
|
|
13433
|
+
if _is_override:
|
|
13434
|
+
_dispatched_model = _loki_clamp_alias(session_model_env)
|
|
13435
|
+
else:
|
|
13436
|
+
_dispatched_model = _resolve_session_pin(session_model_env)
|
|
13437
|
+
|
|
13438
|
+
# Keep session_model_env (the alias) for token-volume tier mapping and provenance,
|
|
13439
|
+
# but record when the cost ceiling actually changed the dispatched model so the
|
|
13440
|
+
# provenance line is honest about the clamp.
|
|
13441
|
+
if _max_tier and _dispatched_model != session_model_env:
|
|
13442
|
+
_session_model_source = 'LOKI_MAX_TIER (clamped)'
|
|
13443
|
+
# Clear the architect-fable disclosure when the ceiling would clamp the architect
|
|
13444
|
+
# iteration too. This is INDEPENDENT of whether the session model itself changed:
|
|
13445
|
+
# e.g. an opus pin under a sonnet cap leaves the session model opus (no change),
|
|
13446
|
+
# but the iter-0 fable architect tier still clamps down to development. Nesting
|
|
13447
|
+
# this under the session-changed branch would over-quote a Fable iteration the
|
|
13448
|
+
# runner actually dispatches as opus (estimator-vs-runner divergence).
|
|
13449
|
+
if _max_tier and _fable_architect and _loki_clamp_alias('fable') != 'fable':
|
|
13450
|
+
_fable_architect = False
|
|
13451
|
+
|
|
13064
13452
|
# v7.29.0: Honor LOKI_COMPLEXITY -- the SAME env var the runner honors at
|
|
13065
13453
|
# run.sh:920 -- so a forced-tier run (e.g. 'loki demo' / 'loki start --simple'
|
|
13066
13454
|
# which export LOKI_COMPLEXITY=simple) quotes the tier it will actually run,
|
|
@@ -13075,15 +13463,31 @@ forced_complexity = {'standard': 'moderate'}.get(_forced_complexity_raw, _forced
|
|
|
13075
13463
|
if forced_complexity not in ('simple', 'moderate', 'complex', 'enterprise'):
|
|
13076
13464
|
forced_complexity = ''
|
|
13077
13465
|
|
|
13078
|
-
# Map session model name to tier key used in tokens_per_tier below.
|
|
13079
|
-
#
|
|
13466
|
+
# Map session model name to tier key used in tokens_per_tier below. This keys the
|
|
13467
|
+
# token VOLUME per iteration off the work tier the session pin selects (e.g. a
|
|
13468
|
+
# sonnet pin = the development tier = 80k/15k tokens). It does NOT decide the
|
|
13469
|
+
# priced model: that is _dispatched_model, the model the runner actually invokes.
|
|
13470
|
+
# Unknown models fall through to 'development' as a safe default.
|
|
13080
13471
|
_session_tier_map = {
|
|
13472
|
+
'fable': 'advisor',
|
|
13081
13473
|
'opus': 'planning',
|
|
13082
13474
|
'sonnet': 'development',
|
|
13083
13475
|
'haiku': 'fast',
|
|
13476
|
+
# Raw tier-name pins key off their own work tier directly (they ARE tier
|
|
13477
|
+
# names), mirroring run.sh's session-pin passthrough arm.
|
|
13478
|
+
'planning': 'planning',
|
|
13479
|
+
'development': 'development',
|
|
13480
|
+
'fast': 'fast',
|
|
13084
13481
|
}
|
|
13085
13482
|
session_tier = _session_tier_map.get(session_model_env, 'development')
|
|
13086
13483
|
|
|
13484
|
+
# Pricing-table key (Fable/Opus/Sonnet/Haiku) for the model the runner dispatches.
|
|
13485
|
+
# The loop below uses tokens_per_tier[tier] for token VOLUME but prices by this
|
|
13486
|
+
# model, so the quote reflects the actually-dispatched model on every route.
|
|
13487
|
+
_PRICED_MODEL_KEY = {'fable': 'Fable', 'opus': 'Opus', 'sonnet': 'Sonnet', 'haiku': 'Haiku'}
|
|
13488
|
+
def _priced_model_for(alias):
|
|
13489
|
+
return _PRICED_MODEL_KEY.get((alias or '').strip().lower(), 'Opus')
|
|
13490
|
+
|
|
13087
13491
|
# Colors (disabled for JSON mode)
|
|
13088
13492
|
if show_json:
|
|
13089
13493
|
RED = GREEN = YELLOW = BLUE = CYAN = BOLD = DIM = NC = ''
|
|
@@ -13256,16 +13660,25 @@ estimated_iterations = (min_iter + max_iter) // 2
|
|
|
13256
13660
|
# RARV cycle: 4 iterations per cycle (plan, develop, develop, test)
|
|
13257
13661
|
# Tokens estimated per iteration step
|
|
13258
13662
|
tokens_per_tier = {
|
|
13663
|
+
'advisor': {'input': 50000, 'output': 8000, 'model': 'Fable'},
|
|
13259
13664
|
'planning': {'input': 50000, 'output': 8000, 'model': 'Opus'},
|
|
13260
13665
|
'development': {'input': 80000, 'output': 15000, 'model': 'Sonnet'},
|
|
13261
13666
|
'fast': {'input': 30000, 'output': 5000, 'model': 'Haiku'},
|
|
13262
13667
|
}
|
|
13263
13668
|
|
|
13264
|
-
# Pricing per 1M tokens
|
|
13669
|
+
# Pricing per 1M tokens. Fable 5 is the top-tier advisory model at exactly 2x
|
|
13670
|
+
# Opus per token (published \$10/\$50 in/out vs Opus \$5/\$25). The Opus row was
|
|
13671
|
+
# corrected from a stale \$15/\$75 to the real Opus 4.8 \$5/\$25, and the Haiku
|
|
13672
|
+
# row from a stale \$0.25/\$1.25 (Haiku 3.5) to the published Claude Haiku 4.5
|
|
13673
|
+
# price of \$1/\$5 (anthropic.com/news/claude-haiku-4-5), so all four rows now
|
|
13674
|
+
# match the three canonical model-keyed tables (run.sh pricing.json, run.sh
|
|
13675
|
+
# check_budget_limit, dashboard _DEFAULT_PRICING) and a fable-forced quote
|
|
13676
|
+
# honestly shows MORE than Opus, not less.
|
|
13265
13677
|
pricing = {
|
|
13266
|
-
'
|
|
13678
|
+
'Fable': {'input': 10.00, 'output': 50.00},
|
|
13679
|
+
'Opus': {'input': 5.00, 'output': 25.00},
|
|
13267
13680
|
'Sonnet': {'input': 3.00, 'output': 15.00},
|
|
13268
|
-
'Haiku': {'input':
|
|
13681
|
+
'Haiku': {'input': 1.00, 'output': 5.00},
|
|
13269
13682
|
}
|
|
13270
13683
|
|
|
13271
13684
|
# Build per-iteration plan
|
|
@@ -13273,8 +13686,8 @@ iteration_plan = []
|
|
|
13273
13686
|
total_input_tokens = 0
|
|
13274
13687
|
total_output_tokens = 0
|
|
13275
13688
|
total_cost = 0.0
|
|
13276
|
-
tier_totals = {'Opus': 0.0, 'Sonnet': 0.0, 'Haiku': 0.0}
|
|
13277
|
-
tier_iterations = {'Opus': 0, 'Sonnet': 0, 'Haiku': 0}
|
|
13689
|
+
tier_totals = {'Fable': 0.0, 'Opus': 0.0, 'Sonnet': 0.0, 'Haiku': 0.0}
|
|
13690
|
+
tier_iterations = {'Fable': 0, 'Opus': 0, 'Sonnet': 0, 'Haiku': 0}
|
|
13278
13691
|
|
|
13279
13692
|
for i in range(estimated_iterations):
|
|
13280
13693
|
rarv_step = i % 4
|
|
@@ -13300,9 +13713,26 @@ for i in range(estimated_iterations):
|
|
|
13300
13713
|
# MUST use the same tier for all iterations to avoid quoting a
|
|
13301
13714
|
# cost higher than the user will actually pay.
|
|
13302
13715
|
tier = session_tier
|
|
13716
|
+
# Architect opt-in: the runner routes ONLY the first iteration to fable
|
|
13717
|
+
# (the architecture pass). Mirror that here so the quote is not blind to
|
|
13718
|
+
# the lever: iteration 0 -> advisor (fable), the rest -> session tier.
|
|
13719
|
+
if _fable_architect and i == 0:
|
|
13720
|
+
tier = 'advisor'
|
|
13303
13721
|
|
|
13304
13722
|
info = tokens_per_tier[tier]
|
|
13305
|
-
|
|
13723
|
+
# Token VOLUME comes from the work tier (info), but the priced MODEL is the
|
|
13724
|
+
# model the runner actually dispatches. In legacy rotation the per-tier model
|
|
13725
|
+
# IS the dispatched model. In session-pinned mode (the default) the runner
|
|
13726
|
+
# resolves the pin through provider config (_dispatched_model): a sonnet pin
|
|
13727
|
+
# dispatches opus, so we price Opus, not the tier's static 'Sonnet' label.
|
|
13728
|
+
# The fable-architect iteration 0 genuinely dispatches fable (already cap-
|
|
13729
|
+
# cleared above when it would clamp), so it keeps the advisor tier's Fable.
|
|
13730
|
+
if legacy_tier_switching:
|
|
13731
|
+
model = info['model']
|
|
13732
|
+
elif _fable_architect and i == 0:
|
|
13733
|
+
model = info['model'] # advisor tier -> Fable
|
|
13734
|
+
else:
|
|
13735
|
+
model = _priced_model_for(_dispatched_model)
|
|
13306
13736
|
inp = info['input']
|
|
13307
13737
|
out = info['output']
|
|
13308
13738
|
|
|
@@ -13476,6 +13906,7 @@ for reason in complexity_reasons:
|
|
|
13476
13906
|
print(f'\n{CYAN}Estimated Iterations{NC}')
|
|
13477
13907
|
print(f' Count: {BOLD}{estimated_iterations}{NC} (range: {min_iter}-{max_iter})')
|
|
13478
13908
|
print(f' RARV cycles: {cycle_count} (4 iterations per cycle)')
|
|
13909
|
+
fable_n = tier_iterations.get('Fable', 0)
|
|
13479
13910
|
opus_n = tier_iterations.get('Opus', 0)
|
|
13480
13911
|
sonnet_n = tier_iterations.get('Sonnet', 0)
|
|
13481
13912
|
haiku_n = tier_iterations.get('Haiku', 0)
|
|
@@ -13485,6 +13916,8 @@ if legacy_tier_switching:
|
|
|
13485
13916
|
else:
|
|
13486
13917
|
# Session-pinned: exactly one tier has all iterations.
|
|
13487
13918
|
pinned_parts = []
|
|
13919
|
+
if fable_n > 0:
|
|
13920
|
+
pinned_parts.append(f'Fable x{fable_n}')
|
|
13488
13921
|
if opus_n > 0:
|
|
13489
13922
|
pinned_parts.append(f'Opus x{opus_n}')
|
|
13490
13923
|
if sonnet_n > 0:
|
|
@@ -13492,7 +13925,20 @@ else:
|
|
|
13492
13925
|
if haiku_n > 0:
|
|
13493
13926
|
pinned_parts.append(f'Haiku x{haiku_n}')
|
|
13494
13927
|
print(' Model distribution: ' + (' | '.join(pinned_parts) if pinned_parts else '(none)'))
|
|
13495
|
-
|
|
13928
|
+
# Provenance: name the lever that actually selected the pinned model, never a
|
|
13929
|
+
# false attribution. _session_model_source is the real source (env, override
|
|
13930
|
+
# file, or maxTier clamp) tracked at selection time. When the session pin is a
|
|
13931
|
+
# tier alias that resolves to a DIFFERENT dispatched model (the default: a
|
|
13932
|
+
# 'sonnet' pin -> development tier -> opus), show the resolution so the
|
|
13933
|
+
# distribution above (Opus x4) is legible against the pin name (sonnet).
|
|
13934
|
+
if (not _is_override) and _dispatched_model != session_model_env:
|
|
13935
|
+
print(f' {DIM}(pinned via {_session_model_source}={session_model_env} -> {session_tier} tier -> {_dispatched_model}; set LOKI_LEGACY_TIER_SWITCHING=true to rotate){NC}')
|
|
13936
|
+
else:
|
|
13937
|
+
print(f' {DIM}(pinned via {_session_model_source}={session_model_env}; set LOKI_LEGACY_TIER_SWITCHING=true to rotate){NC}')
|
|
13938
|
+
if _fable_architect:
|
|
13939
|
+
print(f' {DIM}(+ 1 architecture iteration on Fable via LOKI_FABLE_ARCHITECT=1){NC}')
|
|
13940
|
+
if fable_n > 0:
|
|
13941
|
+
print(f' {YELLOW}Fable 5 is the top-tier advisory model priced at 2x Opus ({chr(36)}10/{chr(36)}50 per MTok).{NC}')
|
|
13496
13942
|
|
|
13497
13943
|
# Tokens
|
|
13498
13944
|
total_tok = total_input_tokens + total_output_tokens
|
|
@@ -13505,7 +13951,7 @@ print(f' Total: {total_tok:>12,} tokens')
|
|
|
13505
13951
|
ds = chr(36) # dollar sign
|
|
13506
13952
|
print(f'\n{CYAN}Cost Estimate{NC}')
|
|
13507
13953
|
print(f' {BOLD}Total: {ds}{total_cost:.2f}{NC}')
|
|
13508
|
-
for model in ['Opus', 'Sonnet', 'Haiku']:
|
|
13954
|
+
for model in ['Fable', 'Opus', 'Sonnet', 'Haiku']:
|
|
13509
13955
|
# v6.81.1: suppress zero-cost tiers (e.g. session-pinned runs). Keeps
|
|
13510
13956
|
# the breakdown focused on models actually used. Legacy mode still
|
|
13511
13957
|
# shows all three because each tier has non-zero iterations.
|
|
@@ -13590,7 +14036,7 @@ cmd_plan() {
|
|
|
13590
14036
|
echo ""
|
|
13591
14037
|
echo "Environment Variables:"
|
|
13592
14038
|
echo " LOKI_SESSION_MODEL Pin cost estimate to a single tier"
|
|
13593
|
-
echo " (opus|sonnet|haiku, default: sonnet)"
|
|
14039
|
+
echo " (opus|sonnet|haiku|fable, default: sonnet)"
|
|
13594
14040
|
echo " LOKI_LEGACY_TIER_SWITCHING Set to 'true' to restore the legacy"
|
|
13595
14041
|
echo " Opus/Sonnet/Haiku per-iteration rotation"
|
|
13596
14042
|
echo ""
|
|
@@ -13724,7 +14170,12 @@ main() {
|
|
|
13724
14170
|
cmd_status "$@"
|
|
13725
14171
|
;;
|
|
13726
14172
|
stats)
|
|
13727
|
-
|
|
14173
|
+
# CLI consolidation (Phase A): 'stats' is a deprecated alias of
|
|
14174
|
+
# 'report session'. On the Bun route this arm is never reached
|
|
14175
|
+
# (stats is Bun-native); the matching deprecation lives in
|
|
14176
|
+
# loki-ts/src/commands/stats.ts so both routes behave identically.
|
|
14177
|
+
_deprecated_alias stats "report session" "$@"
|
|
14178
|
+
cmd_report session "$@"
|
|
13728
14179
|
;;
|
|
13729
14180
|
dashboard)
|
|
13730
14181
|
cmd_dashboard "$@"
|
|
@@ -13732,13 +14183,24 @@ main() {
|
|
|
13732
14183
|
web)
|
|
13733
14184
|
cmd_web "$@"
|
|
13734
14185
|
;;
|
|
13735
|
-
preview
|
|
14186
|
+
preview)
|
|
14187
|
+
cmd_preview "$@"
|
|
14188
|
+
;;
|
|
14189
|
+
open)
|
|
14190
|
+
# CLI consolidation (Phase A): 'open' is a deprecated alias of 'preview'.
|
|
14191
|
+
_deprecated_alias open preview "$@"
|
|
13736
14192
|
cmd_preview "$@"
|
|
13737
14193
|
;;
|
|
13738
14194
|
logs)
|
|
13739
14195
|
cmd_logs "$@"
|
|
13740
14196
|
;;
|
|
13741
14197
|
serve)
|
|
14198
|
+
# CLI consolidation (Phase A): 'serve' is a deprecated alias of
|
|
14199
|
+
# 'api start'. Emit the pointer BEFORE the --help short-circuit so it
|
|
14200
|
+
# fires on every form (including 'serve --help'), consistent with the
|
|
14201
|
+
# other short aliases and testable without binding a TCP port.
|
|
14202
|
+
# Suppressed under --json/-q/--quiet by the helper.
|
|
14203
|
+
_deprecated_alias serve "api start" "$@"
|
|
13742
14204
|
# v7.6.2 B-12 fix: 'serve --help' previously routed to 'cmd_api start --help'
|
|
13743
14205
|
# which started the dashboard as a side effect instead of printing help.
|
|
13744
14206
|
case "${1:-}" in
|
|
@@ -13784,7 +14246,9 @@ main() {
|
|
|
13784
14246
|
cmd_watch "$@"
|
|
13785
14247
|
;;
|
|
13786
14248
|
export)
|
|
13787
|
-
|
|
14249
|
+
# CLI consolidation (Phase A): 'export' -> 'report export'.
|
|
14250
|
+
_deprecated_alias export "report export" "$@"
|
|
14251
|
+
cmd_report export "$@"
|
|
13788
14252
|
;;
|
|
13789
14253
|
assets)
|
|
13790
14254
|
cmd_assets "$@"
|
|
@@ -13802,9 +14266,13 @@ main() {
|
|
|
13802
14266
|
cmd_memory "$@"
|
|
13803
14267
|
;;
|
|
13804
14268
|
compound)
|
|
13805
|
-
|
|
14269
|
+
# CLI consolidation (Phase B): 'compound' -> 'memory compound'.
|
|
14270
|
+
_deprecated_alias compound "memory compound" "$@"
|
|
14271
|
+
cmd_memory compound "$@"
|
|
13806
14272
|
;;
|
|
13807
14273
|
checkpoint|cp)
|
|
14274
|
+
# CLI consolidation (Phase A): 'cp' is a deprecated alias of 'checkpoint'.
|
|
14275
|
+
[ "$command" = "cp" ] && _deprecated_alias cp checkpoint "$@"
|
|
13808
14276
|
cmd_checkpoint "$@"
|
|
13809
14277
|
;;
|
|
13810
14278
|
rollback)
|
|
@@ -13814,7 +14282,9 @@ main() {
|
|
|
13814
14282
|
cmd_council "$@"
|
|
13815
14283
|
;;
|
|
13816
14284
|
dogfood)
|
|
13817
|
-
|
|
14285
|
+
# CLI consolidation (Phase A): 'dogfood' -> 'report dogfood'.
|
|
14286
|
+
_deprecated_alias dogfood "report dogfood" "$@"
|
|
14287
|
+
cmd_report dogfood "$@"
|
|
13818
14288
|
;;
|
|
13819
14289
|
projects)
|
|
13820
14290
|
cmd_projects "$@"
|
|
@@ -13853,8 +14323,13 @@ main() {
|
|
|
13853
14323
|
optimize)
|
|
13854
14324
|
cmd_optimize "$@"
|
|
13855
14325
|
;;
|
|
14326
|
+
modernize)
|
|
14327
|
+
cmd_modernize "$@"
|
|
14328
|
+
;;
|
|
13856
14329
|
heal)
|
|
13857
|
-
|
|
14330
|
+
# CLI consolidation (Phase B): 'heal' -> 'modernize heal'.
|
|
14331
|
+
_deprecated_alias heal "modernize heal" "$@"
|
|
14332
|
+
cmd_modernize heal "$@"
|
|
13858
14333
|
;;
|
|
13859
14334
|
verify)
|
|
13860
14335
|
cmd_verify "$@"
|
|
@@ -13869,12 +14344,16 @@ main() {
|
|
|
13869
14344
|
cmd_mcp "$@"
|
|
13870
14345
|
;;
|
|
13871
14346
|
migrate)
|
|
13872
|
-
|
|
14347
|
+
# CLI consolidation (Phase B): 'migrate' -> 'modernize migrate'.
|
|
14348
|
+
_deprecated_alias migrate "modernize migrate" "$@"
|
|
14349
|
+
cmd_modernize migrate "$@"
|
|
13873
14350
|
;;
|
|
13874
14351
|
cluster)
|
|
13875
14352
|
cmd_cluster "$@"
|
|
13876
14353
|
;;
|
|
13877
14354
|
worktree|wt)
|
|
14355
|
+
# CLI consolidation (Phase A): 'wt' is a deprecated alias of 'worktree'.
|
|
14356
|
+
[ "$command" = "wt" ] && _deprecated_alias wt worktree "$@"
|
|
13878
14357
|
cmd_worktree "$@"
|
|
13879
14358
|
;;
|
|
13880
14359
|
agent)
|
|
@@ -13887,24 +14366,34 @@ main() {
|
|
|
13887
14366
|
cmd_state "$@"
|
|
13888
14367
|
;;
|
|
13889
14368
|
metrics)
|
|
13890
|
-
|
|
14369
|
+
# CLI consolidation (Phase A): 'metrics' -> 'report metrics'.
|
|
14370
|
+
_deprecated_alias metrics "report metrics" "$@"
|
|
14371
|
+
cmd_report metrics "$@"
|
|
13891
14372
|
;;
|
|
13892
14373
|
cost)
|
|
13893
|
-
|
|
14374
|
+
# CLI consolidation (Phase A): 'cost' -> 'report cost'.
|
|
14375
|
+
_deprecated_alias cost "report cost" "$@"
|
|
14376
|
+
cmd_report cost "$@"
|
|
13894
14377
|
;;
|
|
13895
14378
|
trust)
|
|
13896
14379
|
cmd_trust "$@"
|
|
13897
14380
|
;;
|
|
13898
14381
|
trust-metrics)
|
|
13899
|
-
|
|
14382
|
+
# CLI consolidation (Phase A): 'trust-metrics' -> 'trust detail'.
|
|
14383
|
+
_deprecated_alias trust-metrics "trust detail" "$@"
|
|
14384
|
+
cmd_trust detail "$@"
|
|
13900
14385
|
;;
|
|
13901
14386
|
syslog)
|
|
13902
14387
|
cmd_syslog "$@"
|
|
13903
14388
|
;;
|
|
13904
14389
|
telemetry|otel)
|
|
14390
|
+
# CLI consolidation (Phase A): 'otel' is a deprecated alias of 'telemetry'.
|
|
14391
|
+
[ "$command" = "otel" ] && _deprecated_alias otel telemetry "$@"
|
|
13905
14392
|
cmd_telemetry "$@"
|
|
13906
14393
|
;;
|
|
13907
14394
|
remote|rc)
|
|
14395
|
+
# CLI consolidation (Phase A): 'rc' is a deprecated alias of 'remote'.
|
|
14396
|
+
[ "$command" = "rc" ] && _deprecated_alias rc remote "$@"
|
|
13908
14397
|
cmd_remote "$@"
|
|
13909
14398
|
;;
|
|
13910
14399
|
trigger)
|
|
@@ -13916,11 +14405,18 @@ main() {
|
|
|
13916
14405
|
failover)
|
|
13917
14406
|
cmd_failover "$@"
|
|
13918
14407
|
;;
|
|
14408
|
+
analyze)
|
|
14409
|
+
cmd_analyze "$@"
|
|
14410
|
+
;;
|
|
13919
14411
|
onboard)
|
|
13920
|
-
|
|
14412
|
+
# CLI consolidation (Phase B): 'onboard' -> 'analyze onboard'.
|
|
14413
|
+
_deprecated_alias onboard "analyze onboard" "$@"
|
|
14414
|
+
cmd_analyze onboard "$@"
|
|
13921
14415
|
;;
|
|
13922
14416
|
explain)
|
|
13923
|
-
|
|
14417
|
+
# CLI consolidation (Phase B): 'explain' -> 'analyze explain'.
|
|
14418
|
+
_deprecated_alias explain "analyze explain" "$@"
|
|
14419
|
+
cmd_analyze explain "$@"
|
|
13924
14420
|
;;
|
|
13925
14421
|
docs)
|
|
13926
14422
|
cmd_docs "$@"
|
|
@@ -13944,7 +14440,9 @@ main() {
|
|
|
13944
14440
|
cmd_crash "$@"
|
|
13945
14441
|
;;
|
|
13946
14442
|
share)
|
|
13947
|
-
|
|
14443
|
+
# CLI consolidation (Phase A): 'share' -> 'report share'.
|
|
14444
|
+
_deprecated_alias share "report share" "$@"
|
|
14445
|
+
cmd_report share "$@"
|
|
13948
14446
|
;;
|
|
13949
14447
|
proof)
|
|
13950
14448
|
cmd_proof "$@"
|
|
@@ -13953,10 +14451,15 @@ main() {
|
|
|
13953
14451
|
cmd_bench "$@"
|
|
13954
14452
|
;;
|
|
13955
14453
|
context|ctx)
|
|
13956
|
-
|
|
14454
|
+
# CLI consolidation (Phase B): 'context' (and short alias 'ctx')
|
|
14455
|
+
# -> 'analyze context'.
|
|
14456
|
+
_deprecated_alias "$command" "analyze context" "$@"
|
|
14457
|
+
cmd_analyze context "$@"
|
|
13957
14458
|
;;
|
|
13958
14459
|
code)
|
|
13959
|
-
|
|
14460
|
+
# CLI consolidation (Phase B): 'code' -> 'analyze code'.
|
|
14461
|
+
_deprecated_alias code "analyze code" "$@"
|
|
14462
|
+
cmd_analyze code "$@"
|
|
13960
14463
|
;;
|
|
13961
14464
|
version|--version|-v)
|
|
13962
14465
|
cmd_version "$@"
|
|
@@ -13964,8 +14467,24 @@ main() {
|
|
|
13964
14467
|
completions)
|
|
13965
14468
|
cmd_completions "$@"
|
|
13966
14469
|
;;
|
|
14470
|
+
kpis)
|
|
14471
|
+
# CLI consolidation (Phase B): `kpis` is a deprecated alias of the
|
|
14472
|
+
# canonical `report kpis`. Emit the one-line pointer (suppressed
|
|
14473
|
+
# under --json/-q/--quiet by _deprecated_alias), then print the
|
|
14474
|
+
# honest Bun-requirement message via the shared helper. On a machine
|
|
14475
|
+
# WITH Bun, bin/loki routes `kpis` to the Bun runtime before this arm
|
|
14476
|
+
# is ever reached; this arm is the LOKI_LEGACY_BASH / no-Bun path.
|
|
14477
|
+
_deprecated_alias kpis "report kpis" "$@"
|
|
14478
|
+
_kpis_bash_unavailable "$@"
|
|
14479
|
+
;;
|
|
13967
14480
|
help|--help|-h)
|
|
13968
|
-
|
|
14481
|
+
# CLI consolidation (Phase A): `loki help aliases` prints the full
|
|
14482
|
+
# deprecated-alias table; bare help prints the grouped front page.
|
|
14483
|
+
if [ "${1:-}" = "aliases" ]; then
|
|
14484
|
+
show_help_aliases
|
|
14485
|
+
else
|
|
14486
|
+
show_help
|
|
14487
|
+
fi
|
|
13969
14488
|
;;
|
|
13970
14489
|
*)
|
|
13971
14490
|
echo -e "${RED}Unknown command: $command${NC}"
|
|
@@ -16281,6 +16800,7 @@ except Exception as e:
|
|
|
16281
16800
|
echo " export [file] Export to JSON file"
|
|
16282
16801
|
echo " dedupe Remove duplicate entries"
|
|
16283
16802
|
echo " clear [type] Clear learnings (all or specific type)"
|
|
16803
|
+
echo " compound [cmd] Compound-solutions knowledge store (was: loki compound)"
|
|
16284
16804
|
echo ""
|
|
16285
16805
|
echo "Memory System Commands:"
|
|
16286
16806
|
echo " index [rebuild] Show or rebuild the memory index layer"
|
|
@@ -16648,9 +17168,24 @@ print('removed')
|
|
|
16648
17168
|
"
|
|
16649
17169
|
;;
|
|
16650
17170
|
|
|
17171
|
+
compound)
|
|
17172
|
+
# CLI consolidation (Phase B, design item 7): 'compound' is folded
|
|
17173
|
+
# under the 'memory' noun as 'memory compound'. This arm forwards to
|
|
17174
|
+
# the existing cmd_compound handler unchanged (no handler logic
|
|
17175
|
+
# moves). The top-level 'compound' token stays a deprecated
|
|
17176
|
+
# forwarding alias for back-compat. cmd_memory does not shift before
|
|
17177
|
+
# the case, so $@ here is still 'compound <args>'; drop the leading
|
|
17178
|
+
# 'compound' token, then forward the rest verbatim.
|
|
17179
|
+
shift 2>/dev/null || true
|
|
17180
|
+
cmd_compound "$@"
|
|
17181
|
+
;;
|
|
17182
|
+
|
|
16651
17183
|
*)
|
|
16652
|
-
|
|
16653
|
-
|
|
17184
|
+
# Error-channel parity with the consolidated noun family (analyze,
|
|
17185
|
+
# modernize): unknown-subcommand diagnostics go to STDERR so a scripted
|
|
17186
|
+
# consumer capturing stdout is not handed error text. Exit 1 unchanged.
|
|
17187
|
+
echo -e "${RED}Unknown memory command: $subcommand${NC}" >&2
|
|
17188
|
+
echo "Run 'loki memory help' for usage." >&2
|
|
16654
17189
|
exit 1
|
|
16655
17190
|
;;
|
|
16656
17191
|
esac
|
|
@@ -16901,13 +17436,13 @@ COMPOUND_RUN_SCRIPT
|
|
|
16901
17436
|
;;
|
|
16902
17437
|
|
|
16903
17438
|
help|--help|-h)
|
|
16904
|
-
echo -e "${BOLD}loki compound${NC} - Knowledge compounding system"
|
|
17439
|
+
echo -e "${BOLD}loki memory compound${NC} - Knowledge compounding system"
|
|
16905
17440
|
echo ""
|
|
16906
17441
|
echo "Extracts structured solutions from cross-project learnings."
|
|
16907
17442
|
echo "Solutions are stored as markdown files with YAML frontmatter"
|
|
16908
17443
|
echo "at ~/.loki/solutions/{category}/ and fed back into future planning."
|
|
16909
17444
|
echo ""
|
|
16910
|
-
echo "Usage: loki compound <command>"
|
|
17445
|
+
echo "Usage: loki memory compound <command> (alias: loki compound)"
|
|
16911
17446
|
echo ""
|
|
16912
17447
|
echo "Commands:"
|
|
16913
17448
|
echo " list List all solutions by category"
|
|
@@ -19100,6 +19635,31 @@ cmd_syslog() {
|
|
|
19100
19635
|
# dashboard endpoint, and the tests all agree. Honest: with <2 runs it prints
|
|
19101
19636
|
# "not enough history yet", never a fabricated trend.
|
|
19102
19637
|
cmd_trust() {
|
|
19638
|
+
# CLI consolidation (Phase A): `trust detail` is the grouped form of the
|
|
19639
|
+
# trust-metrics breakdown. It forwards 1:1 to cmd_trust_metrics (no handler
|
|
19640
|
+
# logic moves). The bare `loki trust` remains the trajectory view. The
|
|
19641
|
+
# deprecation pointer for the old `trust-metrics` name is emitted at the
|
|
19642
|
+
# alias dispatch arm (main()), never here, so `trust detail` stays clean.
|
|
19643
|
+
#
|
|
19644
|
+
# Flag-anywhere: `detail` is accepted in any position (`trust detail`,
|
|
19645
|
+
# `trust --json detail`, `trust detail --json`) so the bash route matches the
|
|
19646
|
+
# bin/loki routing (which forwards on `detail` anywhere) and both routes
|
|
19647
|
+
# behave byte-identically for the same input. The `detail` token is stripped
|
|
19648
|
+
# and the remaining args (e.g. --json) forward to cmd_trust_metrics.
|
|
19649
|
+
local _trust_detail=0
|
|
19650
|
+
local _trust_rest=()
|
|
19651
|
+
local _ta
|
|
19652
|
+
for _ta in "$@"; do
|
|
19653
|
+
if [ "$_ta" = "detail" ]; then
|
|
19654
|
+
_trust_detail=1
|
|
19655
|
+
else
|
|
19656
|
+
_trust_rest+=("$_ta")
|
|
19657
|
+
fi
|
|
19658
|
+
done
|
|
19659
|
+
if [ "$_trust_detail" -eq 1 ]; then
|
|
19660
|
+
cmd_trust_metrics ${_trust_rest[@]+"${_trust_rest[@]}"}
|
|
19661
|
+
return $?
|
|
19662
|
+
fi
|
|
19103
19663
|
local pass_args=()
|
|
19104
19664
|
while [[ $# -gt 0 ]]; do
|
|
19105
19665
|
case "$1" in
|
|
@@ -19123,7 +19683,9 @@ cmd_trust() {
|
|
|
19123
19683
|
exit 0
|
|
19124
19684
|
;;
|
|
19125
19685
|
--json) pass_args+=("--json"); shift ;;
|
|
19126
|
-
|
|
19686
|
+
# Errors go to STDERR (not stdout) so a --json consumer's stdout
|
|
19687
|
+
# stays clean, matching the Bun route (trust.ts writes to stderr).
|
|
19688
|
+
*) echo -e "${RED}Unknown option: $1${NC}" >&2; echo "Run 'loki trust --help' for usage." >&2; exit 1 ;;
|
|
19127
19689
|
esac
|
|
19128
19690
|
done
|
|
19129
19691
|
|
|
@@ -19951,6 +20513,41 @@ METRICS_SCRIPT
|
|
|
19951
20513
|
fi
|
|
19952
20514
|
}
|
|
19953
20515
|
|
|
20516
|
+
# CLI consolidation (Phase B, design item 8): the 'analyze' noun groups the
|
|
20517
|
+
# repo-intelligence surface (explain/onboard/code/context) under one entry. It
|
|
20518
|
+
# is a THIN dispatcher: every arm forwards verbatim to the existing cmd_*
|
|
20519
|
+
# handler (no handler logic moves). The four top-level tokens remain deprecated
|
|
20520
|
+
# forwarding aliases for back-compat.
|
|
20521
|
+
cmd_analyze() {
|
|
20522
|
+
local subcommand="${1:-help}"
|
|
20523
|
+
case "$subcommand" in
|
|
20524
|
+
explain) shift; cmd_explain "$@"; return $? ;;
|
|
20525
|
+
onboard) shift; cmd_onboard "$@"; return $? ;;
|
|
20526
|
+
code) shift; cmd_code "$@"; return $? ;;
|
|
20527
|
+
context) shift; cmd_context "$@"; return $? ;;
|
|
20528
|
+
--help|-h|help)
|
|
20529
|
+
echo -e "${BOLD}loki analyze${NC} - Project knowledge and codebase intelligence"
|
|
20530
|
+
echo ""
|
|
20531
|
+
echo "Usage: loki analyze <subcommand> [args]"
|
|
20532
|
+
echo ""
|
|
20533
|
+
echo "Subcommands (grouped knowledge surface):"
|
|
20534
|
+
echo " explain [path] Explain a codebase architecture in prose (was: loki explain)"
|
|
20535
|
+
echo " onboard [path] Analyze a repo and generate CLAUDE.md (was: loki onboard)"
|
|
20536
|
+
echo " code [query] Codebase intelligence queries (was: loki code)"
|
|
20537
|
+
echo " context [cmd] Context-window management (was: loki context)"
|
|
20538
|
+
echo ""
|
|
20539
|
+
echo "Run 'loki analyze <subcommand> --help' for each. The old top-level"
|
|
20540
|
+
echo "names still work; see 'loki help aliases'."
|
|
20541
|
+
return 0
|
|
20542
|
+
;;
|
|
20543
|
+
*)
|
|
20544
|
+
echo -e "${RED}Unknown analyze command: $subcommand${NC}" >&2
|
|
20545
|
+
echo "Run 'loki analyze --help' for usage." >&2
|
|
20546
|
+
return 1
|
|
20547
|
+
;;
|
|
20548
|
+
esac
|
|
20549
|
+
}
|
|
20550
|
+
|
|
19954
20551
|
# Context window management (inspired by Kiro CLI /context command)
|
|
19955
20552
|
# Shows token usage breakdown and context window utilization
|
|
19956
20553
|
cmd_context() {
|
|
@@ -25917,7 +26514,73 @@ _test_gen_bats() {
|
|
|
25917
26514
|
}
|
|
25918
26515
|
|
|
25919
26516
|
# Session report generator (v6.27.0)
|
|
26517
|
+
# CLI consolidation (Phase A): `report` noun dispatcher.
|
|
26518
|
+
# Groups the read-only reporting cluster under one noun:
|
|
26519
|
+
# report session -> cmd_stats (session statistics)
|
|
26520
|
+
# report metrics -> cmd_metrics (productivity report)
|
|
26521
|
+
# report cost -> cmd_cost (cost + budget view)
|
|
26522
|
+
# report export -> cmd_export (session data export)
|
|
26523
|
+
# report share -> cmd_share (share report as Gist)
|
|
26524
|
+
# report dogfood -> cmd_dogfood (self-development stats)
|
|
26525
|
+
# Any other first token (including a flag like --format, or no token) falls
|
|
26526
|
+
# through to the legacy session-report generator (cmd_report_legacy) so that the
|
|
26527
|
+
# pre-existing `loki report [--format ...]` surface is byte-for-byte unchanged.
|
|
26528
|
+
# No handler logic moves in Phase A: each subcommand calls the existing bare
|
|
26529
|
+
# cmd_* function so the canonical `report <sub>` output is identical to the
|
|
26530
|
+
# legacy top-level command. The deprecation pointer for the old top-level names
|
|
26531
|
+
# is emitted at the alias dispatch arm (main()), never here, so `report <sub>`
|
|
26532
|
+
# stays clean.
|
|
25920
26533
|
cmd_report() {
|
|
26534
|
+
# CLI consolidation (Phase B): `report kpis` is the canonical KPI snapshot
|
|
26535
|
+
# entry. kpis is Bun-only; on a machine WITH Bun, bin/loki two-token-routes
|
|
26536
|
+
# `report kpis` (flag-anywhere) to the Bun runtime before this function is
|
|
26537
|
+
# reached. This is the bash-route (LOKI_LEGACY_BASH / no-Bun) path: print
|
|
26538
|
+
# the honest requirement message, NO deprecation pointer (this IS the
|
|
26539
|
+
# canonical form), exit 1. `kpis` is the report subcommand ONLY when it is the
|
|
26540
|
+
# FIRST non-flag token after `report` (so `report kpis --json` and `report
|
|
26541
|
+
# --json kpis` both route here, mirroring the bin/loki + Bun cli.ts routing),
|
|
26542
|
+
# NOT when it appears anywhere. A `kpis` token that is a positional VALUE of a
|
|
26543
|
+
# different report subcommand -- e.g. `report export json kpis`, where `kpis`
|
|
26544
|
+
# is the export OUTPUT FILENAME -- must NOT be hijacked; it falls through to
|
|
26545
|
+
# the export arm below and behaves exactly as on main (v7.31.0): exit 0, file
|
|
26546
|
+
# `kpis` created. Only the subcommand token is stripped before forwarding to
|
|
26547
|
+
# the helper; flags (e.g. --json) are passed through so it still emits the
|
|
26548
|
+
# structured machine payload.
|
|
26549
|
+
local _report_first_sub="" _kpis_args=() _dropped_kpis=""
|
|
26550
|
+
local _a
|
|
26551
|
+
for _a in "$@"; do
|
|
26552
|
+
case "$_a" in
|
|
26553
|
+
-*) _kpis_args+=("$_a") ;;
|
|
26554
|
+
*)
|
|
26555
|
+
if [ -z "$_report_first_sub" ]; then
|
|
26556
|
+
_report_first_sub="$_a"
|
|
26557
|
+
# Drop only the subcommand token itself, once.
|
|
26558
|
+
if [ "$_a" = "kpis" ]; then
|
|
26559
|
+
_dropped_kpis=1
|
|
26560
|
+
continue
|
|
26561
|
+
fi
|
|
26562
|
+
fi
|
|
26563
|
+
_kpis_args+=("$_a")
|
|
26564
|
+
;;
|
|
26565
|
+
esac
|
|
26566
|
+
done
|
|
26567
|
+
if [ -n "$_dropped_kpis" ]; then
|
|
26568
|
+
# ${arr[@]:-} guards the empty-array expansion under `set -u` on the
|
|
26569
|
+
# bare `report kpis` case (no extra flags) for older bash (3.2/4.2).
|
|
26570
|
+
_kpis_bash_unavailable "${_kpis_args[@]:-}"
|
|
26571
|
+
fi
|
|
26572
|
+
case "${1:-}" in
|
|
26573
|
+
session) shift; cmd_stats "$@"; return $? ;;
|
|
26574
|
+
metrics) shift; cmd_metrics "$@"; return $? ;;
|
|
26575
|
+
cost) shift; cmd_cost "$@"; return $? ;;
|
|
26576
|
+
export) shift; cmd_export "$@"; return $? ;;
|
|
26577
|
+
share) shift; cmd_share "$@"; return $? ;;
|
|
26578
|
+
dogfood) shift; cmd_dogfood "$@"; return $? ;;
|
|
26579
|
+
esac
|
|
26580
|
+
cmd_report_legacy "$@"
|
|
26581
|
+
}
|
|
26582
|
+
|
|
26583
|
+
cmd_report_legacy() {
|
|
25921
26584
|
local session_id=""
|
|
25922
26585
|
local format="text"
|
|
25923
26586
|
local output_file=""
|
|
@@ -25930,10 +26593,23 @@ cmd_report() {
|
|
|
25930
26593
|
--help|-h)
|
|
25931
26594
|
echo -e "${BOLD}loki report${NC} - Session report generator (v6.27.0)"
|
|
25932
26595
|
echo ""
|
|
25933
|
-
echo "Usage: loki report [options]"
|
|
26596
|
+
echo "Usage: loki report [subcommand] [options]"
|
|
25934
26597
|
echo ""
|
|
25935
26598
|
echo "Generates a readable session report from .loki/ data."
|
|
25936
26599
|
echo ""
|
|
26600
|
+
echo "Subcommands (grouped reporting surface, v7.31):"
|
|
26601
|
+
echo " session Session statistics (was: loki stats)"
|
|
26602
|
+
echo " metrics Efficiency/reward metrics (was: loki metrics)"
|
|
26603
|
+
echo " cost Token cost breakdown (was: loki cost)"
|
|
26604
|
+
echo " kpis Accuracy + efficiency KPI snapshot (was: loki kpis; Bun runtime only)"
|
|
26605
|
+
echo " export Export session data (was: loki export) [json|markdown|csv|timeline]"
|
|
26606
|
+
echo " share Share session assets (was: loki share)"
|
|
26607
|
+
echo " dogfood Self-development stats (was: loki dogfood)"
|
|
26608
|
+
echo " (Run 'loki report <subcommand> --help' for each. The old"
|
|
26609
|
+
echo " top-level names still work; see 'loki help aliases'.)"
|
|
26610
|
+
echo ""
|
|
26611
|
+
echo "With no subcommand, generates the legacy session report below."
|
|
26612
|
+
echo ""
|
|
25937
26613
|
echo "Options:"
|
|
25938
26614
|
echo " --session <id> Report for specific session (default: latest)"
|
|
25939
26615
|
echo " --format <fmt> Output format: text, markdown, html (default: text)"
|
|
@@ -25962,7 +26638,7 @@ cmd_report() {
|
|
|
25962
26638
|
--include-gates) include_gates=true; shift ;;
|
|
25963
26639
|
--include-agents) include_agents=true; shift ;;
|
|
25964
26640
|
--include-timeline) include_timeline=true; shift ;;
|
|
25965
|
-
*) echo -e "${RED}Unknown option: $1${NC}"; echo "Run 'loki report --help' for usage."; exit 1 ;;
|
|
26641
|
+
*) echo -e "${RED}Unknown option: $1${NC}" >&2; echo "Run 'loki report --help' for usage." >&2; exit 1 ;; # stderr for noun-family error-channel parity (analyze/modernize)
|
|
25966
26642
|
esac
|
|
25967
26643
|
done
|
|
25968
26644
|
|