@azure-id/orc 1.9.2 → 2.1.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 (228) hide show
  1. package/CHANGELOG.md +722 -0
  2. package/README-id.md +56 -42
  3. package/README.md +58 -41
  4. package/bin/build-agents.js +125 -29
  5. package/bin/clear-logs.js +620 -0
  6. package/bin/cli.js +1873 -180
  7. package/bin/fix.js +459 -0
  8. package/bin/gotcha-import.js +1081 -0
  9. package/bin/gotcha.js +1566 -0
  10. package/bin/graph-query.js +1 -1
  11. package/bin/graph.js +717 -717
  12. package/bin/habit.js +1538 -0
  13. package/bin/mockrun-catalog.js +284 -276
  14. package/bin/onboarding-content.js +2 -1
  15. package/bin/pricing.json +28 -21
  16. package/bin/run-undo.js +398 -0
  17. package/bin/trace-write.js +869 -0
  18. package/bin/verify-contracts.js +627 -100
  19. package/bin/verify-package.js +68 -10
  20. package/bin/webui/api.js +151 -6
  21. package/bin/webui/app.html +239 -232
  22. package/bin/webui/css/00-tokens.css +110 -92
  23. package/bin/webui/css/04-motion.css +87 -0
  24. package/bin/webui/css/06-responsive.css +203 -178
  25. package/bin/webui/css/panels/behaviour.css +205 -0
  26. package/bin/webui/css/panels/hookui.css +79 -0
  27. package/bin/webui/fixtures/behaviour.js +562 -0
  28. package/bin/webui/fixtures/extra.js +9 -9
  29. package/bin/webui/fixtures/flow.js +81 -81
  30. package/bin/webui/fixtures/hookui.js +506 -431
  31. package/bin/webui/fixtures/index.js +66 -3
  32. package/bin/webui/fixtures/knowledge.js +9 -3
  33. package/bin/webui/fixtures/maintenance.js +123 -42
  34. package/bin/webui/fixtures/rules.js +1007 -1002
  35. package/bin/webui/fixtures/settings.js +6 -7
  36. package/bin/webui/fixtures/stats.js +25 -6
  37. package/bin/webui/fixtures/wait.js +98 -97
  38. package/bin/webui/i18n/en/behaviour.json +156 -0
  39. package/bin/webui/i18n/en/hookui.json +290 -194
  40. package/bin/webui/i18n/en/maintenance.json +71 -52
  41. package/bin/webui/i18n/en/nav.json +25 -24
  42. package/bin/webui/i18n/en/settings.json +60 -60
  43. package/bin/webui/i18n/en/tour.json +37 -35
  44. package/bin/webui/i18n/id/behaviour.json +156 -0
  45. package/bin/webui/i18n/id/hookui.json +290 -194
  46. package/bin/webui/i18n/id/maintenance.json +71 -52
  47. package/bin/webui/i18n/id/nav.json +25 -24
  48. package/bin/webui/i18n/id/settings.json +60 -60
  49. package/bin/webui/i18n/id/tour.json +37 -35
  50. package/bin/webui/js/01-i18n.js +155 -154
  51. package/bin/webui/js/05-banners.js +181 -172
  52. package/bin/webui/js/06-edit.js +193 -183
  53. package/bin/webui/js/90-tour.js +498 -494
  54. package/bin/webui/js/91-shortcuts.js +126 -126
  55. package/bin/webui/js/99-boot.js +121 -118
  56. package/bin/webui/js/panels/behaviour.js +1087 -0
  57. package/bin/webui/js/panels/hookui.js +2196 -1691
  58. package/bin/webui/js/panels/maintenance.js +336 -235
  59. package/bin/webui/js/panels/settings.js +726 -719
  60. package/mock-run/INDEX.md +110 -107
  61. package/mock-run/extra-slots.md +1 -1
  62. package/mock-run/gotcha-import.md +118 -0
  63. package/mock-run/habits.md +129 -0
  64. package/mock-run/orc-aftermath.md +392 -392
  65. package/mock-run/orc-budget.md +29 -33
  66. package/mock-run/orc-cli.md +200 -200
  67. package/mock-run/orc-explain.md +86 -86
  68. package/mock-run/orc-extra.md +389 -392
  69. package/mock-run/orc-fix.md +87 -0
  70. package/mock-run/orc-pattern.md +112 -112
  71. package/mock-run/orc-quick.md +6 -1
  72. package/mock-run/orc.md +157 -157
  73. package/package.json +1 -1
  74. package/templates/agents/MODEL-MAPPING.md +37 -34
  75. package/templates/agents/orc-advisor-opus-5-xhigh.md +1 -7
  76. package/templates/agents/orc-analyze-mini-opus-5-med.md +1 -6
  77. package/templates/agents/orc-analyze-mini-sonnet-5-high.md +2 -5
  78. package/templates/agents/{orc-claude-writer-opus-4-8-high.md → orc-claude-writer-opus-5-low.md} +48 -53
  79. package/templates/agents/orc-context-combiner-opus-5-high.md +1 -11
  80. package/templates/agents/orc-executor-haiku-4-5.md +14 -6
  81. package/templates/agents/orc-executor-opus-4-7-high.md +14 -6
  82. package/templates/agents/orc-executor-opus-4-7-med.md +14 -6
  83. package/templates/agents/orc-executor-opus-4-8-high.md +14 -6
  84. package/templates/agents/orc-executor-opus-5-high.md +14 -6
  85. package/templates/agents/orc-executor-opus-5-low.md +14 -6
  86. package/templates/agents/orc-executor-opus-5-med.md +14 -6
  87. package/templates/agents/orc-executor-sonnet-4-6-high.md +14 -6
  88. package/templates/agents/orc-executor-sonnet-4-6-med.md +14 -6
  89. package/templates/agents/orc-executor-sonnet-5-high.md +15 -7
  90. package/templates/agents/orc-executor-sonnet-5-low.md +155 -0
  91. package/templates/agents/orc-executor-sonnet-5-med.md +155 -0
  92. package/templates/agents/orc-graph-noter-sonnet-4-6-med.md +1 -10
  93. package/templates/agents/orc-judge-opus-5-xhigh.md +5 -9
  94. package/templates/agents/orc-learn-writer-opus-5-low.md +1 -7
  95. package/templates/agents/orc-pattern-codifier-opus-5-med.md +1 -8
  96. package/templates/agents/orc-pattern-codifier-sonnet-5-high.md +58 -63
  97. package/templates/agents/orc-planner-mini-opus-5-med.md +1 -4
  98. package/templates/agents/orc-planner-mini-sonnet-5-high.md +2 -3
  99. package/templates/agents/orc-planner-opus-5-med.md +1 -4
  100. package/templates/agents/orc-recon-opus-5-low.md +1 -8
  101. package/templates/agents/orc-recon-sonnet-4-6-med.md +1 -8
  102. package/templates/agents/orc-retro-opus-5-med.md +6 -8
  103. package/templates/agents/orc-retro-sonnet-5-high.md +7 -8
  104. package/templates/agents/orc-reviewer-opus-5-low.md +96 -0
  105. package/templates/agents/orc-scout-opus-5-low.md +1 -6
  106. package/templates/agents/orc-scout-sonnet-4-6-high.md +35 -39
  107. package/templates/agents/orc-system-analyst-opus-5-high.md +1 -6
  108. package/templates/agents/orc-test-author-opus-5-med.md +4 -5
  109. package/templates/agents/orc-trace-writer-haiku-4-5.md +3 -7
  110. package/templates/agents/orc-verifier-opus-5-med.md +16 -8
  111. package/templates/agents/{orc-wiki-scanner-opus-4-8-high.md → orc-wiki-scanner-opus-5-low.md} +74 -79
  112. package/templates/agents/orc-wiki-scanner-sonnet-5-high.md +97 -106
  113. package/templates/commands/orc-analyze.md +13 -21
  114. package/templates/commands/orc-fast.md +10 -15
  115. package/templates/commands/orc-fix.md +18 -0
  116. package/templates/commands/orc-poly.md +12 -21
  117. package/templates/commands/orc-pr-driver.md +11 -30
  118. package/templates/commands/orc-pr-setup.md +10 -31
  119. package/templates/commands/orc-route.md +11 -41
  120. package/templates/commands/orc-test.md +5 -60
  121. package/templates/commands/orc-wiki.md +64 -64
  122. package/templates/hooks/README.md +109 -7
  123. package/templates/hooks/orc-session-hook.js +339 -0
  124. package/templates/hooks/orc-statusline-render.js +317 -23
  125. package/templates/hooks/orc-statusline.js +165 -50
  126. package/templates/hooks/orc-subagent-line.js +23 -5
  127. package/templates/hooks/orc-weather-fetch.js +123 -0
  128. package/templates/skills/_shared/README.md +9 -0
  129. package/templates/skills/_shared/code-graph.md +47 -55
  130. package/templates/skills/_shared/config-precedence.md +3 -1
  131. package/templates/skills/_shared/extra-dispatch.md +78 -93
  132. package/templates/skills/_shared/gotchas.md +248 -177
  133. package/templates/skills/_shared/habits.md +101 -0
  134. package/templates/skills/_shared/lane-contract.md +84 -0
  135. package/templates/skills/_shared/opus5-only.md +14 -14
  136. package/templates/skills/_shared/phases/README.md +142 -83
  137. package/templates/skills/_shared/phases/analyst-gates.md +10 -21
  138. package/templates/skills/_shared/phases/execution.md +8 -14
  139. package/templates/skills/_shared/phases/house-rules.md +27 -32
  140. package/templates/skills/_shared/phases/intake.md +127 -133
  141. package/templates/skills/_shared/phases/mock-example.md +46 -56
  142. package/templates/skills/_shared/phases/plan-handoff.md +91 -97
  143. package/templates/skills/_shared/phases/planning.md +7 -17
  144. package/templates/skills/_shared/phases/preflight.md +19 -42
  145. package/templates/skills/_shared/phases/review.md +24 -28
  146. package/templates/skills/_shared/phases/rules.md +18 -42
  147. package/templates/skills/_shared/phases/scoring.md +55 -65
  148. package/templates/skills/_shared/phases/security-checklist.md +46 -50
  149. package/templates/skills/_shared/phases/security.md +45 -55
  150. package/templates/skills/_shared/phases/ship.md +6 -15
  151. package/templates/skills/_shared/phases/stop-resume.md +2 -5
  152. package/templates/skills/_shared/phases/summary.md +73 -48
  153. package/templates/skills/_shared/phases/testgen.md +41 -51
  154. package/templates/skills/_shared/phases/trace-verbs.md +447 -0
  155. package/templates/skills/_shared/phases/trace.md +66 -297
  156. package/templates/skills/_shared/phases/verify.md +62 -70
  157. package/templates/skills/_shared/phases/wave-grouping.md +128 -133
  158. package/templates/skills/_shared/phases/wiki-consult.md +10 -6
  159. package/templates/skills/_shared/read-ladder.md +2 -55
  160. package/templates/skills/_shared/return-validation.md +17 -70
  161. package/templates/skills/_shared/review-slice.md +106 -0
  162. package/templates/skills/_shared/smoke-gate.md +46 -28
  163. package/templates/skills/_shared/wait.md +1 -0
  164. package/templates/skills/context-combiner/SKILL.md +15 -44
  165. package/templates/skills/orc/README.md +2 -2
  166. package/templates/skills/orc/SKILL.md +35 -63
  167. package/templates/skills/orc/config.md +25 -27
  168. package/templates/skills/orc/examples/full-run-mock.md +2 -1
  169. package/templates/skills/orc/references/effort-and-mode.md +17 -17
  170. package/templates/skills/orc/references/pattern-gate.md +89 -89
  171. package/templates/skills/orc/references/phases/intake.md +41 -47
  172. package/templates/skills/orc/references/phases/integration.md +13 -19
  173. package/templates/skills/orc/references/preflight-report.md +11 -12
  174. package/templates/skills/orc/references/ultra-mode.md +9 -7
  175. package/templates/skills/orc/schemas/checkpoint.md +122 -122
  176. package/templates/skills/orc/subskills/orc-execution/SKILL.md +27 -73
  177. package/templates/skills/orc/subskills/orc-execution/core.md +12 -99
  178. package/templates/skills/orc/subskills/orc-execution/subagent.md +14 -13
  179. package/templates/skills/orc/subskills/orc-review-verify/SKILL.md +12 -53
  180. package/templates/skills/orc/subskills/orc-review-verify/core.md +52 -135
  181. package/templates/skills/orc/subskills/orc-review-verify/subagent.md +7 -7
  182. package/templates/skills/orc/subskills/orc-testgen/SKILL.md +11 -22
  183. package/templates/skills/orc/subskills/orc-testgen/core.md +20 -59
  184. package/templates/skills/orc/subskills/orc-testgen/subagent.md +7 -7
  185. package/templates/skills/orc-advisor/SKILL.md +56 -60
  186. package/templates/skills/orc-analyze/SKILL.md +33 -67
  187. package/templates/skills/orc-analyze/schemas/report-audit.md +2 -1
  188. package/templates/skills/orc-analyze/schemas/report-prose.md +2 -1
  189. package/templates/skills/orc-analyze-mini/SKILL.md +35 -70
  190. package/templates/skills/orc-claude/SKILL.md +14 -14
  191. package/templates/skills/orc-claude/examples/claude-run-mock.md +65 -65
  192. package/templates/skills/orc-diy/README.md +31 -0
  193. package/templates/skills/orc-diy/SKILL.md +27 -78
  194. package/templates/skills/orc-diy/references/blocks/pattern.md +18 -18
  195. package/templates/skills/orc-fast/SKILL.md +49 -75
  196. package/templates/skills/orc-fix/SKILL.md +110 -0
  197. package/templates/skills/orc-judge/SKILL.md +77 -82
  198. package/templates/skills/orc-mini/SKILL.md +64 -112
  199. package/templates/skills/orc-mini/examples/mini-run-mock.md +4 -2
  200. package/templates/skills/orc-pattern/SKILL.md +27 -48
  201. package/templates/skills/orc-poly/SKILL.md +32 -61
  202. package/templates/skills/orc-pr-driver/SKILL.md +25 -51
  203. package/templates/skills/orc-pr-driver/references/green-gate.md +113 -105
  204. package/templates/skills/orc-pr-setup/SKILL.md +20 -45
  205. package/templates/skills/orc-quick/README.md +44 -3
  206. package/templates/skills/orc-quick/SKILL.md +77 -108
  207. package/templates/skills/orc-quick/references/dispatch-gate.md +18 -6
  208. package/templates/skills/orc-quick/references/gh-mode.md +50 -1
  209. package/templates/skills/orc-quick/references/look.md +3 -1
  210. package/templates/skills/orc-retro/SKILL.md +20 -19
  211. package/templates/skills/orc-retro/examples/retro-mock.md +17 -17
  212. package/templates/skills/orc-route/SKILL.md +25 -45
  213. package/templates/skills/orc-test/SKILL.md +16 -37
  214. package/templates/skills/orc-verify/SKILL.md +14 -32
  215. package/templates/skills/orc-wait/SKILL.md +156 -163
  216. package/templates/skills/orc-wiki/README.md +325 -325
  217. package/templates/skills/orc-wiki/SKILL.md +195 -195
  218. package/templates/skills/orc-wiki/examples/wiki-run-mock.md +37 -37
  219. package/templates/skills/orc-wiki/references/extra.md +2 -2
  220. package/templates/skills/orc-wiki/references/partial-refresh.md +276 -271
  221. package/templates/skills/orc-wiki/references/phases/phase-0.md +2 -7
  222. package/templates/skills/orc-wiki/references/phases/phase-1.md +1 -6
  223. package/templates/skills/orc-wiki/references/phases/phase-2.md +4 -9
  224. package/templates/skills/orc-wiki/references/phases/phase-3.md +1 -6
  225. package/templates/skills/orc-wiki/references/phases/phase-3c.md +4 -8
  226. package/templates/agents/orc-claude-writer-opus-5-med.md +0 -55
  227. package/templates/agents/orc-reviewer-opus-5-med.md +0 -60
  228. package/templates/agents/orc-wiki-scanner-opus-5-med.md +0 -81
