loki-mode 10.6.11 → 10.6.13

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 (98) hide show
  1. package/README.md +20 -2
  2. package/SKILL.md +2 -2
  3. package/VERSION +1 -1
  4. package/autonomy/lib/backlog.py +19 -3
  5. package/autonomy/lib/fast_verify.py +19 -1
  6. package/autonomy/lib/proof-pr.sh +34 -3
  7. package/autonomy/lib/workspace.py +27 -1
  8. package/autonomy/lib/workspace_metrics.py +85 -0
  9. package/autonomy/lib/worktree_prep.py +45 -3
  10. package/autonomy/loki +62 -3
  11. package/autonomy/run.sh +95 -6
  12. package/autonomy/verify.sh +87 -20
  13. package/dashboard/__init__.py +1 -1
  14. package/dashboard/api_operator.py +27 -0
  15. package/dashboard/api_runs.py +52 -0
  16. package/dashboard/api_workspaces.py +140 -0
  17. package/dashboard/audit.py +8 -2
  18. package/dashboard/server.py +42 -21
  19. package/dashboard/static/index.html +4 -3
  20. package/dashboard/static/proofs.html +5 -0
  21. package/dashboard/static/start.html +32 -1
  22. package/docs/INSTALLATION.md +1 -1
  23. package/docs/SIGNED-RECEIPTS.md +39 -0
  24. package/docs/UNREACHABLE-TESTS.md +1 -1
  25. package/docs/WORKSPACES.md +14 -0
  26. package/docs/audit-logging.md +13 -0
  27. package/docs/dev/release-checklist.md +5 -0
  28. package/docs/exit-codes.md +44 -9
  29. package/docs/v10/BOARD.md +73 -25
  30. package/docs/v10/DECISIONS.md +22 -0
  31. package/docs/v10/GUIDE.md +1 -1
  32. package/docs/v10/PROGRESS.md +67 -0
  33. package/loki-ts/dist/loki.js +545 -536
  34. package/mcp/__init__.py +1 -1
  35. package/mcp/tests/test_v10_tools.py +41 -0
  36. package/mcp/v10_tools.py +13 -0
  37. package/package.json +1 -1
  38. package/packages/control-plane/dist/server.js +14 -1
  39. package/packages/control-plane/drizzle/meta/0001_snapshot.json +364 -0
  40. package/packages/control-plane/src/server/app.ts +7 -1
  41. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  42. package/web-app/dist/assets/{AdminPage-BnPQzCoC.js → AdminPage-CVEdde3t.js} +1 -1
  43. package/web-app/dist/assets/{Avatar-DBpPssag.js → Avatar-D1r8n_Da.js} +1 -1
  44. package/web-app/dist/assets/{Badge-CxlVZMg8.js → Badge-BAPyJ97M.js} +1 -1
  45. package/web-app/dist/assets/{Button-BXMmAcji.js → Button-C-GCctTq.js} +1 -1
  46. package/web-app/dist/assets/{CockpitPage-Birxgrlx.js → CockpitPage-CV9MhyuV.js} +1 -1
  47. package/web-app/dist/assets/{ComparePage-BqqDXwLS.js → ComparePage-D1R5Lxmy.js} +1 -1
  48. package/web-app/dist/assets/{ErrorBoundary-C0u7Mi2z.js → ErrorBoundary-CFvJx4vV.js} +1 -1
  49. package/web-app/dist/assets/{EvidenceReceiptPanel-Cp-3O8W8.js → EvidenceReceiptPanel-9I7dfPz0.js} +1 -1
  50. package/web-app/dist/assets/{GitHubIssuesPanel-B_W8k95U.js → GitHubIssuesPanel-8YH6WkTh.js} +1 -1
  51. package/web-app/dist/assets/{GitHubPRsPanel-BqvyZ1r1.js → GitHubPRsPanel-BZvEHBTF.js} +1 -1
  52. package/web-app/dist/assets/{HomePage-BQ7osrwA.js → HomePage-DhjWTo8R.js} +2 -2
  53. package/web-app/dist/assets/{LoginPage-B2aHa3wI.js → LoginPage-BwNGyWxP.js} +1 -1
  54. package/web-app/dist/assets/{MagicPage-DwIxsJzJ.js → MagicPage-Dg9z7deq.js} +1 -1
  55. package/web-app/dist/assets/{MetricsPage-CRhnEmp8.js → MetricsPage-DLF1F6XC.js} +2 -2
  56. package/web-app/dist/assets/{NotFoundPage-ByEZNNyC.js → NotFoundPage-CZClMI3s.js} +1 -1
  57. package/web-app/dist/assets/{ProjectPage-LSU-iiUP.js → ProjectPage-CnAvyHDJ.js} +67 -67
  58. package/web-app/dist/assets/{ProjectsPage-lBbt1xYZ.js → ProjectsPage-D33VGyVL.js} +1 -1
  59. package/web-app/dist/assets/{SettingsPage-DOdfwqdK.js → SettingsPage-DQtlpXPe.js} +1 -1
  60. package/web-app/dist/assets/{ShowcasePage-CjsS6oSQ.js → ShowcasePage-CAitzJlY.js} +1 -1
  61. package/web-app/dist/assets/{SystemSettingsPage-O-HGI2S_.js → SystemSettingsPage-CV49UjwU.js} +1 -1
  62. package/web-app/dist/assets/{TeamsPage-Mt8EP17C.js → TeamsPage-5EKrbkN3.js} +1 -1
  63. package/web-app/dist/assets/{TemplatesPage-CVVy4Iz7.js → TemplatesPage-BRsrm00y.js} +1 -1
  64. package/web-app/dist/assets/{TerminalOutput-XUgC02sX.js → TerminalOutput-DgEnMwnQ.js} +1 -1
  65. package/web-app/dist/assets/{activity-DU5SJfx3.js → activity-hXTYu4Zb.js} +1 -1
  66. package/web-app/dist/assets/{bell-C5YepG66.js → bell-D5QkmMjg.js} +1 -1
  67. package/web-app/dist/assets/{bot-BRExedZU.js → bot-CGOzPami.js} +1 -1
  68. package/web-app/dist/assets/{check-DsyIFRaT.js → check-HxPvzPXm.js} +1 -1
  69. package/web-app/dist/assets/{chevron-left-DdK1iGTs.js → chevron-left-D7HYUp4T.js} +1 -1
  70. package/web-app/dist/assets/{circle-alert-C_4QlfMh.js → circle-alert-BdufMh8P.js} +1 -1
  71. package/web-app/dist/assets/{clock-BY9MJ-Yl.js → clock-DMcS0xjR.js} +1 -1
  72. package/web-app/dist/assets/{cloud-Cl1jOslN.js → cloud-Bt-0bh9m.js} +1 -1
  73. package/web-app/dist/assets/{code-xml-BRRBRbeE.js → code-xml-DRtDKt4_.js} +1 -1
  74. package/web-app/dist/assets/{database-BtZdEQTc.js → database-B0ChKZQ4.js} +1 -1
  75. package/web-app/dist/assets/{dollar-sign-BqQ0fBnq.js → dollar-sign-st_8sF_M.js} +1 -1
  76. package/web-app/dist/assets/{file-code-corner-Q9YsC7IC.js → file-code-corner-UdiLz5N6.js} +1 -1
  77. package/web-app/dist/assets/{file-plus-GrZYy7o7.js → file-plus-De1dimur.js} +1 -1
  78. package/web-app/dist/assets/{globe-CNKuJhK7.js → globe-rrbXmytb.js} +1 -1
  79. package/web-app/dist/assets/{hammer-Cauj0_Mr.js → hammer-CBtNZi-S.js} +1 -1
  80. package/web-app/dist/assets/{index-4c5tMfoO.js → index-D3YvZaMV.js} +3 -3
  81. package/web-app/dist/assets/{layers-IEQ3AZ0i.js → layers-BmthCdkO.js} +1 -1
  82. package/web-app/dist/assets/{loader-circle-DkC6SXf0.js → loader-circle-C_sXFJUa.js} +1 -1
  83. package/web-app/dist/assets/{lock-Db0jlkGG.js → lock-DT6V3D97.js} +1 -1
  84. package/web-app/dist/assets/{package-BkagJWcN.js → package-D86K1oU6.js} +1 -1
  85. package/web-app/dist/assets/{plus-BH3dF7wB.js → plus-Dwkssum8.js} +1 -1
  86. package/web-app/dist/assets/{refresh-cw-3r-v6CaV.js → refresh-cw-DhRlqf1F.js} +1 -1
  87. package/web-app/dist/assets/{rotate-ccw-DuqsPonD.js → rotate-ccw-EvA5ZJVL.js} +1 -1
  88. package/web-app/dist/assets/{scroll-text-OaKgXAF3.js → scroll-text-D4ABo43E.js} +1 -1
  89. package/web-app/dist/assets/{server-283wBoqS.js → server-BAAdr_fD.js} +1 -1
  90. package/web-app/dist/assets/{shield-alert-Cfka1gvI.js → shield-alert-DJPLy9t8.js} +1 -1
  91. package/web-app/dist/assets/{thumbs-up-Be3PhCnV.js → thumbs-up-D07JqmoS.js} +1 -1
  92. package/web-app/dist/assets/{trash-2-BDKSlpn4.js → trash-2-BJYwlHzl.js} +1 -1
  93. package/web-app/dist/assets/{trending-down-SkioL8cr.js → trending-down-Bo8txoOw.js} +1 -1
  94. package/web-app/dist/assets/{trending-up-CzCzHgB7.js → trending-up-Bb13tkln.js} +1 -1
  95. package/web-app/dist/assets/{upload-DlhOQ6b3.js → upload-D60DiVNZ.js} +1 -1
  96. package/web-app/dist/assets/{usePolling-Z7LLnCrf.js → usePolling-DJmgDiio.js} +1 -1
  97. package/web-app/dist/assets/{user-BKegc_v2.js → user-BmXCbSJm.js} +1 -1
  98. package/web-app/dist/index.html +1 -1
