@azure-id/orc 1.9.2 → 2.0.2

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 (172) hide show
  1. package/CHANGELOG.md +321 -0
  2. package/README-id.md +21 -36
  3. package/README.md +25 -36
  4. package/bin/build-agents.js +117 -20
  5. package/bin/cli.js +725 -29
  6. package/bin/gotcha-import.js +1081 -0
  7. package/bin/gotcha.js +1286 -0
  8. package/bin/graph-query.js +1 -1
  9. package/bin/graph.js +717 -717
  10. package/bin/habit.js +1453 -0
  11. package/bin/mockrun-catalog.js +281 -276
  12. package/bin/run-undo.js +398 -0
  13. package/bin/trace-write.js +657 -0
  14. package/bin/verify-contracts.js +525 -79
  15. package/bin/verify-package.js +51 -4
  16. package/bin/webui/api.js +26 -0
  17. package/bin/webui/app.html +239 -232
  18. package/bin/webui/css/00-tokens.css +110 -92
  19. package/bin/webui/css/04-motion.css +87 -0
  20. package/bin/webui/css/06-responsive.css +203 -178
  21. package/bin/webui/css/panels/behaviour.css +205 -0
  22. package/bin/webui/fixtures/behaviour.js +532 -0
  23. package/bin/webui/fixtures/index.js +34 -0
  24. package/bin/webui/fixtures/knowledge.js +7 -1
  25. package/bin/webui/fixtures/stats.js +16 -0
  26. package/bin/webui/i18n/en/behaviour.json +143 -0
  27. package/bin/webui/i18n/en/nav.json +25 -24
  28. package/bin/webui/i18n/en/tour.json +37 -35
  29. package/bin/webui/i18n/id/behaviour.json +143 -0
  30. package/bin/webui/i18n/id/nav.json +25 -24
  31. package/bin/webui/i18n/id/tour.json +37 -35
  32. package/bin/webui/js/01-i18n.js +155 -154
  33. package/bin/webui/js/90-tour.js +498 -494
  34. package/bin/webui/js/91-shortcuts.js +126 -126
  35. package/bin/webui/js/99-boot.js +121 -118
  36. package/bin/webui/js/panels/behaviour.js +1022 -0
  37. package/mock-run/INDEX.md +109 -107
  38. package/mock-run/gotcha-import.md +118 -0
  39. package/mock-run/habits.md +129 -0
  40. package/mock-run/orc-quick.md +6 -1
  41. package/package.json +1 -1
  42. package/templates/agents/MODEL-MAPPING.md +7 -7
  43. package/templates/agents/orc-advisor-opus-5-xhigh.md +1 -7
  44. package/templates/agents/orc-analyze-mini-opus-5-med.md +1 -6
  45. package/templates/agents/orc-analyze-mini-sonnet-5-high.md +1 -4
  46. package/templates/agents/orc-claude-writer-opus-4-8-high.md +48 -53
  47. package/templates/agents/orc-claude-writer-opus-5-med.md +1 -8
  48. package/templates/agents/orc-context-combiner-opus-5-high.md +1 -11
  49. package/templates/agents/orc-executor-haiku-4-5.md +14 -6
  50. package/templates/agents/orc-executor-opus-4-7-high.md +14 -6
  51. package/templates/agents/orc-executor-opus-4-7-med.md +14 -6
  52. package/templates/agents/orc-executor-opus-4-8-high.md +14 -6
  53. package/templates/agents/orc-executor-opus-5-high.md +14 -6
  54. package/templates/agents/orc-executor-opus-5-low.md +14 -6
  55. package/templates/agents/orc-executor-opus-5-med.md +14 -6
  56. package/templates/agents/orc-executor-sonnet-4-6-high.md +14 -6
  57. package/templates/agents/orc-executor-sonnet-4-6-med.md +14 -6
  58. package/templates/agents/orc-executor-sonnet-5-high.md +14 -6
  59. package/templates/agents/orc-graph-noter-sonnet-4-6-med.md +1 -10
  60. package/templates/agents/orc-judge-opus-5-xhigh.md +5 -9
  61. package/templates/agents/orc-learn-writer-opus-5-low.md +1 -7
  62. package/templates/agents/orc-pattern-codifier-opus-5-med.md +1 -8
  63. package/templates/agents/orc-pattern-codifier-sonnet-5-high.md +58 -63
  64. package/templates/agents/orc-planner-mini-opus-5-med.md +1 -4
  65. package/templates/agents/orc-planner-mini-sonnet-5-high.md +1 -2
  66. package/templates/agents/orc-planner-opus-5-med.md +1 -4
  67. package/templates/agents/orc-recon-opus-5-low.md +1 -8
  68. package/templates/agents/orc-recon-sonnet-4-6-med.md +1 -8
  69. package/templates/agents/orc-retro-opus-5-med.md +6 -8
  70. package/templates/agents/orc-retro-sonnet-5-high.md +6 -7
  71. package/templates/agents/orc-reviewer-opus-5-med.md +52 -16
  72. package/templates/agents/orc-scout-opus-5-low.md +1 -6
  73. package/templates/agents/orc-scout-sonnet-4-6-high.md +35 -39
  74. package/templates/agents/orc-system-analyst-opus-5-high.md +1 -6
  75. package/templates/agents/orc-test-author-opus-5-med.md +4 -5
  76. package/templates/agents/orc-trace-writer-haiku-4-5.md +3 -7
  77. package/templates/agents/orc-verifier-opus-5-med.md +16 -8
  78. package/templates/agents/orc-wiki-scanner-opus-4-8-high.md +74 -79
  79. package/templates/agents/orc-wiki-scanner-opus-5-med.md +1 -8
  80. package/templates/agents/orc-wiki-scanner-sonnet-5-high.md +97 -106
  81. package/templates/commands/orc-analyze.md +13 -21
  82. package/templates/commands/orc-fast.md +10 -15
  83. package/templates/commands/orc-poly.md +12 -21
  84. package/templates/commands/orc-pr-driver.md +11 -30
  85. package/templates/commands/orc-pr-setup.md +10 -31
  86. package/templates/commands/orc-route.md +11 -41
  87. package/templates/commands/orc-test.md +5 -60
  88. package/templates/hooks/README.md +34 -0
  89. package/templates/hooks/orc-session-hook.js +264 -0
  90. package/templates/hooks/orc-statusline.js +3 -1
  91. package/templates/skills/_shared/README.md +9 -0
  92. package/templates/skills/_shared/code-graph.md +47 -55
  93. package/templates/skills/_shared/config-precedence.md +3 -1
  94. package/templates/skills/_shared/extra-dispatch.md +73 -88
  95. package/templates/skills/_shared/gotchas.md +228 -177
  96. package/templates/skills/_shared/habits.md +101 -0
  97. package/templates/skills/_shared/lane-contract.md +84 -0
  98. package/templates/skills/_shared/phases/README.md +142 -83
  99. package/templates/skills/_shared/phases/analyst-gates.md +10 -21
  100. package/templates/skills/_shared/phases/execution.md +8 -14
  101. package/templates/skills/_shared/phases/house-rules.md +27 -32
  102. package/templates/skills/_shared/phases/intake.md +127 -133
  103. package/templates/skills/_shared/phases/mock-example.md +46 -56
  104. package/templates/skills/_shared/phases/plan-handoff.md +91 -97
  105. package/templates/skills/_shared/phases/planning.md +7 -17
  106. package/templates/skills/_shared/phases/preflight.md +19 -42
  107. package/templates/skills/_shared/phases/review.md +23 -27
  108. package/templates/skills/_shared/phases/rules.md +18 -42
  109. package/templates/skills/_shared/phases/scoring.md +55 -65
  110. package/templates/skills/_shared/phases/security-checklist.md +46 -50
  111. package/templates/skills/_shared/phases/security.md +45 -55
  112. package/templates/skills/_shared/phases/ship.md +6 -15
  113. package/templates/skills/_shared/phases/stop-resume.md +2 -5
  114. package/templates/skills/_shared/phases/summary.md +73 -48
  115. package/templates/skills/_shared/phases/testgen.md +41 -51
  116. package/templates/skills/_shared/phases/trace-verbs.md +433 -0
  117. package/templates/skills/_shared/phases/trace.md +136 -367
  118. package/templates/skills/_shared/phases/verify.md +62 -70
  119. package/templates/skills/_shared/phases/wave-grouping.md +128 -133
  120. package/templates/skills/_shared/phases/wiki-consult.md +10 -6
  121. package/templates/skills/_shared/read-ladder.md +2 -55
  122. package/templates/skills/_shared/return-validation.md +17 -70
  123. package/templates/skills/_shared/review-slice.md +79 -0
  124. package/templates/skills/_shared/smoke-gate.md +46 -28
  125. package/templates/skills/context-combiner/SKILL.md +15 -44
  126. package/templates/skills/orc/SKILL.md +34 -62
  127. package/templates/skills/orc/references/pattern-gate.md +89 -89
  128. package/templates/skills/orc/references/phases/intake.md +41 -47
  129. package/templates/skills/orc/references/phases/integration.md +13 -19
  130. package/templates/skills/orc/references/preflight-report.md +8 -9
  131. package/templates/skills/orc/references/ultra-mode.md +8 -6
  132. package/templates/skills/orc/subskills/orc-execution/SKILL.md +27 -73
  133. package/templates/skills/orc/subskills/orc-execution/core.md +12 -99
  134. package/templates/skills/orc/subskills/orc-execution/subagent.md +14 -13
  135. package/templates/skills/orc/subskills/orc-review-verify/SKILL.md +11 -52
  136. package/templates/skills/orc/subskills/orc-review-verify/core.md +52 -135
  137. package/templates/skills/orc/subskills/orc-review-verify/subagent.md +7 -7
  138. package/templates/skills/orc/subskills/orc-testgen/SKILL.md +11 -22
  139. package/templates/skills/orc/subskills/orc-testgen/core.md +20 -59
  140. package/templates/skills/orc/subskills/orc-testgen/subagent.md +7 -7
  141. package/templates/skills/orc-advisor/SKILL.md +56 -60
  142. package/templates/skills/orc-analyze/SKILL.md +30 -66
  143. package/templates/skills/orc-analyze/schemas/report-audit.md +2 -1
  144. package/templates/skills/orc-analyze/schemas/report-prose.md +2 -1
  145. package/templates/skills/orc-analyze-mini/SKILL.md +35 -70
  146. package/templates/skills/orc-diy/README.md +31 -0
  147. package/templates/skills/orc-diy/SKILL.md +23 -74
  148. package/templates/skills/orc-diy/references/blocks/pattern.md +18 -18
  149. package/templates/skills/orc-fast/SKILL.md +44 -72
  150. package/templates/skills/orc-judge/SKILL.md +77 -82
  151. package/templates/skills/orc-mini/SKILL.md +61 -109
  152. package/templates/skills/orc-pattern/SKILL.md +27 -48
  153. package/templates/skills/orc-poly/SKILL.md +32 -61
  154. package/templates/skills/orc-pr-driver/SKILL.md +25 -51
  155. package/templates/skills/orc-pr-driver/references/green-gate.md +113 -105
  156. package/templates/skills/orc-pr-setup/SKILL.md +20 -45
  157. package/templates/skills/orc-quick/README.md +43 -2
  158. package/templates/skills/orc-quick/SKILL.md +76 -107
  159. package/templates/skills/orc-quick/references/dispatch-gate.md +16 -5
  160. package/templates/skills/orc-quick/references/gh-mode.md +48 -1
  161. package/templates/skills/orc-quick/references/look.md +3 -1
  162. package/templates/skills/orc-retro/SKILL.md +19 -18
  163. package/templates/skills/orc-retro/examples/retro-mock.md +1 -1
  164. package/templates/skills/orc-route/SKILL.md +25 -45
  165. package/templates/skills/orc-test/SKILL.md +16 -37
  166. package/templates/skills/orc-verify/SKILL.md +14 -32
  167. package/templates/skills/orc-wait/SKILL.md +156 -163
  168. package/templates/skills/orc-wiki/references/phases/phase-0.md +1 -6
  169. package/templates/skills/orc-wiki/references/phases/phase-1.md +1 -6
  170. package/templates/skills/orc-wiki/references/phases/phase-2.md +1 -6
  171. package/templates/skills/orc-wiki/references/phases/phase-3.md +1 -6
  172. package/templates/skills/orc-wiki/references/phases/phase-3c.md +1 -6
