@azure-id/orc 2.0.2 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/CHANGELOG.md +455 -0
  2. package/README-id.md +52 -23
  3. package/README.md +36 -20
  4. package/bin/build-agents.js +205 -206
  5. package/bin/clear-logs.js +620 -0
  6. package/bin/cli.js +47417 -46420
  7. package/bin/fix.js +459 -0
  8. package/bin/gotcha.js +288 -8
  9. package/bin/habit.js +1538 -1453
  10. package/bin/mockrun-catalog.js +3 -0
  11. package/bin/onboarding-content.js +2 -1
  12. package/bin/pricing.json +28 -21
  13. package/bin/trace-write.js +869 -657
  14. package/bin/verify-contracts.js +5552 -5471
  15. package/bin/verify-package.js +716 -705
  16. package/bin/webui/api.js +125 -6
  17. package/bin/webui/css/panels/hookui.css +79 -0
  18. package/bin/webui/fixtures/behaviour.js +30 -0
  19. package/bin/webui/fixtures/extra.js +2036 -2036
  20. package/bin/webui/fixtures/flow.js +81 -81
  21. package/bin/webui/fixtures/hookui.js +506 -431
  22. package/bin/webui/fixtures/index.js +32 -3
  23. package/bin/webui/fixtures/knowledge.js +2 -2
  24. package/bin/webui/fixtures/maintenance.js +123 -42
  25. package/bin/webui/fixtures/rules.js +1007 -1002
  26. package/bin/webui/fixtures/settings.js +6 -7
  27. package/bin/webui/fixtures/stats.js +9 -6
  28. package/bin/webui/fixtures/wait.js +98 -97
  29. package/bin/webui/i18n/en/behaviour.json +13 -0
  30. package/bin/webui/i18n/en/hookui.json +290 -194
  31. package/bin/webui/i18n/en/maintenance.json +71 -52
  32. package/bin/webui/i18n/en/settings.json +60 -60
  33. package/bin/webui/i18n/id/behaviour.json +13 -0
  34. package/bin/webui/i18n/id/hookui.json +290 -194
  35. package/bin/webui/i18n/id/maintenance.json +71 -52
  36. package/bin/webui/i18n/id/settings.json +60 -60
  37. package/bin/webui/js/05-banners.js +181 -172
  38. package/bin/webui/js/06-edit.js +193 -183
  39. package/bin/webui/js/panels/behaviour.js +77 -12
  40. package/bin/webui/js/panels/hookui.js +2196 -1691
  41. package/bin/webui/js/panels/maintenance.js +336 -235
  42. package/bin/webui/js/panels/settings.js +726 -719
  43. package/mock-run/INDEX.md +1 -0
  44. package/mock-run/a-normal-day.md +2 -2
  45. package/mock-run/extra-slots.md +177 -177
  46. package/mock-run/habits.md +1 -1
  47. package/mock-run/orc-aftermath.md +392 -392
  48. package/mock-run/orc-budget.md +29 -33
  49. package/mock-run/orc-cli.md +200 -200
  50. package/mock-run/orc-explain.md +86 -86
  51. package/mock-run/orc-extra.md +389 -392
  52. package/mock-run/orc-fast.md +2 -2
  53. package/mock-run/orc-fix.md +87 -0
  54. package/mock-run/orc-pattern.md +112 -112
  55. package/mock-run/orc-quick.md +146 -146
  56. package/mock-run/orc.md +4 -4
  57. package/package.json +1 -1
  58. package/templates/agents/MODEL-MAPPING.md +176 -173
  59. package/templates/agents/orc-analyze-mini-sonnet-5-high.md +2 -2
  60. package/templates/agents/{orc-claude-writer-opus-5-med.md → orc-claude-writer-opus-5-low.md} +4 -4
  61. package/templates/agents/orc-executor-haiku-4-5.md +1 -1
  62. package/templates/agents/orc-executor-opus-5-low.md +1 -1
  63. package/templates/agents/orc-executor-sonnet-4-6-high.md +1 -1
  64. package/templates/agents/orc-executor-sonnet-4-6-med.md +1 -1
  65. package/templates/agents/orc-executor-sonnet-5-high.md +2 -2
  66. package/templates/agents/orc-executor-sonnet-5-low.md +155 -0
  67. package/templates/agents/orc-executor-sonnet-5-med.md +155 -0
  68. package/templates/agents/orc-pattern-codifier-sonnet-5-high.md +2 -2
  69. package/templates/agents/orc-planner-mini-sonnet-5-high.md +2 -2
  70. package/templates/agents/{orc-recon-sonnet-4-6-med.md → orc-recon-sonnet-5-med.md} +4 -4
  71. package/templates/agents/orc-retro-sonnet-5-high.md +2 -2
  72. package/templates/agents/{orc-reviewer-opus-5-med.md → orc-reviewer-opus-5-low.md} +4 -4
  73. package/templates/agents/{orc-wiki-scanner-opus-5-med.md → orc-wiki-scanner-opus-5-low.md} +3 -3
  74. package/templates/agents/orc-wiki-scanner-sonnet-5-high.md +2 -2
  75. package/templates/commands/orc-fast.md +10 -10
  76. package/templates/commands/orc-fix.md +18 -0
  77. package/templates/commands/orc-wiki.md +64 -64
  78. package/templates/hooks/README.md +75 -7
  79. package/templates/hooks/orc-session-hook.js +78 -3
  80. package/templates/hooks/orc-statusline-render.js +317 -23
  81. package/templates/hooks/orc-statusline.js +162 -49
  82. package/templates/hooks/orc-subagent-line.js +23 -5
  83. package/templates/hooks/orc-weather-fetch.js +123 -0
  84. package/templates/skills/_shared/extra-dispatch.md +1331 -1331
  85. package/templates/skills/_shared/gotchas.md +27 -7
  86. package/templates/skills/_shared/habits.md +2 -2
  87. package/templates/skills/_shared/opus5-only.md +137 -137
  88. package/templates/skills/_shared/phases/review.md +2 -2
  89. package/templates/skills/_shared/phases/trace-verbs.md +17 -3
  90. package/templates/skills/_shared/phases/trace.md +136 -136
  91. package/templates/skills/_shared/review-slice.md +28 -1
  92. package/templates/skills/_shared/wait.md +1 -0
  93. package/templates/skills/orc/README.md +2 -2
  94. package/templates/skills/orc/SKILL.md +227 -227
  95. package/templates/skills/orc/config.md +25 -27
  96. package/templates/skills/orc/examples/full-run-mock.md +74 -73
  97. package/templates/skills/orc/references/effort-and-mode.md +222 -222
  98. package/templates/skills/orc/references/preflight-report.md +3 -3
  99. package/templates/skills/orc/references/ultra-mode.md +1 -1
  100. package/templates/skills/orc/schemas/checkpoint.md +122 -122
  101. package/templates/skills/orc/subskills/orc-review-verify/SKILL.md +3 -3
  102. package/templates/skills/orc/subskills/orc-review-verify/core.md +1 -1
  103. package/templates/skills/orc-analyze/SKILL.md +4 -2
  104. package/templates/skills/orc-claude/SKILL.md +14 -14
  105. package/templates/skills/orc-claude/examples/claude-run-mock.md +65 -65
  106. package/templates/skills/orc-diy/SKILL.md +5 -5
  107. package/templates/skills/orc-fast/SKILL.md +210 -208
  108. package/templates/skills/orc-fix/SKILL.md +110 -0
  109. package/templates/skills/orc-grill/SKILL.md +232 -232
  110. package/templates/skills/orc-grill/references/grill-doc.md +1 -1
  111. package/templates/skills/orc-mini/SKILL.md +4 -4
  112. package/templates/skills/orc-mini/examples/mini-run-mock.md +4 -2
  113. package/templates/skills/orc-quick/README.md +536 -536
  114. package/templates/skills/orc-quick/SKILL.md +2 -2
  115. package/templates/skills/orc-quick/references/context-doc.md +145 -145
  116. package/templates/skills/orc-quick/references/dispatch-gate.md +220 -219
  117. package/templates/skills/orc-quick/references/gh-mode.md +4 -2
  118. package/templates/skills/orc-retro/SKILL.md +1 -1
  119. package/templates/skills/orc-retro/examples/retro-mock.md +16 -16
  120. package/templates/skills/orc-wiki/README.md +325 -325
  121. package/templates/skills/orc-wiki/SKILL.md +195 -195
  122. package/templates/skills/orc-wiki/examples/wiki-run-mock.md +37 -37
  123. package/templates/skills/orc-wiki/references/extra.md +2 -2
  124. package/templates/skills/orc-wiki/references/partial-refresh.md +276 -271
  125. package/templates/skills/orc-wiki/references/phases/phase-0.md +1 -1
  126. package/templates/skills/orc-wiki/references/phases/phase-2.md +3 -3
  127. package/templates/skills/orc-wiki/references/phases/phase-3c.md +3 -2
  128. package/templates/agents/orc-claude-writer-opus-4-8-high.md +0 -48
  129. package/templates/agents/orc-wiki-scanner-opus-4-8-high.md +0 -74