package/mcp/__init__.py CHANGED
@@ -75,4 +75,4 @@ try:
75
75
  except ImportError:
76
76
  __all__ = ['mcp']
77
77
 
78
- __version__ = '10.6.11'
78
+ __version__ = '10.6.13'
@@ -61,6 +61,47 @@ class RunTests(unittest.TestCase):
61
61
  self.assertTrue(r["log_path"].startswith(os.path.realpath(repo)))
62
62
 
63
63
 
64
+ class EarlyExitTests(unittest.TestCase):
65
+ def _stub(self, d, body):
66
+ path = os.path.join(d, "loki-stub")
67
+ with open(path, "w") as f:
68
+ f.write("#!/bin/sh\n" + body + "\n")
69
+ os.chmod(path, 0o755)
70
+ return path
71
+
72
+ def test_early_crash_reports_exit_code_and_log_tail(self):
73
+ with tempfile.TemporaryDirectory() as repo, tempfile.TemporaryDirectory() as bindir:
74
+ stub = self._stub(bindir, "echo boom\nexit 2")
75
+ with mock.patch.object(v10_tools, "LOKI_BIN", stub), \
76
+ mock.patch.object(v10_tools, "V10_RUN_ID_WAIT_S", 3):
77
+ r = v10_tools.v10_run("fix it", repo, _ok)
78
+ self.assertIn("exited 2", r.get("error", ""))
79
+ self.assertIn("boom", r["error"])
80
+ self.assertIsNone(r.get("run_id"))
81
+
82
+ def test_log_tail_is_last_20_lines(self):
83
+ with tempfile.TemporaryDirectory() as repo, tempfile.TemporaryDirectory() as bindir:
84
+ stub = self._stub(bindir, "i=1; while [ $i -le 30 ]; do echo line$i; i=$((i+1)); done; exit 3")
85
+ with mock.patch.object(v10_tools, "LOKI_BIN", stub), \
86
+ mock.patch.object(v10_tools, "V10_RUN_ID_WAIT_S", 3):
87
+ r = v10_tools.v10_run("fix it", repo, _ok)
88
+ self.assertIn("exited 3", r["error"])
89
+ self.assertIn("line30", r["error"])
90
+ self.assertIn("line11", r["error"])
91
+ self.assertNotIn("line10\n", r["error"] + "\n")
92
+
93
+ def test_still_running_without_run_id_keeps_shape(self):
94
+ with tempfile.TemporaryDirectory() as repo, tempfile.TemporaryDirectory() as bindir:
95
+ stub = self._stub(bindir, "sleep 2")
96
+ with mock.patch.object(v10_tools, "LOKI_BIN", stub), \
97
+ mock.patch.object(v10_tools, "V10_RUN_ID_WAIT_S", 0.3):
98
+ r = v10_tools.v10_run("fix it", repo, _ok)
99
+ self.assertNotIn("error", r)
100
+ self.assertIsNone(r["run_id"])
101
+ self.assertIn("pid", r)
102
+ self.assertIn("log_path", r)
103
+
104
+
64
105
  class EnvTests(unittest.TestCase):
65
106
  def test_env_forces_headless_and_drops_secrets(self):
