loki-mode 11.4.2 → 11.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/SKILL.md +2 -2
  2. package/VERSION +1 -1
  3. package/autonomy/lib/chat.sh +6 -3
  4. package/autonomy/lib/engine10-push.sh +19 -1
  5. package/autonomy/lib/help-core.sh +1 -1
  6. package/autonomy/lib/proof-generator.py +23 -5
  7. package/autonomy/lib/proof-verify.py +607 -5
  8. package/autonomy/lib/run-summary.sh +17 -1
  9. package/autonomy/lib/setup-requirements.json +35 -0
  10. package/autonomy/loki +232 -36
  11. package/autonomy/provider-offer.sh +95 -47
  12. package/autonomy/route.sh +38 -6
  13. package/autonomy/run.sh +42 -14
  14. package/autonomy/setup-wizard.sh +19 -10
  15. package/autonomy/verify.sh +40 -1
  16. package/completions/_loki +3 -1
  17. package/completions/loki.bash +9 -3
  18. package/dashboard/__init__.py +1 -1
  19. package/docs/CLI-REFERENCE.md +3 -1
  20. package/docs/EVALUATING.md +27 -0
  21. package/docs/HARNESS-INTELLIGENCE-PLAN.md +20 -0
  22. package/docs/INSTALLATION.md +1 -1
  23. package/docs/KNOWN-ISSUES.md +10 -1
  24. package/docs/PROOF-PASSPORT.md +114 -0
  25. package/docs/alternative-installations.md +103 -0
  26. package/docs/cost-controls.md +139 -0
  27. package/docs/environment-variables.md +36 -0
  28. package/docs/exit-codes.md +102 -2
  29. package/docs/spec-first-intent.md +29 -0
  30. package/docs/v10/CLI-MODERN.md +2 -2
  31. package/docs/v10/ENGINE.md +32 -0
  32. package/loki-ts/data/model-pricing.json +1 -0
  33. package/loki-ts/dist/loki.js +1095 -1043
  34. package/mcp/__init__.py +1 -1
  35. package/package.json +2 -2
  36. package/packages/control-plane/dist/server.js +1721 -707
  37. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  38. package/plugins/loki-mode/README.md +41 -1
  39. package/plugins/loki-mode/commands/handoff.md +12 -0
  40. package/plugins/loki-mode/commands/loki-verify.md +9 -2
  41. package/plugins/loki-mode/commands/start.md +20 -0
  42. package/plugins/loki-mode/hooks/hooks.json +11 -0
  43. package/plugins/loki-mode/scripts/loki-stop-verify.sh +84 -0
  44. package/web-app/dist/assets/{AdminPage-CYJ2BWRC.js → AdminPage-Cbu3pXL8.js} +1 -1
  45. package/web-app/dist/assets/{Avatar-DdK4wynI.js → Avatar-DGqGGcGx.js} +1 -1
  46. package/web-app/dist/assets/{Badge-CrX3MI7J.js → Badge-Bhlipt6v.js} +1 -1
  47. package/web-app/dist/assets/{Button-709-wwGM.js → Button-CpN2sO61.js} +1 -1
  48. package/web-app/dist/assets/{CockpitPage-BapdTInO.js → CockpitPage-BqeDJwdL.js} +1 -1
  49. package/web-app/dist/assets/{ComparePage-DT99pGvA.js → ComparePage-DUieA2ba.js} +1 -1
  50. package/web-app/dist/assets/{ErrorBoundary-B0uJSys8.js → ErrorBoundary-DTWsYrOm.js} +1 -1
  51. package/web-app/dist/assets/{EvidenceReceiptPanel-BtMFgElP.js → EvidenceReceiptPanel-BU_B9X19.js} +1 -1
  52. package/web-app/dist/assets/{GitHubIssuesPanel-DjJuU941.js → GitHubIssuesPanel-DEZoLOsP.js} +1 -1
  53. package/web-app/dist/assets/{GitHubPRsPanel-HXfEX1BC.js → GitHubPRsPanel-BEsnrq7Y.js} +1 -1
  54. package/web-app/dist/assets/{HomePage-Cqyves-S.js → HomePage-BoUVuq7h.js} +2 -2
  55. package/web-app/dist/assets/{LoginPage-jUbtC7_Y.js → LoginPage-CmOED7vb.js} +1 -1
  56. package/web-app/dist/assets/{MagicPage-DHRsqjgR.js → MagicPage-DATZitrZ.js} +1 -1
  57. package/web-app/dist/assets/{MetricsPage-B-pED05R.js → MetricsPage-DGL1v0I-.js} +1 -1
  58. package/web-app/dist/assets/{NotFoundPage-CsbUs21R.js → NotFoundPage-C2WgOjZd.js} +1 -1
  59. package/web-app/dist/assets/{ProjectPage-BtlLEQwB.js → ProjectPage-BMi1tepj.js} +3 -3
  60. package/web-app/dist/assets/{ProjectsPage-DaRbRjSp.js → ProjectsPage-vc3y5YPh.js} +1 -1
  61. package/web-app/dist/assets/{SettingsPage-DALrtP3F.js → SettingsPage-kbq0Ftvi.js} +1 -1
  62. package/web-app/dist/assets/{ShowcasePage-CoPHSNP0.js → ShowcasePage-Bn6Q8Kbg.js} +1 -1
  63. package/web-app/dist/assets/{SystemSettingsPage-kOuSN50O.js → SystemSettingsPage-Basmhs_b.js} +1 -1
  64. package/web-app/dist/assets/{TeamsPage-DGWK0LMp.js → TeamsPage-BTqOc4TR.js} +1 -1
  65. package/web-app/dist/assets/{TemplatesPage-C-_10CwA.js → TemplatesPage-2P9002VL.js} +1 -1
  66. package/web-app/dist/assets/{TerminalOutput-Cz_vPbXL.js → TerminalOutput-CAoSPBrg.js} +1 -1
  67. package/web-app/dist/assets/{activity-icIsXJVa.js → activity-BcWVWCqU.js} +1 -1
  68. package/web-app/dist/assets/{bell-DbPR2_4r.js → bell-CYRqqgZ6.js} +1 -1
  69. package/web-app/dist/assets/{bot-DvjP3Yh5.js → bot-CU7gdn3g.js} +1 -1
  70. package/web-app/dist/assets/{check-D1OBxHeU.js → check-DLlCpz9V.js} +1 -1
  71. package/web-app/dist/assets/{chevron-left-D09jDZ29.js → chevron-left-CE9Cb30C.js} +1 -1
  72. package/web-app/dist/assets/{circle-alert-DyZMLOcC.js → circle-alert-uou4bjua.js} +1 -1
  73. package/web-app/dist/assets/{clock-CdpdJTId.js → clock-C_dSJcDX.js} +1 -1
  74. package/web-app/dist/assets/{cloud-CoLTlmoj.js → cloud-DZ22ITF6.js} +1 -1
  75. package/web-app/dist/assets/{code-xml-DrkE2ZZY.js → code-xml-BrJS0hhS.js} +1 -1
  76. package/web-app/dist/assets/{database-CMQQuM2R.js → database-D2HOmxtl.js} +1 -1
  77. package/web-app/dist/assets/{dollar-sign-CuioDaOG.js → dollar-sign-CQ1vK_6q.js} +1 -1
  78. package/web-app/dist/assets/{file-code-corner-Cx9j9K1S.js → file-code-corner-w2vpGm5d.js} +1 -1
  79. package/web-app/dist/assets/{file-plus-CYD2zaaI.js → file-plus-DlF6wjkD.js} +1 -1
  80. package/web-app/dist/assets/{globe-6w-hnQkL.js → globe-Dy3lBJg1.js} +1 -1
  81. package/web-app/dist/assets/{hammer-BpqtautL.js → hammer-C3xIiRXN.js} +1 -1
  82. package/web-app/dist/assets/{index-Dzs-9EgJ.js → index-4t-_IW2y.js} +3 -3
  83. package/web-app/dist/assets/{layers-D5hd7jb7.js → layers-Y8Fs8qXI.js} +1 -1
  84. package/web-app/dist/assets/{loader-circle-C6Y9x_7H.js → loader-circle-CxgN1v4m.js} +1 -1
  85. package/web-app/dist/assets/{lock-D51L7rYh.js → lock-DFFNbXDs.js} +1 -1
  86. package/web-app/dist/assets/{package-BJF8hkP8.js → package-5e6qCTUg.js} +1 -1
  87. package/web-app/dist/assets/{plus-D5kUQ79n.js → plus-C0Go_ytj.js} +1 -1
  88. package/web-app/dist/assets/{refresh-cw-BKdlCa7F.js → refresh-cw-D4i-NW3o.js} +1 -1
  89. package/web-app/dist/assets/{rotate-ccw-_bd_YI1e.js → rotate-ccw-YlKkiUW6.js} +1 -1
  90. package/web-app/dist/assets/{scroll-text-DbkegIxF.js → scroll-text-4qXOHTap.js} +1 -1
  91. package/web-app/dist/assets/{server-DAwqFIsq.js → server-BB6zXNp9.js} +1 -1
  92. package/web-app/dist/assets/{shield-alert-BuR1tPHu.js → shield-alert-D4tzjB1H.js} +1 -1
  93. package/web-app/dist/assets/{thumbs-up-BF6p3mRd.js → thumbs-up-DZiRwdFj.js} +1 -1
  94. package/web-app/dist/assets/{trash-2-BVpnkqqz.js → trash-2-Dv_LkRIK.js} +1 -1
  95. package/web-app/dist/assets/{trending-down-DpsDvRwW.js → trending-down-BJYp9PQe.js} +1 -1
  96. package/web-app/dist/assets/{trending-up-mFogTjt3.js → trending-up-BTcXLklK.js} +1 -1
  97. package/web-app/dist/assets/{upload-4T8zpBsi.js → upload-fcHqwOxr.js} +1 -1
  98. package/web-app/dist/assets/{usePolling-DPXQv8WO.js → usePolling-BeJYw_4L.js} +1 -1
  99. package/web-app/dist/assets/{user-BYGIFaTm.js → user-BcDwUCsO.js} +1 -1
  100. package/web-app/dist/index.html +1 -1