@@ -214,18 +214,76 @@ move, and it tells you why.
214
214
  Every component can be changed: its words, its colour, its shape, how wide it
215
215
  is, and when it is allowed to disappear.
216
216
 
217
- **The shape decides what the other settings can do.** Only these shapes print a
218
- short name in front of the value: `plain`, `label-value`, `bracket`, `angle`,
219
- `badge`, `pill` and `stack`. A bar, an icon and a bare value have no room for
220
- one, so the panel switches the Name box off for them and says so. The same is
221
- true of the number settings: a bar has a width and a colour ramp, and no number
222
- format at all.
217
+ **The shape decides what the other settings can do.** The panel switches off a
218
+ setting that the shape does not use, and says so.
219
+
220
+ **Words around any shape** (v2.0.4). Every shape draws the name you type. You
221
+ choose where it goes: before, after, above or below the part. A bar can also have
222
+ text before and after it (for example `[██░░]`) and its number beside it. If you
223
+ type no name, only `plain`, `label-value`, `bracket`, `angle`, `badge`, `pill` and
224
+ `stack` print the part's built-in name.
225
+
226
+ **Captions.** A caption is a short text on a row above or below a part, at the
227
+ column of that part. `{value}` and `{label}` in the caption are replaced. A caption
228
+ makes the line one row taller.
229
+
230
+ **Small text.** A terminal has no font size. The letter styles `small` (ꜱᴍᴀʟʟ
231
+ ᴄᴀᴘꜱ), `super` (ˢᵘᵖᵉʳ) and `sub` (₆₁) use Unicode letters that look smaller.
232
+ A character with no small form stays as it is.
233
+
234
+ **Bars in your own colours and characters.** A bar can have its own fill colour,
235
+ empty colour, colour range (for example `#22c55e,#eab308,#ef4444`, blended cell by
236
+ cell on `gradient`), and its own fill and empty characters (one cell each).
237
+
238
+ **Every part** can also have brackets, space on the left and right, a background,
239
+ any `#rrggbb` colour, more than one weight, and its own colour and symbol per
240
+ state. A line can be aligned right or centre, and its separator can have a colour.
241
+ `text`, `divider`, `spacer`, `icon-static`, `fill` (pushes the rest to the right
242
+ edge) and `config` take their text in the part's settings.
243
+
244
+ **Claude Code removes the spaces at the start of every row.** So when a row of
245
+ your status line starts with a space (a caption row, a line prefix, a
246
+ right-aligned line), ORC makes that first space a braille blank (U+2800). It is one
247
+ cell wide and Claude Code keeps it, so a caption stays above or below its own part.
248
+ Under `ORC_STATUSLINE_ASCII=1` this is not done, because U+2800 is not ASCII.
249
+
250
+ **Pets that move** (the "Pets and motion" group): a pixel cat drawn in braille
251
+ dots, a mouse, a cat that chases the mouse, the text cat ᓚᘏᗢ, pac-man, a fish, a
252
+ bird and a dog. Each can `run` across its track, `bounce` right and back, or stay
253
+ `idle` and move its legs.
254
+
255
+ **Motion needs a timer.** Claude Code redraws the status line when something
256
+ happens (a message, typing), or every `refreshInterval` seconds if you set it (at
257
+ least 1). Set it with:
258
+
259
+ ```
260
+ orc statusline refresh 1
261
+ ```
262
+
263
+ and remove it with `orc statusline refresh off`. With the timer the hook runs
264
+ every second even when you do nothing, and each run starts `node`. So a pet moves
265
+ about one step per second.
266
+
267
+ **Weather** (`weather`, `weather-icon`, `weather-desc`). This is the ONLY part of
268
+ the status line that uses the network. The hook itself never waits for it: it reads
269
+ `.claude/orc/weather.json`, and when that is older than 30 minutes it starts
270
+ `orc-weather-fetch.js` in the background, which asks wttr.in and writes the file.
271
+ With an empty `location`, wttr.in finds your place from your network address. It
272
+ runs only while a weather part is on your layout. `ORC_STATUSLINE_NO_NET=1` stops
273
+ it. Every text it gets back is cleaned of control characters before it is saved.
274
+
275
+ **The panel shows your unsaved changes.** The picture above the board is drawn
276
+ from a temporary copy with your staged changes, so you see the result before you
277
+ press Apply. Nothing is written until you press Apply.
278
+
279
+ A layout may not carry a control character (for example ESC) in any text. It is
280
+ printed to your terminal, so the CLI refuses it.
223
281
 
