@azure-id/orc 1.9.1 → 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.
- package/CHANGELOG.md +390 -0
- package/README-id.md +20 -36
- package/README.md +24 -34
- package/bin/build-agents.js +206 -109
- package/bin/cli.js +760 -62
- package/bin/gotcha-import.js +1081 -0
- package/bin/gotcha.js +1286 -0
- package/bin/graph-query.js +1 -1
- package/bin/graph.js +717 -717
- package/bin/habit.js +1453 -0
- package/bin/mockrun-catalog.js +281 -276
- package/bin/onboarding-content.js +4 -4
- package/bin/pricing.json +207 -200
- package/bin/run-undo.js +398 -0
- package/bin/trace-write.js +657 -0
- package/bin/verify-contracts.js +527 -81
- package/bin/verify-package.js +58 -11
- package/bin/webui/api.js +26 -0
- package/bin/webui/app.html +239 -232
- package/bin/webui/css/00-tokens.css +110 -92
- package/bin/webui/css/04-motion.css +249 -152
- package/bin/webui/css/06-responsive.css +203 -178
- package/bin/webui/css/panels/behaviour.css +205 -0
- package/bin/webui/css/panels/knowledge.css +563 -116
- package/bin/webui/fixtures/behaviour.js +532 -0
- package/bin/webui/fixtures/extra.js +2036 -2036
- package/bin/webui/fixtures/hookui.js +5 -5
- package/bin/webui/fixtures/index.js +34 -0
- package/bin/webui/fixtures/knowledge.js +7 -1
- package/bin/webui/fixtures/settings.js +305 -305
- package/bin/webui/fixtures/stats.js +16 -0
- package/bin/webui/i18n/en/behaviour.json +143 -0
- package/bin/webui/i18n/en/knowledge.json +117 -2
- package/bin/webui/i18n/en/nav.json +25 -24
- package/bin/webui/i18n/en/tour.json +37 -35
- package/bin/webui/i18n/id/behaviour.json +143 -0
- package/bin/webui/i18n/id/knowledge.json +117 -2
- package/bin/webui/i18n/id/nav.json +25 -24
- package/bin/webui/i18n/id/tour.json +37 -35
- package/bin/webui/js/01-i18n.js +155 -154
- package/bin/webui/js/90-tour.js +498 -494
- package/bin/webui/js/91-shortcuts.js +126 -126
- package/bin/webui/js/99-boot.js +121 -118
- package/bin/webui/js/panels/behaviour.js +1022 -0
- package/bin/webui/js/panels/knowledge.js +552 -7
- package/bin/webui/js/panels/overview.js +13 -4
- package/mock-run/INDEX.md +109 -107
- package/mock-run/context-combiner.md +100 -100
- package/mock-run/gotcha-import.md +118 -0
- package/mock-run/habits.md +129 -0
- package/mock-run/orc-budget.md +534 -534
- package/mock-run/orc-challenge-council.md +262 -262
- package/mock-run/orc-quick.md +6 -1
- package/mock-run/orc-ultra.md +103 -103
- package/package.json +1 -1
- package/templates/agents/MODEL-MAPPING.md +49 -49
- package/templates/agents/orc-advisor-opus-5-xhigh.md +50 -56
- package/templates/agents/orc-analyze-mini-opus-5-med.md +55 -60
- package/templates/agents/orc-analyze-mini-sonnet-5-high.md +55 -58
- package/templates/agents/orc-challenge-advisor-opus-5-med.md +75 -75
- package/templates/agents/orc-challenge-contrarian-opus-5-high.md +110 -110
- package/templates/agents/orc-challenge-executor-opus-5-med.md +114 -114
- package/templates/agents/orc-challenge-expansionist-opus-5-med.md +112 -112
- package/templates/agents/orc-challenge-judge-opus-5-high.md +132 -132
- package/templates/agents/orc-challenge-outsider-opus-5-low.md +109 -109
- package/templates/agents/orc-challenge-principles-opus-5-high.md +109 -109
- package/templates/agents/orc-challenge-reader-opus-5-low.md +90 -90
- package/templates/agents/orc-claude-writer-opus-4-8-high.md +48 -53
- package/templates/agents/orc-claude-writer-opus-5-med.md +48 -55
- package/templates/agents/orc-context-combiner-opus-5-high.md +78 -88
- package/templates/agents/orc-doc-checker-opus-5-low.md +108 -108
- package/templates/agents/orc-doc-writer-opus-5-med.md +134 -134
- package/templates/agents/orc-executor-haiku-4-5.md +14 -6
- package/templates/agents/orc-executor-opus-4-7-high.md +14 -6
- package/templates/agents/orc-executor-opus-4-7-med.md +14 -6
- package/templates/agents/orc-executor-opus-4-8-high.md +14 -6
- package/templates/agents/orc-executor-opus-5-high.md +15 -7
- package/templates/agents/orc-executor-opus-5-low.md +15 -7
- package/templates/agents/orc-executor-opus-5-med.md +15 -7
- package/templates/agents/orc-executor-sonnet-4-6-high.md +14 -6
- package/templates/agents/orc-executor-sonnet-4-6-med.md +14 -6
- package/templates/agents/orc-executor-sonnet-5-high.md +14 -6
- package/templates/agents/orc-graph-noter-sonnet-4-6-med.md +1 -10
- package/templates/agents/orc-judge-opus-5-xhigh.md +81 -85
- package/templates/agents/orc-learn-writer-opus-5-low.md +67 -73
- package/templates/agents/orc-pattern-codifier-opus-5-med.md +58 -65
- package/templates/agents/orc-pattern-codifier-sonnet-5-high.md +58 -63
- package/templates/agents/orc-planner-mini-opus-5-med.md +4 -7
- package/templates/agents/orc-planner-mini-sonnet-5-high.md +2 -3
- package/templates/agents/orc-planner-opus-5-med.md +157 -160
- package/templates/agents/orc-recon-opus-5-low.md +3 -10
- package/templates/agents/orc-recon-sonnet-4-6-med.md +1 -8
- package/templates/agents/orc-retro-opus-5-med.md +8 -10
- package/templates/agents/orc-retro-sonnet-5-high.md +6 -7
- package/templates/agents/orc-reviewer-opus-5-med.md +96 -60
- package/templates/agents/orc-scout-opus-5-low.md +35 -40
- package/templates/agents/orc-scout-sonnet-4-6-high.md +35 -39
- package/templates/agents/orc-system-analyst-opus-5-high.md +115 -120
- package/templates/agents/orc-test-author-opus-5-med.md +70 -71
- package/templates/agents/orc-test-designer-opus-5-high.md +158 -158
- package/templates/agents/orc-test-interpreter-opus-5-low.md +130 -130
- package/templates/agents/orc-trace-writer-haiku-4-5.md +3 -7
- package/templates/agents/orc-verifier-opus-5-med.md +77 -69
- package/templates/agents/orc-wiki-scanner-opus-4-8-high.md +74 -79
- package/templates/agents/orc-wiki-scanner-opus-5-med.md +74 -81
- package/templates/agents/orc-wiki-scanner-sonnet-5-high.md +97 -106
- package/templates/commands/orc-analyze.md +13 -21
- package/templates/commands/orc-fast.md +10 -15
- package/templates/commands/orc-poly.md +12 -21
- package/templates/commands/orc-pr-driver.md +11 -30
- package/templates/commands/orc-pr-setup.md +10 -31
- package/templates/commands/orc-route.md +11 -41
- package/templates/commands/orc-test.md +5 -60
- package/templates/commands/orc-ultra.md +17 -17
- package/templates/commands/orc-verify.md +11 -11
- package/templates/hooks/README.md +36 -2
- package/templates/hooks/orc-effort-guard.js +178 -178
- package/templates/hooks/orc-session-hook.js +264 -0
- package/templates/hooks/orc-statusline.js +9 -7
- package/templates/skills/_shared/README.md +9 -0
- package/templates/skills/_shared/code-graph.md +48 -56
- package/templates/skills/_shared/config-precedence.md +200 -198
- package/templates/skills/_shared/extra-dispatch.md +1331 -1346
- package/templates/skills/_shared/gotchas.md +228 -177
- package/templates/skills/_shared/habits.md +101 -0
- package/templates/skills/_shared/lane-contract.md +84 -0
- package/templates/skills/_shared/opus5-only.md +8 -8
- package/templates/skills/_shared/phases/README.md +142 -83
- package/templates/skills/_shared/phases/analyst-gates.md +125 -136
- package/templates/skills/_shared/phases/execution.md +8 -14
- package/templates/skills/_shared/phases/house-rules.md +27 -32
- package/templates/skills/_shared/phases/intake.md +127 -133
- package/templates/skills/_shared/phases/mock-example.md +46 -56
- package/templates/skills/_shared/phases/plan-handoff.md +91 -97
- package/templates/skills/_shared/phases/planning.md +7 -17
- package/templates/skills/_shared/phases/preflight.md +19 -42
- package/templates/skills/_shared/phases/review.md +23 -27
- package/templates/skills/_shared/phases/rules.md +18 -42
- package/templates/skills/_shared/phases/scoring.md +55 -65
- package/templates/skills/_shared/phases/security-checklist.md +46 -50
- package/templates/skills/_shared/phases/security.md +45 -55
- package/templates/skills/_shared/phases/ship.md +6 -15
- package/templates/skills/_shared/phases/stop-resume.md +2 -5
- package/templates/skills/_shared/phases/summary.md +73 -48
- package/templates/skills/_shared/phases/testgen.md +41 -51
- package/templates/skills/_shared/phases/trace-verbs.md +433 -0
- package/templates/skills/_shared/phases/trace.md +136 -367
- package/templates/skills/_shared/phases/verify.md +62 -70
- package/templates/skills/_shared/phases/wave-grouping.md +128 -133
- package/templates/skills/_shared/phases/wiki-consult.md +10 -6
- package/templates/skills/_shared/read-ladder.md +2 -55
- package/templates/skills/_shared/return-validation.md +17 -70
- package/templates/skills/_shared/review-slice.md +79 -0
- package/templates/skills/_shared/smoke-gate.md +46 -28
- package/templates/skills/context-combiner/SKILL.md +16 -45
- package/templates/skills/context-combiner/schemas/combined-report.md +78 -78
- package/templates/skills/orc/README.md +2 -2
- package/templates/skills/orc/SKILL.md +41 -69
- package/templates/skills/orc/config.md +9 -9
- package/templates/skills/orc/examples/full-run-mock.md +73 -73
- package/templates/skills/orc/references/effort-and-mode.md +222 -222
- package/templates/skills/orc/references/pattern-gate.md +89 -89
- package/templates/skills/orc/references/phases/intake.md +41 -47
- package/templates/skills/orc/references/phases/integration.md +13 -19
- package/templates/skills/orc/references/preflight-report.md +10 -11
- package/templates/skills/orc/references/ultra-mode.md +11 -9
- package/templates/skills/orc/subskills/orc-execution/SKILL.md +27 -73
- package/templates/skills/orc/subskills/orc-execution/core.md +12 -99
- package/templates/skills/orc/subskills/orc-execution/subagent.md +14 -13
- package/templates/skills/orc/subskills/orc-planner/SKILL.md +2 -2
- package/templates/skills/orc/subskills/orc-planner-mini/SKILL.md +121 -121
- package/templates/skills/orc/subskills/orc-review-verify/SKILL.md +35 -76
- package/templates/skills/orc/subskills/orc-review-verify/core.md +52 -135
- package/templates/skills/orc/subskills/orc-review-verify/subagent.md +7 -7
- package/templates/skills/orc/subskills/orc-testgen/SKILL.md +34 -45
- package/templates/skills/orc/subskills/orc-testgen/core.md +20 -59
- package/templates/skills/orc/subskills/orc-testgen/subagent.md +7 -7
- package/templates/skills/orc-advisor/SKILL.md +56 -60
- package/templates/skills/orc-analyze/SKILL.md +32 -68
- package/templates/skills/orc-analyze/examples/analyze-mock.md +42 -42
- package/templates/skills/orc-analyze/schemas/report-audit.md +84 -83
- package/templates/skills/orc-analyze/schemas/report-prose.md +64 -63
- package/templates/skills/orc-analyze/schemas/report-requirement.md +78 -78
- package/templates/skills/orc-analyze-mini/SKILL.md +35 -70
- package/templates/skills/orc-challenge/README.md +142 -142
- package/templates/skills/orc-challenge/examples/council-full-roster.md +273 -273
- package/templates/skills/orc-challenge/references/council.md +315 -315
- package/templates/skills/orc-challenge/references/intake.md +171 -171
- package/templates/skills/orc-claude/SKILL.md +14 -14
- package/templates/skills/orc-diy/README.md +174 -143
- package/templates/skills/orc-diy/SKILL.md +23 -74
- package/templates/skills/orc-diy/references/blocks/pattern.md +18 -18
- package/templates/skills/orc-diy/references/flow-schema.md +1 -1
- package/templates/skills/orc-doc/README.md +229 -229
- package/templates/skills/orc-doc/examples/orc-doc-prd-run.md +325 -325
- package/templates/skills/orc-doc/references/chunking.md +527 -527
- package/templates/skills/orc-fast/SKILL.md +45 -73
- package/templates/skills/orc-judge/SKILL.md +77 -82
- package/templates/skills/orc-learn/SKILL.md +14 -14
- package/templates/skills/orc-learn/examples/learn-run-mock.md +61 -61
- package/templates/skills/orc-mini/SKILL.md +65 -113
- package/templates/skills/orc-pattern/SKILL.md +27 -48
- package/templates/skills/orc-poly/SKILL.md +32 -61
- package/templates/skills/orc-pr-driver/SKILL.md +25 -51
- package/templates/skills/orc-pr-driver/references/green-gate.md +113 -105
- package/templates/skills/orc-pr-setup/SKILL.md +20 -45
- package/templates/skills/orc-quick/README.md +43 -2
- package/templates/skills/orc-quick/SKILL.md +76 -107
- package/templates/skills/orc-quick/references/dispatch-gate.md +16 -5
- package/templates/skills/orc-quick/references/gh-mode.md +48 -1
- package/templates/skills/orc-quick/references/look.md +3 -1
- package/templates/skills/orc-retro/SKILL.md +19 -18
- package/templates/skills/orc-retro/examples/retro-mock.md +1 -1
- package/templates/skills/orc-route/SKILL.md +25 -45
- package/templates/skills/orc-test/SKILL.md +16 -37
- package/templates/skills/orc-verify/SKILL.md +21 -39
- package/templates/skills/orc-verify/examples/verify-mock.md +33 -33
- package/templates/skills/orc-wait/SKILL.md +156 -163
- package/templates/skills/orc-wiki/references/extra.md +1 -1
- package/templates/skills/orc-wiki/references/phases/phase-0.md +1 -6
- package/templates/skills/orc-wiki/references/phases/phase-1.md +1 -6
- package/templates/skills/orc-wiki/references/phases/phase-2.md +1 -6
- package/templates/skills/orc-wiki/references/phases/phase-3.md +1 -6
- package/templates/skills/orc-wiki/references/phases/phase-3c.md +1 -6
|
@@ -13,7 +13,7 @@ Every value comes from a file on your disk or from data Claude Code gives the
|
|
|
13
13
|
hook. No value costs model tokens. Nothing here starts a run.
|
|
14
14
|
|
|
15
15
|
```
|
|
16
|
-
🚀 ORC v1.2.1 - Opus 5/high · context (34%) · 5h 41% (2h13m) ↔ wk 12% · ucs 6% · wiki: fresh
|
|
16
|
+
🚀 ORC v1.2.1 - Opus 5.5/high · context (34%) · 5h 41% (2h13m) ↔ wk 12% · ucs 6% · wiki: fresh
|
|
17
17
|
▰ status: quick · Q3 DO · agents 7 (2 running) · orc-extra: on · Dur 48m · MTok 412K · main
|
|
18
18
|
```
|
|
19
19
|
|
|
@@ -24,7 +24,7 @@ Line 2 answers: **what is this session doing?**
|
|
|
24
24
|
|
|
25
25
|
## Line 1
|
|
26
26
|
|
|
27
|
-
### 1. The icon and `ORC v1.2.1 - Opus 5/high`
|
|
27
|
+
### 1. The icon and `ORC v1.2.1 - Opus 5.5/high`
|
|
28
28
|
|
|
29
29
|
The icon is the verdict. The words are the ORC version you have installed and
|
|
30
30
|
the model and effort you are running now.
|
|
@@ -414,6 +414,40 @@ outside an ORC run, and never when the map does not exist.
|
|
|
414
414
|
|
|
415
415
|
---
|
|
416
416
|
|
|
417
|
+
## The session hook
|
|
418
|
+
|
|
419
|
+
This is a fourth hook, `orc-session-hook.js`. It has four jobs. All of them are
|
|
420
|
+
silent when no ORC run is open.
|
|
421
|
+
|
|
422
|
+
- **The bell.** A turn of an ORC run ends, and your terminal rings once. This
|
|
423
|
+
job is off until you turn it on:
|
|
424
|
+
|
|
425
|
+
```
|
|
426
|
+
orc config set notify bell # ring when a turn of a run ends
|
|
427
|
+
orc config set notify off # silent (the default)
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
It rings only when the run moved since the last bell. A normal chat never
|
|
431
|
+
rings. It sends no OS notification and uses no network.
|
|
432
|
+
- **After a compaction.** Claude Code keeps only the start of each skill when it
|
|
433
|
+
compacts a long session, so a long run can lose its place. When a run is in
|
|
434
|
+
flight, the hook adds ONE line: the run name, and the `state-of-play.md` file
|
|
435
|
+
to read first. It costs about 40 tokens, once per compaction.
|
|
436
|
+
|
|
437
|
+
- **The narration guard** (v2.0.2). A run dispatched agents but wrote no trace
|
|
438
|
+
line of its own. Then the hook stops the turn ONCE and says which command
|
|
439
|
+
writes the trace. It never stops twice, and never stops a subagent.
|
|
440
|
+
- **The review card** (v2.0.2). When a reviewer, verifier or judge starts, the
|
|
441
|
+
hook asks `orc gotcha card` for the files git sees as changed and gives the
|
|
442
|
+
card to that agent. No match → nothing. This is why a review always sees the
|
|
443
|
+
mistakes this project already made, even if the lane forgot the card.
|
|
444
|
+
|
|
445
|
+
It never writes model text, and it blocks a stop only for the narration guard.
|
|
446
|
+
`orc doctor` tells you whether all three events are wired (`Stop`,
|
|
447
|
+
`SessionStart compact`, `SubagentStart`).
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
417
451
|
## For maintainers
|
|
418
452
|
|
|
419
453
|
- The hook is `orc-statusline.js`. `orc init` installs it and wires it into
|
|
@@ -1,178 +1,178 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* ORC effort guard — a Claude Code PreToolUse hook.
|
|
6
|
-
*
|
|
7
|
-
* Blocks invocation of the full `orc` orchestrator skill unless the MAIN
|
|
8
|
-
* session is running at HIGH reasoning effort (Opus 5 / Fable 5 clear at
|
|
9
|
-
* medium+ — see the session-model bridge below). This is the one half of the
|
|
10
|
-
* "run ORC on Opus 4.8 high or better" rule hooks can enforce deterministically:
|
|
11
|
-
* Claude Code exposes `effort.level` (and $CLAUDE_EFFORT) to PreToolUse, but
|
|
12
|
-
* NOT the model id (only SessionStart may see the model, and cannot block).
|
|
13
|
-
* The model tier is surfaced separately by the statusline warning + the
|
|
14
|
-
* self-check inside skills/orc/SKILL.md.
|
|
15
|
-
*
|
|
16
|
-
* Wiring (installed by `orc init` into .claude/settings.json):
|
|
17
|
-
* hooks.PreToolUse[] { matcher: "Skill", hooks:[{ type:"command",
|
|
18
|
-
* command: 'node "<.claude>/hooks/orc-effort-guard.js"' }] }
|
|
19
|
-
*
|
|
20
|
-
* Contract: read PreToolUse JSON from stdin. Exit 0 = allow. Exit 2 = block
|
|
21
|
-
* the tool call and show stderr to Claude, which relays it to the user.
|
|
22
|
-
*
|
|
23
|
-
* Also surfaces a "newer orc version available" nudge here — this hook fires
|
|
24
|
-
* exactly when /orc is invoked, so it's the natural place to tell the user
|
|
25
|
-
* without them running `orc version`. On allow it emits a `systemMessage`
|
|
26
|
-
* (shown to the user, NOT added to model context → zero tokens); on block it
|
|
27
|
-
* appends the nudge to the block reason. Version check is cached 24h and
|
|
28
|
-
* fail-silent. (systemMessage handling may vary by Claude Code version — verify
|
|
29
|
-
* with /doctor if you don't see it; the statusline shows it regardless.)
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
// Shared update-check helper (sibling file). Degrade gracefully if absent.
|
|
33
|
-
let updater = null;
|
|
34
|
-
try {
|
|
35
|
-
updater = require("./orc-update-lib.js");
|
|
36
|
-
} catch (_) {
|
|
37
|
-
updater = null;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Effort ladder: low < medium < high < xhigh < max. A tier that meets the bar
|
|
41
|
-
// meets it at that rung OR anything stronger — so "requires high" allows xhigh
|
|
42
|
-
// and max too (the bug this fixes: an xhigh/max session was hard-blocked).
|
|
43
|
-
const EFFORT_LADDER = ["low", "medium", "high", "xhigh", "max"];
|
|
44
|
-
const effortsAtOrAbove = (e) => {
|
|
45
|
-
const i = EFFORT_LADDER.indexOf(String(e).toLowerCase());
|
|
46
|
-
return i === -1 ? ["high", "xhigh", "max"] : EFFORT_LADDER.slice(i);
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
// Session-model bridge (written by orc-statusline.js). The guard can't see the
|
|
50
|
-
// model id itself, so it reads it here to grant the medium-effort allowance of
|
|
51
|
-
// the models that are strictly stronger than the Opus 4.8 baseline (Opus 5 and
|
|
52
|
-
// Fable 5). Fail-OPEN: missing / unreadable / stale → null, and the guard
|
|
53
|
-
// behaves exactly as it would without a bridge (never blocks on our own error).
|
|
54
|
-
// Stale = older than this window; a live session re-renders the statusline far
|
|
55
|
-
// more often, so a fresh file always exists during active use.
|
|
56
|
-
const BRIDGE_MAX_AGE_MS = 30 * 60 * 1000;
|
|
57
|
-
function readSessionModel(projectDir) {
|
|
58
|
-
try {
|
|
59
|
-
const fs = require("fs");
|
|
60
|
-
const path = require("path");
|
|
61
|
-
const p = path.join(projectDir, ".claude", "orc", "session-model.json");
|
|
62
|
-
const j = JSON.parse(fs.readFileSync(p, "utf8").replace(/^/, ""));
|
|
63
|
-
if (!j || typeof j.written_at !== "number") return null;
|
|
64
|
-
if (Date.now() - j.written_at > BRIDGE_MAX_AGE_MS) return null;
|
|
65
|
-
return String(j.model_id || "");
|
|
66
|
-
} catch (_) {
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
// Models that clear /orc at medium effort — strictly stronger than the Opus 4.8
|
|
71
|
-
// baseline, so medium on them beats high on it. Opus 5 (v0.34.0) joins Fable 5.
|
|
72
|
-
// The `\b` after the 5 keeps 4.8 / 4.7 / 5-something-else correctly gated.
|
|
73
|
-
const isMediumOkModel = (id) =>
|
|
74
|
-
/(opus|fable)[\s._-]?5\b/.test(String(id || "").toLowerCase());
|
|
75
|
-
|
|
76
|
-
let raw = "";
|
|
77
|
-
process.stdin.on("data", (c) => (raw += c));
|
|
78
|
-
process.stdin.on("end", () => {
|
|
79
|
-
let data = {};
|
|
80
|
-
try {
|
|
81
|
-
data = JSON.parse(raw || "{}");
|
|
82
|
-
} catch (_) {
|
|
83
|
-
// Unparseable payload — never block on our own error.
|
|
84
|
-
process.exit(0);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const tool = data.tool_name || "";
|
|
88
|
-
const input = data.tool_input || {};
|
|
89
|
-
const skill = String(input.skill || input.name || "");
|
|
90
|
-
|
|
91
|
-
// Only gate the full orchestrator and the compiled DIY lane. orc-mini /
|
|
92
|
-
// orc-fast / orc-analyze / subskills pass — orc-fast is DESIGNED to run at
|
|
93
|
-
// Sonnet medium; never widen this match to include it.
|
|
94
|
-
const isOrc = tool === "Skill" && /^orc$/i.test(skill);
|
|
95
|
-
const isDiy = tool === "Skill" && /^orc-diy$/i.test(skill);
|
|
96
|
-
if (!isOrc && !isDiy) process.exit(0);
|
|
97
|
-
|
|
98
|
-
const effort = String(
|
|
99
|
-
(data.effort && data.effort.level) || process.env.CLAUDE_EFFORT || ""
|
|
100
|
-
).toLowerCase();
|
|
101
|
-
const installed = updater ? updater.installedVersion(__dirname) : null;
|
|
102
|
-
const projectDir = data.cwd || process.cwd();
|
|
103
|
-
|
|
104
|
-
// Baseline /orc: the session must be at high effort OR stronger (xhigh/max).
|
|
105
|
-
// Opus 5 and Fable 5 additionally clear at medium — strictly-capable models —
|
|
106
|
-
// detected through the session-model bridge (the guard can't see the model id
|
|
107
|
-
// itself; fail-open when the bridge is missing/stale, i.e. medium stays blocked).
|
|
108
|
-
let requiredEfforts = effortsAtOrAbove("high");
|
|
109
|
-
let requiredLabel =
|
|
110
|
-
"Opus 4.8 at high effort (Opus 5 / Fable 5 also clear at medium+)";
|
|
111
|
-
if (!isDiy && isMediumOkModel(readSessionModel(projectDir))) {
|
|
112
|
-
requiredEfforts = ["medium", ...requiredEfforts];
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// /orc-diy: the required tier is whatever the flow was COMPILED for — read
|
|
116
|
-
// it from flow.lock.json (written only by the `orc diy` CLI). Fail closed:
|
|
117
|
-
// no lock = no compiled flow = deterministic onboarding block. `compile` /
|
|
118
|
-
// `status` invocations pass at any effort (they just shell out to the CLI).
|
|
119
|
-
if (isDiy) {
|
|
120
|
-
if (/^\s*(compile|status)\b/i.test(String(input.args || ""))) process.exit(0);
|
|
121
|
-
const fs = require("fs");
|
|
122
|
-
const path = require("path");
|
|
123
|
-
let lock = null;
|
|
124
|
-
try {
|
|
125
|
-
lock = JSON.parse(
|
|
126
|
-
fs
|
|
127
|
-
.readFileSync(
|
|
128
|
-
path.join(projectDir, ".claude", "orc", "diy", "flow.lock.json"),
|
|
129
|
-
"utf8"
|
|
130
|
-
)
|
|
131
|
-
.replace(/^\uFEFF/, "")
|
|
132
|
-
);
|
|
133
|
-
} catch (_) {}
|
|
134
|
-
if (!lock || !lock.session_tier) {
|
|
135
|
-
process.stderr.write(
|
|
136
|
-
"\n⛔ /orc-diy blocked — no compiled flow in this project (fail-closed gate)." +
|
|
137
|
-
"\n Compose and build your flow in the terminal first:" +
|
|
138
|
-
"\n orc diy init (then shape it: orc diy set <key> <value>)" +
|
|
139
|
-
"\n orc diy compile" +
|
|
140
|
-
"\n Guide: .claude/skills/orc-diy/README.md — or use plain /orc for this request.\n"
|
|
141
|
-
);
|
|
142
|
-
process.exit(2);
|
|
143
|
-
}
|
|
144
|
-
// Effort half is DETERMINISTIC from the compiled slug's suffix (allow that
|
|
145
|
-
// effort or anything stronger); the model half is warn-only on the
|
|
146
|
-
// statusline (hooks can't block on model). Covers the full tier grid —
|
|
147
|
-
// sonnet-4-6 / opus-4-7 / opus-4-8 / fable-5 at med|high|xhigh|max.
|
|
148
|
-
const m = String(lock.session_tier).match(/-(med|high|xhigh|max)$/);
|
|
149
|
-
const slugEffort = m ? (m[1] === "med" ? "medium" : m[1]) : "high";
|
|
150
|
-
requiredEfforts = effortsAtOrAbove(slugEffort);
|
|
151
|
-
requiredLabel = `${lock.session_tier} (compiled session_tier — needs effort ${slugEffort}+)`;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const decide = (nudge) => {
|
|
155
|
-
if (requiredEfforts.includes(effort)) {
|
|
156
|
-
// Requirement met — allow. Surface a version nudge if one exists.
|
|
157
|
-
if (nudge) {
|
|
158
|
-
try {
|
|
159
|
-
process.stdout.write(JSON.stringify({ systemMessage: nudge }));
|
|
160
|
-
} catch (_) {}
|
|
161
|
-
}
|
|
162
|
-
process.exit(0);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
process.stderr.write(
|
|
166
|
-
`\n⛔ ${isDiy ? "/orc-diy" : "ORC"} blocked — required effort not met` +
|
|
167
|
-
(effort ? ` (current effort: ${effort}).` : " (effort not detected).") +
|
|
168
|
-
`\n Switch this session to ${requiredLabel}, then re-run.` +
|
|
169
|
-
"\n Run the MAIN session at (or above) the required tier — subagents cannot" +
|
|
170
|
-
"\n exceed the main tier, so pinned agents silently downgrade below it.\n" +
|
|
171
|
-
(nudge ? "\n" + nudge + "\n" : "")
|
|
172
|
-
);
|
|
173
|
-
process.exit(2);
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
if (updater) updater.refreshAndNudge(installed, decide);
|
|
177
|
-
else decide(null);
|
|
178
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* ORC effort guard — a Claude Code PreToolUse hook.
|
|
6
|
+
*
|
|
7
|
+
* Blocks invocation of the full `orc` orchestrator skill unless the MAIN
|
|
8
|
+
* session is running at HIGH reasoning effort (Opus 5.5 / Fable 5 clear at
|
|
9
|
+
* medium+ — see the session-model bridge below). This is the one half of the
|
|
10
|
+
* "run ORC on Opus 4.8 high or better" rule hooks can enforce deterministically:
|
|
11
|
+
* Claude Code exposes `effort.level` (and $CLAUDE_EFFORT) to PreToolUse, but
|
|
12
|
+
* NOT the model id (only SessionStart may see the model, and cannot block).
|
|
13
|
+
* The model tier is surfaced separately by the statusline warning + the
|
|
14
|
+
* self-check inside skills/orc/SKILL.md.
|
|
15
|
+
*
|
|
16
|
+
* Wiring (installed by `orc init` into .claude/settings.json):
|
|
17
|
+
* hooks.PreToolUse[] { matcher: "Skill", hooks:[{ type:"command",
|
|
18
|
+
* command: 'node "<.claude>/hooks/orc-effort-guard.js"' }] }
|
|
19
|
+
*
|
|
20
|
+
* Contract: read PreToolUse JSON from stdin. Exit 0 = allow. Exit 2 = block
|
|
21
|
+
* the tool call and show stderr to Claude, which relays it to the user.
|
|
22
|
+
*
|
|
23
|
+
* Also surfaces a "newer orc version available" nudge here — this hook fires
|
|
24
|
+
* exactly when /orc is invoked, so it's the natural place to tell the user
|
|
25
|
+
* without them running `orc version`. On allow it emits a `systemMessage`
|
|
26
|
+
* (shown to the user, NOT added to model context → zero tokens); on block it
|
|
27
|
+
* appends the nudge to the block reason. Version check is cached 24h and
|
|
28
|
+
* fail-silent. (systemMessage handling may vary by Claude Code version — verify
|
|
29
|
+
* with /doctor if you don't see it; the statusline shows it regardless.)
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
// Shared update-check helper (sibling file). Degrade gracefully if absent.
|
|
33
|
+
let updater = null;
|
|
34
|
+
try {
|
|
35
|
+
updater = require("./orc-update-lib.js");
|
|
36
|
+
} catch (_) {
|
|
37
|
+
updater = null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Effort ladder: low < medium < high < xhigh < max. A tier that meets the bar
|
|
41
|
+
// meets it at that rung OR anything stronger — so "requires high" allows xhigh
|
|
42
|
+
// and max too (the bug this fixes: an xhigh/max session was hard-blocked).
|
|
43
|
+
const EFFORT_LADDER = ["low", "medium", "high", "xhigh", "max"];
|
|
44
|
+
const effortsAtOrAbove = (e) => {
|
|
45
|
+
const i = EFFORT_LADDER.indexOf(String(e).toLowerCase());
|
|
46
|
+
return i === -1 ? ["high", "xhigh", "max"] : EFFORT_LADDER.slice(i);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Session-model bridge (written by orc-statusline.js). The guard can't see the
|
|
50
|
+
// model id itself, so it reads it here to grant the medium-effort allowance of
|
|
51
|
+
// the models that are strictly stronger than the Opus 4.8 baseline (Opus 5.5 and
|
|
52
|
+
// Fable 5). Fail-OPEN: missing / unreadable / stale → null, and the guard
|
|
53
|
+
// behaves exactly as it would without a bridge (never blocks on our own error).
|
|
54
|
+
// Stale = older than this window; a live session re-renders the statusline far
|
|
55
|
+
// more often, so a fresh file always exists during active use.
|
|
56
|
+
const BRIDGE_MAX_AGE_MS = 30 * 60 * 1000;
|
|
57
|
+
function readSessionModel(projectDir) {
|
|
58
|
+
try {
|
|
59
|
+
const fs = require("fs");
|
|
60
|
+
const path = require("path");
|
|
61
|
+
const p = path.join(projectDir, ".claude", "orc", "session-model.json");
|
|
62
|
+
const j = JSON.parse(fs.readFileSync(p, "utf8").replace(/^/, ""));
|
|
63
|
+
if (!j || typeof j.written_at !== "number") return null;
|
|
64
|
+
if (Date.now() - j.written_at > BRIDGE_MAX_AGE_MS) return null;
|
|
65
|
+
return String(j.model_id || "");
|
|
66
|
+
} catch (_) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Models that clear /orc at medium effort — strictly stronger than the Opus 4.8
|
|
71
|
+
// baseline, so medium on them beats high on it. Opus 5.5 (v0.34.0) joins Fable 5.
|
|
72
|
+
// The `\b` after the 5 keeps 4.8 / 4.7 / 5-something-else correctly gated.
|
|
73
|
+
const isMediumOkModel = (id) =>
|
|
74
|
+
/(opus|fable)[\s._-]?5\b/.test(String(id || "").toLowerCase());
|
|
75
|
+
|
|
76
|
+
let raw = "";
|
|
77
|
+
process.stdin.on("data", (c) => (raw += c));
|
|
78
|
+
process.stdin.on("end", () => {
|
|
79
|
+
let data = {};
|
|
80
|
+
try {
|
|
81
|
+
data = JSON.parse(raw || "{}");
|
|
82
|
+
} catch (_) {
|
|
83
|
+
// Unparseable payload — never block on our own error.
|
|
84
|
+
process.exit(0);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const tool = data.tool_name || "";
|
|
88
|
+
const input = data.tool_input || {};
|
|
89
|
+
const skill = String(input.skill || input.name || "");
|
|
90
|
+
|
|
91
|
+
// Only gate the full orchestrator and the compiled DIY lane. orc-mini /
|
|
92
|
+
// orc-fast / orc-analyze / subskills pass — orc-fast is DESIGNED to run at
|
|
93
|
+
// Sonnet medium; never widen this match to include it.
|
|
94
|
+
const isOrc = tool === "Skill" && /^orc$/i.test(skill);
|
|
95
|
+
const isDiy = tool === "Skill" && /^orc-diy$/i.test(skill);
|
|
96
|
+
if (!isOrc && !isDiy) process.exit(0);
|
|
97
|
+
|
|
98
|
+
const effort = String(
|
|
99
|
+
(data.effort && data.effort.level) || process.env.CLAUDE_EFFORT || ""
|
|
100
|
+
).toLowerCase();
|
|
101
|
+
const installed = updater ? updater.installedVersion(__dirname) : null;
|
|
102
|
+
const projectDir = data.cwd || process.cwd();
|
|
103
|
+
|
|
104
|
+
// Baseline /orc: the session must be at high effort OR stronger (xhigh/max).
|
|
105
|
+
// Opus 5.5 and Fable 5 additionally clear at medium — strictly-capable models —
|
|
106
|
+
// detected through the session-model bridge (the guard can't see the model id
|
|
107
|
+
// itself; fail-open when the bridge is missing/stale, i.e. medium stays blocked).
|
|
108
|
+
let requiredEfforts = effortsAtOrAbove("high");
|
|
109
|
+
let requiredLabel =
|
|
110
|
+
"Opus 4.8 at high effort (Opus 5.5 / Fable 5 also clear at medium+)";
|
|
111
|
+
if (!isDiy && isMediumOkModel(readSessionModel(projectDir))) {
|
|
112
|
+
requiredEfforts = ["medium", ...requiredEfforts];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// /orc-diy: the required tier is whatever the flow was COMPILED for — read
|
|
116
|
+
// it from flow.lock.json (written only by the `orc diy` CLI). Fail closed:
|
|
117
|
+
// no lock = no compiled flow = deterministic onboarding block. `compile` /
|
|
118
|
+
// `status` invocations pass at any effort (they just shell out to the CLI).
|
|
119
|
+
if (isDiy) {
|
|
120
|
+
if (/^\s*(compile|status)\b/i.test(String(input.args || ""))) process.exit(0);
|
|
121
|
+
const fs = require("fs");
|
|
122
|
+
const path = require("path");
|
|
123
|
+
let lock = null;
|
|
124
|
+
try {
|
|
125
|
+
lock = JSON.parse(
|
|
126
|
+
fs
|
|
127
|
+
.readFileSync(
|
|
128
|
+
path.join(projectDir, ".claude", "orc", "diy", "flow.lock.json"),
|
|
129
|
+
"utf8"
|
|
130
|
+
)
|
|
131
|
+
.replace(/^\uFEFF/, "")
|
|
132
|
+
);
|
|
133
|
+
} catch (_) {}
|
|
134
|
+
if (!lock || !lock.session_tier) {
|
|
135
|
+
process.stderr.write(
|
|
136
|
+
"\n⛔ /orc-diy blocked — no compiled flow in this project (fail-closed gate)." +
|
|
137
|
+
"\n Compose and build your flow in the terminal first:" +
|
|
138
|
+
"\n orc diy init (then shape it: orc diy set <key> <value>)" +
|
|
139
|
+
"\n orc diy compile" +
|
|
140
|
+
"\n Guide: .claude/skills/orc-diy/README.md — or use plain /orc for this request.\n"
|
|
141
|
+
);
|
|
142
|
+
process.exit(2);
|
|
143
|
+
}
|
|
144
|
+
// Effort half is DETERMINISTIC from the compiled slug's suffix (allow that
|
|
145
|
+
// effort or anything stronger); the model half is warn-only on the
|
|
146
|
+
// statusline (hooks can't block on model). Covers the full tier grid —
|
|
147
|
+
// sonnet-4-6 / opus-4-7 / opus-4-8 / fable-5 at med|high|xhigh|max.
|
|
148
|
+
const m = String(lock.session_tier).match(/-(med|high|xhigh|max)$/);
|
|
149
|
+
const slugEffort = m ? (m[1] === "med" ? "medium" : m[1]) : "high";
|
|
150
|
+
requiredEfforts = effortsAtOrAbove(slugEffort);
|
|
151
|
+
requiredLabel = `${lock.session_tier} (compiled session_tier — needs effort ${slugEffort}+)`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const decide = (nudge) => {
|
|
155
|
+
if (requiredEfforts.includes(effort)) {
|
|
156
|
+
// Requirement met — allow. Surface a version nudge if one exists.
|
|
157
|
+
if (nudge) {
|
|
158
|
+
try {
|
|
159
|
+
process.stdout.write(JSON.stringify({ systemMessage: nudge }));
|
|
160
|
+
} catch (_) {}
|
|
161
|
+
}
|
|
162
|
+
process.exit(0);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
process.stderr.write(
|
|
166
|
+
`\n⛔ ${isDiy ? "/orc-diy" : "ORC"} blocked — required effort not met` +
|
|
167
|
+
(effort ? ` (current effort: ${effort}).` : " (effort not detected).") +
|
|
168
|
+
`\n Switch this session to ${requiredLabel}, then re-run.` +
|
|
169
|
+
"\n Run the MAIN session at (or above) the required tier — subagents cannot" +
|
|
170
|
+
"\n exceed the main tier, so pinned agents silently downgrade below it.\n" +
|
|
171
|
+
(nudge ? "\n" + nudge + "\n" : "")
|
|
172
|
+
);
|
|
173
|
+
process.exit(2);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
if (updater) updater.refreshAndNudge(installed, decide);
|
|
177
|
+
else decide(null);
|
|
178
|
+
});
|