@@ -0,0 +1,35 @@
1
+ {
2
+ "version": 1,
3
+ "comment": "Single source of truth for Loki setup requirements per execution path. Read by loki-ts/src/commands/doctor.ts (doctor, runtime errors), autonomy/provider-offer.sh (bash fallback), autonomy/setup-wizard.sh (loki setup) and tests (the generated block in docs/alternative-installations.md is asserted against it). Edit here, then run UPDATE_CONTRACT=1 bun test tests/commands/doctor.test.ts from loki-ts.",
4
+ "paths": [
5
+ { "id": "common", "title": "All paths", "summary": "Needed by every Loki command" },
6
+ { "id": "claude-cli", "title": "Claude Code CLI path (default, loki start)", "summary": "Loki drives the claude binary you installed and logged in" },
7
+ { "id": "api-key-sdk", "title": "Anthropic API-key path (Agent SDK, no claude CLI)", "summary": "Opt-in with LOKI_SDK_MODE=full; the bundled Agent SDK runs the loop over HTTPS" },
8
+ { "id": "other-providers", "title": "Other provider CLIs", "summary": "codex (degraded), cline, aider, opencode: any one replaces the claude CLI" }
9
+ ],
10
+ "requirements": [
11
+ { "id": "node", "path": "common", "level": "required", "kind": "command", "target": "node", "min": "20.0", "label": "Node.js 20 or newer", "fix": "install Node 20+ from https://nodejs.org" },
12
+ { "id": "python3", "path": "common", "level": "required", "kind": "command", "target": "python3", "min": "3.8", "label": "Python 3.8 or newer", "fix": "install python3 with your package manager (brew install python3)" },
13
+ { "id": "jq", "path": "common", "level": "required", "kind": "command", "target": "jq", "label": "jq", "fix": "install jq with your package manager (brew install jq)" },
14
+ { "id": "git", "path": "common", "level": "required", "kind": "command", "target": "git", "label": "git", "fix": "install git with your package manager (brew install git)" },
15
+ { "id": "curl", "path": "common", "level": "required", "kind": "command", "target": "curl", "label": "curl", "fix": "install curl with your package manager (brew install curl)" },
16
+ { "id": "bash", "path": "common", "level": "optional", "kind": "command", "target": "bash", "min": "4.0", "label": "bash 4.0 or newer", "fix": "install a newer bash (brew install bash); macOS ships 3.2" },
17
+ { "id": "bun", "path": "common", "level": "optional", "kind": "command", "target": "bun", "min": "1.3", "label": "Bun 1.3 or newer (bundled by npm install)", "fix": "reinstall without --no-optional (npm bundles @oven/bun-*) or install from https://bun.sh; without it loki falls back to the slower bash CLI" },
18
+
19
+ { "id": "claude", "path": "claude-cli", "level": "required", "kind": "command", "target": "claude", "label": "claude CLI on PATH (then log in: claude auth login)", "fix": "npm install -g @anthropic-ai/claude-code" },
20
+
21
+ { "id": "sdk-mode", "path": "api-key-sdk", "level": "required", "kind": "env_value", "target": ["LOKI_SDK_LOOP"], "values": ["1", "true"], "label": "SDK loop enabled (LOKI_SDK_MODE=full)", "fix": "export LOKI_SDK_MODE=full" },
22
+ { "id": "sdk-credentials", "path": "api-key-sdk", "level": "required", "kind": "env_any", "target": ["ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN", "ANTHROPIC_BASE_URL"], "label": "Anthropic credentials (API key, auth token or gateway URL)", "fix": "export ANTHROPIC_API_KEY=<your key>" },
23
+ { "id": "sdk-binary", "path": "api-key-sdk", "level": "required", "kind": "sdk_binary", "target": "@anthropic-ai/claude-agent-sdk", "label": "Claude Agent SDK platform binary (optional npm dependency)", "fix": "reinstall without --no-optional: npm install -g loki-mode (the SDK is an optionalDependency)" },
24
+ { "id": "sdk-bun", "path": "api-key-sdk", "level": "required", "kind": "command", "target": "bun", "min": "1.3", "label": "Bun runtime (the SDK loop runs only on the Bun route)", "fix": "reinstall without --no-optional (npm bundles @oven/bun-*) or install from https://bun.sh" },
25
+
26
+ { "id": "codex", "path": "other-providers", "level": "optional", "kind": "command", "target": "codex", "label": "codex CLI", "fix": "npm install -g @openai/codex" },
27
+ { "id": "cline", "path": "other-providers", "level": "optional", "kind": "command", "target": "cline", "label": "cline CLI", "fix": "npm install -g cline" },
28
+ { "id": "aider", "path": "other-providers", "level": "optional", "kind": "command", "target": "aider", "label": "aider CLI", "fix": "pip install aider-chat" },
29
+ { "id": "opencode", "path": "other-providers", "level": "optional", "kind": "command", "target": "opencode", "label": "opencode CLI", "fix": "npm install -g opencode-ai" }
30
+ ],
31
+ "missing_optional": [
32
+ { "dependency": "@anthropic-ai/claude-agent-sdk (or its platform binary)", "effect": "The api-key-sdk path is unavailable and doctor reports it not ready. The claude-cli path and all other providers are unaffected." },
33
+ { "dependency": "@oven/bun-* (Bun)", "effect": "bin/loki falls back to the bash CLI (slower). The api-key-sdk path is unavailable because the SDK loop runs only on Bun." }
34
+ ]
35
+ }
package/autonomy/loki CHANGED
@@ -71,6 +71,15 @@ if [ -f "$_LOKI_SCRIPT_DIR/lib/lock.sh" ]; then
71
71
  source "$_LOKI_SCRIPT_DIR/lib/lock.sh"