224
282
  **Three things a terminal cannot do**, said plainly so you do not look for them:
225
283
 
226
284
  | You may want | What you get |
227
285
  |---|---|
228
- | A bigger font | The terminal owns the font size. Use **bold**, or make a component wider — a bar at width 12 is a big object. |
286
+ | A bigger font | The terminal owns the font size. Use **bold**, a small letter style for the words around it, or make a component wider — a bar at width 12 is a big object. |
229
287
  | A blinking part | Refused. Half of terminals turn it off, and no part of a status line needs it. |
230
288
  | Icon-font symbols | Not shipped. They need a font we cannot check for, and they draw as empty boxes without it. |
231
289
 
@@ -472,6 +530,16 @@ It never writes model text, and it blocks a stop only for the narration guard.
472
530
  authored. If the compiled file is missing, stale, or does not pass a cheap
473
531
  shape check, the hook silently renders the shipped lines instead and
474
532
  `orc doctor` names the reason.
533
+ - **Stale means the parts changed, not the version** (v2.0.3). `orc init` and
534
+ `orc update` write `catalog_hash` into `hooks/orc-version.json`, and the hook
535
+ compares it with the `catalog_hash` in `statusline.lock.json`. An install with
536
+ no hash compares the version, as before. `orc update` also recompiles every
537
+ layout that exists and validates, so an upgrade never leaves your bar on the
538
+ shipped lines. A good render deletes `orc/statusline-state.json`, so
539
+ `orc doctor` never reports a fallback that is already gone.
540
+ - `orc statusline show --json` carries `hook`: the hook the settings point to,
541
+ its version, whether it will run this layout, the last fallback, and the fix.
542
+ `orc ui` shows that on the status card.
475
543
  - The cache file is `.claude/orc/usage-session.json`. The hook reads it once and
