loki-mode 11.3.9 → 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 (119) hide show
  1. package/README.md +34 -18
  2. package/SKILL.md +3 -3
  3. package/VERSION +1 -1
  4. package/agents/custom_agents.py +280 -0
  5. package/autonomy/council-v2.sh +22 -1
  6. package/autonomy/edit.sh +242 -0
  7. package/autonomy/lib/chat.sh +315 -0
  8. package/autonomy/lib/engine10-push.sh +19 -1
  9. package/autonomy/lib/help-core.sh +164 -0
  10. package/autonomy/lib/json-output.sh +38 -0
  11. package/autonomy/lib/proof-generator.py +23 -5
  12. package/autonomy/lib/proof-verify.py +607 -5
  13. package/autonomy/lib/run-summary.sh +112 -0
  14. package/autonomy/lib/setup-requirements.json +35 -0
  15. package/autonomy/loki +337 -46
  16. package/autonomy/provider-offer.sh +95 -47
  17. package/autonomy/route.sh +302 -0
  18. package/autonomy/run.sh +48 -14
  19. package/autonomy/setup-wizard.sh +170 -0
  20. package/autonomy/verify.sh +62 -3
  21. package/bin/loki +73 -18
  22. package/completions/_loki +58 -1
  23. package/completions/loki.bash +57 -5
  24. package/dashboard/__init__.py +1 -1
  25. package/docs/CLI-CORE-VERBS.md +112 -0
  26. package/docs/CLI-REFERENCE.md +9 -1
  27. package/docs/COMPARE-OPENCODE.md +94 -0
  28. package/docs/COMPONENTS.md +1 -0
  29. package/docs/EVALUATING.md +27 -0
  30. package/docs/HARNESS-INTELLIGENCE-PLAN.md +20 -0
  31. package/docs/INSTALLATION.md +56 -12
  32. package/docs/KNOWN-ISSUES.md +8 -9
  33. package/docs/PROOF-PASSPORT.md +114 -0
  34. package/docs/QUICKSTART.md +108 -0
  35. package/docs/alternative-installations.md +103 -0
  36. package/docs/cost-controls.md +139 -0
  37. package/docs/environment-variables.md +45 -2
  38. package/docs/exit-codes.md +129 -12
  39. package/docs/spec-first-intent.md +29 -0
  40. package/docs/v10/CLI-MODERN.md +11 -5
  41. package/docs/v10/ENGINE.md +32 -0
  42. package/docs/v10/GUIDE.md +270 -44
  43. package/docs/v10/OPENCODE-PARITY-PLAN.md +84 -0
  44. package/loki-ts/data/model-pricing.json +1 -0
  45. package/loki-ts/dist/loki.js +1065 -978
  46. package/mcp/__init__.py +1 -1
  47. package/package.json +2 -2
  48. package/packages/control-plane/dist/server.js +1829 -715
  49. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  50. package/plugins/loki-mode/README.md +41 -1
  51. package/plugins/loki-mode/commands/handoff.md +12 -0
  52. package/plugins/loki-mode/commands/loki-verify.md +9 -2
  53. package/plugins/loki-mode/commands/start.md +20 -0
  54. package/plugins/loki-mode/hooks/hooks.json +11 -0
  55. package/plugins/loki-mode/scripts/loki-stop-verify.sh +84 -0
  56. package/providers/claude.sh +16 -2
  57. package/providers/model_route_exec.sh +22 -0
  58. package/providers/model_routes.sh +188 -0
  59. package/skills/providers.md +13 -0
  60. package/templates/agents/planner.md +10 -0
  61. package/templates/agents/reviewer.md +10 -0
  62. package/templates/agents/tester.md +10 -0
  63. package/web-app/dist/assets/{AdminPage-QK-i_x-O.js → AdminPage-Cbu3pXL8.js} +1 -1
  64. package/web-app/dist/assets/{Avatar-DblkLNjB.js → Avatar-DGqGGcGx.js} +1 -1
  65. package/web-app/dist/assets/{Badge-DIV2ny5H.js → Badge-Bhlipt6v.js} +1 -1
  66. package/web-app/dist/assets/{Button--rePJOlQ.js → Button-CpN2sO61.js} +1 -1
  67. package/web-app/dist/assets/{CockpitPage-BZlKxpGy.js → CockpitPage-BqeDJwdL.js} +1 -1
  68. package/web-app/dist/assets/{ComparePage-3_INSVux.js → ComparePage-DUieA2ba.js} +1 -1
  69. package/web-app/dist/assets/{ErrorBoundary-DHG63CNr.js → ErrorBoundary-DTWsYrOm.js} +1 -1
  70. package/web-app/dist/assets/{EvidenceReceiptPanel-DFsBnLlO.js → EvidenceReceiptPanel-BU_B9X19.js} +1 -1
  71. package/web-app/dist/assets/{GitHubIssuesPanel-phPGah98.js → GitHubIssuesPanel-DEZoLOsP.js} +1 -1
  72. package/web-app/dist/assets/{GitHubPRsPanel-T7KQqJmx.js → GitHubPRsPanel-BEsnrq7Y.js} +1 -1
  73. package/web-app/dist/assets/{HomePage-DFjGUF5I.js → HomePage-BoUVuq7h.js} +2 -2
  74. package/web-app/dist/assets/{LoginPage-CorUid_5.js → LoginPage-CmOED7vb.js} +1 -1
  75. package/web-app/dist/assets/{MagicPage-DofbSxKD.js → MagicPage-DATZitrZ.js} +1 -1
  76. package/web-app/dist/assets/{MetricsPage-qzrhhfLC.js → MetricsPage-DGL1v0I-.js} +1 -1
  77. package/web-app/dist/assets/{NotFoundPage-BAZR0mMb.js → NotFoundPage-C2WgOjZd.js} +1 -1
  78. package/web-app/dist/assets/{ProjectPage-BOSUzG9J.js → ProjectPage-BMi1tepj.js} +3 -3
  79. package/web-app/dist/assets/{ProjectsPage-Ba0YLtzD.js → ProjectsPage-vc3y5YPh.js} +1 -1
  80. package/web-app/dist/assets/{SettingsPage-Cr10Ue2K.js → SettingsPage-kbq0Ftvi.js} +1 -1
  81. package/web-app/dist/assets/{ShowcasePage-Czn655fe.js → ShowcasePage-Bn6Q8Kbg.js} +1 -1
  82. package/web-app/dist/assets/{SystemSettingsPage-Dwjnw6Ob.js → SystemSettingsPage-Basmhs_b.js} +1 -1
  83. package/web-app/dist/assets/{TeamsPage-D8nc69Be.js → TeamsPage-BTqOc4TR.js} +1 -1
  84. package/web-app/dist/assets/{TemplatesPage-DHsaCDGv.js → TemplatesPage-2P9002VL.js} +1 -1
  85. package/web-app/dist/assets/{TerminalOutput-DBejni2a.js → TerminalOutput-CAoSPBrg.js} +1 -1
  86. package/web-app/dist/assets/{activity-B2VsBl1C.js → activity-BcWVWCqU.js} +1 -1
  87. package/web-app/dist/assets/{bell-CT77Fq2H.js → bell-CYRqqgZ6.js} +1 -1
  88. package/web-app/dist/assets/{bot-DfaWgF4H.js → bot-CU7gdn3g.js} +1 -1
  89. package/web-app/dist/assets/{check-D1yp7Ndv.js → check-DLlCpz9V.js} +1 -1
  90. package/web-app/dist/assets/{chevron-left-B6Pa8IOX.js → chevron-left-CE9Cb30C.js} +1 -1
  91. package/web-app/dist/assets/{circle-alert-D5jrL_Y7.js → circle-alert-uou4bjua.js} +1 -1
  92. package/web-app/dist/assets/{clock-DIsY_fa_.js → clock-C_dSJcDX.js} +1 -1
  93. package/web-app/dist/assets/{cloud-eGwLh1As.js → cloud-DZ22ITF6.js} +1 -1
  94. package/web-app/dist/assets/{code-xml-CGDIcZ3m.js → code-xml-BrJS0hhS.js} +1 -1
  95. package/web-app/dist/assets/{database-CWpdaykI.js → database-D2HOmxtl.js} +1 -1
  96. package/web-app/dist/assets/{dollar-sign-DZEuTCs6.js → dollar-sign-CQ1vK_6q.js} +1 -1
  97. package/web-app/dist/assets/{file-code-corner-BHBW6m9v.js → file-code-corner-w2vpGm5d.js} +1 -1
  98. package/web-app/dist/assets/{file-plus-D4h1jZSR.js → file-plus-DlF6wjkD.js} +1 -1
  99. package/web-app/dist/assets/{globe-CXdqKgwd.js → globe-Dy3lBJg1.js} +1 -1
  100. package/web-app/dist/assets/{hammer-B3YuuYI_.js → hammer-C3xIiRXN.js} +1 -1
  101. package/web-app/dist/assets/{index-BLEnboSv.js → index-4t-_IW2y.js} +3 -3
  102. package/web-app/dist/assets/{layers-Dvp5nFtw.js → layers-Y8Fs8qXI.js} +1 -1
  103. package/web-app/dist/assets/{loader-circle-DodDmIZ3.js → loader-circle-CxgN1v4m.js} +1 -1
  104. package/web-app/dist/assets/{lock-EiuaH1Ep.js → lock-DFFNbXDs.js} +1 -1
  105. package/web-app/dist/assets/{package-Cx4m2hFg.js → package-5e6qCTUg.js} +1 -1
  106. package/web-app/dist/assets/{plus-D-Sd5rpH.js → plus-C0Go_ytj.js} +1 -1
  107. package/web-app/dist/assets/{refresh-cw-C0w1cdmF.js → refresh-cw-D4i-NW3o.js} +1 -1
  108. package/web-app/dist/assets/{rotate-ccw-C_qk6MTT.js → rotate-ccw-YlKkiUW6.js} +1 -1
  109. package/web-app/dist/assets/{scroll-text-DSxiFtkc.js → scroll-text-4qXOHTap.js} +1 -1
  110. package/web-app/dist/assets/{server-NTJt7I8M.js → server-BB6zXNp9.js} +1 -1
  111. package/web-app/dist/assets/{shield-alert-HBxMQOvH.js → shield-alert-D4tzjB1H.js} +1 -1
  112. package/web-app/dist/assets/{thumbs-up-DEgsHzSD.js → thumbs-up-DZiRwdFj.js} +1 -1
  113. package/web-app/dist/assets/{trash-2-px46FnTD.js → trash-2-Dv_LkRIK.js} +1 -1
  114. package/web-app/dist/assets/{trending-down-CBosVGKa.js → trending-down-BJYp9PQe.js} +1 -1
  115. package/web-app/dist/assets/{trending-up-Dbd7zrQj.js → trending-up-BTcXLklK.js} +1 -1
  116. package/web-app/dist/assets/{upload-nhdFKAs1.js → upload-fcHqwOxr.js} +1 -1
  117. package/web-app/dist/assets/{usePolling-BgaGdinh.js → usePolling-BeJYw_4L.js} +1 -1
  118. package/web-app/dist/assets/{user-CzIihUy9.js → user-BcDwUCsO.js} +1 -1
  119. package/web-app/dist/index.html +1 -1
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "loki-mode",
4
4
  "displayName": "Loki Mode",