72
72
  fi
73
73
 
74
+ # Default per-run spend cap (single source: default_max_cost_usd in loki-ts/data/model-pricing.json,
75
+ # also read by loki-ts/src/e10ext/budget_cap.ts and autonomy/run.sh). Prints a plain number; 100 only if the file is unreadable.
76
+ loki_budget_default_usd() {
77
+ local v=""
78
+ [ -f "$_LOKI_SCRIPT_DIR/../loki-ts/data/model-pricing.json" ] && \
79
+ v="$(sed -n 's/.*"default_max_cost_usd"[[:space:]]*:[[:space:]]*\([0-9][0-9.]*\).*/\1/p' "$_LOKI_SCRIPT_DIR/../loki-ts/data/model-pricing.json" | head -1)"
80
+ printf '%s\n' "${v:-100}"
81
+ }
82
+
74
83
  # Guarded browser opener (loki_open_url). Every browser open goes through it
75
84
  # so tests, CI and non-TTY runs never pop a tab. If missing, nothing opens.
76
85
  if [ -f "$_LOKI_SCRIPT_DIR/lib/browser-open.sh" ]; then
@@ -1838,7 +1847,7 @@ show_help_all() {
1838
1847
  echo " --spec FILE Run against an edited spec (written by 'loki plan <task> --spec'; see also --spec-first)"
1839
1848
  echo " --fresh-prd Regenerate the PRD from the codebase (no-PRD runs; ignores the reusable generated PRD)"
1840
1849
  echo " --compliance PRESET Enable compliance mode (default|healthcare|fintech|government)"
1841
- echo " --budget USD Set cost budget limit (display in dashboard/status)"
1850
+ echo " --budget USD Per-run cost cap in USD (default $(loki_budget_default_usd); auto-pause when exceeded; see docs/cost-controls.md)"
1842
1851
  echo " --bmad-project PATH Use BMAD Method project artifacts as input"
1843
1852
  echo " --openspec PATH Use OpenSpec change directory as input"
1844
1853
  echo " --config PATH Load settings from a config file (.yaml/.json/.env). Alias: --env-file, --vars"
@@ -2608,7 +2617,7 @@ cmd_start() {
2608
2617
  echo " (ignores the reusable generated PRD; aliases: --regen-prd,"
2609
2618
  echo " --regenerate-prd, --regen, or LOKI_PRD_REGEN=1)"
2610
2619
  echo " --compliance PRESET Enable compliance mode (default|healthcare|fintech|government)"
2611
- echo " --budget USD Cost budget limit (auto-pause when exceeded)"
2620
+ echo " --budget USD Per-run cost cap (default $(loki_budget_default_usd); auto-pause when exceeded)"
2612
2621
  echo " --bmad-project PATH Use BMAD Method project artifacts as input"
2613
2622
  echo " --openspec PATH Use OpenSpec change directory as input"
2614
2623
  echo " --mirofish [URL] Enable MiroFish market validation (default: localhost:5001)"
@@ -2651,7 +2660,7 @@ cmd_start() {
2651
2660
  echo " LOKI_AUTO_CONFIRM Set to 'true'/'false' to control prompts (takes precedence over CI)"
2652
2661
  echo " CI Fallback: auto-confirms when 'true' and LOKI_AUTO_CONFIRM is unset"
2653
2662
  echo " LOKI_MAX_ITERATIONS Max iteration count"
2654
- echo " LOKI_BUDGET_LIMIT Cost budget limit in USD"
2663
+ echo " LOKI_BUDGET_LIMIT Cost cap in USD (default $(loki_budget_default_usd); empty means unlimited)"
2655
2664
  echo ""
2656
2665
  echo "Examples:"
2657
2666
  echo " loki start # Interactive, analyze current dir"
@@ -3049,7 +3058,7 @@ cmd_start() {
3049
3058
  echo -e "${RED}--budget must be greater than 0 (a 0 budget pauses before any work)${NC}" >&2
3050
3059
  exit 1
3051
3060
  fi
3052
- export LOKI_BUDGET_LIMIT="$2"
3061
+ export LOKI_BUDGET_LIMIT="$2" LOKI_BUDGET_SOURCE=flag
3053
3062
  shift 2
3054
3063
  else
3055
3064
  echo -e "${RED}--budget requires a USD amount (e.g., --budget 5.00)${NC}"
@@ -3067,7 +3076,7 @@ cmd_start() {
3067
3076
  echo -e "${RED}--budget must be greater than 0 (a 0 budget pauses before any work)${NC}" >&2
3068
3077
  exit 1
3069
3078
  fi
3070
- export LOKI_BUDGET_LIMIT="$budget_val"
3079
+ export LOKI_BUDGET_LIMIT="$budget_val" LOKI_BUDGET_SOURCE=flag
3071
3080
  shift
3072
3081
  ;;
3073
3082
  # v6.84.0: explicit mode flags (override auto-detection)
@@ -3956,15 +3965,8 @@ cmd_start() {
3956
3965
  if ! command -v "$effective_provider" &>/dev/null; then
3957
3966
  echo -e "${RED}Error: Provider CLI '$effective_provider' is not installed.${NC}"
3958
3967
  echo ""
3959
- echo "Install it first:"
3960
- case "$effective_provider" in
3961
- claude) echo " npm install -g @anthropic-ai/claude-code" ;;
3962
- codex) echo " npm install -g @openai/codex" ;;
3963
- cline) echo " npm install -g cline" ;;
3964
- aider) echo " pip install aider-chat" ;;
3965
- opencode) echo " npm install -g opencode-ai" ;;
3966
- *) echo " Check the provider documentation for installation." ;;
3967
- esac
3968
+ echo "Install it first (same fix as loki doctor):"
3969
+ _loki_req_fix_line "$effective_provider"
3968
3970
  echo ""
