@mobrienv/autoloop 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (328) hide show
  1. package/README.md +13 -0
  2. package/bin/autoloop +0 -0
  3. package/dist/agent-map.d.ts +10 -0
  4. package/dist/agent-map.js +58 -0
  5. package/dist/agent-map.js.map +1 -0
  6. package/dist/backend/acp-client.d.ts +32 -0
  7. package/dist/backend/acp-client.js +210 -0
  8. package/dist/backend/acp-client.js.map +1 -0
  9. package/dist/backend/index.d.ts +1 -1
  10. package/dist/backend/index.js +17 -5
  11. package/dist/backend/index.js.map +1 -1
  12. package/dist/backend/kiro-bridge.d.ts +17 -0
  13. package/dist/backend/kiro-bridge.js +80 -0
  14. package/dist/backend/kiro-bridge.js.map +1 -0
  15. package/dist/backend/kiro-worker.d.ts +1 -0
  16. package/dist/backend/kiro-worker.js +83 -0
  17. package/dist/backend/kiro-worker.js.map +1 -0
  18. package/dist/backend/run-command.js +2 -2
  19. package/dist/backend/run-command.js.map +1 -1
  20. package/dist/backend/run-kiro.d.ts +3 -0
  21. package/dist/backend/run-kiro.js +12 -0
  22. package/dist/backend/run-kiro.js.map +1 -0
  23. package/dist/backend/run-pi.js.map +1 -1
  24. package/dist/chains/budget.js +16 -4
  25. package/dist/chains/budget.js.map +1 -1
  26. package/dist/chains/load.d.ts +7 -1
  27. package/dist/chains/load.js +58 -9
  28. package/dist/chains/load.js.map +1 -1
  29. package/dist/chains/render.js +36 -8
  30. package/dist/chains/render.js.map +1 -1
  31. package/dist/chains/run.js +129 -44
  32. package/dist/chains/run.js.map +1 -1
  33. package/dist/chains/types.d.ts +1 -0
  34. package/dist/chains.d.ts +5 -4
  35. package/dist/chains.js +3 -3
  36. package/dist/chains.js.map +1 -1
  37. package/dist/commands/chain.js +6 -4
  38. package/dist/commands/chain.js.map +1 -1
  39. package/dist/commands/config.d.ts +1 -0
  40. package/dist/commands/config.js +74 -0
  41. package/dist/commands/config.js.map +1 -0
  42. package/dist/commands/dashboard.d.ts +1 -0
  43. package/dist/commands/dashboard.js +68 -0
  44. package/dist/commands/dashboard.js.map +1 -0
  45. package/dist/commands/inspect.js +125 -17
  46. package/dist/commands/inspect.js.map +1 -1
  47. package/dist/commands/list.d.ts +1 -1
  48. package/dist/commands/list.js +6 -3
  49. package/dist/commands/list.js.map +1 -1
  50. package/dist/commands/loops.js +11 -11
  51. package/dist/commands/loops.js.map +1 -1
  52. package/dist/commands/memory.js +2 -2
  53. package/dist/commands/memory.js.map +1 -1
  54. package/dist/commands/run.d.ts +1 -1
  55. package/dist/commands/run.js +102 -15
  56. package/dist/commands/run.js.map +1 -1
  57. package/dist/commands/runs.d.ts +1 -0
  58. package/dist/commands/runs.js +50 -0
  59. package/dist/commands/runs.js.map +1 -0
  60. package/dist/commands/task.d.ts +1 -0
  61. package/dist/commands/task.js +74 -0
  62. package/dist/commands/task.js.map +1 -0
  63. package/dist/commands/worktree.d.ts +1 -0
  64. package/dist/commands/worktree.js +162 -0
  65. package/dist/commands/worktree.js.map +1 -0
  66. package/dist/config.d.ts +15 -1
  67. package/dist/config.js +90 -21
  68. package/dist/config.js.map +1 -1
  69. package/dist/dashboard/app.d.ts +12 -0
  70. package/dist/dashboard/app.js +23 -0
  71. package/dist/dashboard/app.js.map +1 -0
  72. package/dist/dashboard/routes/api.d.ts +3 -0
  73. package/dist/dashboard/routes/api.js +103 -0
  74. package/dist/dashboard/routes/api.js.map +1 -0
  75. package/dist/dashboard/routes/pages.d.ts +2 -0
  76. package/dist/dashboard/routes/pages.js +14 -0
  77. package/dist/dashboard/routes/pages.js.map +1 -0
  78. package/dist/dashboard/views/alpine-vendor.d.ts +1 -0
  79. package/dist/dashboard/views/alpine-vendor.js +10 -0
  80. package/dist/dashboard/views/alpine-vendor.js.map +1 -0
  81. package/dist/dashboard/views/shell.d.ts +1 -0
  82. package/dist/dashboard/views/shell.js +714 -0
  83. package/dist/dashboard/views/shell.js.map +1 -0
  84. package/dist/events/decode.js.map +1 -1
  85. package/dist/events/encode.js +2 -2
  86. package/dist/events/encode.js.map +1 -1
  87. package/dist/harness/config-helpers.d.ts +11 -0
  88. package/dist/harness/config-helpers.js +242 -64
  89. package/dist/harness/config-helpers.js.map +1 -1
  90. package/dist/harness/coordination.js +12 -12
  91. package/dist/harness/coordination.js.map +1 -1
  92. package/dist/harness/display.d.ts +4 -3
  93. package/dist/harness/display.js +57 -11
  94. package/dist/harness/display.js.map +1 -1
  95. package/dist/harness/emit.js +31 -13
  96. package/dist/harness/emit.js.map +1 -1
  97. package/dist/harness/index.d.ts +7 -7
  98. package/dist/harness/index.js +151 -35
  99. package/dist/harness/index.js.map +1 -1
  100. package/dist/harness/iteration.d.ts +12 -0
  101. package/dist/harness/iteration.js +66 -64
  102. package/dist/harness/iteration.js.map +1 -1
  103. package/dist/harness/journal.d.ts +17 -0
  104. package/dist/harness/journal.js +74 -5
  105. package/dist/harness/journal.js.map +1 -1
  106. package/dist/harness/metareview.js +30 -15
  107. package/dist/harness/metareview.js.map +1 -1
  108. package/dist/harness/metrics.js +12 -12
  109. package/dist/harness/metrics.js.map +1 -1
  110. package/dist/harness/parallel.d.ts +1 -1
  111. package/dist/harness/parallel.js +141 -62
  112. package/dist/harness/parallel.js.map +1 -1
  113. package/dist/harness/prompt.d.ts +18 -9
  114. package/dist/harness/prompt.js +80 -44
  115. package/dist/harness/prompt.js.map +1 -1
  116. package/dist/harness/scratchpad.js +8 -6
  117. package/dist/harness/scratchpad.js.map +1 -1
  118. package/dist/harness/stop.js +16 -9
  119. package/dist/harness/stop.js.map +1 -1
  120. package/dist/harness/tools.js +39 -13
  121. package/dist/harness/tools.js.map +1 -1
  122. package/dist/harness/types.d.ts +29 -0
  123. package/dist/harness/wave/finalize-wave.d.ts +1 -1
  124. package/dist/harness/wave/finalize-wave.js +36 -9
  125. package/dist/harness/wave/finalize-wave.js.map +1 -1
  126. package/dist/harness/wave/launch-branches.d.ts +2 -2
  127. package/dist/harness/wave/launch-branches.js +147 -51
  128. package/dist/harness/wave/launch-branches.js.map +1 -1
  129. package/dist/harness/wave/parse-objectives.js +4 -1
  130. package/dist/harness/wave/parse-objectives.js.map +1 -1
  131. package/dist/harness/wave.d.ts +2 -2
  132. package/dist/harness/wave.js +89 -28
  133. package/dist/harness/wave.js.map +1 -1
  134. package/dist/isolation/index.d.ts +4 -0
  135. package/dist/isolation/index.js +3 -0
  136. package/dist/isolation/index.js.map +1 -0
  137. package/dist/isolation/resolve.d.ts +39 -0
  138. package/dist/isolation/resolve.js +118 -0
  139. package/dist/isolation/resolve.js.map +1 -0
  140. package/dist/isolation/run-scope.d.ts +21 -0
  141. package/dist/isolation/run-scope.js +50 -0
  142. package/dist/isolation/run-scope.js.map +1 -0
  143. package/dist/json.js +10 -8
  144. package/dist/json.js.map +1 -1
  145. package/dist/loops/health.d.ts +4 -2
  146. package/dist/loops/health.js +73 -25
  147. package/dist/loops/health.js.map +1 -1
  148. package/dist/loops/list.d.ts +2 -2
  149. package/dist/loops/list.js +6 -6
  150. package/dist/loops/list.js.map +1 -1
  151. package/dist/loops/policy.d.ts +6 -0
  152. package/dist/loops/policy.js +41 -0
  153. package/dist/loops/policy.js.map +1 -0
  154. package/dist/loops/render.d.ts +2 -1
  155. package/dist/loops/render.js +24 -11
  156. package/dist/loops/render.js.map +1 -1
  157. package/dist/loops/show.d.ts +2 -2
  158. package/dist/loops/show.js +13 -13
  159. package/dist/loops/show.js.map +1 -1
  160. package/dist/loops/watch.d.ts +7 -1
  161. package/dist/loops/watch.js +82 -16
  162. package/dist/loops/watch.js.map +1 -1
  163. package/dist/main.js +48 -11
  164. package/dist/main.js.map +1 -1
  165. package/dist/markdown.js +11 -11
  166. package/dist/markdown.js.map +1 -1
  167. package/dist/memory-render.js +5 -5
  168. package/dist/memory-render.js.map +1 -1
  169. package/dist/memory.d.ts +1 -2
  170. package/dist/memory.js +44 -40
  171. package/dist/memory.js.map +1 -1
  172. package/dist/pi-adapter.js +11 -11
  173. package/dist/pi-adapter.js.map +1 -1
  174. package/dist/profiles.d.ts +12 -0
  175. package/dist/profiles.js +71 -0
  176. package/dist/profiles.js.map +1 -0
  177. package/dist/registry/derive.js +10 -3
  178. package/dist/registry/derive.js.map +1 -1
  179. package/dist/registry/discover.d.ts +20 -0
  180. package/dist/registry/discover.js +98 -0
  181. package/dist/registry/discover.js.map +1 -0
  182. package/dist/registry/harness.js +8 -2
  183. package/dist/registry/harness.js.map +1 -1
  184. package/dist/registry/index.d.ts +4 -3
  185. package/dist/registry/index.js +3 -2
  186. package/dist/registry/index.js.map +1 -1
  187. package/dist/registry/read.js.map +1 -1
  188. package/dist/registry/rebuild.js +7 -4
  189. package/dist/registry/rebuild.js.map +1 -1
  190. package/dist/registry/types.d.ts +9 -0
  191. package/dist/registry/update.js +1 -1
  192. package/dist/registry/update.js.map +1 -1
  193. package/dist/tasks-render.d.ts +2 -0
  194. package/dist/tasks-render.js +44 -0
  195. package/dist/tasks-render.js.map +1 -0
  196. package/dist/tasks.d.ts +24 -0
  197. package/dist/tasks.js +184 -0
  198. package/dist/tasks.js.map +1 -0
  199. package/dist/testing/mock-backend.js +2 -2
  200. package/dist/testing/mock-backend.js.map +1 -1
  201. package/dist/topology.d.ts +6 -0
  202. package/dist/topology.js +129 -8
  203. package/dist/topology.js.map +1 -1
  204. package/dist/usage.d.ts +1 -0
  205. package/dist/usage.js +56 -11
  206. package/dist/usage.js.map +1 -1
  207. package/dist/utils.d.ts +6 -0
  208. package/dist/utils.js +272 -2
  209. package/dist/utils.js.map +1 -1
  210. package/dist/worktree/clean.d.ts +12 -0
  211. package/dist/worktree/clean.js +98 -0
  212. package/dist/worktree/clean.js.map +1 -0
  213. package/dist/worktree/create.d.ts +14 -0
  214. package/dist/worktree/create.js +71 -0
  215. package/dist/worktree/create.js.map +1 -0
  216. package/dist/worktree/index.d.ts +10 -0
  217. package/dist/worktree/index.js +6 -0
  218. package/dist/worktree/index.js.map +1 -0
  219. package/dist/worktree/list.d.ts +9 -0
  220. package/dist/worktree/list.js +24 -0
  221. package/dist/worktree/list.js.map +1 -0
  222. package/dist/worktree/merge.d.ts +11 -0
  223. package/dist/worktree/merge.js +129 -0
  224. package/dist/worktree/merge.js.map +1 -0
  225. package/dist/worktree/meta.d.ts +17 -0
  226. package/dist/worktree/meta.js +34 -0
  227. package/dist/worktree/meta.js.map +1 -0
  228. package/package.json +24 -4
  229. package/presets/autocode/README.md +1 -1
  230. package/presets/autocode/autoloops.toml +9 -1
  231. package/presets/autocode/harness.md +12 -5
  232. package/presets/autocode/roles/build.md +5 -4
  233. package/presets/autocode/roles/critic.md +2 -2
  234. package/presets/autocode/roles/finalizer.md +2 -2
  235. package/presets/autocode/roles/planner.md +7 -7
  236. package/presets/autodoc/README.md +1 -1
  237. package/presets/autodoc/autoloops.toml +1 -1
  238. package/presets/autodoc/harness.md +6 -6
  239. package/presets/autodoc/roles/auditor.md +3 -3
  240. package/presets/autodoc/roles/checker.md +2 -2
  241. package/presets/autodoc/roles/publisher.md +3 -3
  242. package/presets/autodoc/roles/writer.md +4 -4
  243. package/presets/autofix/README.md +1 -1
  244. package/presets/autofix/autoloops.toml +5 -2
  245. package/presets/autofix/harness.md +8 -7
  246. package/presets/autofix/roles/closer.md +4 -4
  247. package/presets/autofix/roles/diagnoser.md +6 -5
  248. package/presets/autofix/roles/fixer.md +4 -4
  249. package/presets/autofix/roles/verifier.md +2 -2
  250. package/presets/autoideas/README.md +1 -1
  251. package/presets/autoideas/autoloops.toml +1 -1
  252. package/presets/autoideas/harness.md +11 -11
  253. package/presets/autoideas/roles/analyst.md +4 -4
  254. package/presets/autoideas/roles/reviewer.md +3 -3
  255. package/presets/autoideas/roles/scanner.md +3 -3
  256. package/presets/autoideas/roles/synthesizer.md +20 -7
  257. package/presets/automerge/README.md +3 -0
  258. package/presets/automerge/autoloops.toml +12 -0
  259. package/presets/automerge/harness.md +10 -0
  260. package/presets/automerge/miniloops.toml +12 -0
  261. package/presets/automerge/roles/merge.md +10 -0
  262. package/presets/automerge/topology.toml +10 -0
  263. package/presets/autoperf/README.md +1 -1
  264. package/presets/autoperf/autoloops.toml +1 -1
  265. package/presets/autoperf/harness.md +5 -5
  266. package/presets/autoperf/roles/judge.md +3 -3
  267. package/presets/autoperf/roles/measurer.md +4 -4
  268. package/presets/autoperf/roles/optimizer.md +4 -4
  269. package/presets/autoperf/roles/profiler.md +4 -4
  270. package/presets/autopr/README.md +99 -0
  271. package/presets/autopr/autoloops.toml +22 -0
  272. package/presets/autopr/harness.md +27 -0
  273. package/presets/autopr/miniloops.toml +22 -0
  274. package/presets/autopr/roles/collector.md +55 -0
  275. package/presets/autopr/roles/drafter.md +38 -0
  276. package/presets/autopr/roles/publisher.md +28 -0
  277. package/presets/autopr/roles/validator.md +31 -0
  278. package/presets/autopr/topology.toml +32 -0
  279. package/presets/autoqa/README.md +1 -1
  280. package/presets/autoqa/autoloops.toml +1 -1
  281. package/presets/autoqa/harness.md +10 -10
  282. package/presets/autoqa/roles/executor.md +3 -3
  283. package/presets/autoqa/roles/inspector.md +2 -2
  284. package/presets/autoqa/roles/planner.md +6 -6
  285. package/presets/autoqa/roles/reporter.md +9 -9
  286. package/presets/autoresearch/README.md +1 -1
  287. package/presets/autoresearch/autoloops.toml +1 -1
  288. package/presets/autoresearch/harness.md +5 -5
  289. package/presets/autoresearch/roles/benchmarker.md +3 -3
  290. package/presets/autoresearch/roles/evaluator.md +5 -5
  291. package/presets/autoresearch/roles/implementer.md +4 -4
  292. package/presets/autoresearch/roles/strategist.md +6 -6
  293. package/presets/autoreview/README.md +1 -1
  294. package/presets/autoreview/autoloops.toml +1 -1
  295. package/presets/autoreview/harness.md +5 -5
  296. package/presets/autoreview/roles/checker.md +4 -4
  297. package/presets/autoreview/roles/reader.md +5 -5
  298. package/presets/autoreview/roles/suggester.md +4 -4
  299. package/presets/autoreview/roles/summarizer.md +4 -4
  300. package/presets/autosec/README.md +1 -1
  301. package/presets/autosec/autoloops.toml +1 -1
  302. package/presets/autosec/harness.md +5 -5
  303. package/presets/autosec/roles/analyst.md +4 -4
  304. package/presets/autosec/roles/hardener.md +2 -2
  305. package/presets/autosec/roles/reporter.md +4 -4
  306. package/presets/autosec/roles/scanner.md +3 -3
  307. package/presets/autosimplify/README.md +1 -1
  308. package/presets/autosimplify/autoloops.toml +7 -3
  309. package/presets/autosimplify/harness.md +7 -7
  310. package/presets/autosimplify/roles/reviewer.md +3 -3
  311. package/presets/autosimplify/roles/scoper.md +7 -7
  312. package/presets/autosimplify/roles/simplifier.md +4 -4
  313. package/presets/autosimplify/roles/verifier.md +3 -3
  314. package/presets/autospec/README.md +1 -1
  315. package/presets/autospec/autoloops.toml +1 -1
  316. package/presets/autospec/harness.md +6 -6
  317. package/presets/autospec/roles/clarifier.md +4 -3
  318. package/presets/autospec/roles/critic.md +1 -1
  319. package/presets/autospec/roles/designer.md +3 -3
  320. package/presets/autospec/roles/planner.md +2 -2
  321. package/presets/autospec/roles/researcher.md +3 -3
  322. package/presets/autotest/README.md +1 -1
  323. package/presets/autotest/autoloops.toml +1 -1
  324. package/presets/autotest/harness.md +6 -6
  325. package/presets/autotest/roles/assessor.md +3 -3
  326. package/presets/autotest/roles/runner.md +2 -2
  327. package/presets/autotest/roles/surveyor.md +4 -4
  328. package/presets/autotest/roles/writer.md +4 -4