5
- "version": "11.3.9",
5
+ "version": "11.5.1",
6
6
  "description": "Autonomous spec-to-product build system with a built-in trust layer (RARV-C closure loop, 8 quality gates, completion council). Ships Loki's spec-hardening, drift-detection, and deterministic PR verification commands plus the Loki MCP server.",
7
7
  "author": {
8
8
  "name": "Autonomi",
@@ -35,7 +35,14 @@ Slash commands (namespaced as `loki-mode:<command>`):
35
35
  - `loki-mode:loki-spec-status` - check whether the spec has drifted from its
36
36
  lock using deterministic living-spec drift detection.
37
37
  - `loki-mode:loki-verify` - run Loki's deterministic PR verification on the
38
- current change and summarize the evidence verdict.
38
+ current change and summarize the evidence verdict. `--fast [base]` runs
39
+ `loki verify --fast --diff-base ${base:-HEAD}` instead: deterministic, no
40
+ model or network call (exit 0 PASS, 1 FAIL, 3 INCONCLUSIVE).
41
+ - `loki-mode:start <task or file>` - hand a task to Loki (`loki start --bg`).
42
+ Free-text tasks are written to `.loki/handoff-task.md` first. Claude Code
43
+ does not build it; Loki owns provider setup, budget and routing.
44
+ - `loki-mode:handoff` - print `.loki/review/HANDOFF.md`, else `./HANDOFF.md`,
45
+ else `loki own`. Never invents text.
39
46
 
40
47
  MCP server (`loki-mode`): exposes Loki's tools (memory, task queue, code
41
48
  search, build management) to Claude Code.
@@ -82,6 +89,39 @@ runs before:
82
89
  With `LOKI_GUARD` unset the hook always allows the command through, so it never
83
90
  interferes unless you ask it to.
84
91
 