3969
3971
  echo "Check your environment: loki doctor"
3970
3972
  exit 1
@@ -5170,6 +5172,17 @@ cmd_resume() {
5170
5172
  exit 0
5171
5173
  fi
5172
5174
 
5175
+ # RECOVERY: an interrupted engine10 run is resumed at its phase by loki-ts/src/util/run_outputs.ts
5176
+ # (exit 0 resumed, 4 phase reported, 5 publish failed, anything else: nothing for it, fall through).
5177
+ if ls "$LOKI_DIR"/runs/e10-*/events.jsonl >/dev/null 2>&1; then
5178
+ local _e10_rc=0
5179
+ ( _loki_bun_delegate engine10 resume "$@" ) || _e10_rc=$?
5180
+ case "$_e10_rc" in
5181
+ 0 | 4) return 0 ;;
5182
+ 5) return 1 ;;
5183
+ esac
5184
+ fi
5185
+
5173
5186
  # Check if there is anything to resume before touching signal files
5174
5187
  local has_pause_signal=false
5175
5188
  local has_stop_signal=false
@@ -7119,8 +7132,14 @@ cmd_provider() {
7119
7132
  cmd_provider_list
7120
7133
  ;;
7121
7134
  info)
7135
+ case " $* " in
7136
+ *" --json "*) _loki_bun_delegate provider info "$@" || { echo "loki provider info --json needs bun: ${_LOKI_DELEGATE_WHY}" >&2; return 1; } ;;
7137
+ esac
7122
7138
  cmd_provider_info "$@"