@@ -1,126 +1,126 @@
1
- "use strict";
2
- /* 91-shortcuts.js — orc ui client
3
- Single keys, and ONLY when you are not typing into something.
4
-
5
- Loaded by app.html in the order its numeric prefix names. Classic
6
- script, no import/export: an ES module import carries no query string,
7
- and every static request here needs the per-launch session token. */
8
-
9
- /* =============================================================== shortcuts == */
10
-
11
- // Keyboard nav for a panel app that is otherwise all mouse. Deliberately small
12
- // and unmodified: single keys, and ONLY when you are not typing into something.
13
- const SHORTCUTS = () => [
14
- ["1 – 9, 0", t("shortcuts.panels")],
15
- ["d · c · p · b · h · e · m", t("shortcuts.panelsLetters")],
16
- ["/", t("shortcuts.filter")],
17
- ["r", t("shortcuts.reload")],
18
- ["t", t("shortcuts.theme")],
19
- ["l", t("shortcuts.lang")],
20
- ["?", t("shortcuts.list")],
21
- ["Esc", t("shortcuts.escape")],
22
- ];
23
-
24
- // A keystroke must never be stolen from an input, a textarea or a select — that
25
- // is how a UI eats the "r" in the middle of a path somebody is typing.
26
- function typingInto(t) {
27
- return !!t && (t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.tagName === "SELECT" || t.isContentEditable);
28
- }
29
-
30
- function showShortcuts() {
31
- const body = el("div", "stack stack-sm");
32
- const list = el("dl", "kv");
33
- for (const [key, what] of SHORTCUTS()) {
34
- const dt = el("dt");
35
- dt.append(el("kbd", null, key));
36
- list.append(dt, el("dd", null, what));
37
- }
38
- body.append(list);
39
- body.append(el("div", "note", t("shortcuts.note")));
40
- modal({
41
- title: t("shortcuts.title"),
42
- body,
43
- actions: [
44
- // Dismissing the tour must never be a one-way door: it is skippable
45
- // precisely because it can be replayed.
46
- {
47
- label: t("shortcuts.replay"),
48
- onClick: (c) => {
49
- c();
50
- startFirstRunTour(metaInfo.project_root);
51
- },
52
- },
53
- { label: t("common.close"), onClick: (c) => c() },
54
- ],
55
- });
56
- }
57
-
58
- function installShortcuts() {
59
- document.addEventListener("keydown", (e) => {
60
- if (e.metaKey || e.ctrlKey || e.altKey) return;
61
- // A dialog owns the keyboard while it is open; it has its own Esc handler.
62
- if (!$("#modal-host").hidden) return;
63
- // …and so does a blocking tour step. Its own capture-phase handler has
64
- // already swallowed the key by now; this is the second lock on the door.
65
- if (tourActive && tourActive.blocking) return;
66
- if (typingInto(e.target)) return;
67
-
68
- if (e.key >= "0" && e.key <= "9") {
69
- // Matched on data-idx, not on position: the rail's order is HTML's to
70
- // decide, and a positional lookup silently rebinds every key the moment a
71
- // panel is inserted in the middle.
72
- const target = document.querySelector('#nav a[data-idx="' + e.key + '"]');
73
- if (target) {
74
- e.preventDefault();
75
- location.hash = target.getAttribute("href");
76
- }
77
- return;
78
- }
79
- // v0.46.0: the rail outgrew ten digits, so several panels carry a LETTER key.
80
- // Same lookup, same rule — matched on data-idx, never on position — and the
81
- // letters are checked before the r/t/l actions so a rail key can never be
82
- // shadowed by one of them. Adding a panel whose letter collides with an
83
- // action would break the action; p/b/h/m/c/d were free.
84
- //
85
- // The class is derived from the rail rather than re-listed, because that is
86
- // what went wrong the first time: `c` was given to Challenge in the markup
87
- // and never added to a hardcoded `[pbhm]`, so the key did nothing and
88
- // nothing failed. A rail key now works because it is IN THE RAIL.
89
- if (/^[a-z]$/.test(e.key)) {
90
- const target = document.querySelector('#nav a[data-idx="' + e.key + '"]');
91
- if (target) {
92
- e.preventDefault();
93
- location.hash = target.getAttribute("href");
94
- return;
95
- }
96
- }
97
- if (e.key === "/") {
98
- const f = $("#settings-filter");
99
- if (f) {
100
- e.preventDefault();
101
- f.focus();
102
- f.select();
103
- }
104
- return;
105
- }
106
- if (e.key === "r") {
107
- e.preventDefault();
108
- route();
109
- return;
110
- }
111
- if (e.key === "t") {
112
- e.preventDefault();
113
- $("#theme-toggle").click();
114
- return;
115
- }
116
- if (e.key === "l") {
117
- e.preventDefault();
118
- cycleLang();
119
- return;
120
- }
121
- if (e.key === "?") {
122
- e.preventDefault();
123
- showShortcuts();
124
- }
125
- });
126
- }
1
+ "use strict";
2
+ /* 91-shortcuts.js — orc ui client
3
+ Single keys, and ONLY when you are not typing into something.
4
+
5
+ Loaded by app.html in the order its numeric prefix names. Classic
6
+ script, no import/export: an ES module import carries no query string,
7
+ and every static request here needs the per-launch session token. */
8
+
9
+ /* =============================================================== shortcuts == */
10
+
11
+ // Keyboard nav for a panel app that is otherwise all mouse. Deliberately small
12
+ // and unmodified: single keys, and ONLY when you are not typing into something.
13
+ const SHORTCUTS = () => [
14
+ ["1 – 9, 0", t("shortcuts.panels")],
15
+ ["d · c · p · b · h · e · m · u", t("shortcuts.panelsLetters")],
16
+ ["/", t("shortcuts.filter")],
17
+ ["r", t("shortcuts.reload")],
18
+ ["t", t("shortcuts.theme")],
19
+ ["l", t("shortcuts.lang")],
20
+ ["?", t("shortcuts.list")],
21
+ ["Esc", t("shortcuts.escape")],
22
+ ];
23
+
24
+ // A keystroke must never be stolen from an input, a textarea or a select — that
25
+ // is how a UI eats the "r" in the middle of a path somebody is typing.
26
+ function typingInto(t) {
27
+ return !!t && (t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.tagName === "SELECT" || t.isContentEditable);
28
+ }
29
+
30
+ function showShortcuts() {
31
+ const body = el("div", "stack stack-sm");
32
+ const list = el("dl", "kv");
33
+ for (const [key, what] of SHORTCUTS()) {
34
+ const dt = el("dt");
35
+ dt.append(el("kbd", null, key));
36
+ list.append(dt, el("dd", null, what));
37
+ }
38
+ body.append(list);
39
+ body.append(el("div", "note", t("shortcuts.note")));
40
+ modal({
41
+ title: t("shortcuts.title"),
42
+ body,
43
+ actions: [
44
+ // Dismissing the tour must never be a one-way door: it is skippable
45
+ // precisely because it can be replayed.
46
+ {
47
+ label: t("shortcuts.replay"),
48
+ onClick: (c) => {
49
+ c();
50
+ startFirstRunTour(metaInfo.project_root);
51
+ },
52
+ },
53
+ { label: t("common.close"), onClick: (c) => c() },
54
+ ],
55
+ });
56
+ }
57
+
58
+ function installShortcuts() {
59
+ document.addEventListener("keydown", (e) => {
60
+ if (e.metaKey || e.ctrlKey || e.altKey) return;
61
+ // A dialog owns the keyboard while it is open; it has its own Esc handler.
62
+ if (!$("#modal-host").hidden) return;
63
+ // …and so does a blocking tour step. Its own capture-phase handler has
64
+ // already swallowed the key by now; this is the second lock on the door.
65
+ if (tourActive && tourActive.blocking) return;
66
+ if (typingInto(e.target)) return;
67
+
68
+ if (e.key >= "0" && e.key <= "9") {
69
+ // Matched on data-idx, not on position: the rail's order is HTML's to
70
+ // decide, and a positional lookup silently rebinds every key the moment a
71
+ // panel is inserted in the middle.
72
+ const target = document.querySelector('#nav a[data-idx="' + e.key + '"]');
73
+ if (target) {
74
+ e.preventDefault();
75
+ location.hash = target.getAttribute("href");
76
+ }
77
+ return;
78
+ }
79
+ // v0.46.0: the rail outgrew ten digits, so several panels carry a LETTER key.
80
+ // Same lookup, same rule — matched on data-idx, never on position — and the
81
+ // letters are checked before the r/t/l actions so a rail key can never be
82
+ // shadowed by one of them. Adding a panel whose letter collides with an
83
+ // action would break the action; p/b/h/m/c/d were free.
84
+ //
85
+ // The class is derived from the rail rather than re-listed, because that is
86
+ // what went wrong the first time: `c` was given to Challenge in the markup
87
+ // and never added to a hardcoded `[pbhm]`, so the key did nothing and
88
+ // nothing failed. A rail key now works because it is IN THE RAIL.
89
+ if (/^[a-z]$/.test(e.key)) {
90
+ const target = document.querySelector('#nav a[data-idx="' + e.key + '"]');
91
+ if (target) {
92
+ e.preventDefault();
93
+ location.hash = target.getAttribute("href");
94
+ return;
95
+ }
96
+ }
97
+ if (e.key === "/") {
98
+ const f = $("#settings-filter");
99
+ if (f) {
100
+ e.preventDefault();
101
+ f.focus();
102
+ f.select();
103
+ }
104
+ return;
105
+ }
106
+ if (e.key === "r") {
107
+ e.preventDefault();
108
+ route();
109
+ return;
110
+ }
111
+ if (e.key === "t") {
112
+ e.preventDefault();
113
+ $("#theme-toggle").click();
114
+ return;
115
+ }
116
+ if (e.key === "l") {
117
+ e.preventDefault();
118
+ cycleLang();
119
+ return;
120
+ }
121
+ if (e.key === "?") {
122
+ e.preventDefault();
123
+ showShortcuts();
124
+ }
125
+ });
126
+ }
@@ -1,118 +1,121 @@
1
- "use strict";
2
- /* 99-boot.js — orc ui client
3
- MUST LOAD LAST. `boot()` is the one load-time call in the panel, and it reads
4
- bindings from every file above — a classic-script `const` is in TDZ until its
5
- own file has been evaluated.
6
-
7
- Loaded by app.html in the order its numeric prefix names. Classic
8
- script, no import/export: an ES module import carries no query string,
9
- and every static request here needs the per-launch session token. */
10
-
11
-
12
- /* ================================================================= startup == */
13
-
14
- async function boot() {
15
- // Language first, and English is loaded UNCONDITIONALLY: it is the fallback
16
- // table every other language falls back to, so it must exist before the first
17
- // t() call regardless of which language is selected. If even English cannot be
18
- // fetched, t() returns the key and the page is ugly but functional — never
19
- // blank, and never blocked on a file.
20
- try {
21
- DICT_EN = await loadLang("en");
22
- } catch (_) {
23
- DICT_EN = {};
24
- }
25
- let savedLang = "en";
26
- try {
27
- savedLang = localStorage.getItem(LANG_KEY) || "en";
28
- } catch (_) {}
29
- // No rerender: nothing has been routed yet, and route() is called below.
30
- await setLang(savedLang, { rerender: false });
31
-
32
- // Meta next: it names the project in the rail and tells us whether we are
33
- // looking at fixtures, which must never be mistaken for a real install.
34
- try {
35
- const meta = await api("/api/meta");
36
- metaInfo = meta;
37
- const proj = $("#rail-project");
38
- proj.textContent = meta.fixtures ? t("rail.fixtures") : meta.project_root || "";
39
- proj.title = meta.project_root || "";
40
- $("#rail-version").textContent = (meta.fixtures ? "fixtures · " : "") + "v" + (meta.version || "?");
41
-
42
- // A newer release is worth ONE quiet dot in the rail, on every panel — not a
43
- // banner, not a modal. It links to the panel that can actually install it;
44
- // it never installs anything itself.
45
- versionInfo()
46
- .then((v) => {
47
- if (!v || !v.update_available) return;
48
- const link = el("a", "rail-update", "");
49
- link.href = "#/maintenance";
50
- link.append(el("span", "dot dot-warn"), document.createTextNode(`v${v.latest} available`));
51
- link.title = `You have ${v.version}. Maintenance → upgrade installs ${v.latest}.`;
52
- $("#rail-version").after(link);
53
- })
54
- .catch(() => {});
55
- if (meta.fixtures) {
56
- const b = el("div", "banner");
57
- b.append(el("div", null, t("banner.fixtures")));
58
- $("#banners").append(b);
59
- }
60
- } catch (_) {
61
- document.body.replaceChildren(
62
- (() => {
63
- const e2 = el("div", "empty", t("banner.noToken"));
64
- e2.append(el("div", "note", t("banner.noTokenHint")));
65
- return e2;
66
- })()
67
- );
68
- return;
69
- }
70
-
71
- // Theme: dark-first, remembered, and both painted explicitly.
72
- const saved = localStorage.getItem("orc-ui-theme");
73
- if (saved) document.documentElement.setAttribute("data-theme", saved);
74
- const tt = $("#theme-toggle");
75
- const syncTheme = () => (tt.textContent = document.documentElement.getAttribute("data-theme") === "light" ? t("rail.dark") : t("rail.light"));
76
- syncTheme();
77
- tt.addEventListener("click", () => {
78
- const next = document.documentElement.getAttribute("data-theme") === "light" ? "dark" : "light";
79
- document.documentElement.setAttribute("data-theme", next);
80
- localStorage.setItem("orc-ui-theme", next);
81
- syncTheme();
82
- });
83
-
84
- // Language, right below it in the rail: the same class of control (a
85
- // per-browser display preference), so it lives in the same place and is
86
- // remembered the same way. It never touches project config.
87
- const lb = $("#lang-toggle");
88
- if (lb) lb.addEventListener("click", cycleLang);
89
- applyStaticText();
90
-
91
- installShortcuts();
92
- const help = $("#shortcut-hint");
93
- if (help) help.addEventListener("click", showShortcuts);
94
-
95
- window.addEventListener("hashchange", route);
96
- route();
97
-
98
- // First run for THIS project → the tour. Fixture mode is excluded: a tour of
99
- // canned data would teach the panel using numbers that are not real.
100
- if (!metaInfo.fixtures && !tourSeen(metaInfo.project_root)) {
101
- // Let the first panel finish its fetch, so step one has something to point
102
- // at rather than a skeleton.
103
- setTimeout(() => startFirstRunTour(metaInfo.project_root), 600);
104
- }
105
-
106
- // Heartbeat: no ping from any client for 60s and the server exits, so closing
107
- // this tab shuts down a write surface instead of leaving it holding a token.
108
- const ping = () => api("/api/ping").catch(() => {});
109
- ping();
110
- setInterval(ping, 15000);
111
- window.addEventListener("beforeunload", () => {
112
- try {
113
- navigator.sendBeacon("/api/bye?t=" + encodeURIComponent(TOKEN));
114
- } catch (_) {}
115
- });
116
- }
117
-
118
- boot();
1
+ "use strict";
2
+ /* 99-boot.js — orc ui client
3
+ MUST LOAD LAST. `boot()` is the one load-time call in the panel, and it reads
4
+ bindings from every file above — a classic-script `const` is in TDZ until its
5
+ own file has been evaluated.
6
+
7
+ Loaded by app.html in the order its numeric prefix names. Classic
8
+ script, no import/export: an ES module import carries no query string,
9
+ and every static request here needs the per-launch session token. */
10
+
11
+
12
+ /* ================================================================= startup == */
13
+
14
+ async function boot() {
15
+ // Language first, and English is loaded UNCONDITIONALLY: it is the fallback
16
+ // table every other language falls back to, so it must exist before the first
17
+ // t() call regardless of which language is selected. If even English cannot be
18
+ // fetched, t() returns the key and the page is ugly but functional — never
19
+ // blank, and never blocked on a file.
20
+ try {
21
+ DICT_EN = await loadLang("en");
22
+ } catch (_) {
23
+ DICT_EN = {};
24
+ }
25
+ let savedLang = "en";
26
+ try {
27
+ savedLang = localStorage.getItem(LANG_KEY) || "en";
28
+ } catch (_) {}
29
+ // No rerender: nothing has been routed yet, and route() is called below.
30
+ await setLang(savedLang, { rerender: false });
31
+
32
+ // Meta next: it names the project in the rail and tells us whether we are
33
+ // looking at fixtures, which must never be mistaken for a real install.
34
+ try {
35
+ const meta = await api("/api/meta");
36
+ metaInfo = meta;
37
+ const proj = $("#rail-project");
38
+ proj.textContent = meta.fixtures ? t("rail.fixtures") : meta.project_root || "";
39
+ proj.title = meta.project_root || "";
40
+ $("#rail-version").textContent = (meta.fixtures ? "fixtures · " : "") + "v" + (meta.version || "?");
41
+
42
+ // A newer release is worth ONE quiet dot in the rail, on every panel — not a
43
+ // banner, not a modal. It links to the panel that can actually install it;
44
+ // it never installs anything itself.
45
+ versionInfo()
46
+ .then((v) => {
47
+ if (!v || !v.update_available) return;
48
+ const link = el("a", "rail-update", "");
49
+ link.href = "#/maintenance";
50
+ link.append(el("span", "dot dot-warn"), document.createTextNode(`v${v.latest} available`));
51
+ link.title = `You have ${v.version}. Maintenance → upgrade installs ${v.latest}.`;
52
+ $("#rail-version").after(link);
53
+ })
54
+ .catch(() => {});
55
+ if (meta.fixtures) {
56
+ const b = el("div", "banner");
57
+ b.append(el("div", null, t("banner.fixtures")));
58
+ $("#banners").append(b);
59
+ }
60
+ } catch (_) {
61
+ document.body.replaceChildren(
62
+ (() => {
63
+ const e2 = el("div", "empty", t("banner.noToken"));
64
+ e2.append(el("div", "note", t("banner.noTokenHint")));
65
+ return e2;
66
+ })()
67
+ );
68
+ return;
69
+ }
70
+
71
+ // Theme: dark-first, remembered, and both painted explicitly.
72
+ const saved = localStorage.getItem("orc-ui-theme");
73
+ if (saved) document.documentElement.setAttribute("data-theme", saved);
74
+ const tt = $("#theme-toggle");
75
+ const syncTheme = () => (tt.textContent = document.documentElement.getAttribute("data-theme") === "light" ? t("rail.dark") : t("rail.light"));
76
+ syncTheme();
77
+ tt.addEventListener("click", () => {
78
+ const next = document.documentElement.getAttribute("data-theme") === "light" ? "dark" : "light";
79
+ document.documentElement.setAttribute("data-theme", next);
80
+ localStorage.setItem("orc-ui-theme", next);
81
+ syncTheme();
82
+ });
83
+
84
+ // Language, right below it in the rail: the same class of control (a
85
+ // per-browser display preference), so it lives in the same place and is
86
+ // remembered the same way. It never touches project config.
87
+ const lb = $("#lang-toggle");
88
+ if (lb) lb.addEventListener("click", cycleLang);
89
+ applyStaticText();
90
+
91
+ installShortcuts();
92
+ const help = $("#shortcut-hint");
93
+ if (help) help.addEventListener("click", showShortcuts);
94
+
95
+ window.addEventListener("hashchange", route);
96
+ route();
97
+ // v2.0.0 W7 — a waiting habit proposal is ONE quiet dot on the Behaviour rail
98
+ // link. It is the only place outside that panel that shows habit state.
99
+ behaviourRailDot();
100
+
101
+ // First run for THIS project → the tour. Fixture mode is excluded: a tour of
102
+ // canned data would teach the panel using numbers that are not real.
103
+ if (!metaInfo.fixtures && !tourSeen(metaInfo.project_root)) {
104
+ // Let the first panel finish its fetch, so step one has something to point
105
+ // at rather than a skeleton.
106
+ setTimeout(() => startFirstRunTour(metaInfo.project_root), 600);
107
+ }
108
+
109
+ // Heartbeat: no ping from any client for 60s and the server exits, so closing
110
+ // this tab shuts down a write surface instead of leaving it holding a token.
111
+ const ping = () => api("/api/ping").catch(() => {});
112
+ ping();
113
+ setInterval(ping, 15000);
114
+ window.addEventListener("beforeunload", () => {
115
+ try {
116
+ navigator.sendBeacon("/api/bye?t=" + encodeURIComponent(TOKEN));
117
+ } catch (_) {}
118
+ });
119
+ }
120
+
121
+ boot();