92
+ ## Optional Stop hook: fast verifier gate (off by default)
93
+
94
+ The plugin ships a `Stop` hook (`scripts/loki-stop-verify.sh`, hook timeout
95
+ 30 s) that is a no-op unless `LOKI_STOP_VERIFY=1` (environment or Claude Code
96
+ settings `env`) or the plugin option `CLAUDE_PLUGIN_OPTION_STOP_VERIFY` is set.
97
+ It blocks completion only when `loki verify --fast --diff-base HEAD` exits 1
98
+ (FAIL: at least one high-severity finding), emitting
99
+ `{"decision":"block","reason":"..."}` with the first 15 verifier lines.
100
+
101
+ It allows the stop (fail-open) when: not opted in; the event has
102
+ `stop_hook_active: true` (never loops after one block); `loki`, `git` or a git
103
+ work tree is absent; no tracked file has uncommitted changes; the verifier
104
+ times out (`LOKI_STOP_VERIFY_TIMEOUT`, default 20 s); or it exits with anything
105
+ other than 1 (INCONCLUSIVE is not a block). Untracked new files are outside the
106
+ diff scope, and the fast verifier covers its detector set, not your test suite.
107
+
108
+ ## Configure Claude Code from the CLI
109
+
110
+ ```
111
+ loki setup-skill --claude-code [--print] [--project] [--stop-hook] [--settings FILE]
112
+ ```
113
+
114
+ Merges `extraKnownMarketplaces.loki-mode`, `enabledPlugins["loki-mode@loki-mode"]`
115
+ and, with `--stop-hook`, `env.LOKI_STOP_VERIFY` into `~/.claude/settings.json`
116
+ (`--project`: `./.claude/settings.json`). Existing keys are kept as you set them
117
+ (even `false`), invalid JSON is refused (exit 3) with the file untouched,
118
+ writes are atomic, and a second run reports `UNCHANGED`. `--print` prints the
119
+ snippet and writes nothing. Then run `/reload-plugins` in Claude Code.
120
+
121
+ Claude Code keeps tool permissions, the Stop-hook protocol, plugin
122
+ install/update and the session itself; the plugin adds no model loop,
123
+ permission system or sandbox.
124
+
85
125
  ## License
86
126
 
87
127
  SEE LICENSE IN LICENSE (BUSL-1.1, source-available). See the LICENSE file at the
@@ -0,0 +1,12 @@
1
+ ---
2
+ description: Print Loki's review handoff (.loki/review/HANDOFF.md) for the last run.
3
+ allowed-tools: Read, Bash(ls:*), Bash(loki own:*)
4
+ ---
5
+
6
+ Show the user what Loki handed off, verbatim.
7
+
8
+ 1. If `.loki/review/HANDOFF.md` exists, Read it and print it unchanged.
9
+ 2. Else if `HANDOFF.md` exists at the project root, Read and print that.
10
+ 3. Else run `loki own` and print its output.
11
+ 4. If none yields content, say no handoff exists yet and suggest
12
+ `/loki-mode:start <task>`. Do not invent a summary.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  description: Run Loki's deterministic PR verification on the current change and summarize the evidence verdict.
3
- argument-hint: "[base-ref] (default: main)"
4
- allowed-tools: Bash(loki verify:*), Read
3
+ argument-hint: "[--fast] [base-ref] (default: main; --fast defaults to HEAD, the uncommitted changes)"
4
+ allowed-tools: Bash(loki verify:*), Bash(command -v:*), Read
5
5
  ---
6
6
 
7
7
  Run Loki's Autonomi Verify on the current working tree and report the verdict
@@ -9,6 +9,13 @@ with its evidence, not just an opinion in chat. The differentiator is the
9
9
  auditable artifact: a verdict that refuses to silently pass on inconclusive
10
10
  evidence.
11
11
 
12
+ Fast mode: if $ARGUMENTS starts with `--fast`, check `command -v loki` (stop if
13
+ missing) and run `loki verify --fast --diff-base <ref>` (ref defaults to
14
+ `HEAD`). It is deterministic, with no model or network call. Exit codes: 0 PASS,
15
+ 1 FAIL, 3 INCONCLUSIVE. Print the verdict line and each finding as
16
+ `severity rule path:line`, say which findings block (severity high), and never
17
+ describe INCONCLUSIVE as a pass. Skip the steps below.
18
+
12
19
  Steps:
13
20
 
14
21
  1. Run the verifier against the base ref ($ARGUMENTS, or `main` if empty):
@@ -0,0 +1,20 @@
1
+ ---
2
+ description: Hand the current task to Loki (loki start) in the background and report where the handoff will land.
3
+ argument-hint: "<task description or path to a PRD/spec file>"
4
+ allowed-tools: Bash(loki start:*), Bash(loki status:*), Bash(command -v:*), Bash(ls:*)
5
+ ---
6
+
7
+ Delegate the task in $ARGUMENTS to Loki Mode so it runs autonomously while the
8
+ user does something else. Do not start building the task yourself.
9
+
10
+ 1. Confirm the CLI exists: `command -v loki`. If missing, say
11
+ `npm install -g loki-mode` is required and stop.
12
+ 2. If $ARGUMENTS is an existing file path, run `loki start --bg <path>`.
13
+ Otherwise write the text to `.loki/handoff-task.md` (create `.loki/` if
14
+ needed) and run `loki start --bg .loki/handoff-task.md`. Do not pass any
15
+ permission-bypass flags of your own; `loki start` owns its provider setup.
16
+ 3. Report in at most five lines: that the run started in the background, the
17
+ command to check progress (`loki status`), and where results land:
18
+ `.loki/review/HANDOFF.md` (read it with `/loki-mode:handoff`) and the
19
+ evidence verdict (`/loki-mode:loki-verify --fast`).
20
+ 4. If `loki start` exits non-zero, show its first error line verbatim and stop.
@@ -10,6 +10,17 @@
10
10
  }
11
11
  ]
12
12
  }
13
+ ],
14
+ "Stop": [
15
+ {
16
+ "hooks": [
17
+ {
18
+ "type": "command",
19
+ "command": "\"${CLAUDE_PLUGIN_ROOT}\"/scripts/loki-stop-verify.sh",
20
+ "timeout": 30
21
+ }
22
+ ]
23
+ }
13
24
  ]
14
25
  }