7123
7139
  ;;
7140
+ matrix)
7141
+ _loki_bun_delegate provider matrix "$@" || { echo "loki provider matrix needs bun: ${_LOKI_DELEGATE_WHY}" >&2; return 1; }
7142
+ ;;
7124
7143
  models)
7125
7144
  cmd_provider_models
7126
7145
  ;;
@@ -7133,7 +7152,8 @@ cmd_provider() {
7133
7152
  echo " show Show current provider (default)"
7134
7153
  echo " set Set provider for this project"
7135
7154
  echo " list List available providers"
7136
- echo " info Show provider details"
7155
+ echo " info Show provider details (--json: capability contract)"
7156
+ echo " matrix Show the capability matrix per provider and engine"
7137
7157
  echo " models Show resolved model configuration for all providers"
7138
7158
  echo ""
7139
7159
  echo "Precedence (highest wins):"
@@ -10865,7 +10885,7 @@ cmd_run() {
10865
10885
  echo " --no-dashboard Disable web dashboard"
10866
10886
  echo " --sandbox Run in Docker sandbox (default: off; requires Docker)"
10867
10887
  echo " --no-plan Skip auto-shown PRD analysis at startup"
10868
- echo " --budget USD Set cost budget limit"
10888
+ echo " --budget USD Per-run cost cap (default $(loki_budget_default_usd))"
10869
10889
  echo ""
10870
10890
  echo "Progressive Isolation:"
10871
10891
  echo " --worktree, -w Git worktree isolation (separate branch)"
@@ -13475,6 +13495,100 @@ cmd_config_path() {
13475
13495
  echo "Create a config file with: loki config init"
13476
13496
  }
13477
13497
 
13498
+ # Merge the Loki plugin into a Claude Code settings.json (loki setup-skill --claude-code).
13499
+ # Existing keys are never overwritten, invalid JSON is refused (rc 3), writes are atomic.
13500
+ _setup_skill_claude_code() {
13501
+ local print_only=0 project=0 stop=0 settings=""
13502
+ while [ $# -gt 0 ]; do
13503
+ case "$1" in
13504
+ --print) print_only=1 ;;
13505
+ --project) project=1 ;;
13506
+ --stop-hook) stop=1 ;;
13507
+ --settings) settings="${2:-}"; shift ;;
13508
+ --help|-h)
13509
+ echo "Usage: loki setup-skill --claude-code [--print] [--project] [--stop-hook] [--settings FILE]"
13510
+ echo ""
13511
+ echo "Registers the Loki plugin marketplace and enables the plugin in Claude Code"
13512
+ echo "settings (default ~/.claude/settings.json; --project uses ./.claude/settings.json)."
13513
+ echo "Merges only: existing keys are kept, invalid JSON is refused."
13514
+ echo ""
13515
+ echo " --print Print the settings snippet and change nothing"
13516
+ echo " --stop-hook Also opt in to the Stop-hook fast verifier (LOKI_STOP_VERIFY=1)"
13517
+ return 0
13518
+ ;;
13519
+ *) echo "Unknown option: $1" >&2; return 2 ;;
13520
+ esac
13521
+ shift
13522
+ done
13523
+ command -v python3 >/dev/null 2>&1 || { echo "python3 is required" >&2; return 1; }
13524
+ if [ -z "$settings" ] && [ "$print_only" -eq 0 ]; then
13525
+ if [ "$project" -eq 1 ]; then
13526
+ settings="${TARGET_DIR:-.}/.claude/settings.json"
13527
+ else
13528
+ settings="$HOME/.claude/settings.json"
13529
+ fi
13530
+ fi
13531
+ local out rc
13532
+ out="$(python3 - "$settings" "$stop" "$print_only" <<'PY'
13533
+ import json, os, sys, tempfile
13534
+ path, stop, print_only = sys.argv[1], sys.argv[2] == "1", sys.argv[3] == "1"
13535
+ want = {
13536
+ "extraKnownMarketplaces": {
13537
+ "loki-mode": {"source": {"source": "github", "repo": "asklokesh/loki-mode"}}
13538
+ },
13539
+ "enabledPlugins": {"loki-mode@loki-mode": True},
13540
+ }
13541
+ if stop:
13542
+ want["env"] = {"LOKI_STOP_VERIFY": "1"}
13543
+ if print_only:
13544
+ print(json.dumps(want, indent=2))
13545
+ sys.exit(0)
13546
+ cur = {}
13547
+ if os.path.exists(path):
13548
+ try:
13549
+ raw = open(path).read()
13550
+ cur = json.loads(raw) if raw.strip() else {}
13551
+ except Exception as e:
13552
+ print("REFUSED %s is not valid JSON (%s); fix or move it aside" % (path, e))
13553
+ sys.exit(3)
13554
+ if not isinstance(cur, dict):
13555
+ print("REFUSED %s top level is not an object" % path)
13556
+ sys.exit(3)
13557
+ added = []
13558
+ for top, kv in want.items():
13559
+ node = cur.get(top)
13560
+ if node is None:
13561
+ cur[top] = {}
13562
+ node = cur[top]
13563
+ elif not isinstance(node, dict):
13564
+ print("KEPT %s (existing value is not an object)" % top)
13565
+ continue
13566
+ for k, v in kv.items():
13567
+ if k in node:
13568
+ print("KEPT %s.%s" % (top, k))
13569
+ else:
13570
+ node[k] = v
13571
+ added.append("%s.%s" % (top, k))
13572
+ print("ADDED %s.%s" % (top, k))
13573
+ if added:
13574
+ d = os.path.dirname(os.path.abspath(path))
13575
+ os.makedirs(d, exist_ok=True)
13576
+ fd, tmp = tempfile.mkstemp(dir=d, prefix=".settings.", suffix=".tmp")
13577
+ with os.fdopen(fd, "w") as f:
13578
+ json.dump(cur, f, indent=2)
13579
+ f.write("\n")
13580
+ os.replace(tmp, path)
13581
+ else:
13582
+ print("UNCHANGED %s" % path)
13583
+ PY
13584
+ )"
13585
+ rc=$?
13586
+ printf '%s\n' "$out"
13587
+ [ "$rc" -eq 0 ] || return "$rc"
13588
+ [ "$print_only" -eq 1 ] || echo "Next: in Claude Code run /reload-plugins, then try /loki-mode:start, /loki-mode:loki-verify, /loki-mode:handoff."
13589
+ return 0
13590
+ }
13591
+
13478
13592
  # Set up skill symlinks for all providers