@@ -0,0 +1,714 @@
1
+ export function htmlShell() {
2
+ return `<!DOCTYPE html>
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="utf-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+ <title>autoloop dashboard</title>
8
+ <style>
9
+ :root {
10
+ --bg: #fff; --fg: #1a1a1a; --muted: #666; --border: #e0e0e0;
11
+ --card-bg: #fafafa; --badge-bg: #eee;
12
+ --active: #2563eb; --watching: #d97706; --stuck: #dc2626;
13
+ --failed: #dc2626; --completed: #16a34a;
14
+ }
15
+ @media (prefers-color-scheme: dark) {
16
+ :root {
17
+ --bg: #111; --fg: #e0e0e0; --muted: #999; --border: #333;
18
+ --card-bg: #1a1a1a; --badge-bg: #2a2a2a;
19
+ }
20
+ }
21
+ * { box-sizing: border-box; margin: 0; padding: 0; }
22
+ body { font-family: system-ui, -apple-system, sans-serif; background: var(--bg); color: var(--fg); max-width: 900px; margin: 0 auto; padding: 1rem; }
23
+ h1 { font-size: 1.25rem; font-weight: 600; }
24
+ header { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 1rem; border-bottom: 1px solid var(--border); padding-bottom: 0.5rem; }
25
+ header .updated { font-size: 0.75rem; color: var(--muted); font-family: monospace; }
26
+
27
+ .chatbox { background: var(--card-bg); border: 1px solid var(--border); border-radius: 6px; padding: 0.75rem; margin-bottom: 1rem; display: flex; gap: 0.5rem; align-items: flex-end; }
28
+ .chatbox select { padding: 0.4rem; border: 1px solid var(--border); border-radius: 4px; background: var(--bg); color: var(--fg); font-size: 0.8rem; }
29
+ .chatbox textarea { flex: 1; resize: vertical; min-height: 2.5rem; max-height: 8rem; padding: 0.4rem; border: 1px solid var(--border); border-radius: 4px; font-family: inherit; font-size: 0.85rem; background: var(--bg); color: var(--fg); }
30
+ .chatbox button { padding: 0.4rem 1rem; border: none; border-radius: 4px; background: var(--active); color: #fff; cursor: pointer; font-size: 0.85rem; white-space: nowrap; }
31
+ .chatbox button:hover { opacity: 0.9; }
32
+
33
+ .section summary { cursor: pointer; font-weight: 600; font-size: 0.9rem; padding: 0.4rem 0; list-style: none; }
34
+ .section summary::-webkit-details-marker { display: none; }
35
+ .section summary::before { content: "\\25b6 "; font-size: 0.7rem; }
36
+ .section[open] summary::before { content: "\\25bc "; }
37
+ .badge { display: inline-block; font-size: 0.7rem; padding: 0.1rem 0.4rem; border-radius: 8px; background: var(--badge-bg); margin-left: 0.3rem; font-weight: normal; }
38
+ .badge[data-status="active"] { background: var(--active); color: #fff; }
39
+ .badge[data-status="watching"] { background: var(--watching); color: #fff; }
40
+ .badge[data-status="stuck"] { background: var(--stuck); color: #fff; }
41
+ .badge[data-status="failed"] { background: var(--failed); color: #fff; }
42
+ .badge[data-status="completed"] { background: var(--completed); color: #fff; }
43
+ .wt-badge { background: var(--badge-bg); color: var(--muted); font-weight: 600; letter-spacing: 0.03em; }
44
+
45
+ .run-list { list-style: none; padding: 0; }
46
+ .run-item { display: grid; grid-template-columns: 1fr auto; gap: 0.5rem; padding: 0.4rem 0.5rem; border-bottom: 1px solid var(--border); cursor: pointer; font-size: 0.8rem; font-family: monospace; }
47
+ .run-item:hover { background: var(--card-bg); }
48
+ .run-item .meta { color: var(--muted); text-align: right; white-space: nowrap; }
49
+
50
+ .detail-pane { background: var(--card-bg); border: 1px solid var(--border); border-radius: 6px; padding: 1rem; margin-top: 1rem; }
51
+ .detail-pane h3 { font-size: 0.95rem; margin-bottom: 0.5rem; }
52
+ .detail-pane .field { font-size: 0.8rem; font-family: monospace; margin-bottom: 0.25rem; }
53
+ .detail-pane .field label { color: var(--muted); }
54
+ .events-list { margin-top: 0.75rem; max-height: 400px; overflow-y: auto; }
55
+ .event-item { font-size: 0.75rem; font-family: monospace; padding: 0.2rem 0; border-bottom: 1px solid var(--border); word-break: break-all; }
56
+ .event-item summary { cursor: pointer; }
57
+ .event-item.ev-system { border-left: 3px solid var(--muted); padding-left: 0.4rem; }
58
+ .event-item.ev-backend { border-left: 3px solid var(--muted); padding-left: 0.4rem; }
59
+ .event-item.ev-review { border-left: 3px solid var(--watching); padding-left: 0.4rem; background: rgba(217,119,6,0.03); }
60
+ .event-item.ev-error { border-left: 3px solid var(--failed); padding-left: 0.4rem; background: rgba(220,38,38,0.05); }
61
+ .event-item.ev-coordination { border-left: 3px solid var(--active); padding-left: 0.4rem; }
62
+ .event-item.ev-completion { border-left: 3px solid var(--completed); padding-left: 0.4rem; }
63
+ .event-item.ev-highlight { border-left-color: var(--active); }
64
+ .event-item.ev-backend summary { opacity: 0.6; }
65
+ .event-item.ev-highlight summary { opacity: 1; }
66
+ .event-item.ev-backend:hover summary { opacity: 1; }
67
+ .event-field { margin: 0.15rem 0; font-size: 0.7rem; }
68
+ .event-field strong { color: var(--muted); margin-right: 0.3rem; }
69
+ .event-field pre { display: inline; white-space: pre-wrap; }
70
+ .event-field details summary { cursor: pointer; color: var(--muted); font-style: italic; }
71
+
72
+ .md-content { font-family: system-ui, -apple-system, sans-serif; font-size: 0.75rem; line-height: 1.5; white-space: normal; }
73
+ .md-content h1, .md-content h2, .md-content h3, .md-content h4, .md-content h5, .md-content h6 { margin: 0.5em 0 0.25em; font-weight: 600; }
74
+ .md-content h1 { font-size: 1.1em; } .md-content h2 { font-size: 1em; } .md-content h3 { font-size: 0.95em; }
75
+ .md-content p { margin: 0.3em 0; }
76
+ .md-content code { background: var(--badge-bg); padding: 0.1em 0.3em; border-radius: 3px; font-size: 0.9em; }
77
+ .md-content pre { background: var(--badge-bg); padding: 0.5em; border-radius: 4px; overflow-x: auto; margin: 0.4em 0; }
78
+ .md-content pre code { background: none; padding: 0; }
79
+ .md-content ul, .md-content ol { margin: 0.3em 0; padding-left: 1.5em; }
80
+ .md-content li { margin: 0.15em 0; }
81
+ .md-content strong { font-weight: 600; }
82
+ .md-content em { font-style: italic; }
83
+
84
+ .empty { color: var(--muted); font-size: 0.8rem; padding: 0.5rem 0; }
85
+
86
+ /* Structured prompt styles */
87
+ .prompt-structured { font-size: 0.75rem; line-height: 1.5; }
88
+ .prompt-section { border-bottom: 1px solid var(--border); padding: 0.5rem 0; }
89
+ .prompt-section:last-child { border-bottom: none; }
90
+ .prompt-objective { background: color-mix(in srgb, var(--active) 10%, transparent); border: 1px solid color-mix(in srgb, var(--active) 30%, transparent); border-radius: 6px; padding: 0.6rem 0.8rem; margin-bottom: 0.5rem; }
91
+ .prompt-objective h4 { font-size: 0.8rem; color: var(--active); margin-bottom: 0.25rem; }
92
+ .prompt-topology { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; padding: 0.4rem 0; font-size: 0.7rem; font-family: monospace; }
93
+ .prompt-topology .topo-label { color: var(--muted); margin-right: 0.15rem; }
94
+ .prompt-topology .topo-badge { display: inline-block; padding: 0.1rem 0.4rem; border-radius: 8px; background: var(--badge-bg); font-size: 0.7rem; }
95
+ .prompt-scratchpad-entries { margin-top: 0.3rem; }
96
+ .prompt-show-more { background: none; border: 1px solid var(--border); border-radius: 4px; padding: 0.2rem 0.5rem; font-size: 0.7rem; color: var(--muted); cursor: pointer; margin-bottom: 0.3rem; }
97
+ .prompt-show-more:hover { background: var(--card-bg); color: var(--fg); }
98
+ .prompt-raw-toggle { margin-top: 0.5rem; }
99
+ .prompt-raw-toggle button { background: none; border: 1px solid var(--border); border-radius: 4px; padding: 0.2rem 0.6rem; font-size: 0.7rem; color: var(--muted); cursor: pointer; }
100
+ .prompt-raw-toggle button:hover { background: var(--card-bg); color: var(--fg); }
101
+ .prompt-raw-toggle pre { white-space: pre-wrap; font-size: 0.65rem; margin-top: 0.3rem; max-height: 400px; overflow-y: auto; background: var(--badge-bg); padding: 0.5rem; border-radius: 4px; }
102
+
103
+ /* Routing badges and backpressure */
104
+ .routing-badge { display: inline-block; font-size: 0.65rem; padding: 0.1rem 0.35rem; border-radius: 6px; background: var(--badge-bg); margin: 0.1rem 0.15rem; font-family: monospace; }
105
+ .bp-none { color: var(--muted); font-style: italic; font-size: 0.7rem; }
106
+ .bp-warning { display: inline-block; font-size: 0.7rem; padding: 0.15rem 0.4rem; border-radius: 6px; background: color-mix(in srgb, var(--watching) 15%, transparent); border: 1px solid color-mix(in srgb, var(--watching) 40%, transparent); color: var(--watching); font-family: monospace; }
107
+
108
+ /* Merged indicator */
109
+ .merged-badge { color: var(--completed); font-weight: 600; }
110
+ .merge-detail { margin-top: 0.5rem; padding: 0.5rem; background: color-mix(in srgb, var(--completed) 8%, transparent); border: 1px solid color-mix(in srgb, var(--completed) 25%, transparent); border-radius: 4px; }
111
+ .merge-detail .field label { color: var(--completed); }
112
+ </style>
113
+ </head>
114
+ <body x-data="dashboard()" x-init="startPolling()">
115
+
116
+ <header>
117
+ <h1>autoloop dashboard</h1>
118
+ <span class="updated" x-text="lastUpdated ? 'updated ' + lastUpdated : 'loading...'"></span>
119
+ </header>
120
+
121
+ <div class="chatbox">
122
+ <select x-model="selectedPreset">
123
+ <template x-for="p in presets" :key="p.name">
124
+ <option :value="p.name" x-text="p.name"></option>
125
+ </template>
126
+ </select>
127
+ <textarea x-model="newPrompt" placeholder="Describe what you want to do..." @keydown.meta.enter="startLoop()" @keydown.ctrl.enter="startLoop()"></textarea>
128
+ <button @click="startLoop()">Start</button>
129
+ </div>
130
+
131
+ <template x-for="cat in categories" :key="cat.key">
132
+ <details class="section" :open="sectionOpen[cat.key]" @toggle="sectionUserToggled[cat.key] = true; sectionOpen[cat.key] = $el.open">
133
+ <summary>
134
+ <span x-text="cat.label"></span>
135
+ <span class="badge" :data-status="cat.key" x-text="cat.items.length"></span>
136
+ </summary>
137
+ <ul class="run-list">
138
+ <template x-for="run in cat.items" :key="run.run_id">
139
+ <li class="run-item" @click="selectRun(run.run_id)">
140
+ <span>
141
+ <span x-text="run.run_id.slice(0, 16)"></span>
142
+ <span style="color:var(--muted)"> &middot; </span>
143
+ <span x-text="run.preset"></span>
144
+ <span style="color:var(--muted)"> &middot; iter </span>
145
+ <span x-text="run.iteration + '/' + (run.max_iterations || '?')"></span>
146
+ <template x-if="run.isolation_mode === 'worktree'">
147
+ <span>
148
+ <span class="badge wt-badge" :title="run.worktree_name || ''">WT</span>
149
+ <template x-if="run.worktree_merged">
150
+ <span class="merged-badge" title="merged"> &#x2713;</span>
151
+ </template>
152
+ </span>
153
+ </template>
154
+ </span>
155
+ <span class="meta">
156
+ <span x-text="run.latest_event || '-'"></span>
157
+ <span style="color:var(--muted)"> &middot; </span>
158
+ <span x-text="timeAgo(run.created_at)"></span>
159
+ </span>
160
+ </li>
161
+ </template>
162
+ <li class="empty" x-show="cat.items.length === 0">None</li>
163
+ </ul>
164
+ </details>
165
+ </template>
166
+
167
+ <div class="detail-pane" x-show="selectedRun" x-cloak>
168
+ <h3 x-text="'Run: ' + (selectedRun || '')"></h3>
169
+ <template x-if="selectedRunDetail">
170
+ <div>
171
+ <div class="field"><label>Status: </label><span x-text="selectedRunDetail.status"></span></div>
172
+ <div class="field"><label>Preset: </label><span x-text="selectedRunDetail.preset"></span></div>
173
+ <div class="field"><label>Objective: </label><span x-text="selectedRunDetail.objective"></span></div>
174
+ <div class="field"><label>Iteration: </label><span x-text="selectedRunDetail.iteration + '/' + (selectedRunDetail.max_iterations || '?')"></span></div>
175
+ <div class="field"><label>Workspace: </label><span :title="workspacePath(selectedRunDetail)" x-text="workspaceLabel(selectedRunDetail)"></span></div>
176
+ <div class="field"><label>Created: </label><span :title="selectedRunDetail.created_at" x-text="timeAgo(selectedRunDetail.created_at) + ' ago'"></span></div>
177
+ <div class="field"><label>Updated: </label><span :title="selectedRunDetail.updated_at" x-text="timeAgo(selectedRunDetail.updated_at) + ' ago'"></span></div>
178
+ <div class="field"><label>Duration: </label><span x-text="runDuration()"></span></div>
179
+ <div class="field"><label>Latest event: </label><span x-text="'[iter ' + selectedRunDetail.iteration + '] ' + selectedRunDetail.latest_event"></span></div>
180
+ <div class="field"><label>Events: </label><span x-text="selectedRunEvents.length"></span></div>
181
+ <template x-if="selectedRunDetail.worktree_merged">
182
+ <div class="merge-detail">
183
+ <div class="field"><label>Merge status: </label><span>merged</span></div>
184
+ <div class="field" x-show="selectedRunDetail.worktree_merged_at"><label>Merged at: </label><span x-text="selectedRunDetail.worktree_merged_at"></span></div>
185
+ <div class="field" x-show="selectedRunDetail.worktree_merge_strategy"><label>Merge strategy: </label><span x-text="selectedRunDetail.worktree_merge_strategy"></span></div>
186
+ </div>
187
+ </template>
188
+ </div>
189
+ </template>
190
+ <div class="events-list">
191
+ <div style="display:flex;justify-content:space-between;align-items:center">
192
+ <h3>Events</h3>
193
+ <label style="font-size:0.75rem;cursor:pointer"><input type="checkbox" x-model="showVerbose" style="margin-right:0.3rem">Show backend events</label>
194
+ </div>
195
+ <template x-for="(ev, idx) in visibleRunEvents()" :key="idx">
196
+ <details :class="eventClasses(ev)">
197
+ <summary x-text="eventSummary(ev)"></summary>
198
+ <div style="padding:0.3rem">
199
+ <template x-for="[k,v] in eventDisplayEntries(ev)" :key="k">
200
+ <div class="event-field">
201
+ <strong x-text="k + ':'"></strong>
202
+ <template x-if="isPromptField(ev.topic, k)">
203
+ <div x-data="{ showRaw: false, showAllScratchpad: false }" class="prompt-structured">
204
+ <template x-if="parsePromptSections(String(v))">
205
+ <div>
206
+ <template x-if="parsePromptSections(String(v)).objective">
207
+ <div class="prompt-section prompt-objective">
208
+ <h4>Objective</h4>
209
+ <div class="md-content" x-html="renderMarkdown(parsePromptSections(String(v)).objective)"></div>
210
+ </div>
211
+ </template>
212
+ <template x-if="parsePromptSections(String(v)).topology">
213
+ <div class="prompt-section">
214
+ <strong style="font-size:0.75rem">Topology</strong>
215
+ <div class="prompt-topology" x-html="renderTopologyBlock(parsePromptSections(String(v)).topology)"></div>
216
+ </div>
217
+ </template>
218
+ <template x-if="parsePromptSections(String(v)).scratchpad">
219
+ <div class="prompt-section">
220
+ <strong style="font-size:0.75rem">Scratchpad</strong>
221
+ <div class="prompt-scratchpad-entries">
222
+ <template x-if="getScratchpadEntries(parsePromptSections(String(v)).scratchpad).length > 3">
223
+ <div>
224
+ <button class="prompt-show-more" x-show="!showAllScratchpad" @click="showAllScratchpad = true"
225
+ x-text="'Show ' + (getScratchpadEntries(parsePromptSections(String(v)).scratchpad).length - 3) + ' older entries'"></button>
226
+ <template x-if="showAllScratchpad">
227
+ <div class="md-content" x-html="renderMarkdown(getScratchpadEntries(parsePromptSections(String(v)).scratchpad).slice(0, -3).join('\\n\\n'))"></div>
228
+ </template>
229
+ </div>
230
+ </template>
231
+ <div class="md-content" x-html="renderMarkdown(getScratchpadEntries(parsePromptSections(String(v)).scratchpad).slice(-3).join('\\n\\n'))"></div>
232
+ </div>
233
+ </div>
234
+ </template>
235
+ <template x-if="parsePromptSections(String(v)).memory">
236
+ <details class="prompt-section">
237
+ <summary style="cursor:pointer;font-size:0.75rem;font-weight:600">Loop Memory</summary>
238
+ <div class="md-content" style="margin-top:0.3rem" x-html="renderMarkdown(parsePromptSections(String(v)).memory)"></div>
239
+ </details>
240
+ </template>
241
+ <template x-if="parsePromptSections(String(v)).rules">
242
+ <details class="prompt-section">
243
+ <summary style="cursor:pointer;font-size:0.75rem;font-weight:600">Rules &amp; Event Tool</summary>
244
+ <div class="md-content" style="margin-top:0.3rem" x-html="renderMarkdown(parsePromptSections(String(v)).rules)"></div>
245
+ </details>
246
+ </template>
247
+ <template x-if="parsePromptSections(String(v)).config">
248
+ <details class="prompt-section">
249
+ <summary style="cursor:pointer;font-size:0.75rem;font-weight:600">Config</summary>
250
+ <div class="md-content" style="margin-top:0.3rem" x-html="renderMarkdown(parsePromptSections(String(v)).config)"></div>
251
+ </details>
252
+ </template>
253
+ <template x-if="parsePromptSections(String(v)).harness">
254
+ <details class="prompt-section">
255
+ <summary style="cursor:pointer;font-size:0.75rem;font-weight:600">Harness Instructions</summary>
256
+ <div class="md-content" style="margin-top:0.3rem" x-html="renderMarkdown(parsePromptSections(String(v)).harness)"></div>
257
+ </details>
258
+ </template>
259
+ <div class="prompt-raw-toggle">
260
+ <button @click="showRaw = !showRaw" x-text="showRaw ? 'Hide raw prompt' : 'Show raw prompt'"></button>
261
+ <template x-if="showRaw">
262
+ <pre x-text="String(v)"></pre>
263
+ </template>
264
+ </div>
265
+ </div>
266
+ </template>
267
+ </div>
268
+ </template>
269
+ <template x-if="!isPromptField(ev.topic, k) && isRoutingBadgeField(k)">
270
+ <span x-html="renderRoutingValue(k, v)"></span>
271
+ </template>
272
+ <template x-if="!isPromptField(ev.topic, k) && !isRoutingBadgeField(k) && isMarkdownField(ev.topic, k)">
273
+ <template x-if="String(typeof v === 'object' ? JSON.stringify(v,null,2) : v).length <= 200">
274
+ <div class="md-content" x-html="renderMarkdown(typeof v === 'object' ? JSON.stringify(v,null,2) : String(v))"></div>
275
+ </template>
276
+ </template>
277
+ <template x-if="!isPromptField(ev.topic, k) && !isRoutingBadgeField(k) && isMarkdownField(ev.topic, k)">
278
+ <template x-if="String(typeof v === 'object' ? JSON.stringify(v,null,2) : v).length > 200">
279
+ <details>
280
+ <summary x-text="k + ' (' + String(typeof v === 'object' ? JSON.stringify(v,null,2) : v).length + ' chars)'"></summary>
281
+ <div class="md-content" style="font-size:0.7rem;margin-top:0.2rem" x-html="renderMarkdown(typeof v === 'object' ? JSON.stringify(v,null,2) : String(v))"></div>
282
+ </details>
283
+ </template>
284
+ </template>
285
+ <template x-if="!isPromptField(ev.topic, k) && !isRoutingBadgeField(k) && !isMarkdownField(ev.topic, k)">
286
+ <template x-if="String(typeof v === 'object' ? JSON.stringify(v,null,2) : v).length <= 200">
287
+ <pre x-text="typeof v === 'object' ? JSON.stringify(v,null,2) : String(v)"></pre>
288
+ </template>
289
+ </template>
290
+ <template x-if="!isPromptField(ev.topic, k) && !isRoutingBadgeField(k) && !isMarkdownField(ev.topic, k)">
291
+ <template x-if="String(typeof v === 'object' ? JSON.stringify(v,null,2) : v).length > 200">
292
+ <details>
293
+ <summary x-text="k + ' (' + String(typeof v === 'object' ? JSON.stringify(v,null,2) : v).length + ' chars)'"></summary>
294
+ <pre style="white-space:pre-wrap;font-size:0.7rem;margin-top:0.2rem" x-text="typeof v === 'object' ? JSON.stringify(v,null,2) : String(v)"></pre>
295
+ </details>
296
+ </template>
297
+ </template>
298
+ </div>
299
+ </template>
300
+ </div>
301
+ </details>
302
+ </template>
303
+ <p class="empty" x-show="visibleRunEvents().length === 0">No events</p>
304
+ </div>
305
+ </div>
306
+
307
+ <script defer src="/static/alpine.min.js"></script>
308
+ <script>
309
+ function dashboard() {
310
+ return {
311
+ runs: { active: [], watching: [], stuck: [], recentFailed: [], recentCompleted: [] },
312
+ presets: [],
313
+ selectedRun: null,
314
+ selectedRunDetail: null,
315
+ selectedRunEvents: [],
316
+ newPrompt: "",
317
+ selectedPreset: "",
318
+ pollInterval: null,
319
+ lastUpdated: null,
320
+ sectionOpen: { active: false, watching: false, stuck: false, failed: false, completed: false },
321
+ sectionUserToggled: {},
322
+ showVerbose: false,
323
+
324
+ get categories() {
325
+ return [
326
+ { key: "active", label: "Active", items: this.runs.active },
327
+ { key: "watching", label: "Watching", items: this.runs.watching },
328
+ { key: "stuck", label: "Stuck", items: this.runs.stuck },
329
+ { key: "failed", label: "Failed", items: this.runs.recentFailed },
330
+ { key: "completed", label: "Completed", items: this.runs.recentCompleted },
331
+ ];
332
+ },
333
+
334
+ async startPolling() {
335
+ await this.fetchPresets();
336
+ await this.fetchRuns();
337
+ this.pollInterval = setInterval(() => this.fetchRuns(), 3000);
338
+ },
339
+
340
+ async fetchRuns() {
341
+ try {
342
+ const res = await fetch("/api/runs");
343
+ this.runs = await res.json();
344
+ this.lastUpdated = new Date().toLocaleTimeString();
345
+ const defaultOpen = ['active', 'watching', 'stuck'];
346
+ for (const cat of this.categories) {
347
+ if (!this.sectionUserToggled[cat.key]) {
348
+ this.sectionOpen[cat.key] = cat.items.length > 0 && defaultOpen.includes(cat.key);
349
+ }
350
+ }
351
+ if (this.selectedRun) this.fetchEvents(this.selectedRun);
352
+ } catch (e) { /* retry on next poll */ }
353
+ },
354
+
355
+ async fetchPresets() {
356
+ try {
357
+ const res = await fetch("/api/presets");
358
+ const data = await res.json();
359
+ this.presets = data.presets;
360
+ if (this.presets.length && !this.selectedPreset) {
361
+ this.selectedPreset = this.presets[0].name;
362
+ }
363
+ } catch (e) { /* ignore */ }
364
+ },
365
+
366
+ async selectRun(runId) {
367
+ this.selectedRun = runId;
368
+ try {
369
+ const detailRes = await fetch("/api/runs/" + runId);
370
+ if (detailRes.ok) this.selectedRunDetail = await detailRes.json();
371
+ } catch (e) { /* ignore */ }
372
+ await this.fetchEvents(runId);
373
+ },
374
+
375
+ async fetchEvents(runId) {
376
+ try {
377
+ const res = await fetch("/api/runs/" + runId + "/events");
378
+ const data = await res.json();
379
+ this.selectedRunEvents = data.events;
380
+ } catch (e) { /* ignore */ }
381
+ },
382
+
383
+ async startLoop() {
384
+ if (!this.newPrompt.trim()) return;
385
+ await fetch("/api/runs", {
386
+ method: "POST",
387
+ headers: { "Content-Type": "application/json" },
388
+ body: JSON.stringify({ prompt: this.newPrompt, preset: this.selectedPreset }),
389
+ });
390
+ this.newPrompt = "";
391
+ setTimeout(() => this.fetchRuns(), 1000);
392
+ },
393
+
394
+ timeAgo(iso) {
395
+ if (!iso) return "-";
396
+ const s = Math.floor((Date.now() - new Date(iso)) / 1000);
397
+ if (s < 0) return "0s";
398
+ if (s < 60) return s + "s";
399
+ if (s < 3600) return Math.floor(s / 60) + "m";
400
+ return Math.floor(s / 3600) + "h";
401
+ },
402
+
403
+ runDuration() {
404
+ const d = this.selectedRunDetail;
405
+ if (!d || !d.created_at || !d.updated_at) return "-";
406
+ const ms = new Date(d.updated_at) - new Date(d.created_at);
407
+ if (ms < 0) return "-";
408
+ const s = Math.floor(ms / 1000);
409
+ if (s < 60) return s + "s";
410
+ if (s < 3600) return Math.floor(s / 60) + "m " + (s % 60) + "s";
411
+ return Math.floor(s / 3600) + "h " + Math.floor((s % 3600) / 60) + "m";
412
+ },
413
+
414
+ workspaceLabel(run) {
415
+ if (!run) return "-";
416
+ if (run.isolation_mode === "worktree") {
417
+ return run.worktree_name || "worktree";
418
+ }
419
+ return "shared checkout";
420
+ },
421
+
422
+ workspacePath(run) {
423
+ if (!run) return "";
424
+ if (run.isolation_mode === "worktree") {
425
+ return run.worktree_path || run.work_dir || "";
426
+ }
427
+ return run.work_dir || run.project_dir || "";
428
+ },
429
+
430
+ isMarkdownField(topic, key) {
431
+ if (!topic || !key) return false;
432
+ const t = String(topic);
433
+ const k = String(key);
434
+ if ((t === 'backend.finish' || t === 'iteration.finish') && k === 'output') return true;
435
+ if (t === 'iteration.start' && k === 'prompt') return true;
436
+ return false;
437
+ },
438
+
439
+ renderMarkdown(text) {
440
+ if (!text) return '';
441
+ let html = String(text);
442
+ // Escape HTML
443
+ html = html.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
444
+ // Fenced code blocks
445
+ html = html.replace(/^\\\`\\\`\\\`(\\w*)\\n([\\s\\S]*?)^\\\`\\\`\\\`/gm, function(_, lang, code) {
446
+ return '<pre><code>' + code.trim() + '</code></pre>';
447
+ });
448
+ // Split into lines for block-level processing
449
+ const lines = html.split('\\n');
450
+ const out = [];
451
+ let inList = false;
452
+ let listType = '';
453
+ for (let i = 0; i < lines.length; i++) {
454
+ const line = lines[i];
455
+ // Headings
456
+ const hMatch = line.match(/^(#{1,6})\\s+(.+)$/);
457
+ if (hMatch) {
458
+ if (inList) { out.push('</' + listType + '>'); inList = false; }
459
+ const lvl = hMatch[1].length;
460
+ out.push('<h' + lvl + '>' + this._inlineMd(hMatch[2]) + '</h' + lvl + '>');
461
+ continue;
462
+ }
463
+ // Unordered list
464
+ const ulMatch = line.match(/^[\\-\\*]\\s+(.+)$/);
465
+ if (ulMatch) {
466
+ if (!inList || listType !== 'ul') {
467
+ if (inList) out.push('</' + listType + '>');
468
+ out.push('<ul>'); inList = true; listType = 'ul';
469
+ }
470
+ out.push('<li>' + this._inlineMd(ulMatch[1]) + '</li>');
471
+ continue;
472
+ }
473
+ // Ordered list
474
+ const olMatch = line.match(/^\\d+\\.\\s+(.+)$/);
475
+ if (olMatch) {
476
+ if (!inList || listType !== 'ol') {
477
+ if (inList) out.push('</' + listType + '>');
478
+ out.push('<ol>'); inList = true; listType = 'ol';
479
+ }
480
+ out.push('<li>' + this._inlineMd(olMatch[1]) + '</li>');
481
+ continue;
482
+ }
483
+ // Close list if we hit non-list line
484
+ if (inList) { out.push('</' + listType + '>'); inList = false; }
485
+ // Empty line = paragraph break
486
+ if (line.trim() === '') {
487
+ out.push('');
488
+ continue;
489
+ }
490
+ // Normal paragraph line
491
+ out.push('<p>' + this._inlineMd(line) + '</p>');
492
+ }
493
+ if (inList) out.push('</' + listType + '>');
494
+ return out.join('\\n');
495
+ },
496
+
497
+ _inlineMd(text) {
498
+ // Inline code
499
+ text = text.replace(/\\\`([^\\\`]+)\\\`/g, '<code>$1</code>');
500
+ // Bold
501
+ text = text.replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>');
502
+ text = text.replace(/__(.+?)__/g, '<strong>$1</strong>');
503
+ // Italic
504
+ text = text.replace(/\\*(.+?)\\*/g, '<em>$1</em>');
505
+ text = text.replace(/_(.+?)_/g, '<em>$1</em>');
506
+ return text;
507
+ },
508
+
509
+ eventDisplayEntries(ev) {
510
+ if (ev.topic === 'iteration.start' && ev.fields && typeof ev.fields === 'object') {
511
+ const entries = [];
512
+ for (const [k, v] of Object.entries(ev)) {
513
+ if (k === 'fields') continue;
514
+ entries.push([k, v]);
515
+ }
516
+ const fieldKeys = ['recent_event', 'suggested_roles', 'allowed_events', 'backpressure', 'prompt'];
517
+ for (const fk of fieldKeys) {
518
+ if (ev.fields[fk] !== undefined) {
519
+ entries.push([fk, ev.fields[fk]]);
520
+ }
521
+ }
522
+ return entries;
523
+ }
524
+ return Object.entries(ev);
525
+ },
526
+
527
+ isRoutingBadgeField(key) {
528
+ return ['suggested_roles', 'allowed_events', 'backpressure', 'recent_event'].includes(String(key));
529
+ },
530
+
531
+ renderRoutingValue(key, value) {
532
+ const k = String(key);
533
+ const v = String(value || '');
534
+ if (k === 'backpressure') {
535
+ if (!v || v === 'none' || v === '') {
536
+ return '<span class="bp-none">none</span>';
537
+ }
538
+ return '<span class="bp-warning">' + this._escHtml(v) + '</span>';
539
+ }
540
+ if (k === 'suggested_roles' || k === 'allowed_events') {
541
+ const items = v.split(',').map(s => s.trim()).filter(Boolean);
542
+ if (items.length === 0) return '<span class="bp-none">-</span>';
543
+ return items.map(i => '<span class="routing-badge">' + this._escHtml(i) + '</span>').join(' ');
544
+ }
545
+ return '<span class="routing-badge">' + this._escHtml(v) + '</span>';
546
+ },
547
+
548
+ isPromptField(topic, key) {
549
+ return String(topic) === 'iteration.start' && String(key) === 'prompt';
550
+ },
551
+
552
+ parsePromptSections(text) {
553
+ if (!text) return null;
554
+ const s = String(text);
555
+ const sections = {};
556
+
557
+ // Objective
558
+ const objMatch = s.match(/Objective:\\n([\\s\\S]*?)(?=\\n(?:Loop memory:|Topology \\(advisory\\):|Current scratchpad:|Use the event tool|Iteration:|$))/);
559
+ if (objMatch) sections.objective = objMatch[1].trim();
560
+
561
+ // Topology
562
+ const topoMatch = s.match(/Topology \\(advisory\\):\\n([\\s\\S]*?)(?=\\n(?:Iteration:|Current scratchpad:|Use the event tool|Loop memory:|Objective:|$))/);
563
+ if (topoMatch) sections.topology = topoMatch[1].trim();
564
+
565
+ // Scratchpad
566
+ const scratchMatch = s.match(/Current scratchpad:\\n([\\s\\S]*?)(?=\\n(?:Use the event tool|Backpressure rule:|Plain text alone|$))/);
567
+ if (scratchMatch) sections.scratchpad = scratchMatch[1].trim();
568
+
569
+ // Rules (event tool usage + examples + backpressure)
570
+ const rulesMatch = s.match(/(Use the event tool[\\s\\S]*?)$/);
571
+ if (rulesMatch) sections.rules = rulesMatch[1].trim();
572
+
573
+ // Loop memory
574
+ const memMatch = s.match(/Loop memory:\\n([\\s\\S]*?)(?=\\n(?:Topology \\(advisory\\):|Current scratchpad:|Objective:|Iteration:|$))/);
575
+ if (memMatch) sections.memory = memMatch[1].trim();
576
+
577
+ // Config (Iteration, Log level, Completion event, etc.)
578
+ const cfgMatch = s.match(/(Iteration:[\\s\\S]*?)(?=\\n(?:Current scratchpad:|Use the event tool|$))/);
579
+ if (cfgMatch) sections.config = cfgMatch[1].trim();
580
+
581
+ // Harness instructions
582
+ const harnessMatch = s.match(/Live harness instructions:\\n([\\s\\S]*?)(?=\\n(?:Context pressure:|Objective:|Loop memory:|Topology|$))/);
583
+ if (harnessMatch) sections.harness = harnessMatch[1].trim();
584
+
585
+ return Object.keys(sections).length > 0 ? sections : null;
586
+ },
587
+
588
+ renderTopologyBlock(text) {
589
+ if (!text) return '';
590
+ let html = '';
591
+ const lines = String(text).split('\\n');
592
+ for (const line of lines) {
593
+ const kv = line.match(/^([^:]+):\\s*(.+)$/);
594
+ if (kv) {
595
+ const label = kv[1].trim();
596
+ const val = kv[2].trim();
597
+ if (label === 'Suggested next roles' || label === 'Allowed next events' || label === 'Recent routing event') {
598
+ html += '<span class="topo-label">' + this._escHtml(label) + ':</span> ';
599
+ const items = val.split(',').map(s => s.trim()).filter(Boolean);
600
+ for (const item of items) {
601
+ html += '<span class="topo-badge">' + this._escHtml(item) + '</span> ';
602
+ }
603
+ } else {
604
+ html += '<span class="topo-label">' + this._escHtml(label) + ':</span> <span>' + this._escHtml(val) + '</span> ';
605
+ }
606
+ }
607
+ }
608
+ // Role deck
609
+ const deckMatch = text.match(/Role deck:\\n([\\s\\S]*?)$/);
610
+ if (deckMatch) {
611
+ const roles = deckMatch[1].match(/- role \`([^\`]+)\`/g);
612
+ if (roles) {
613
+ html += '<span class="topo-label">Roles:</span> ';
614
+ for (const r of roles) {
615
+ const name = r.match(/\`([^\`]+)\`/);
616
+ if (name) html += '<span class="topo-badge">' + this._escHtml(name[1]) + '</span> ';
617
+ }
618
+ }
619
+ }
620
+ return html;
621
+ },
622
+
623
+ getScratchpadEntries(text) {
624
+ if (!text) return [];
625
+ // Split by "## Iteration N" headers
626
+ const parts = String(text).split(/(?=## Iteration \\d+)/);
627
+ return parts.filter(p => p.trim().length > 0);
628
+ },
629
+
630
+ _escHtml(s) {
631
+ return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
632
+ },
633
+
634
+ visibleRunEvents() {
635
+ return this.selectedRunEvents.filter(ev => this.showVerbose || !this.isBackendEvent(ev));
636
+ },
637
+
638
+ isBackendEvent(ev) {
639
+ const t = String(ev?.topic || '');
640
+ return t.startsWith('backend.');
641
+ },
642
+
643
+ eventCategory(ev) {
644
+ const t = ev.topic || '';
645
+ if (['event.invalid','wave.timeout','wave.failed','loop.stop'].includes(t)) return 'ev-error';
646
+ if (t === 'task.complete' || t === 'loop.complete') return 'ev-completion';
647
+ if (t.startsWith('review.')) return 'ev-review';
648
+ if (t.startsWith('backend.')) return 'ev-backend';
649
+ if (t.startsWith('iteration.')) return 'ev-system';
650
+ return 'ev-coordination';
651
+ },
652
+
653
+ eventClasses(ev) {
654
+ const classes = ['event-item', this.eventCategory(ev)];
655
+ if ((ev.topic || '') === 'iteration.start') classes.push('ev-highlight');
656
+ return classes.join(' ');
657
+ },
658
+
659
+ eventSummary(ev) {
660
+ const parts = [];
661
+ if (ev.iteration) parts.push('[iter ' + ev.iteration + ']');
662
+ if (ev.ts) parts.push(this.timeAgo(ev.ts));
663
+ parts.push(ev.topic || JSON.stringify(ev).slice(0, 80));
664
+ const f = ev.fields || {};
665
+ const t = ev.topic || '';
666
+ if (t === 'iteration.start') {
667
+ const hints = [];
668
+ if (f.recent_event) hints.push(f.recent_event);
669
+ if (f.suggested_roles) hints.push('\\u2192 ' + f.suggested_roles);
670
+ if (f.allowed_events) hints.push('\\u00b7 emits ' + f.allowed_events);
671
+ if (f.backpressure && String(f.backpressure) !== 'none' && String(f.backpressure).trim() !== '') {
672
+ hints.push('[BP: ' + f.backpressure + ']');
673
+ }
674
+ if (hints.length) {
675
+ parts.push('\\u2014 ' + hints.join(' '));
676
+ } else if (f.prompt) {
677
+ const objMatch = String(f.prompt).match(/Objective:\\s*\\n?([^\\n]+)/);
678
+ if (objMatch) {
679
+ const preview = objMatch[1].trim().slice(0, 100);
680
+ parts.push('\\u2014 ' + preview + (objMatch[1].trim().length > 100 ? '...' : ''));
681
+ }
682
+ }
683
+ } else if (t === 'review.start') {
684
+ const f = ev.fields || {};
685
+ parts.push('Review Started');
686
+ const hint = [];
687
+ if (f.reason) hint.push(String(f.reason).slice(0, 60));
688
+ if (hint.length) parts.push('\\u2014 ' + hint.join(' '));
689
+ } else if (t === 'review.finish') {
690
+ const f = ev.fields || {};
691
+ parts.push('Review Finished');
692
+ const hint = [];
693
+ if (f.decision) hint.push('decision=' + f.decision);
694
+ if (f.output) hint.push(String(f.output).replace(/\\s+/g, ' ').trim().slice(0, 80));
695
+ if (hint.length) parts.push('\\u2014 ' + hint.join(' '));
696
+ } else if (t === 'iteration.finish' || t === 'backend.finish') {
697
+ const hint = [];
698
+ if (f.exit_code !== undefined) hint.push('exit=' + f.exit_code);
699
+ if (f.timed_out === 'true' || f.timed_out === true) hint.push('TIMEOUT');
700
+ if (f.elapsed_s !== undefined) hint.push(f.elapsed_s + 's');
701
+ if (f.output) hint.push(String(f.output).replace(/\\s+/g, ' ').trim().slice(0, 80));
702
+ if (hint.length) parts.push('\\u2014 ' + hint.join(' '));
703
+ } else if (ev.payload) {
704
+ parts.push('\\u2014 ' + String(ev.payload).slice(0, 60));
705
+ }
706
+ return parts.join(' ');
707
+ },
708
+ };
709
+ }
710
+ </script>
711
+ </body>
712
+ </html>`;
713
+ }
714
+ //# sourceMappingURL=shell.js.map