15
26
  }
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env bash
2
+ # Loki Stop hook: run the fast deterministic verifier on the working tree and
3
+ # block completion when the uncommitted change carries a blocking finding.
4
+ #
5
+ # Opt-in. No-op unless LOKI_STOP_VERIFY is truthy (or the plugin option
6
+ # CLAUDE_PLUGIN_OPTION_STOP_VERIFY is). Fail-open: every uncertain path
7
+ # (loki missing, not a git repo, clean tree, timeout, verifier error,
8
+ # INCONCLUSIVE, stop_hook_active) allows the stop. Only an explicit verifier
9
+ # exit 1 (FAIL, a high-severity finding) blocks.
10
+ #
11
+ # Block contract: print {"decision":"block","reason":...} on stdout, exit 0.
12
+
13
+ set -u
14
+
15
+ allow() { exit 0; }
16
+
17
+ truthy() {
18
+ case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in
19
+ 1 | true | yes | on | y) return 0 ;;
20
+ *) return 1 ;;
21
+ esac
22
+ }
23
+
24
+ if ! truthy "${LOKI_STOP_VERIFY:-}" && ! truthy "${CLAUDE_PLUGIN_OPTION_STOP_VERIFY:-}"; then
25
+ allow
26
+ fi
27
+
28
+ event="$(cat 2>/dev/null || true)"
29
+
30
+ # A previous block already re-entered the model once: never loop.
31
+ case "$event" in
32
+ *'"stop_hook_active": true'* | *'"stop_hook_active":true'*) allow ;;
33
+ esac
34
+
35
+ command -v loki >/dev/null 2>&1 || allow
36
+ command -v git >/dev/null 2>&1 || allow
37
+ git rev-parse --is-inside-work-tree >/dev/null 2>&1 || allow
38
+
39
+ # Nothing changed: nothing the agent could have broken.
40
+ [ -n "$(git status --porcelain --untracked-files=no 2>/dev/null)" ] || allow
41
+
42
+ limit="${LOKI_STOP_VERIFY_TIMEOUT:-20}"
43
+ out_file="$(mktemp "${TMPDIR:-/tmp}/loki-stop-verify.XXXXXX")" || allow
44
+ trap 'rm -f "$out_file"' EXIT
45
+
46
+ run_verifier() { loki verify --fast --diff-base HEAD >"$out_file" 2>/dev/null; }
47
+
48
+ if command -v timeout >/dev/null 2>&1; then
49
+ timeout -k 2 "$limit" loki verify --fast --diff-base HEAD >"$out_file" 2>/dev/null
50
+ rc=$?
51
+ elif command -v gtimeout >/dev/null 2>&1; then
52
+ gtimeout -k 2 "$limit" loki verify --fast --diff-base HEAD >"$out_file" 2>/dev/null
53
+ rc=$?
54
+ else
55
+ # No timeout binary (stock macOS): watchdog on the recorded child PID only.
56
+ run_verifier &
57
+ child=$!
58
+ ( sleep "$limit"; kill "$child" 2>/dev/null ) >/dev/null 2>&1 &
59
+ watchdog=$!
60
+ wait "$child" 2>/dev/null
61
+ rc=$?
62
+ kill "$watchdog" 2>/dev/null
63
+ wait "$watchdog" 2>/dev/null
64
+ fi
65
+
66
+ # Only an explicit FAIL blocks. 0, 2, 3, 64, 124, 137 and anything else allow.
67
+ [ "$rc" -eq 1 ] || allow
68
+
69
+ summary="$(head -n 15 "$out_file" 2>/dev/null)"
70
+ reason="Loki fast verify found blocking problems in your uncommitted change. Fix them before finishing:
71
+ ${summary}
72
+ Re-run: loki verify --fast --diff-base HEAD"
73
+
74
+ if command -v python3 >/dev/null 2>&1; then
75
+ printf '%s' "$reason" | python3 -c '
76
+ import sys, json
77
+ print(json.dumps({"decision": "block", "reason": sys.stdin.read()}))
78
+ '
79
+ else
80
+ # No python3: single-line fallback with minimal escaping.
81
+ esc="$(printf '%s' "$reason" | tr '\n' ' ' | sed 's/\\/\\\\/g; s/"/\\"/g')"
82
+ printf '{"decision":"block","reason":"%s"}\n' "$esc"
83
+ fi
84
+ exit 0
@@ -151,6 +151,11 @@ unset _loki_dap_root
151
151
 
152
152
  # Source the v7.5.22 Phase D mcp-config helper (idempotent).
153
153
  # shellcheck source=../autonomy/lib/mcp-config.sh
154
+ # OC-MODELS: model routes (openrouter/ollama/... over the Claude harness).
155
+ _loki_model_routes_helper="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/model_routes.sh"
156
+ # shellcheck disable=SC1090
157
+ [ -f "$_loki_model_routes_helper" ] && . "$_loki_model_routes_helper"
158
+
154
159
  _loki_mcp_config_helper="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/autonomy/lib/mcp-config.sh"
155
160
  if [ -f "$_loki_mcp_config_helper" ]; then
156
161
  # shellcheck disable=SC1090
@@ -385,7 +390,8 @@ provider_invoke() {
385
390
  # array is empty, and a bare "${arr[@]}" under `set -u` aborts with "unbound
386
391
  # variable" on bash 3.2 (stock macOS /bin/bash). ${arr[@]+...} expands to
387
392
  # nothing when unset/empty and preserves spaced elements otherwise.
388
- claude --dangerously-skip-permissions "${_LOKI_CLAUDE_AUTO_FLAGS[@]+"${_LOKI_CLAUDE_AUTO_FLAGS[@]}"}" -p "$prompt" "$@" < /dev/null
393
+ loki_model_route_prefix
394
+ "${_LOKI_ROUTE_EXEC[@]+"${_LOKI_ROUTE_EXEC[@]}"}" claude --dangerously-skip-permissions "${_LOKI_CLAUDE_AUTO_FLAGS[@]+"${_LOKI_CLAUDE_AUTO_FLAGS[@]}"}" -p "$prompt" "$@" < /dev/null
389
395
  }
390
396
 
391
397
  # provider_invoke_argv <tier> <prompt> -- populate _LOKI_INVOKE_ARGV with the