13479
13593
  cmd_setup_skill() {
13480
13594
  # v7.6.2 B-13 fix: --help must print help, not install skills.
@@ -13488,11 +13602,17 @@ cmd_setup_skill() {
13488
13602
  echo "skills directory: ~/.claude/skills/, ~/.codex/skills/, etc."
13489
13603
  echo ""
13490
13604
  echo "Options:"
13605
+ echo " --claude-code Merge the Loki plugin into Claude Code settings (see --claude-code --help)"
13491
13606
  echo " --help, -h Show this help and exit"
13492
13607
  echo ""
13493
13608
  echo "Side effects: writes symlinks/files under ~/.<provider>/skills/loki-mode/."
13494
13609
  return 0
13495
13610
  ;;
13611
+ --claude-code)
13612
+ shift
13613
+ _setup_skill_claude_code "$@"
13614
+ return $?
13615
+ ;;
13496
13616
  esac
13497
13617
  echo -e "${BOLD}Loki Mode Skill Setup${NC}"
13498
13618
  echo ""
@@ -14080,6 +14200,18 @@ _loki_bun_delegate() {
14080
14200
  exec "$bun_bin" "$cli" "$cmd" "$@"
14081
14201
  }
14082
14202
 
14203
+ # One-line fix for a requirement id (a provider name is an id), read from the shared
14204
+ # table autonomy/lib/setup-requirements.json so runtime errors match loki doctor.
14205
+ _loki_req_fix_line() {
14206
+ local _id="$1" _fix=""
14207
+ _fix="$(jq -r --arg id "$_id" '.requirements[] | select(.id == $id) | .fix' "${SKILL_DIR}/autonomy/lib/setup-requirements.json" 2>/dev/null | head -n 1)" || _fix=""
14208
+ if [ -n "$_fix" ]; then
14209
+ echo " $_fix"
14210
+ else
14211
+ echo " Check the provider documentation for installation."
14212
+ fi
14213
+ }
14214
+
14083
14215
  # loki doctor. The one implementation is loki-ts/src/commands/doctor.ts; this is a
14084
14216
  # delegator. Only --airgap stays in bash (_loki_airgap_audit). If the Bun route is
14085
14217
  # unavailable we print a minimal diagnostic (never a second copy of the checks).
@@ -14125,7 +14257,12 @@ cmd_doctor_json() {
14125
14257
  why="${why//\\/\\\\}"; why="${why//\"/\\\"}"
14126
14258
  fix="${fix//\\/\\\\}"; fix="${fix//\"/\\\"}"
14127
14259
  ver="${ver//\\/\\\\}"; ver="${ver//\"/\\\"}"
14128
- printf '{"loki_mode_version":"%s","checks":[],"provider_availability":null,"disk":null,"ai_provider":null,"skills":[],"sentrux":null,"memory":null,"model_catalog":null,"summary":{"passed":0,"failed":1,"warnings":0,"ok":false},"route":"unavailable","reason":"%s","fix":"%s"}\n' "$ver" "$why" "$fix"
14260
+ local reqs=null
14261
+ if declare -F loki_requirements_json >/dev/null 2>&1; then
14262
+ reqs="$(loki_requirements_json 2>/dev/null | jq -c . 2>/dev/null)" || reqs=null
14263
+ [ -n "$reqs" ] || reqs=null
14264
+ fi
14265
+ printf '{"loki_mode_version":"%s","checks":[],"provider_availability":null,"disk":null,"ai_provider":null,"skills":[],"sentrux":null,"memory":null,"model_catalog":null,"requirements":%s,"summary":{"passed":0,"failed":1,"warnings":0,"ok":false},"route":"unavailable","reason":"%s","fix":"%s"}\n' "$ver" "$reqs" "$why" "$fix"
14129
14266
  }
14130
14267
 
14131
14268
  # Architectural-drift gate (v7.5.14, opt-in).
@@ -15686,15 +15823,6 @@ cmd_quick() {
15686
15823
  exit 64
15687
15824
  fi
15688
15825
 
15689
- # v7.29.0: provider pre-flight gate. Offer install on a TTY; print honest
15690
- # manual instructions and exit 2 on non-TTY/CI, before any spend. --help and
15691
- # the no-args usage path already returned/exited above.
15692
- if declare -f provider_offer_gate >/dev/null 2>&1; then
15693
- if ! provider_offer_gate; then
15694
- exit 2
15695
- fi
15696
- fi
15697
-
15698
15826
  # #691: drop the unified config-file aliases from the task description. The
15699
15827
  # main() pre-pass already applied the file; without this, a trailing
15700
15828
  # `--config f.env` would be swept into task_desc (cmd_quick has no arg loop).
@@ -15711,6 +15839,12 @@ cmd_quick() {
15711
15839
  --verbose|-v) if [ "$_quick_task_seen" = "false" ]; then export LOKI_VERBOSE=1; continue; fi ;;
15712
15840
  --config|--vars|--env-file) _skip_next=true; continue ;;
15713
15841
  --config=*|--vars=*|--env-file=*) continue ;;