476
544
  writes it once, after the text is ready. It stores raw numbers only — never a
477
545
  word like `fresh` or `STALE`, which is computed each time it is shown.
@@ -113,10 +113,45 @@ function narrationGuard(data) {
113
113
  } catch (_) {
114
114
  return false;
115
115
  }
116
- const lines = text.split(/\r?\n/).filter((l) => /^\[/.test(l));
116
+ const all = text.split(/\r?\n/).filter((l) => l.trim());
117
+ const lines = all.filter((l) => /^\[/.test(l));
117
118
  if (!lines.some((l) => /\]\s+hook\s+SPAWN /.test(l))) return false; // nothing was dispatched yet
118
- if (lines.some((l) => !/\]\s+hook\s+/.test(l))) return false; // a packet already landed
119
119
  const statePath = path.join(CLAUDE_DIR, "orc", "narration-guard.json");
120
+ // v2.1.0 (E21 D7) — narration written BY HAND. A live /orc-quick appended its
121
+ // lines itself (no `.mmm` stamp, or no stamp at all), so `orc trace write` never
122
+ // saw them: no grammar check, and none of the FINISH reminders (observe, which
123
+ // review, config, phases) ran — the lane then claimed the findings were
124
+ // "logged". The CLI writes every line to the .txt AND its .jsonl twin, so more
125
+ // narration lines than jsonl rows, or a line without the CLI's stamp, is proof.
126
+ const narr = all.filter((l) => !/^\[[^\]]*\]\s+hook\s+/.test(l));
127
+ if (narr.length) {
128
+ let rows = 0;
129
+ try {
130
+ rows = fs.readFileSync(path.join(dir, best.f + ".jsonl"), "utf8").split(/\r?\n/).filter((l) => l.trim()).length;
131
+ } catch (_) {}
132
+ // NOT the `.mmm` stamp: `orc trace write` also accepts HH:MM:SS (a live run
133
+ // proved it, and an early guard blocked a correct run on it).
134
+ const hand = narr.some((l) => !/^\[\d{6} \d{2}:\d{2}:\d{2}(?:\.\d{1,3})?\] /.test(l)) || narr.length > rows;
135
+ if (!hand) return reviewGuard(best, text, statePath); // a CLI packet already landed
136
+ try {
137
+ const st = JSON.parse(fs.readFileSync(statePath, "utf8"));
138
+ if (st.run === best.f && st.kind === "hand") return false;
139
+ } catch (_) {}
140
+ try {
141
+ fs.mkdirSync(path.dirname(statePath), { recursive: true });
142
+ fs.writeFileSync(statePath, JSON.stringify({ run: best.f, kind: "hand", at: Date.now() }) + "\n");
143
+ } catch (_) {}
144
+ process.stdout.write(
145
+ JSON.stringify({
146
+ decision: "block",
147
+ reason:
148
+ `ORC: the trace ${best.f} has lines written by hand — \`orc trace write\` never saw them, so no check and no FINISH reminder ran. ` +
149
+ `Never append to a trace yourself. Send the FINISH packet now through \`orc trace write --packet -\` (\`run: ${best.f.replace(/\.txt$/, "")}\` if .current is gone) ` +
150
+ `and do what its reply says (for a review: one \`orc gotcha observe\` per finding), then finish.`,
151
+ })
152
+ );
153
+ return true;
154
+ }
120
155
  try {
121
156
  if (JSON.parse(fs.readFileSync(statePath, "utf8")).run === best.f) return false;
122
157
  } catch (_) {}