@@ -427,7 +433,9 @@ provider_invoke_argv() {
427
433
  # silently unenforced on exactly the timeout-safe seam. resolve_model_for_tier
428
434
  # is base -> route -> clamp -> alt-provider in one call; keep both paths on it.
429
435
  model="$(resolve_model_for_tier "$tier" 2>/dev/null || provider_get_tier_param "$tier")"
436
+ loki_model_route_prefix
430
437
  _LOKI_INVOKE_ARGV=(
438
+ "${_LOKI_ROUTE_EXEC[@]+"${_LOKI_ROUTE_EXEC[@]}"}"
431
439
  claude --dangerously-skip-permissions
432
440
  "${_LOKI_CLAUDE_AUTO_FLAGS[@]+"${_LOKI_CLAUDE_AUTO_FLAGS[@]}"}"
433
441
  )
@@ -638,6 +646,11 @@ resolve_model_for_tier() {
638
646
  model="$LOKI_MODEL_OVERRIDE"
639
647
  fi
640
648
 
649
+ # OC-MODELS: an active model route (openrouter/ollama/...) wins; plain ids unchanged.
650
+ local _route_model
651
+ _route_model="$(loki_model_route_model 2>/dev/null || true)"
652
+ [ -n "$_route_model" ] && model="$_route_model"
653
+
641
654
  echo "$model"
642
655
  }
643
656
 
@@ -656,5 +669,6 @@ provider_invoke_with_tier() {
656
669
  # Guard empty auto-flag array under `set -u` on bash 3.2 (stock macOS): a bare
657
670
  # "${arr[@]}" on an empty array aborts with "unbound variable". ${arr[@]+...}
658
671
  # expands to nothing when empty and preserves spaced elements otherwise.
659
- claude --dangerously-skip-permissions --model "$model" "${_LOKI_CLAUDE_AUTO_FLAGS[@]+"${_LOKI_CLAUDE_AUTO_FLAGS[@]}"}" -p "$prompt" "$@" < /dev/null
672
+ loki_model_route_prefix
673
+ "${_LOKI_ROUTE_EXEC[@]+"${_LOKI_ROUTE_EXEC[@]}"}" claude --dangerously-skip-permissions --model "$model" "${_LOKI_CLAUDE_AUTO_FLAGS[@]+"${_LOKI_CLAUDE_AUTO_FLAGS[@]}"}" -p "$prompt" "$@" < /dev/null
660
674
  }
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env bash
2
+ # Exec a command (the claude CLI) with the active model route applied to ITS env
3
+ # only. The token is read from the already-inherited env here, so it never
4
+ # appears on any command line and is never printed.
5
+ # Usage: bash model_route_exec.sh claude [args...]
6
+ _dir="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
7
+ # shellcheck source=model_routes.sh
8
+ . "$_dir/model_routes.sh"
9
+
10
+ spec="$(loki_model_route_spec)"
11
+ if [ -n "$spec" ] && loki_model_route_parse "$spec"; then
12
+ token=""
13
+ if [ -n "$LOKI_ROUTE_TOKEN_VAR" ]; then
14
+ token="${!LOKI_ROUTE_TOKEN_VAR:-}"
15
+ fi
16
+ export ANTHROPIC_BASE_URL="$LOKI_ROUTE_BASE"
17
+ export ANTHROPIC_MODEL="$LOKI_ROUTE_MODEL"
18
+ export ANTHROPIC_SMALL_FAST_MODEL="$LOKI_ROUTE_MODEL"
19
+ export ANTHROPIC_AUTH_TOKEN="${token:-loki-local-route}"
20
+ export ANTHROPIC_API_KEY=""
21
+ fi
22
+ exec "$@"
@@ -0,0 +1,188 @@
1
+ #!/usr/bin/env bash
2
+ # Model routes: run non-Anthropic models through the Tier-1 Claude harness.
3
+ #
4
+ # Claude Code honors ANTHROPIC_BASE_URL, ANTHROPIC_AUTH_TOKEN and ANTHROPIC_MODEL,
5
+ # so any Anthropic-compatible endpoint keeps every Loki control (cost, kill
6
+ # blocking, receipts). Mirror of loki-ts/src/providers/model_routes.ts.
7
+ #
8
+ # Spec forms (env LOKI_MODEL_ROUTE, or LOKI_MODEL_OVERRIDE when it is a route):
9
+ # openrouter/<id> https://openrouter.ai/api token: OPENROUTER_API_KEY
10
+ # ollama/<id> http://localhost:11434
11
+ # lmstudio/<id> http://localhost:1234
12
+ # llamacpp/<id> http://localhost:8080
13
+ # vllm/<id> http://localhost:8000
14
+ # custom/<id> LOKI_MODEL_BASE_URL (+ optional LOKI_MODEL_API_KEY)
15
+ # Plain Claude ids (opus, sonnet, claude-*) are not routes: behavior unchanged.
16
+ #
17
+ # The env is applied only inside providers/model_route_exec.sh, which execs the
18
+ # claude child. It is never exported into the parent shell, and the token is
19
+ # never placed on a command line or logged.
20
+
21
+ # kind -> "base_url|token_var"
22
+ loki_model_route_table() {
23
+ case "$1" in
24
+ openrouter) echo "https://openrouter.ai/api|OPENROUTER_API_KEY" ;;
25
+ ollama) echo "http://localhost:11434|" ;;
26
+ lmstudio) echo "http://localhost:1234|" ;;
27
+ llamacpp) echo "http://localhost:8080|" ;;
28
+ vllm) echo "http://localhost:8000|" ;;
29
+ custom) echo "${LOKI_MODEL_BASE_URL:-}|LOKI_MODEL_API_KEY" ;;
30
+ *) return 1 ;;
31
+ esac
32
+ }
33
+
34
+ # Echo the active route spec, or nothing when none is configured.
35
+ loki_model_route_spec() {
36
+ local s="${LOKI_MODEL_ROUTE:-}"
37
+ if [ -z "$s" ]; then
38
+ s="${LOKI_MODEL_OVERRIDE:-}"
39
+ case "$s" in
40
+ */*) loki_model_route_table "${s%%/*}" >/dev/null 2>&1 || s="" ;;
41
+ *) s="" ;;
42
+ esac
43
+ fi
44
+ [ -n "$s" ] && printf '%s\n' "$s"
45
+ return 0
46
+ }
47
+
48
+ # Parse a spec. Sets LOKI_ROUTE_KIND, LOKI_ROUTE_MODEL, LOKI_ROUTE_BASE, LOKI_ROUTE_TOKEN_VAR.
49
+ # Returns 1 when the spec is not a usable route.
50
+ loki_model_route_parse() {
51
+ local spec="${1:-}" kind model entry
52
+ case "$spec" in */*) ;; *) return 1 ;; esac
53
+ kind="${spec%%/*}"
54
+ model="${spec#*/}"
55
+ [ -n "$model" ] || return 1
56
+ entry="$(loki_model_route_table "$kind")" || return 1
57
+ LOKI_ROUTE_KIND="$kind"
58
+ LOKI_ROUTE_MODEL="$model"
59
+ LOKI_ROUTE_BASE="${entry%%|*}"
60
+ LOKI_ROUTE_TOKEN_VAR="${entry#*|}"
61
+ [ -n "$LOKI_ROUTE_BASE" ] || return 1
62
+ return 0
63
+ }
64
+
65
+ # Echo the model id served by the active route (empty when none).
66
+ loki_model_route_model() {
67
+ local spec
68
+ spec="$(loki_model_route_spec)"
69
+ [ -n "$spec" ] || return 0
70
+ loki_model_route_parse "$spec" && printf '%s\n' "$LOKI_ROUTE_MODEL"
71
+ return 0
72
+ }
73
+
74
+ # Populate _LOKI_ROUTE_EXEC with a command prefix for the claude child. Empty
75
+ # array when no route is active, so callers expand it with ${arr[@]+...}.
76
+ loki_model_route_prefix() {
77
+ _LOKI_ROUTE_EXEC=()
78
+ local spec dir
79
+ spec="$(loki_model_route_spec)"
80
+ [ -n "$spec" ] || return 0
81
+ loki_model_route_parse "$spec" || return 0
82
+ dir="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" 2>/dev/null && pwd)"
83
+ _LOKI_ROUTE_EXEC=(bash "$dir/model_route_exec.sh")
84
+ }
85
+
86
+ # Local runtime probe: print "<runtime>\t<model id>" per discovered model.
87
+ # 1s timeouts. Uses python3 only to parse JSON.
88
+ loki_models_probe_local() {
89
+ local rt url path body
90
+ for rt in "ollama|http://localhost:11434|/api/tags" \
91
+ "lmstudio|http://localhost:1234|/v1/models" \
92
+ "llamacpp|http://localhost:8080|/v1/models" \
93
+ "vllm|http://localhost:8000|/v1/models"; do
94
+ IFS='|' read -r name url path <<EOF
95
+ $rt
96
+ EOF
97
+ body="$(curl -s --connect-timeout 1 --max-time 1 "${url}${path}" 2>/dev/null)" || continue
98
+ [ -n "$body" ] || continue
99
+ printf '%s' "$body" | python3 -c '
100
+ import json, sys
101
+ name = sys.argv[1]
102
+ try:
103
+ d = json.load(sys.stdin)
104
+ except Exception:
105
+ sys.exit(0)
106
+ items = d.get("models") if isinstance(d, dict) and "models" in d else d.get("data", []) if isinstance(d, dict) else []
107
+ for m in items or []:
108
+ mid = (m.get("name") or m.get("model") or m.get("id")) if isinstance(m, dict) else None
109
+ if mid:
110
+ print(name + "\t" + mid)
111
+ ' "$name"
112
+ done
113
+ }
114
+
115
+ # loki models [--routes] [--local] [--json]
116
+ loki_models_cmd() {
117
+ local want_local=0 json=0 arg
118
+ for arg in "$@"; do
119
+ case "$arg" in
120
+ --local) want_local=1 ;;
121
+ --json) json=1 ;;
122
+ --routes) ;;
123
+ -h|--help)
124
+ echo "Usage: loki models [--local] [--json]"
125
+ echo ""
126
+ echo " (default) List model routes usable as --model / LOKI_MODEL_ROUTE values"
127
+ echo " --local Probe local runtimes (ollama, lmstudio, llamacpp, vllm) for models"
128
+ echo " --json Machine-readable output"
129
+ echo ""
130
+ echo "Route env: LOKI_MODEL_ROUTE=openrouter/<id> | ollama/<id> | lmstudio/<id> |"
131
+ echo " llamacpp/<id> | vllm/<id> | custom/<id> (needs LOKI_MODEL_BASE_URL)"
132
+ return 0
133
+ ;;
134
+ *) echo "loki models: unknown option: $arg" >&2; return 2 ;;
135
+ esac
136
+ done
137
+
138
+ local kinds="openrouter ollama lmstudio llamacpp vllm custom" k entry base tvar
139
+ if [ "$want_local" = "1" ]; then
140
+ local found
141
+ found="$(loki_models_probe_local)"
142
+ if [ "$json" = "1" ]; then
143
+ printf '%s' "$found" | python3 -c '
144
+ import json, sys
145
+ out = []
146
+ for line in sys.stdin.read().splitlines():
147
+ rt, _, mid = line.partition("\t")
148
+ out.append({"runtime": rt, "id": mid, "model": rt + "/" + mid})
149
+ print(json.dumps({"local": out}, indent=2))
150
+ '
151
+ elif [ -z "$found" ]; then
152
+ echo "No local runtimes responded (probed :11434 ollama, :1234 lmstudio, :8080 llamacpp, :8000 vllm)."
153
+ else
154
+ echo "Discovered local models (paste as LOKI_MODEL_ROUTE=<value>):"
155
+ printf '%s\n' "$found" | while IFS="$(printf '\t')" read -r rt mid; do
156
+ echo " $rt/$mid"
157
+ done
158
+ fi
159
+ return 0
160
+ fi
161
+
162
+ if [ "$json" = "1" ]; then
163
+ local sep="" first=1
164
+ printf '{"routes": ['
165
+ for k in $kinds; do
166
+ entry="$(loki_model_route_table "$k")"; base="${entry%%|*}"; tvar="${entry#*|}"
167
+ [ "$first" = "1" ] || printf ','
168
+ first=0
169
+ printf '{"kind":"%s","base_url":"%s","token_env":"%s","example":"%s/<model-id>"}' "$k" "$base" "$tvar" "$k"
170
+ done
171
+ printf ']}\n'
172
+ return 0
173
+ fi
174
+ echo "Model routes (run any Anthropic-compatible endpoint through the Claude harness):"
175
+ for k in $kinds; do
176
+ entry="$(loki_model_route_table "$k")"; base="${entry%%|*}"; tvar="${entry#*|}"
177
+ [ "$k" = "custom" ] && base="\$LOKI_MODEL_BASE_URL"
178
+ printf ' %-11s %-28s %s\n' "$k/<id>" "$base" "${tvar:+token: \$$tvar}"
179
+ done
180
+ echo ""
181
+ echo "Use: LOKI_MODEL_ROUTE=ollama/qwen3-coder loki start ./prd.md"
182
+ echo "Discover local models: loki models --local"
183
+ }
184
+
185
+ # Allow direct execution: bash providers/model_routes.sh [args]
186
+ if [ "${BASH_SOURCE[0]:-}" = "$0" ]; then
187
+ loki_models_cmd "$@"
188
+ fi
@@ -19,6 +19,7 @@ Loki Mode supports five AI providers for autonomous execution.
19
19
  | **Model Tiers** | 3 (opus/sonnet/haiku) | 1 (effort param) | 1 (external) | 1 (external) | 1 (external) |