15842
+ # LM-006: a leading unknown --flag is a usage error, never task text (a typo must not start a paid run).
15843
+ --?*) if [ "$_quick_task_seen" = "false" ] &&[[ "$_qa" != *" "* ]]; then
15844
+ echo -e "${RED}Error: unknown option: $_qa${NC}" >&2
15845
+ echo "Usage: loki quick [--verbose] \"description of what to do\"" >&2
15846
+ exit 64
15847
+ fi ;;
15714
15848
  esac
15715
15849
  _quick_task_seen=true
15716
15850
  _quick_args+=("$_qa")
@@ -15722,6 +15856,16 @@ cmd_quick() {
15722
15856
  exit 64
15723
15857
  fi
15724
15858
 
15859
+ # v7.29.0: provider pre-flight gate. Offer install on a TTY; print honest
15860
+ # manual instructions and exit 2 on non-TTY/CI, before any spend. --help and
15861
+ # every usage error (no task, unknown option, exit 64) is decided above,
15862
+ # so a host with no provider still reports a typo as 64, not 2.
15863
+ if declare -f provider_offer_gate >/dev/null 2>&1; then
15864
+ if ! provider_offer_gate; then
15865
+ exit 2
15866
+ fi
15867
+ fi
15868
+
15725
15869
  local task_desc="$*"
15726
15870
  local version=$(get_version)
15727
15871
  local max_iter="${LOKI_MAX_ITERATIONS:-3}"
@@ -15786,14 +15930,8 @@ QPRDEOF
15786
15930
  if ! command -v "$_quick_provider" &>/dev/null; then
15787
15931
  echo -e "${RED}Error: Provider CLI '$_quick_provider' is not installed.${NC}"
15788
15932
  echo ""
15789
- echo "Install your AI provider CLI first:"
15790
- case "$_quick_provider" in
15791
- claude) echo " npm install -g @anthropic-ai/claude-code" ;;
15792
- codex) echo " npm install -g @openai/codex" ;;
15793
- cline) echo " npm install -g cline" ;;
15794
- aider) echo " pip install aider-chat" ;;
15795
- *) echo " Check the provider documentation for installation." ;;
15796
- esac
15933
+ echo "Install your AI provider CLI first (same fix as loki doctor):"
15934
+ _loki_req_fix_line "$_quick_provider"
15797
15935
  echo ""
15798
15936
  echo "Then verify: loki doctor"
15799
15937
  exit 1
@@ -20708,8 +20846,33 @@ print()
20708
20846
  # Dry-run PRD analysis and cost estimation (v6.18.0; refactored v6.81.1)
20709
20847
  # Thin wrapper around show_prd_plan() so the same analysis is reusable from
20710
20848
  # `loki start`, `loki run`, and other commands without duplicated code.
20849
+ # Read-only view of .loki/state/requirements-map.json (REQTRACE; contract: docs/spec-first-intent.md).
20850
+ _plan_show_requirements() {
20851
+ local as_json="${1:-false}" map="${LOKI_DIR:-.loki}/state/requirements-map.json"
20852
+ if [ ! -s "$map" ]; then
20853
+ if [ "$as_json" = true ]; then printf '{"error":"no requirements map","path":"%s"}\n' "$map"; return 1; fi
20854
+ echo "No requirements map at $map (written after a run that had a PRD checklist, a spec, or a multi-requirement PRD)."
20855
+ return 1
20856
+ fi
20857
+ if [ "$as_json" = true ]; then cat "$map"; return 0; fi
20858
+ LOKI_RM="$map" python3 -I -c "
20859
+ import json, os, sys
20860
+ try:
20861
+ m = json.load(open(os.environ['LOKI_RM']))
20862
+ reqs = m['requirements']
20863
+ except Exception:
20864
+ print('requirements map is unreadable: ' + os.environ['LOKI_RM']); sys.exit(1)
20865
+ cov = sum(1 for r in reqs if r.get('status') == 'covered')
20866
+ print('Requirements: %d/%d covered (revision %s)' % (cov, len(reqs), str(m.get('revision', ''))[:12]))
20867
+ for r in reqs:
20868
+ chk = (' <- ' + ', '.join(r.get('checks', []))) if r.get('checks') else ''
20869
+ print(' %s %-9s %-9s %s%s' % (r.get('id'), r.get('status'), r.get('method'), str(r.get('text', ''))[:80], chk))
20870
+ "
20871
+ }
20872
+
20711
20873
  cmd_plan() {
20712
20874
  local prd_file=""
20875
+ local show_requirements=false
20713
20876
  local show_json=false
20714
20877
  local show_verbose=false
20715
20878
 
@@ -20726,6 +20889,7 @@ cmd_plan() {
20726
20889
  echo "Options:"
20727
20890
  echo " --json Machine-readable JSON output"
20728
20891
  echo " --spec With a task: write the acceptance criteria to .loki/specs/<slug>.md for you to edit"
20892
+ echo " --requirements Print the last run's requirement-to-check map (.loki/state/requirements-map.json), read-only"
20729
20893
  echo " --verbose Show detailed per-iteration breakdown"
20730
20894
  echo " --help, -h Show this help"
20731
20895
  echo ""
@@ -20743,6 +20907,7 @@ cmd_plan() {
20743
20907
  ;;
20744
20908
  --json) show_json=true; shift ;;
20745
20909
  --verbose) show_verbose=true; shift ;;
20910
+ --requirements) show_requirements=true; shift ;;
20746
20911
  *)