@@ -136,8 +171,48 @@ function narrationGuard(data) {
136
171
  return true;
137
172
  }
138
173
 
174
+ // v2.1.0 (E21 D8) — a FINISHED run that reviewed and recorded nothing. A live
175
+ // /orc ignored the FINISH observe reminder after a review with findings; Review
176
+ // Quality then counts the review but learns no acceptance from it. ONCE per run,
177
+ // in the main session: the trace has a reviewer RETURN and a FINISH line, the
178
+ // review was not clean, and no observation names this run → block one stop.
179
+ function reviewGuard(best, text, statePath) {
180
+ if (!/\]\s+hook\s+RETURN\s+orc-reviewer-/.test(text) || !/\]\s+\S+\s+FINISH\b/.test(text)) return false;
181
+ const fo = /FINDING-OUTCOME addressed=(\d+) disputed=(\d+) wontfix=(\d+) open=(\d+)/.exec(text);
182
+ const f = /\]\s+\S+\s+FINDING p0=(\d+) p1=(\d+) p2=(\d+) p3=(\d+)/.exec(text);
183
+ if ((fo && fo.slice(1, 5).every((n) => n === "0")) || (!fo && f && f.slice(1, 5).every((n) => n === "0"))) return false; // clean
184
+ const run = best.f.replace(/\.txt$/, "");
185
+ try {
186
+ for (const line of fs.readFileSync(path.join(CLAUDE_DIR, "orc", "observations.jsonl"), "utf8").split(/\r?\n/)) {
187
+ if (!line.trim()) continue;
188
+ try {
189
+ const o = JSON.parse(line);
190
+ if (o && o.author === "orc" && o.run === run) return false;
191
+ } catch (_) {}
192
+ }
193
+ } catch (_) {}
194
+ try {
195
+ const st = JSON.parse(fs.readFileSync(statePath, "utf8"));
196
+ if (st.run === best.f && st.kind === "review") return false;
197
+ } catch (_) {}
198
+ try {
199
+ fs.mkdirSync(path.dirname(statePath), { recursive: true });
200
+ fs.writeFileSync(statePath, JSON.stringify({ run: best.f, kind: "review", at: Date.now() }) + "\n");
201
+ } catch (_) {}
202
+ process.stdout.write(
203
+ JSON.stringify({
204
+ decision: "block",
205
+ reason:
206
+ `ORC: the run ${run} ran a review with findings and recorded no outcome. Pipe ONE object per finding to \`orc gotcha observe - --json\` ` +
207
+ `(source review · author orc · run ${run} · ref "${run} · F<n>" · outcome addressed|disputed|wontfix|open · category · rule or sig), ` +
208
+ `and write the FINDING-OUTCOME line if it is missing (\`orc trace write --packet -\`, run: ${run}). Then finish.`,
209
+ })
210
+ );
211
+ return true;
212
+ }
213
+
139
214
  // ── The review card at SubagentStart (v2.0.2, eval D13) ─────────────────────
140
- // A live /orc-quick review dispatched `orc-reviewer-opus-5-med` with NO gotcha
215
+ // A live /orc-quick review dispatched `orc-reviewer-opus-5-low` with NO gotcha
141
216
  // card in its prompt in 5 of 5 runs, although review learning is always on. The
142
217
  // card is a CLI answer, so the hook hands it over itself: for a reviewer,
143
218
  // verifier or judge, the files git sees as changed → `orc gotcha card` → one