20
20
  | **Multi-Provider** | Claude only | OpenAI only | 12+ providers | 18+ providers | 75+ providers + custom endpoints |
21
21
  | **Skill Directory** | ~/.claude/skills | None | None | None | ~/.config/opencode |
22
+ | **Guardrails (engine10)** | Native hooks + permission flags | Timeout + budget kill, diff scan, sandbox flag | Timeout + budget kill, diff scan | Timeout + budget kill, diff scan, no-auto-commits | Timeout + budget kill, diff scan |
22
23
 
23
24
  ## Provider Selection
24
25
 
@@ -396,3 +397,15 @@ PROVIDER_DEGRADED=false
396
397
  | Local models (Ollama, free) | opencode, Aider, or Cline |
397
398
  | Architect mode (dual model) | Aider |
398
399
  | Sequential-only is acceptable | Codex or Aider |
400
+
401
+ ---
402
+
403
+ ## Guardrail Parity (engine10)
404
+
405
+ Claude blocks destructive commands inline through its hooks and permission flags. For codex, cline, aider and opencode, engine10 applies `loki-ts/src/providers/provider_guard.ts` around every session:
406
+
407
+ 1. Wall-clock timeout (`limitS`) and a budget-stop poll (`BUDGET_LIMIT`) kill the whole process group.
408
+ 2. After each session a diff scan fails the iteration and reverts: mass deletion of tracked files, `.git/config` edits, new git hooks, symlinks or paths that point outside the project.
409
+ 3. Native CLI flags are used where they exist: codex `--sandbox workspace-write`, aider `--no-auto-commits`. Cline and opencode have no filesystem sandbox flag.
410
+
411
+ The scan is post-hoc, not inline, so the receipt still lists `kill blocking not enforced` for non-Claude providers. Violations emit a `guard.violation` event.
@@ -0,0 +1,10 @@
1
+ ---
2
+ name: planner
3
+ description: Breaks a goal into small ordered tasks with acceptance checks
4
+ model: opus
5
+ provider: claude
6
+ permission: plan
7
+ tools: [Read, Grep, Glob]
8
+ deny: [Edit, Write, Bash]
9
+ ---
10
+ You are a planning agent. Read the repository and split the goal into small, independently verifiable tasks. For each task give: goal, files likely touched, acceptance check, and risk. Order tasks by dependency. Do not write code.
@@ -0,0 +1,10 @@
1
+ ---
2
+ name: reviewer
3
+ description: Skeptical code reviewer for correctness, tests and maintainability
4
+ model: sonnet
5
+ provider: claude
6
+ permission: plan
7
+ tools: [Read, Grep, Glob]
8
+ deny: [Edit, Write]
9
+ ---
10
+ You are a senior code reviewer. Review the supplied evidence for correctness bugs, missing tests, security problems and maintainability issues. Cite file and line for every finding. Do not modify files. Report APPROVE only when you found no blocking issue.
@@ -0,0 +1,10 @@
1
+ ---
2
+ name: tester
3
+ description: Writes and runs focused tests for a change, reports gaps
4
+ model: sonnet
5
+ provider: claude
6
+ permission: edit
7
+ tools: [Read, Grep, Glob, Edit, Write, Bash]
8
+ deny: []
9
+ ---
10
+ You are a test engineer. For the given change, add the smallest set of tests that fail before the change and pass after. Run them and report exact commands and results. Do not alter production code; report any production bug you find instead.
@@ -1,4 +1,4 @@
1
- import{c as L,r as x,j as e,w as re,Z as ne,m as le,h as ie,p as P,F as ye,U as I,X as ce,L as de,x as oe,M as Ae,T as Z,S as we,l as xe,k as me,y as Ce,E as Fe,a0 as Se,a as U,a2 as he}from"./index-BLEnboSv.js";import{B as C}from"./Button--rePJOlQ.js";import{T as $e}from"./trending-up-Dbd7zrQj.js";import{C as z}from"./clock-DIsY_fa_.js";import{A as Be,U as ue,M as De}from"./Avatar-DblkLNjB.js";import{A as J}from"./activity-B2VsBl1C.js";import{R as pe}from"./refresh-cw-C0w1cdmF.js";import{G as ze}from"./globe-CXdqKgwd.js";import{C as Le}from"./chevron-left-B6Pa8IOX.js";import{C as je}from"./check-D1yp7Ndv.js";import{D as Y}from"./dollar-sign-DZEuTCs6.js";import{U as Te}from"./user-CzIihUy9.js";import{D as Me}from"./database-CWpdaykI.js";import{L as Ue}from"./lock-EiuaH1Ep.js";import{S as Re}from"./scroll-text-DSxiFtkc.js";import{S as Ie}from"./shield-alert-HBxMQOvH.js";/**
1
+ import{c as L,r as x,j as e,w as re,Z as ne,m as le,h as ie,p as P,F as ye,U as I,X as ce,L as de,x as oe,M as Ae,T as Z,S as we,l as xe,k as me,y as Ce,E as Fe,a0 as Se,a as U,a2 as he}from"./index-4t-_IW2y.js";import{B as C}from"./Button-CpN2sO61.js";import{T as $e}from"./trending-up-BTcXLklK.js";import{C as z}from"./clock-C_dSJcDX.js";import{A as Be,U as ue,M as De}from"./Avatar-DGqGGcGx.js";import{A as J}from"./activity-BcWVWCqU.js";import{R as pe}from"./refresh-cw-D4i-NW3o.js";import{G as ze}from"./globe-Dy3lBJg1.js";import{C as Le}from"./chevron-left-CE9Cb30C.js";import{C as je}from"./check-DLlCpz9V.js";import{D as Y}from"./dollar-sign-CQ1vK_6q.js";import{U as Te}from"./user-BcDwUCsO.js";import{D as Me}from"./database-D2HOmxtl.js";import{L as Ue}from"./lock-DFFNbXDs.js";import{S as Re}from"./scroll-text-4qXOHTap.js";import{S as Ie}from"./shield-alert-D4tzjB1H.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as l,j as s}from"./index-BLEnboSv.js";/**
1
+ import{c as l,j as s}from"./index-4t-_IW2y.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- import{j as e}from"./index-BLEnboSv.js";import{C as r}from"./clock-DIsY_fa_.js";import{C as a}from"./circle-alert-D5jrL_Y7.js";import{C as l}from"./check-D1yp7Ndv.js";const i={completed:"bg-[#1FC5A8]/10 text-[#1FC5A8]",running:"bg-[#553DE9]/10 text-[#553DE9]",failed:"bg-[#C45B5B]/10 text-[#C45B5B]",started:"bg-[#D4A03C]/10 text-[#D4A03C]",empty:"bg-[#F8F4F0] text-[#6B6960]",version:"bg-[#553DE9]/10 text-[#553DE9]"};function o({status:t}){switch(t){case"completed":return e.jsx(l,{size:12});case"running":return e.jsxs("span",{className:"relative flex h-2 w-2",children:[e.jsx("span",{className:"animate-ping motion-reduce:animate-none absolute inline-flex h-full w-full rounded-full bg-current opacity-75"}),e.jsx("span",{className:"relative inline-flex rounded-full h-2 w-2 bg-current"})]});case"failed":return e.jsx(a,{size:12});case"started":return e.jsx(r,{size:12});default:return null}}function f({status:t,children:n,className:s=""}){return e.jsxs("span",{className:["inline-flex items-center gap-1 rounded-btn px-2.5 py-0.5 text-xs font-semibold",i[t],s].filter(Boolean).join(" "),children:[e.jsx(o,{status:t}),n]})}export{f as B};
1
+ import{j as e}from"./index-4t-_IW2y.js";import{C as r}from"./clock-C_dSJcDX.js";import{C as a}from"./circle-alert-uou4bjua.js";import{C as l}from"./check-DLlCpz9V.js";const i={completed:"bg-[#1FC5A8]/10 text-[#1FC5A8]",running:"bg-[#553DE9]/10 text-[#553DE9]",failed:"bg-[#C45B5B]/10 text-[#C45B5B]",started:"bg-[#D4A03C]/10 text-[#D4A03C]",empty:"bg-[#F8F4F0] text-[#6B6960]",version:"bg-[#553DE9]/10 text-[#553DE9]"};function o({status:t}){switch(t){case"completed":return e.jsx(l,{size:12});case"running":return e.jsxs("span",{className:"relative flex h-2 w-2",children:[e.jsx("span",{className:"animate-ping motion-reduce:animate-none absolute inline-flex h-full w-full rounded-full bg-current opacity-75"}),e.jsx("span",{className:"relative inline-flex rounded-full h-2 w-2 bg-current"})]});case"failed":return e.jsx(a,{size:12});case"started":return e.jsx(r,{size:12});default:return null}}function f({status:t,children:n,className:s=""}){return e.jsxs("span",{className:["inline-flex items-center gap-1 rounded-btn px-2.5 py-0.5 text-xs font-semibold",i[t],s].filter(Boolean).join(" "),children:[e.jsx(o,{status:t}),n]})}export{f as B};
@@ -1 +1 @@
1
- import{r as p,j as t}from"./index-BLEnboSv.js";const m={primary:"bg-[#553DE9] text-white hover:bg-[#4432c4] shadow-button rounded-btn",secondary:"border border-[#553DE9] text-[#553DE9] hover:bg-[#E8E4FD] bg-transparent rounded-btn",ghost:"text-[#36342E] hover:bg-[#F8F4F0] rounded-btn",danger:"bg-[#C45B5B]/10 text-[#C45B5B] border border-[#C45B5B]/20 hover:bg-[#C45B5B]/20 rounded-btn"},b={sm:"px-3 py-1.5 text-xs",md:"px-4 py-2 text-sm",lg:"px-6 py-3 text-base"},u={sm:14,md:16,lg:18};function h({size:e}){return t.jsxs("svg",{className:"animate-spin",width:e,height:e,viewBox:"0 0 24 24",fill:"none",children:[t.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),t.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"})]})}const B=p.forwardRef(({variant:e="primary",size:o="md",icon:n,iconRight:i,loading:r=!1,disabled:a,className:c="",children:l,...x},d)=>{const s=u[o];return t.jsxs("button",{ref:d,disabled:a||r,className:["inline-flex items-center justify-center gap-2 font-medium transition-colors",m[e],b[o],(a||r)&&"opacity-60 cursor-not-allowed",c].filter(Boolean).join(" "),...x,children:[r?t.jsx(h,{size:s}):n?t.jsx(n,{size:s}):null,l,i&&!r&&t.jsx(i,{size:s})]})});B.displayName="Button";export{B};
1
+ import{r as p,j as t}from"./index-4t-_IW2y.js";const m={primary:"bg-[#553DE9] text-white hover:bg-[#4432c4] shadow-button rounded-btn",secondary:"border border-[#553DE9] text-[#553DE9] hover:bg-[#E8E4FD] bg-transparent rounded-btn",ghost:"text-[#36342E] hover:bg-[#F8F4F0] rounded-btn",danger:"bg-[#C45B5B]/10 text-[#C45B5B] border border-[#C45B5B]/20 hover:bg-[#C45B5B]/20 rounded-btn"},b={sm:"px-3 py-1.5 text-xs",md:"px-4 py-2 text-sm",lg:"px-6 py-3 text-base"},u={sm:14,md:16,lg:18};function h({size:e}){return t.jsxs("svg",{className:"animate-spin",width:e,height:e,viewBox:"0 0 24 24",fill:"none",children:[t.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),t.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"})]})}const B=p.forwardRef(({variant:e="primary",size:o="md",icon:n,iconRight:i,loading:r=!1,disabled:a,className:c="",children:l,...x},d)=>{const s=u[o];return t.jsxs("button",{ref:d,disabled:a||r,className:["inline-flex items-center justify-center gap-2 font-medium transition-colors",m[e],b[o],(a||r)&&"opacity-60 cursor-not-allowed",c].filter(Boolean).join(" "),...x,children:[r?t.jsx(h,{size:s}):n?t.jsx(n,{size:s}):null,l,i&&!r&&t.jsx(i,{size:s})]})});B.displayName="Button";export{B};
@@ -1,4 +1,4 @@
1
- import{c as $,r as l,j as e,G as we,Q as Se,F as Ce,h as Re,T as U,Z as Ee,a as S,V as Me,S as $e,Y as Te,X as Pe,P as ie,i as Ae,g as ze,C as Ie,l as Le,N as I,J as _e,K as De,O as Fe,u as Oe}from"./index-BLEnboSv.js";import{P as K,S as He,E as Be}from"./ErrorBoundary-DHG63CNr.js";import{C as qe,F as Ge,M as Ue,H as Ke,a as We,G as Ve,T as Qe}from"./thumbs-up-DEgsHzSD.js";import{C as oe}from"./check-D1yp7Ndv.js";import{C as Ye}from"./clock-DIsY_fa_.js";import{D as Xe}from"./dollar-sign-DZEuTCs6.js";import{U as Ze}from"./upload-nhdFKAs1.js";import{L as V}from"./loader-circle-DodDmIZ3.js";/**
1
+ import{c as $,r as l,j as e,G as we,Q as Se,F as Ce,h as Re,T as U,Z as Ee,a as S,V as Me,S as $e,Y as Te,X as Pe,P as ie,i as Ae,g as ze,C as Ie,l as Le,N as I,J as _e,K as De,O as Fe,u as Oe}from"./index-4t-_IW2y.js";import{P as K,S as He,E as Be}from"./ErrorBoundary-DTWsYrOm.js";import{C as qe,F as Ge,M as Ue,H as Ke,a as We,G as Ve,T as Qe}from"./thumbs-up-DZiRwdFj.js";import{C as oe}from"./check-DLlCpz9V.js";import{C as Ye}from"./clock-C_dSJcDX.js";import{D as Xe}from"./dollar-sign-CQ1vK_6q.js";import{U as Ze}from"./upload-fcHqwOxr.js";import{L as V}from"./loader-circle-CxgN1v4m.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.