20747
20912
  if [ -z "$prd_file" ]; then
20748
20913
  prd_file="$1"
@@ -20752,6 +20917,11 @@ cmd_plan() {
20752
20917
  esac
20753
20918
  done
20754
20919
 
20920
+ if [ "$show_requirements" = true ]; then
20921
+ _plan_show_requirements "$show_json"
20922
+ return $?
20923
+ fi
20924
+
20755
20925
  if [ -z "$prd_file" ]; then
20756
20926
  # Under --json, a machine consumer expects JSON on every path, including
20757
20927
  # errors -- never ANSI-colored prose. Emit a structured error to stdout.
@@ -36094,7 +36264,7 @@ PYEOF
36094
36264
  }
36095
36265
 
36096
36266
  # loki bench - head-to-head benchmark harness (R2).
36097
- # Subcommands: run <task> | vs <task> | list | verify <result.json>.
36267
+ # Subcommands: run <task> | vs <task> | list | verify <result.json> | attribution.
36098
36268
  # Thin pass-through to benchmarks/bench/run.sh (shared python core runner.py).
36099
36269
  # CREDIBILITY: Loki NEVER grades its own success. Success is decided by a
36100
36270
  # held-out acceptance command run by the grader OUTSIDE the agent (exit code).
@@ -36128,6 +36298,7 @@ cmd_bench() {
36128
36298
  echo " verify <file> Recompute task_hash + check tool versions for a result.json"
36129
36299
  echo " oracles Are the graders trustworthy? Hash-bound, no spend, no provider"
36130
36300
  echo " report <files> Build results.json + RESULTS.md from per-tool result-rows"
36301
+ echo " attribution Same-model harness attribution: raw Claude Code vs Loki (--dry-run, --execute)"
36131
36302
  echo ""
36132
36303
  echo "Options:"
36133
36304
  echo " --trials N Number of trials per tool (default 3)"
@@ -36324,7 +36495,9 @@ cmd_proof() {
36324
36495
  echo " reads UNKNOWN when it cannot be computed"
36325
36496
  echo " phases [--json] Measured phase history from real phase_change"
36326
36497
  echo " events; unrecorded boundaries read UNKNOWN"
36327
- echo " show <id> Pretty-print .loki/proofs/<id>/proof.json"
36498
+ echo " show <id> [--json] Compact reviewer summary (label, revision, checks,"
36499
+ echo " requirements, modified tests); --json prints the"
36500
+ echo " full .loki/proofs/<id>/proof.json"
36328
36501
  echo " verify <id> Re-check a receipt against the repo (tamper + drift)."
36329
36502
  echo " Exit 0 clean, 1 tamper/drift, 2 could not check,"
36330
36503
  echo " 64 usage (no id, more than one id, unknown option,"
@@ -36496,6 +36669,14 @@ PYEOF
36496
36669
  exit 0
36497
36670
  ;;
36498
36671
  show)
36672
+ local _ps_json=0 _ps_a _ps_args=()
36673
+ for _ps_a in "$@"; do
36674
+ case "$_ps_a" in
36675
+ --json) _ps_json=1 ;;
36676
+ *) _ps_args+=("$_ps_a") ;;
36677
+ esac
36678
+ done
36679
+ set -- "${_ps_args[@]+"${_ps_args[@]}"}"
36499
36680
  local id="${1:-}"
36500
36681
  if [ -z "$id" ]; then
36501
36682
  echo -e "${RED}Missing proof id.${NC} Use 'loki proof list'."
@@ -36507,6 +36688,21 @@ PYEOF
36507
36688
  echo "Use 'loki proof list' to see available proofs."
36508
36689
  exit 1
36509
36690
  fi
36691
+ if [ "$_ps_json" -eq 0 ]; then
36692
+ # Compact reviewer summary (docs/PROOF-PASSPORT.md).
36693
+ local _ps_py _ps_vr="${_LOKI_SCRIPT_DIR}/lib/proof-verify.py"
36694
+ _ps_py="$(loki_verify_tool_path python3)" || {
36695
+ echo -e "${RED}python3 is unavailable${NC}" >&2
36696
+ exit 2
36697
+ }
36698
+ local _ps_root
36699
+ _ps_root="$(loki_verify_root 2>/dev/null)" || {
36700
+ echo -e "${YELLOW}Summary unavailable (project tree not resolved; not checking the ambient cwd).${NC} Use 'loki proof show ${id} --json' for the full record." >&2
36701
+ exit 2
36702
+ }
36703
+ "$_ps_py" -E "$_ps_vr" summary "$pj" --repo "$_ps_root" --loki-dir "$loki_dir" || exit $?
36704
+ exit 0
36705
+ fi
36510
36706
  if command -v jq &>/dev/null; then
36511
36707
  jq . "$pj"
36512
36708
  else