@@ -1,172 +1,181 @@
1
- "use strict";
2
- /* 05-banners.js — orc ui client
3
- The shared version promise, the update banner, the changelog modal, and
4
- renderBanners — including the global-install warning that must be visible on
5
- EVERY panel (config does not merge, so a global install can win skill
6
- resolution while this panel edits a project file nothing reads).
7
-
8
- Loaded by app.html in the order its numeric prefix names. Classic
9
- script, no import/export: an ES module import carries no query string,
10
- and every static request here needs the per-launch session token. */
11
-
12
- /* --------------------------------------------------------------- version -- */
13
- // `orc version --json` performs a REAL bounded network check against the
14
- // install source and reports {version, latest, update_available}. Three places
15
- // want that answer — the Overview tile, the rail badge and the Maintenance
16
- // upgrade row — so the promise is shared: one check per page load, not three.
17
- // `refresh()` is the only way to force a second one, and it is a button the
18
- // user presses; nothing here polls for a new release on its own.
19
- let versionPromise = null;
20
- const versionInfo = () => (versionPromise = versionPromise || read("/api/version").then((r) => r.data));
21
- function refreshVersion() {
22
- versionPromise = null;
23
- return versionInfo();
24
- }
25
-
26
- // The one place that decides what a version payload MEANS, so the tile, the rail
27
- // and the upgrade row can never disagree about it. `latest: null` is not "up to
28
- // date" — it is "we could not tell", which is a different thing to show.
29
- function versionState(v) {
30
- if (!v) return { kind: "", label: t("version.unknown.label"), note: t("version.unknown.note") };
31
- if (v.check_disabled) return { kind: "", label: t("version.off.label"), note: t("version.off.note") };
32
- if (!v.latest) return { kind: "warn", label: t("version.offline.label"), note: t("version.offline.note") };
33
- if (v.update_available)
34
- return {
35
- kind: "warn",
36
- label: t("version.available.label", { latest: v.latest }),
37
- note: t("version.available.note", { version: v.version }),
38
- };
39
- return {
40
- kind: "ok",
41
- label: t("version.current.label"),
42
- // `install_spec` is CLI data (a package spec), so it is interpolated, never
43
- // translated — only the sentence around it is ours.
44
- note: t("version.current.note", { version: v.version, source: v.install_spec || "the install source" }),
45
- };
46
- }
47
-
48
- /* ---------------------------------------------------------------- banners -- */
49
-
50
- // The ONE thing that must be visible on every panel: config does NOT merge, so
51
- // a global install can win skill resolution while this panel edits a project
52
- // file nothing reads. Reported here, never fixed here — this UI never edits
53
- // global config, by design.
54
- // The update banner. It is a BUTTON, not a notice: the useful question is
55
- // "what changed", and the answer is one click away rather than on GitHub.
56
- async function renderUpdateBanner(host) {
57
- let v;
58
- try {
59
- v = await versionInfo();
60
- } catch (_) {
61
- return;
62
- }
63
- if (!v || !v.update_available) return;
64
-
65
- const b = el("button", "banner banner-update");
66
- b.type = "button";
67
- const inner = el("div");
68
- inner.append(el("strong", null, t("banner.update.title", { latest: v.latest, version: v.version })));
69
- inner.append(el("div", "note", t("banner.update.note")));
70
- b.append(el("span", "banner-badge", "NEW"), inner, el("span", "banner-more", t("banner.update.cta")));
71
- b.addEventListener("click", () => showChangelog(v));
72
- host.append(b);
73
- }
74
-
75
- // Fetched lazily — the modal is what needs the changelog, and paying for that
76
- // request on every page load to fill a box nobody opened is waste.
77
- async function showChangelog(v) {
78
- const body = el("div", "stack stack-sm");
79
- const slot = el("div", "stack stack-sm");
80
- slot.append(skeleton(4));
81
- body.append(slot);
82
-
83
- const close = modal({
84
- title: t("changelog.title", { version: v.latest }),
85
- body,
86
- actions: [
87
- { label: t("common.later"), onClick: (c) => c() },
88
- {
89
- label: t("changelog.goUpgrade"),
90
- cls: "btn-primary",
91
- onClick: (c) => {
92
- c();
93
- location.hash = "#/maintenance";
94
- // The spotlight is armed here and lands after the panel renders — a
95
- // highlight fired now would point at a node that does not exist yet.
96
- startUpgradeSpotlight();
97
- },
98
- },
99
- ],
100
- });
101
-
102
- try {
103
- const d = (await read("/api/changelog")).data;
104
- slot.replaceChildren();
105
- if (d.check_disabled) {
106
- slot.append(el("div", "note", t("changelog.disabled")));
107
- } else if (!d.fetched) {
108
- slot.append(el("div", "note", t("changelog.offline")));
109
- slot.append(el("div", "note", t("changelog.offlineNote")));
110
- } else if (!d.entries.length) {
111
- slot.append(el("div", "note", t("changelog.empty")));
112
- } else {
113
- for (const e of d.entries) {
114
- const sec = el("div", "cl-entry");
115
- const h = el("div", "cl-head");
116
- h.append(el("span", "cl-version", "v" + e.version));
117
- if (e.date) h.append(el("span", "cl-date", e.date));
118
- sec.append(h);
119
- if (e.title) sec.append(el("div", "cl-title", stripMd(e.title)));
120
- if (e.body) sec.append(el("div", "cl-body", reflowMd(stripMd(e.body))));
121
- slot.append(sec);
122
- }
123
- slot.append(el("div", "note", t("changelog.source", { src: d.source })));
124
- }
125
- } catch (e) {
126
- slot.replaceChildren(el("div", "note", t("changelog.loadFail", { err: e.message })));
127
- }
128
- return close;
129
- }
130
-
131
- async function renderBanners() {
132
- const host = $("#banners");
133
- host.replaceChildren();
134
- renderUpdateBanner(host);
135
- let doctor;
136
- try {
137
- doctor = (await read("/api/doctor")).data;
138
- } catch (_) {
139
- return;
140
- }
141
- if (!doctor) return;
142
- const g = doctor.global_install || {};
143
- if (g.present && g.shadows) {
144
- const finding = (doctor.findings || []).find((f) => f.id === "global-skew" || f.id === "global-retired-agents");
145
- const b = el("div", "banner");
146
- // Prose inside the banner flex row, not a panel container: its lines are
147
- // meant to read tight, so it is deliberately not a `stack`.
148
- const bannerBody = el("div");
149
- bannerBody.append(el("strong", null, t("banner.global.title")));
150
- bannerBody.append(el("div", null, t("banner.global.body")));
151
- // The finding's own text is the CLI speaking. It is shown verbatim in every
152
- // language — a doctor message names files and commands.
153
- if (finding) bannerBody.append(el("div", "note", finding.message));
154
- // The command that actually clears the finding, copyable. This panel is
155
- // project-scoped and never writes global config, so handing over the exact
156
- // line to paste is the whole of what it can do — and a wrong line here is
157
- // why the warning felt permanent.
158
- const fixCmd = (doctor.findings || []).map((f) => f.fix_command).find(Boolean);
159
- if (fixCmd) {
160
- const row = el("div", "banner-fix");
161
- row.append(el("code", "action-cmd", fixCmd));
162
- const cp = el("button", "btn btn-ghost btn-sm", t("common.copy"));
163
- cp.type = "button";
164
- cp.addEventListener("click", () => copy(fixCmd, t("common.copied")));
165
- row.append(cp);
166
- bannerBody.append(row);
167
- }
168
- bannerBody.append(el("div", "note", t("banner.global.check")));
169
- b.append(bannerBody);
170
- host.append(b);
171
- }
172
- }
1
+ "use strict";
2
+ /* 05-banners.js — orc ui client
3
+ The shared version promise, the update banner, the changelog modal, and
4
+ renderBanners — including the global-install warning that must be visible on
5
+ EVERY panel (config does not merge, so a global install can win skill
6
+ resolution while this panel edits a project file nothing reads).
7
+
8
+ Loaded by app.html in the order its numeric prefix names. Classic
9
+ script, no import/export: an ES module import carries no query string,
10
+ and every static request here needs the per-launch session token. */
11
+
12
+ /* --------------------------------------------------------------- version -- */
13
+ // `orc version --json` performs a REAL bounded network check against the
14
+ // install source and reports {version, latest, update_available}. Three places
15
+ // want that answer — the Overview tile, the rail badge and the Maintenance
16
+ // upgrade row — so the promise is shared: one check per page load, not three.
17
+ // `refresh()` is the only way to force a second one, and it is a button the
18
+ // user presses; nothing here polls for a new release on its own.
19
+ let versionPromise = null;
20
+ const versionInfo = () => (versionPromise = versionPromise || read("/api/version").then((r) => r.data));
21
+ function refreshVersion() {
22
+ versionPromise = null;
23
+ return versionInfo();
24
+ }
25
+
26
+ // The one place that decides what a version payload MEANS, so the tile, the rail
27
+ // and the upgrade row can never disagree about it. `latest: null` is not "up to
28
+ // date" — it is "we could not tell", which is a different thing to show.
29
+ function versionState(v) {
30
+ if (!v) return { kind: "", label: t("version.unknown.label"), note: t("version.unknown.note") };
31
+ if (v.check_disabled) return { kind: "", label: t("version.off.label"), note: t("version.off.note") };
32
+ if (!v.latest) return { kind: "warn", label: t("version.offline.label"), note: t("version.offline.note") };
33
+ if (v.update_available)
34
+ return {
35
+ kind: "warn",
36
+ label: t("version.available.label", { latest: v.latest }),
37
+ note: t("version.available.note", { version: v.version }),
38
+ };
39
+ return {
40
+ kind: "ok",
41
+ label: t("version.current.label"),
42
+ // `install_spec` is CLI data (a package spec), so it is interpolated, never
43
+ // translated — only the sentence around it is ours.
44
+ note: t("version.current.note", { version: v.version, source: v.install_spec || "the install source" }),
45
+ };
46
+ }
47
+
48
+ /* ---------------------------------------------------------------- banners -- */
49
+
50
+ // The ONE thing that must be visible on every panel: config does NOT merge, so
51
+ // a global install can win skill resolution while this panel edits a project
52
+ // file nothing reads. Reported here, never fixed here — this UI never edits
53
+ // global config, by design.
54
+ // The update banner. It is a BUTTON, not a notice: the useful question is
55
+ // "what changed", and the answer is one click away rather than on GitHub.
56
+ async function renderUpdateBanner(host, gen) {
57
+ let v;
58
+ try {
59
+ v = await versionInfo();
60
+ } catch (_) {
61
+ return;
62
+ }
63
+ // A newer renderBanners call has cleared the host: do not add a second copy.
64
+ if (gen !== bannerGen) return;
65
+ if (!v || !v.update_available) return;
66
+
67
+ const b = el("button", "banner banner-update");
68
+ b.type = "button";
69
+ const inner = el("div");
70
+ inner.append(el("strong", null, t("banner.update.title", { latest: v.latest, version: v.version })));
71
+ inner.append(el("div", "note", t("banner.update.note")));
72
+ b.append(el("span", "banner-badge", "NEW"), inner, el("span", "banner-more", t("banner.update.cta")));
73
+ b.addEventListener("click", () => showChangelog(v));
74
+ host.append(b);
75
+ }
76
+
77
+ // Fetched lazily — the modal is what needs the changelog, and paying for that
78
+ // request on every page load to fill a box nobody opened is waste.
79
+ async function showChangelog(v) {
80
+ const body = el("div", "stack stack-sm");
81
+ const slot = el("div", "stack stack-sm");
82
+ slot.append(skeleton(4));
83
+ body.append(slot);
84
+
85
+ const close = modal({
86
+ title: t("changelog.title", { version: v.latest }),
87
+ body,
88
+ actions: [
89
+ { label: t("common.later"), onClick: (c) => c() },
90
+ {
91
+ label: t("changelog.goUpgrade"),
92
+ cls: "btn-primary",
93
+ onClick: (c) => {
94
+ c();
95
+ location.hash = "#/maintenance";
96
+ // The spotlight is armed here and lands after the panel renders — a
97
+ // highlight fired now would point at a node that does not exist yet.
98
+ startUpgradeSpotlight();
99
+ },
100
+ },
101
+ ],
102
+ });
103
+
104
+ try {
105
+ const d = (await read("/api/changelog")).data;
106
+ slot.replaceChildren();
107
+ if (d.check_disabled) {
108
+ slot.append(el("div", "note", t("changelog.disabled")));
109
+ } else if (!d.fetched) {
110
+ slot.append(el("div", "note", t("changelog.offline")));
111
+ slot.append(el("div", "note", t("changelog.offlineNote")));
112
+ } else if (!d.entries.length) {
113
+ slot.append(el("div", "note", t("changelog.empty")));
114
+ } else {
115
+ for (const e of d.entries) {
116
+ const sec = el("div", "cl-entry");
117
+ const h = el("div", "cl-head");
118
+ h.append(el("span", "cl-version", "v" + e.version));
119
+ if (e.date) h.append(el("span", "cl-date", e.date));
120
+ sec.append(h);
121
+ if (e.title) sec.append(el("div", "cl-title", stripMd(e.title)));
122
+ if (e.body) sec.append(el("div", "cl-body", reflowMd(stripMd(e.body))));
123
+ slot.append(sec);
124
+ }
125
+ slot.append(el("div", "note", t("changelog.source", { src: d.source })));
126
+ }
127
+ } catch (e) {
128
+ slot.replaceChildren(el("div", "note", t("changelog.loadFail", { err: e.message })));
129
+ }
130
+ return close;
131
+ }
132
+
133
+ // The router calls this on every route, so two calls can overlap. Both banners
134
+ // are appended after an await, so only the NEWEST call may append (here and in
135
+ // renderUpdateBanner), or a banner shows twice.
136
+ let bannerGen = 0;
137
+ async function renderBanners() {
138
+ const gen = ++bannerGen;
139
+ const host = $("#banners");
140
+ host.replaceChildren();
141
+ renderUpdateBanner(host, gen);
142
+ let doctor;
143
+ try {
144
+ doctor = (await read("/api/doctor")).data;
145
+ } catch (_) {
146
+ return;
147
+ }
148
+ if (gen !== bannerGen) return;
149
+ if (!doctor) return;
150
+ const g = doctor.global_install || {};
151
+ if (g.present && g.shadows) {
152
+ const finding = (doctor.findings || []).find((f) => f.id === "global-skew" || f.id === "global-retired-agents");
153
+ const b = el("div", "banner");
154
+ // Prose inside the banner flex row, not a panel container: its lines are
155
+ // meant to read tight, so it is deliberately not a `stack`.
156
+ const bannerBody = el("div");
157
+ bannerBody.append(el("strong", null, t("banner.global.title")));
158
+ bannerBody.append(el("div", null, t("banner.global.body")));
159
+ // The finding's own text is the CLI speaking. It is shown verbatim in every
160
+ // language — a doctor message names files and commands.
161
+ if (finding) bannerBody.append(el("div", "note", finding.message));
162
+ // The command that actually clears the finding, copyable. This panel is
163
+ // project-scoped and never writes global config, so handing over the exact
164
+ // line to paste is the whole of what it can do — and a wrong line here is
165
+ // why the warning felt permanent.
166
+ // The global finding's OWN command — not the first fix of any finding.
167
+ const fixCmd = finding && finding.fix_command;
168
+ if (fixCmd) {
169
+ const row = el("div", "banner-fix");
170
+ row.append(el("code", "action-cmd", fixCmd));
171
+ const cp = el("button", "btn btn-ghost btn-sm", t("common.copy"));
172
+ cp.type = "button";
173
+ cp.addEventListener("click", () => copy(fixCmd, t("common.copied")));
174
+ row.append(cp);
175
+ bannerBody.append(row);
176
+ }
177
+ bannerBody.append(el("div", "note", t("banner.global.check")));
178
+ b.append(bannerBody);
179
+ host.append(b);
180
+ }
181
+ }