66
107
  secrets = {"LOKI_CONTROL_TOKEN": "t", "SLACK_BOT_TOKEN": "xoxb-1", "SLACK_SIGNING_SECRET": "s",
package/mcp/v10_tools.py CHANGED
@@ -38,6 +38,15 @@ def _run_ids(repo: str) -> List[str]:
38
38
  return sorted(n for n in os.listdir(d) if os.path.isdir(os.path.join(d, n)))
39
39
 
40
40
 
41
+ def _log_tail(path: str, lines: int = 20) -> str:
42
+ try:
43
+ with open(path, "rb") as f:
44
+ data = f.read()[-65536:]
45
+ except OSError as e:
46
+ return f"(log unreadable: {e})"
47
+ return "\n".join(data.decode("utf-8", "replace").splitlines()[-lines:])
48
+
49
+
41
50
  def v10_run(ref: str, repo_path: str, validate: Callable[[str], str]) -> dict:
42
51
  if not isinstance(ref, str) or not ref.strip() or ref.startswith("-") or "\x00" in ref:
43
52
  return {"error": "invalid ref: must be a non-empty task or issue reference that does not start with '-'"}
@@ -71,6 +80,10 @@ def v10_run(ref: str, repo_path: str, validate: Callable[[str], str]) -> dict:
71
80
  if new:
72
81
  run_id = new[-1]
73
82
  break
83
+ rc = proc.poll()
84
+ if isinstance(rc, int):
85
+ return {"error": f"loki exited {rc} before a run started. Log tail:\n{_log_tail(log_path)}",
86
+ "run_id": None, "pid": proc.pid, "log_path": log_path}
74
87
  if time.time() >= deadline:
75
88
  break
76
89
  time.sleep(0.1)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "loki-mode",
3
3
  "mcpName": "io.github.asklokesh/loki-mode",
4
- "version": "10.6.11",
4
+ "version": "10.6.13",
5
5
  "description": "An autonomous software factory that knows what it is supposed to deliver, and proves it did.",
6
6
  "keywords": [
7
7
  "agent",
@@ -5946,6 +5946,14 @@ function createApp(opts) {
5946
5946
  app.use("*", hostGuard());
5947
5947
  if (opts.token)
5948
5948
  app.use("/v1/*", tokenGuard(opts.token));
5949
+ app.use("*", async (c, next) => {
5950
+ try {
5951
+ decodeURIComponent(new URL(c.req.url).pathname);
5952
+ } catch {
5953
+ return c.json({ error: "malformed URL encoding" }, 400);
5954
+ }
5955
+ await next();
5956
+ });
5949
5957
  app.get("/health", (c) => c.json({ service: "loki-control", pid: process.pid, install_path: import.meta.dir }));
5950
5958
  app.get("/ready", (c) => {
5951
5959
  try {
@@ -6004,7 +6012,12 @@ function createApp(opts) {
6004
6012
  return i < 0 ? c.json({ error: "id must be source:run" }, 404) : detail(c, id.slice(0, i), id.slice(i + 1));
6005
6013
  });
6006
6014
  app.get("*", (c) => {
6007
- const p = decodeURIComponent(new URL(c.req.url).pathname);
6015
+ let p;
6016
+ try {
6017
+ p = decodeURIComponent(new URL(c.req.url).pathname);
6018
+ } catch {
6019
+ return c.json({ error: "malformed URL encoding" }, 400);
6020
+ }
6008
6021
  if (!uiDir || p.startsWith("/v1/"))
6009
6022
  return c.json({ error: "not found" }, 404);
6010
6023
  const root = resolve(uiDir);
@@ -0,0 +1,364 @@
1
+ {
2
+ "version": "6",
3
+ "dialect": "sqlite",
4
+ "id": "588cdb28-e5be-4390-b68d-79b4c8385191",
5
+ "prevId": "32fa548f-e5a1-47e9-9a54-1be1f970a2de",
6
+ "tables": {
7
+ "events": {
8
+ "name": "events",
9
+ "columns": {
10
+ "source_id": {
11
+ "name": "source_id",
12
+ "type": "text",
13
+ "primaryKey": false,
14
+ "notNull": true,
15
+ "autoincrement": false
16
+ },
17
+ "run_id": {
18
+ "name": "run_id",
19
+ "type": "text",
20
+ "primaryKey": false,
21
+ "notNull": true,
22
+ "autoincrement": false
23
+ },
24
+ "seq": {
25
+ "name": "seq",
26
+ "type": "integer",
27
+ "primaryKey": false,
28
+ "notNull": true,
29
+ "autoincrement": false
30
+ },
31
+ "ts": {
32
+ "name": "ts",
33
+ "type": "text",
34
+ "primaryKey": false,
35
+ "notNull": true,
36
+ "autoincrement": false
37
+ },
38
+ "type": {
39
+ "name": "type",
40
+ "type": "text",
41
+ "primaryKey": false,
42
+ "notNull": true,
43
+ "autoincrement": false
44
+ },
45
+ "stage": {
46
+ "name": "stage",
47
+ "type": "text",
48
+ "primaryKey": false,
49
+ "notNull": false,
50
+ "autoincrement": false
51
+ },
52
+ "data": {
53
+ "name": "data",
54
+ "type": "text",
55
+ "primaryKey": false,
56
+ "notNull": true,
57
+ "autoincrement": false
58
+ },
59
+ "line_sha256": {
60
+ "name": "line_sha256",
61
+ "type": "text",
62
+ "primaryKey": false,
63
+ "notNull": true,
64
+ "autoincrement": false
65
+ },
66
+ "received_at": {
67
+ "name": "received_at",
68
+ "type": "text",
69
+ "primaryKey": false,
70
+ "notNull": true,
71
+ "autoincrement": false
72
+ }
73
+ },
74
+ "indexes": {
75
+ "events_run_seq": {
76
+ "name": "events_run_seq",
77
+ "columns": [
78
+ "run_id",
79
+ "seq"
80
+ ],
81
+ "isUnique": false
82
+ },
83
+ "events_type_ts": {
84
+ "name": "events_type_ts",
85
+ "columns": [
86
+ "type",
87
+ "ts"
88
+ ],
89
+ "isUnique": false
90
+ }
91
+ },
92
+ "foreignKeys": {},
93
+ "compositePrimaryKeys": {
94
+ "events_source_id_run_id_seq_pk": {
95
+ "columns": [
96
+ "source_id",
97
+ "run_id",
98
+ "seq"
99
+ ],
100
+ "name": "events_source_id_run_id_seq_pk"
101
+ }
102
+ },
103
+ "uniqueConstraints": {},
104
+ "checkConstraints": {}
105
+ },
106
+ "runs": {
107
+ "name": "runs",
108
+ "columns": {
109
+ "source_id": {
110
+ "name": "source_id",
111
+ "type": "text",
112
+ "primaryKey": false,
113
+ "notNull": true,
114
+ "autoincrement": false
115
+ },
116
+ "run_id": {
117
+ "name": "run_id",
118
+ "type": "text",
119
+ "primaryKey": false,
120
+ "notNull": true,
121
+ "autoincrement": false
122
+ },
123
+ "origin_repo": {
124
+ "name": "origin_repo",
125
+ "type": "text",
126
+ "primaryKey": false,
127
+ "notNull": false,
128
+ "autoincrement": false
129
+ },
130
+ "issue_ref": {
131
+ "name": "issue_ref",
132
+ "type": "text",
133
+ "primaryKey": false,
134
+ "notNull": false,
135
+ "autoincrement": false
136
+ },
137
+ "task_source": {
138
+ "name": "task_source",
139
+ "type": "text",
140
+ "primaryKey": false,
141
+ "notNull": false,
142
+ "autoincrement": false
143
+ },
144
+ "provider": {
145
+ "name": "provider",
146
+ "type": "text",
147
+ "primaryKey": false,
148
+ "notNull": false,
149
+ "autoincrement": false
150
+ },
151
+ "model": {
152
+ "name": "model",
153
+ "type": "text",
154
+ "primaryKey": false,
155
+ "notNull": false,
156
+ "autoincrement": false
157
+ },
158
+ "started_at": {
159
+ "name": "started_at",
160
+ "type": "text",
161
+ "primaryKey": false,
162
+ "notNull": false,
163
+ "autoincrement": false
164
+ },
165
+ "ended_at": {
166
+ "name": "ended_at",
167
+ "type": "text",
168
+ "primaryKey": false,
169
+ "notNull": false,
170
+ "autoincrement": false
171
+ },
172
+ "verdict": {
173
+ "name": "verdict",
174
+ "type": "text",
175
+ "primaryKey": false,
176
+ "notNull": false,
177
+ "autoincrement": false
178
+ },
179
+ "pr_url": {
180
+ "name": "pr_url",
181
+ "type": "text",
182
+ "primaryKey": false,
183
+ "notNull": false,
184
+ "autoincrement": false
185
+ },
186
+ "pr_draft": {
187
+ "name": "pr_draft",
188
+ "type": "integer",
189
+ "primaryKey": false,
190
+ "notNull": false,
191
+ "autoincrement": false
192
+ },
193
+ "cost_usd": {
194
+ "name": "cost_usd",
195
+ "type": "real",
196
+ "primaryKey": false,
197
+ "notNull": false,
198
+ "autoincrement": false
199
+ },
200
+ "partial_usd": {
201
+ "name": "partial_usd",
202
+ "type": "real",
203
+ "primaryKey": false,
204
+ "notNull": true,
205
+ "autoincrement": false
206
+ },
207
+ "measured_sessions": {
208
+ "name": "measured_sessions",
209
+ "type": "integer",
210
+ "primaryKey": false,
211
+ "notNull": true,
212
+ "autoincrement": false
213
+ },
214
+ "total_sessions": {
215
+ "name": "total_sessions",
216
+ "type": "integer",
217
+ "primaryKey": false,
218
+ "notNull": true,
219
+ "autoincrement": false
220
+ },
221
+ "input_tokens": {
222
+ "name": "input_tokens",
223
+ "type": "integer",
224
+ "primaryKey": false,
225
+ "notNull": true,
226
+ "autoincrement": false
227
+ },
228
+ "output_tokens": {
229
+ "name": "output_tokens",
230
+ "type": "integer",
231
+ "primaryKey": false,
232
+ "notNull": true,
233
+ "autoincrement": false
234
+ },
235
+ "wall_s": {
236
+ "name": "wall_s",
237
+ "type": "real",
238
+ "primaryKey": false,
239
+ "notNull": false,
240
+ "autoincrement": false
241
+ },
242
+ "last_seq": {
243
+ "name": "last_seq",
244
+ "type": "integer",
245
+ "primaryKey": false,
246
+ "notNull": true,
247
+ "autoincrement": false
248
+ },
249
+ "last_event_at": {
250
+ "name": "last_event_at",
251
+ "type": "text",
252
+ "primaryKey": false,
253
+ "notNull": false,
254
+ "autoincrement": false
255
+ },
256
+ "tampered": {
257
+ "name": "tampered",
258
+ "type": "integer",
259
+ "primaryKey": false,
260
+ "notNull": true,
261
+ "autoincrement": false
262
+ },
263
+ "conflict": {
264
+ "name": "conflict",
265
+ "type": "integer",
266
+ "primaryKey": false,
267
+ "notNull": true,
268
+ "autoincrement": false,
269
+ "default": 0
270
+ },
271
+ "group_id": {
272
+ "name": "group_id",
273
+ "type": "text",
274
+ "primaryKey": false,
275
+ "notNull": false,
276
+ "autoincrement": false
277
+ },
278
+ "unit_id": {
279
+ "name": "unit_id",
280
+ "type": "text",
281
+ "primaryKey": false,
282
+ "notNull": false,
283
+ "autoincrement": false
284
+ }
285
+ },
286
+ "indexes": {
287
+ "runs_started": {
288
+ "name": "runs_started",
289
+ "columns": [
290
+ "started_at"
291
+ ],
292
+ "isUnique": false
293
+ },
294
+ "runs_verdict": {
295
+ "name": "runs_verdict",
296
+ "columns": [
297
+ "verdict"
298
+ ],
299
+ "isUnique": false
300
+ },
301
+ "runs_group": {
302
+ "name": "runs_group",
303
+ "columns": [
304
+ "group_id"
305
+ ],
306
+ "isUnique": false
307
+ }
308
+ },
309
+ "foreignKeys": {},
310
+ "compositePrimaryKeys": {
311
+ "runs_source_id_run_id_pk": {
312
+ "columns": [
313
+ "source_id",
314
+ "run_id"
315
+ ],
316
+ "name": "runs_source_id_run_id_pk"
317
+ }
318
+ },
319
+ "uniqueConstraints": {},
320
+ "checkConstraints": {}
321
+ },
322
+ "sources": {
323
+ "name": "sources",
324
+ "columns": {
325
+ "id": {
326
+ "name": "id",
327
+ "type": "text",
328
+ "primaryKey": true,
329
+ "notNull": true,
330
+ "autoincrement": false
331
+ },
332
+ "first_seen": {
333
+ "name": "first_seen",
334
+ "type": "text",
335
+ "primaryKey": false,
336
+ "notNull": true,
337
+ "autoincrement": false
338
+ },
339
+ "last_seen": {
340
+ "name": "last_seen",
341
+ "type": "text",
342
+ "primaryKey": false,
343
+ "notNull": true,
344
+ "autoincrement": false
345
+ }
346
+ },
347
+ "indexes": {},
348
+ "foreignKeys": {},
349
+ "compositePrimaryKeys": {},
350
+ "uniqueConstraints": {},
351
+ "checkConstraints": {}
352
+ }
353
+ },
354
+ "views": {},
355
+ "enums": {},
356
+ "_meta": {
357
+ "schemas": {},
358
+ "tables": {},
359
+ "columns": {}
360
+ },
361
+ "internal": {
362
+ "indexes": {}
363
+ }
364
+ }
@@ -22,6 +22,11 @@ export function createApp(opts: { dbPath: string; uiDir?: string; answerDir?: st
22
22
  // token: bearer required on /v1/*. loopbackOnly: reject a non-loopback Host (DNS rebinding). Both off by default; serve.ts sets them from env.
23
23
  if (opts.loopbackOnly) app.use("*", hostGuard());
24
24
  if (opts.token) app.use("/v1/*", tokenGuard(opts.token));
25
+ // Malformed percent-encoding is a client error on every route, not a 500 (or a misleading 404).
26
+ app.use("*", async (c, next) => {
27
+ try { decodeURIComponent(new URL(c.req.url).pathname); } catch { return c.json({ error: "malformed URL encoding" }, 400); }
28
+ await next();
29
+ });
25
30
 
26
31
  app.get("/health", (c) => c.json({ service: "loki-control", pid: process.pid, install_path: import.meta.dir }));
27
32
  app.get("/ready", (c) => {
@@ -70,7 +75,8 @@ export function createApp(opts: { dbPath: string; uiDir?: string; answerDir?: st
70
75
 
71
76
  // Static UI with SPA fallback. Registered last so /v1, /health and /ready always win.
72
77
  app.get("*", (c) => {
73
- const p = decodeURIComponent(new URL(c.req.url).pathname);
78
+ let p: string;
79
+ try { p = decodeURIComponent(new URL(c.req.url).pathname); } catch { return c.json({ error: "malformed URL encoding" }, 400); }
74
80
  if (!uiDir || p.startsWith("/v1/")) return c.json({ error: "not found" }, 404);
75
81
  const root = resolve(uiDir);
76
82
  const f = resolve(root, `.${p}`);
@@ -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": "10.6.11",
5
+ "version": "10.6.13",
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",
@@ -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-4c5tMfoO.js";import{B as C}from"./Button-BXMmAcji.js";import{T as $e}from"./trending-up-CzCzHgB7.js";import{C as z}from"./clock-BY9MJ-Yl.js";import{A as Be,U as ue,M as De}from"./Avatar-DBpPssag.js";import{A as J}from"./activity-DU5SJfx3.js";import{R as pe}from"./refresh-cw-3r-v6CaV.js";import{G as ze}from"./globe-CNKuJhK7.js";import{C as Le}from"./chevron-left-DdK1iGTs.js";import{C as je}from"./check-DsyIFRaT.js";import{D as Y}from"./dollar-sign-BqQ0fBnq.js";import{U as Te}from"./user-BKegc_v2.js";import{D as Me}from"./database-BtZdEQTc.js";import{L as Ue}from"./lock-Db0jlkGG.js";import{S as Re}from"./scroll-text-OaKgXAF3.js";import{S as Ie}from"./shield-alert-Cfka1gvI.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-D3YvZaMV.js";import{B as C}from"./Button-C-GCctTq.js";import{T as $e}from"./trending-up-Bb13tkln.js";import{C as z}from"./clock-DMcS0xjR.js";import{A as Be,U as ue,M as De}from"./Avatar-D1r8n_Da.js";import{A as J}from"./activity-hXTYu4Zb.js";import{R as pe}from"./refresh-cw-DhRlqf1F.js";import{G as ze}from"./globe-rrbXmytb.js";import{C as Le}from"./chevron-left-D7HYUp4T.js";import{C as je}from"./check-HxPvzPXm.js";import{D as Y}from"./dollar-sign-st_8sF_M.js";import{U as Te}from"./user-BmXCbSJm.js";import{D as Me}from"./database-B0ChKZQ4.js";import{L as Ue}from"./lock-DT6V3D97.js";import{S as Re}from"./scroll-text-D4ABo43E.js";import{S as Ie}from"./shield-alert-DJPLy9t8.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-4c5tMfoO.js";/**
1
+ import{c as l,j as s}from"./index-D3YvZaMV.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-4c5tMfoO.js";import{C as r}from"./clock-BY9MJ-Yl.js";import{C as a}from"./circle-alert-C_4QlfMh.js";import{C as l}from"./check-DsyIFRaT.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-D3YvZaMV.js";import{C as r}from"./clock-DMcS0xjR.js";import{C as a}from"./circle-alert-BdufMh8P.js";import{C as l}from"./check-HxPvzPXm.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-4c5tMfoO.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-D3YvZaMV.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-4c5tMfoO.js";import{P as K,S as He,E as Be}from"./ErrorBoundary-C0u7Mi2z.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-Be3PhCnV.js";import{C as oe}from"./check-DsyIFRaT.js";import{C as Ye}from"./clock-BY9MJ-Yl.js";import{D as Xe}from"./dollar-sign-BqQ0fBnq.js";import{U as Ze}from"./upload-DlhOQ6b3.js";import{L as V}from"./loader-circle-DkC6SXf0.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-D3YvZaMV.js";import{P as K,S as He,E as Be}from"./ErrorBoundary-CFvJx4vV.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-D07JqmoS.js";import{C as oe}from"./check-HxPvzPXm.js";import{C as Ye}from"./clock-DMcS0xjR.js";import{D as Xe}from"./dollar-sign-st_8sF_M.js";import{U as Ze}from"./upload-D60DiVNZ.js";import{L as V}from"./loader-circle-C_sXFJUa.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{r as o,j as e}from"./index-4c5tMfoO.js";function f({value:r,prefix:t="",suffix:s=""}){const[u,n]=o.useState(0),c=o.useRef(0);return o.useEffect(()=>{const a=c.current,h=r-a;if(h===0)return;const i=600,d=performance.now();function l(b){const v=b-d,m=Math.min(v/i,1),N=1-Math.pow(1-m,3),p=Math.round(a+h*N);n(p),c.current=p,m<1&&requestAnimationFrame(l)}requestAnimationFrame(l)},[r]),e.jsxs("span",{className:"tabular-nums",children:[t,u.toLocaleString(),s]})}function g(){const[r,t]=o.useState(5),[s,u]=o.useState(30),[n,c]=o.useState(75),a=.4,i=Math.round(r*s*a*4.33),d=Math.round(i*n);return e.jsxs("div",{className:"bg-white border border-[#ECEAE3] rounded-xl p-6 shadow-sm",children:[e.jsx("h3",{className:"text-lg font-bold text-[#36342E] mb-1",children:"ROI Calculator"}),e.jsx("p",{className:"text-sm text-[#6B6960] mb-6",children:"Estimate how much your team could save with autonomous development."}),e.jsxs("div",{className:"space-y-5",children:[e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("label",{className:"text-sm font-medium text-[#36342E]",children:"Team size"}),e.jsxs("span",{className:"text-sm font-semibold text-[#553DE9] tabular-nums",children:[r," developers"]})]}),e.jsx("input",{type:"range",min:1,max:50,value:r,onChange:l=>t(Number(l.target.value)),className:"w-full h-2 bg-[#ECEAE3] rounded-full appearance-none cursor-pointer accent-[#553DE9]"}),e.jsxs("div",{className:"flex justify-between text-xs text-[#939084] mt-1",children:[e.jsx("span",{children:"1"}),e.jsx("span",{children:"50"})]})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("label",{className:"text-sm font-medium text-[#36342E]",children:"Hours coding per week"}),e.jsxs("span",{className:"text-sm font-semibold text-[#553DE9] tabular-nums",children:[s,"h"]})]}),e.jsx("input",{type:"range",min:5,max:60,value:s,onChange:l=>u(Number(l.target.value)),className:"w-full h-2 bg-[#ECEAE3] rounded-full appearance-none cursor-pointer accent-[#553DE9]"}),e.jsxs("div",{className:"flex justify-between text-xs text-[#939084] mt-1",children:[e.jsx("span",{children:"5h"}),e.jsx("span",{children:"60h"})]})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("label",{className:"text-sm font-medium text-[#36342E]",children:"Average hourly rate"}),e.jsxs("span",{className:"text-sm font-semibold text-[#553DE9] tabular-nums",children:["$",n,"/hr"]})]}),e.jsx("input",{type:"range",min:25,max:250,step:5,value:n,onChange:l=>c(Number(l.target.value)),className:"w-full h-2 bg-[#ECEAE3] rounded-full appearance-none cursor-pointer accent-[#553DE9]"}),e.jsxs("div",{className:"flex justify-between text-xs text-[#939084] mt-1",children:[e.jsx("span",{children:"$25"}),e.jsx("span",{children:"$250"})]})]})]}),e.jsxs("div",{className:"mt-6 pt-6 border-t border-[#ECEAE3]",children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 text-center",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-2xl font-bold text-[#553DE9]",children:e.jsx(f,{value:i,suffix:"h"})}),e.jsx("div",{className:"text-xs text-[#6B6960] mt-1",children:"Est. hours saved / month"})]}),e.jsxs("div",{children:[e.jsx("div",{className:"text-2xl font-bold text-[#1FC5A8]",children:e.jsx(f,{value:d,prefix:"$"})}),e.jsx("div",{className:"text-xs text-[#6B6960] mt-1",children:"Est. cost saved / month"})]})]}),e.jsxs("div",{className:"mt-4 p-3 rounded-lg bg-[#553DE9]/5 border border-[#553DE9]/10",children:[e.jsxs("p",{className:"text-sm text-[#36342E] text-center",children:["If Loki Mode saved ",Math.round(a*100),"% of coding time, your team could save"," ",e.jsxs("span",{className:"font-bold text-[#553DE9]",children:[i.toLocaleString()," hours"]})," ","and"," ",e.jsxs("span",{className:"font-bold text-[#1FC5A8]",children:["$",d.toLocaleString()]})," ","per month."]}),e.jsxs("p",{className:"mt-2 text-xs text-[#6B6960] text-center",children:["The ",Math.round(a*100),"% is an assumption, not a measurement. Model and API spend is not included."]})]})]})]})}const j=[{feature:"Claude (Opus/Sonnet/Haiku)",category:"AI Models",lokiMode:!0,boltNew:!1,replit:!1,lovable:!0},{feature:"GPT-4o / Codex",category:"AI Models",lokiMode:!0,boltNew:!0,replit:!0,lovable:!1},{feature:"5-provider auto-failover",category:"AI Models",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"8 automated quality gates",category:"Quality",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Blind 3-reviewer code review",category:"Quality",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Anti-sycophancy checks",category:"Quality",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Automated test generation",category:"Quality",lokiMode:!0,boltNew:!1,replit:"Partial",lovable:!1},{feature:"Docker container generation",category:"Deployment",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"CI/CD pipeline generation",category:"Deployment",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"One-click cloud deploy",category:"Deployment",lokiMode:"Planned",boltNew:!0,replit:!0,lovable:!0},{feature:"Self-hosted (your keys)",category:"Cost",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Free tier available",category:"Cost",lokiMode:"Source-available (BUSL-1.1)",boltNew:!0,replit:!0,lovable:!0},{feature:"No vendor lock-in",category:"Cost",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"SSO / OIDC authentication",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"RBAC access control",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"Audit logging",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"Air-gapped deployment",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1}];function y(){return e.jsx("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",className:"text-[#1FC5A8]",children:e.jsx("path",{d:"M20 6L9 17l-5-5",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round"})})}function w(){return e.jsx("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",className:"text-[#C45B5B]/40",children:e.jsx("path",{d:"M18 6L6 18M6 6l12 12",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})}function x({value:r}){return typeof r=="string"?e.jsx("span",{className:"text-xs text-[#6B6960] font-medium",children:r}):r?e.jsx(y,{}):e.jsx(w,{})}const k=[{title:"Full Autonomy",description:"Describe what you want. Loki Mode handles planning, coding, testing, and verification automatically.",icon:e.jsx("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#553DE9]",children:e.jsx("path",{d:"M13 2L3 14h9l-1 8 10-12h-9l1-8z",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})},{title:"Production Quality",description:"8 quality gates, blind code review, and anti-sycophancy checks ensure code that actually works.",icon:e.jsx("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#1FC5A8]",children:e.jsx("path",{d:"M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})},{title:"Your Infrastructure",description:"Self-hosted, source-available (BUSL-1.1), no data leaves your network. Use your own API keys across 5 providers.",icon:e.jsxs("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#2F71E3]",children:[e.jsx("rect",{x:"2",y:"2",width:"20",height:"8",rx:"2",stroke:"currentColor",strokeWidth:"2"}),e.jsx("rect",{x:"2",y:"14",width:"20",height:"8",rx:"2",stroke:"currentColor",strokeWidth:"2"}),e.jsx("circle",{cx:"6",cy:"6",r:"1",fill:"currentColor"}),e.jsx("circle",{cx:"6",cy:"18",r:"1",fill:"currentColor"})]})},{title:"Built for Teams",description:"Enterprise auth, RBAC, audit logs, and parallel workflows for teams of any size.",icon:e.jsxs("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#E93D82]",children:[e.jsx("path",{d:"M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}),e.jsx("circle",{cx:"9",cy:"7",r:"4",stroke:"currentColor",strokeWidth:"2"}),e.jsx("path",{d:"M23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})]})}];function C(){const r=[...new Set(j.map(t=>t.category))];return e.jsx("div",{className:"min-h-screen bg-[#FAF9F6]",children:e.jsxs("div",{className:"max-w-6xl mx-auto px-6 py-8",children:[e.jsxs("div",{className:"mb-8",children:[e.jsx("h1",{className:"text-2xl font-bold text-[#36342E]",children:"Compare"}),e.jsx("p",{className:"text-[#6B6960] mt-1",children:"See how Loki Mode stacks up against other AI development platforms."})]}),e.jsx("div",{className:"bg-white border border-[#ECEAE3] rounded-xl overflow-hidden shadow-sm",children:e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-[#ECEAE3]",children:[e.jsx("th",{className:"text-left px-4 py-3 font-medium text-[#6B6960] w-[240px]",children:"Feature"}),e.jsx("th",{className:"text-center px-4 py-3 font-bold text-[#553DE9] bg-[#553DE9]/5 w-[130px]",children:"Loki Mode"}),e.jsx("th",{className:"text-center px-4 py-3 font-medium text-[#6B6960] w-[130px]",children:"bolt.new"}),e.jsx("th",{className:"text-center px-4 py-3 font-medium text-[#6B6960] w-[130px]",children:"Replit"}),e.jsx("th",{className:"text-center px-4 py-3 font-medium text-[#6B6960] w-[130px]",children:"Lovable"})]})}),e.jsx("tbody",{children:r.map(t=>e.jsxs(e.Fragment,{children:[e.jsx("tr",{className:"bg-[#FAF9F6]",children:e.jsx("td",{colSpan:5,className:"px-4 py-2 text-xs font-bold text-[#939084] uppercase tracking-wider",children:t})},`cat-${t}`),j.filter(s=>s.category===t).map(s=>e.jsxs("tr",{className:"border-b border-[#ECEAE3] last:border-b-0",children:[e.jsx("td",{className:"px-4 py-2.5 text-[#36342E]",children:s.feature}),e.jsx("td",{className:"px-4 py-2.5 text-center bg-[#553DE9]/5",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.lokiMode})})}),e.jsx("td",{className:"px-4 py-2.5 text-center",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.boltNew})})}),e.jsx("td",{className:"px-4 py-2.5 text-center",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.replit})})}),e.jsx("td",{className:"px-4 py-2.5 text-center",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.lovable})})})]},s.feature))]}))})]})})}),e.jsxs("div",{className:"mt-12",children:[e.jsx("h2",{className:"text-xl font-bold text-[#36342E] mb-6",children:"Why developers choose Loki Mode"}),e.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-5",children:k.map(t=>e.jsxs("div",{className:"bg-white border border-[#ECEAE3] rounded-xl p-5 shadow-sm hover:shadow-md transition-shadow",children:[e.jsx("div",{className:"mb-3",children:t.icon}),e.jsx("h3",{className:"text-base font-bold text-[#36342E] mb-1",children:t.title}),e.jsx("p",{className:"text-sm text-[#6B6960]",children:t.description})]},t.title))})]}),e.jsx("div",{className:"mt-12 max-w-xl mx-auto",children:e.jsx(g,{})})]})})}export{C as default};
1
+ import{r as o,j as e}from"./index-D3YvZaMV.js";function f({value:r,prefix:t="",suffix:s=""}){const[u,n]=o.useState(0),c=o.useRef(0);return o.useEffect(()=>{const a=c.current,h=r-a;if(h===0)return;const i=600,d=performance.now();function l(b){const v=b-d,m=Math.min(v/i,1),N=1-Math.pow(1-m,3),p=Math.round(a+h*N);n(p),c.current=p,m<1&&requestAnimationFrame(l)}requestAnimationFrame(l)},[r]),e.jsxs("span",{className:"tabular-nums",children:[t,u.toLocaleString(),s]})}function g(){const[r,t]=o.useState(5),[s,u]=o.useState(30),[n,c]=o.useState(75),a=.4,i=Math.round(r*s*a*4.33),d=Math.round(i*n);return e.jsxs("div",{className:"bg-white border border-[#ECEAE3] rounded-xl p-6 shadow-sm",children:[e.jsx("h3",{className:"text-lg font-bold text-[#36342E] mb-1",children:"ROI Calculator"}),e.jsx("p",{className:"text-sm text-[#6B6960] mb-6",children:"Estimate how much your team could save with autonomous development."}),e.jsxs("div",{className:"space-y-5",children:[e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("label",{className:"text-sm font-medium text-[#36342E]",children:"Team size"}),e.jsxs("span",{className:"text-sm font-semibold text-[#553DE9] tabular-nums",children:[r," developers"]})]}),e.jsx("input",{type:"range",min:1,max:50,value:r,onChange:l=>t(Number(l.target.value)),className:"w-full h-2 bg-[#ECEAE3] rounded-full appearance-none cursor-pointer accent-[#553DE9]"}),e.jsxs("div",{className:"flex justify-between text-xs text-[#939084] mt-1",children:[e.jsx("span",{children:"1"}),e.jsx("span",{children:"50"})]})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("label",{className:"text-sm font-medium text-[#36342E]",children:"Hours coding per week"}),e.jsxs("span",{className:"text-sm font-semibold text-[#553DE9] tabular-nums",children:[s,"h"]})]}),e.jsx("input",{type:"range",min:5,max:60,value:s,onChange:l=>u(Number(l.target.value)),className:"w-full h-2 bg-[#ECEAE3] rounded-full appearance-none cursor-pointer accent-[#553DE9]"}),e.jsxs("div",{className:"flex justify-between text-xs text-[#939084] mt-1",children:[e.jsx("span",{children:"5h"}),e.jsx("span",{children:"60h"})]})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("label",{className:"text-sm font-medium text-[#36342E]",children:"Average hourly rate"}),e.jsxs("span",{className:"text-sm font-semibold text-[#553DE9] tabular-nums",children:["$",n,"/hr"]})]}),e.jsx("input",{type:"range",min:25,max:250,step:5,value:n,onChange:l=>c(Number(l.target.value)),className:"w-full h-2 bg-[#ECEAE3] rounded-full appearance-none cursor-pointer accent-[#553DE9]"}),e.jsxs("div",{className:"flex justify-between text-xs text-[#939084] mt-1",children:[e.jsx("span",{children:"$25"}),e.jsx("span",{children:"$250"})]})]})]}),e.jsxs("div",{className:"mt-6 pt-6 border-t border-[#ECEAE3]",children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 text-center",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-2xl font-bold text-[#553DE9]",children:e.jsx(f,{value:i,suffix:"h"})}),e.jsx("div",{className:"text-xs text-[#6B6960] mt-1",children:"Est. hours saved / month"})]}),e.jsxs("div",{children:[e.jsx("div",{className:"text-2xl font-bold text-[#1FC5A8]",children:e.jsx(f,{value:d,prefix:"$"})}),e.jsx("div",{className:"text-xs text-[#6B6960] mt-1",children:"Est. cost saved / month"})]})]}),e.jsxs("div",{className:"mt-4 p-3 rounded-lg bg-[#553DE9]/5 border border-[#553DE9]/10",children:[e.jsxs("p",{className:"text-sm text-[#36342E] text-center",children:["If Loki Mode saved ",Math.round(a*100),"% of coding time, your team could save"," ",e.jsxs("span",{className:"font-bold text-[#553DE9]",children:[i.toLocaleString()," hours"]})," ","and"," ",e.jsxs("span",{className:"font-bold text-[#1FC5A8]",children:["$",d.toLocaleString()]})," ","per month."]}),e.jsxs("p",{className:"mt-2 text-xs text-[#6B6960] text-center",children:["The ",Math.round(a*100),"% is an assumption, not a measurement. Model and API spend is not included."]})]})]})]})}const j=[{feature:"Claude (Opus/Sonnet/Haiku)",category:"AI Models",lokiMode:!0,boltNew:!1,replit:!1,lovable:!0},{feature:"GPT-4o / Codex",category:"AI Models",lokiMode:!0,boltNew:!0,replit:!0,lovable:!1},{feature:"5-provider auto-failover",category:"AI Models",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"8 automated quality gates",category:"Quality",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Blind 3-reviewer code review",category:"Quality",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Anti-sycophancy checks",category:"Quality",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Automated test generation",category:"Quality",lokiMode:!0,boltNew:!1,replit:"Partial",lovable:!1},{feature:"Docker container generation",category:"Deployment",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"CI/CD pipeline generation",category:"Deployment",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"One-click cloud deploy",category:"Deployment",lokiMode:"Planned",boltNew:!0,replit:!0,lovable:!0},{feature:"Self-hosted (your keys)",category:"Cost",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Free tier available",category:"Cost",lokiMode:"Source-available (BUSL-1.1)",boltNew:!0,replit:!0,lovable:!0},{feature:"No vendor lock-in",category:"Cost",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"SSO / OIDC authentication",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"RBAC access control",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"Audit logging",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"Air-gapped deployment",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1}];function y(){return e.jsx("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",className:"text-[#1FC5A8]",children:e.jsx("path",{d:"M20 6L9 17l-5-5",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round"})})}function w(){return e.jsx("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",className:"text-[#C45B5B]/40",children:e.jsx("path",{d:"M18 6L6 18M6 6l12 12",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})}function x({value:r}){return typeof r=="string"?e.jsx("span",{className:"text-xs text-[#6B6960] font-medium",children:r}):r?e.jsx(y,{}):e.jsx(w,{})}const k=[{title:"Full Autonomy",description:"Describe what you want. Loki Mode handles planning, coding, testing, and verification automatically.",icon:e.jsx("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#553DE9]",children:e.jsx("path",{d:"M13 2L3 14h9l-1 8 10-12h-9l1-8z",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})},{title:"Production Quality",description:"8 quality gates, blind code review, and anti-sycophancy checks ensure code that actually works.",icon:e.jsx("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#1FC5A8]",children:e.jsx("path",{d:"M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})},{title:"Your Infrastructure",description:"Self-hosted, source-available (BUSL-1.1), no data leaves your network. Use your own API keys across 5 providers.",icon:e.jsxs("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#2F71E3]",children:[e.jsx("rect",{x:"2",y:"2",width:"20",height:"8",rx:"2",stroke:"currentColor",strokeWidth:"2"}),e.jsx("rect",{x:"2",y:"14",width:"20",height:"8",rx:"2",stroke:"currentColor",strokeWidth:"2"}),e.jsx("circle",{cx:"6",cy:"6",r:"1",fill:"currentColor"}),e.jsx("circle",{cx:"6",cy:"18",r:"1",fill:"currentColor"})]})},{title:"Built for Teams",description:"Enterprise auth, RBAC, audit logs, and parallel workflows for teams of any size.",icon:e.jsxs("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#E93D82]",children:[e.jsx("path",{d:"M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}),e.jsx("circle",{cx:"9",cy:"7",r:"4",stroke:"currentColor",strokeWidth:"2"}),e.jsx("path",{d:"M23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})]})}];function C(){const r=[...new Set(j.map(t=>t.category))];return e.jsx("div",{className:"min-h-screen bg-[#FAF9F6]",children:e.jsxs("div",{className:"max-w-6xl mx-auto px-6 py-8",children:[e.jsxs("div",{className:"mb-8",children:[e.jsx("h1",{className:"text-2xl font-bold text-[#36342E]",children:"Compare"}),e.jsx("p",{className:"text-[#6B6960] mt-1",children:"See how Loki Mode stacks up against other AI development platforms."})]}),e.jsx("div",{className:"bg-white border border-[#ECEAE3] rounded-xl overflow-hidden shadow-sm",children:e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-[#ECEAE3]",children:[e.jsx("th",{className:"text-left px-4 py-3 font-medium text-[#6B6960] w-[240px]",children:"Feature"}),e.jsx("th",{className:"text-center px-4 py-3 font-bold text-[#553DE9] bg-[#553DE9]/5 w-[130px]",children:"Loki Mode"}),e.jsx("th",{className:"text-center px-4 py-3 font-medium text-[#6B6960] w-[130px]",children:"bolt.new"}),e.jsx("th",{className:"text-center px-4 py-3 font-medium text-[#6B6960] w-[130px]",children:"Replit"}),e.jsx("th",{className:"text-center px-4 py-3 font-medium text-[#6B6960] w-[130px]",children:"Lovable"})]})}),e.jsx("tbody",{children:r.map(t=>e.jsxs(e.Fragment,{children:[e.jsx("tr",{className:"bg-[#FAF9F6]",children:e.jsx("td",{colSpan:5,className:"px-4 py-2 text-xs font-bold text-[#939084] uppercase tracking-wider",children:t})},`cat-${t}`),j.filter(s=>s.category===t).map(s=>e.jsxs("tr",{className:"border-b border-[#ECEAE3] last:border-b-0",children:[e.jsx("td",{className:"px-4 py-2.5 text-[#36342E]",children:s.feature}),e.jsx("td",{className:"px-4 py-2.5 text-center bg-[#553DE9]/5",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.lokiMode})})}),e.jsx("td",{className:"px-4 py-2.5 text-center",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.boltNew})})}),e.jsx("td",{className:"px-4 py-2.5 text-center",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.replit})})}),e.jsx("td",{className:"px-4 py-2.5 text-center",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.lovable})})})]},s.feature))]}))})]})})}),e.jsxs("div",{className:"mt-12",children:[e.jsx("h2",{className:"text-xl font-bold text-[#36342E] mb-6",children:"Why developers choose Loki Mode"}),e.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-5",children:k.map(t=>e.jsxs("div",{className:"bg-white border border-[#ECEAE3] rounded-xl p-5 shadow-sm hover:shadow-md transition-shadow",children:[e.jsx("div",{className:"mb-3",children:t.icon}),e.jsx("h3",{className:"text-base font-bold text-[#36342E] mb-1",children:t.title}),e.jsx("p",{className:"text-sm text-[#6B6960]",children:t.description})]},t.title))})]}),e.jsx("div",{className:"mt-12 max-w-xl mx-auto",children:e.jsx(g,{})})]})})}export{C as default};
@@ -1,4 +1,4 @@
1
- import{c as o,r as a,j as e,T as n,h as i,p as c}from"./index-4c5tMfoO.js";/**
1
+ import{c as o,r as a,j as e,T as n,h as i,p as c}from"./index-D3YvZaMV.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.