@azure-id/orc 2.1.0 → 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.
- package/CHANGELOG.md +54 -0
- package/README.md +20 -32
- package/bin/build-agents.js +205 -205
- package/bin/cli.js +47417 -47417
- package/bin/verify-contracts.js +5552 -5552
- package/bin/verify-package.js +716 -716
- package/bin/webui/fixtures/extra.js +2036 -2036
- package/mock-run/a-normal-day.md +2 -2
- package/mock-run/extra-slots.md +177 -177
- package/mock-run/orc-fast.md +2 -2
- package/mock-run/orc-quick.md +146 -146
- package/mock-run/orc.md +157 -157
- package/package.json +1 -1
- package/templates/agents/MODEL-MAPPING.md +176 -176
- package/templates/agents/{orc-recon-sonnet-4-6-med.md → orc-recon-sonnet-5-med.md} +4 -4
- package/templates/commands/orc-fast.md +10 -10
- package/templates/skills/_shared/extra-dispatch.md +1331 -1331
- package/templates/skills/_shared/opus5-only.md +137 -137
- package/templates/skills/orc/SKILL.md +227 -227
- package/templates/skills/orc/examples/full-run-mock.md +74 -74
- package/templates/skills/orc/references/effort-and-mode.md +222 -222
- package/templates/skills/orc-fast/SKILL.md +210 -210
- package/templates/skills/orc-grill/SKILL.md +232 -232
- package/templates/skills/orc-grill/references/grill-doc.md +1 -1
- package/templates/skills/orc-quick/README.md +536 -536
- package/templates/skills/orc-quick/references/context-doc.md +145 -145
- package/templates/skills/orc-quick/references/dispatch-gate.md +220 -220
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,60 @@ Format: `### v<version> — <title> _(<date>)_`.
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
+
### v2.1.1 — the quick and fast lanes move to Sonnet 5 _(2026-10-02)_
|
|
14
|
+
|
|
15
|
+
**Still on the unscoped `orc` package?** Do this once first - your `orc upgrade`
|
|
16
|
+
is the pre-v0.56.0 one and cannot install itself. Full detail in the CAUTION at
|
|
17
|
+
the top of this file.
|
|
18
|
+
|
|
19
|
+
- **Step 1 - release the command from the old package:** `npm uninstall -g orc`
|
|
20
|
+
- **Step 2 - install the current package:** `npm i -g @azure-id/orc`
|
|
21
|
+
- **Step 3 - re-apply it to your project:** `orc update`
|
|
22
|
+
|
|
23
|
+
**Do not use `npm i -g -f`.** Full detail in v0.56.0 below.
|
|
24
|
+
|
|
25
|
+
This release changes the models that `/orc-quick`, `/orc-fast` and `orc extra`
|
|
26
|
+
use. Sonnet 4.6 leaves the coding lanes. Sonnet 5 (`claude-sonnet-5-5`) replaces it.
|
|
27
|
+
|
|
28
|
+
**1. The quick dispatch gate**
|
|
29
|
+
|
|
30
|
+
- Code entries: `orc-executor-sonnet-5-med` or `orc-executor-opus-5-low`. The
|
|
31
|
+
default suggestion for a mechanical edit is `orc-executor-sonnet-5-med`.
|
|
32
|
+
- Read-only entries: `orc-recon-sonnet-5-med` or `orc-recon-opus-5-low`.
|
|
33
|
+
- An agent's model change is a rename:
|
|
34
|
+
`orc-recon-sonnet-4-6-med` → `orc-recon-sonnet-5-med`. The agent count stays 51.
|
|
35
|
+
|
|
36
|
+
**2. orc extra**
|
|
37
|
+
|
|
38
|
+
- The `quick-executor` slot uses `orc-executor-sonnet-5-med`.
|
|
39
|
+
- `extra_fallback_agent` options and the fallback menu show
|
|
40
|
+
`orc-executor-sonnet-5-high`. They showed `orc-executor-sonnet-4-6-high`. A
|
|
41
|
+
config that names the old agent is still accepted.
|
|
42
|
+
|
|
43
|
+
**3. /orc-fast**
|
|
44
|
+
|
|
45
|
+
- The one executor is `orc-executor-sonnet-5-med`. It was
|
|
46
|
+
`orc-executor-sonnet-4-6-high`. The `fast-executor` extra slot, `orc budget`
|
|
47
|
+
and the `opus5_only` table use the new name. Under `opus5_only` it is still
|
|
48
|
+
`orc-executor-opus-5-low`.
|
|
49
|
+
- Run the lane from a Sonnet 5 (or higher) main session. A Sonnet 4.6 session is
|
|
50
|
+
below the executor's pin.
|
|
51
|
+
|
|
52
|
+
**4. Text and examples**
|
|
53
|
+
|
|
54
|
+
- `/orc`: the "even the smallest task gets a cheap subagent" rule names Sonnet 5
|
|
55
|
+
low. The examples dispatch Sonnet 5 high for scores 31–40.
|
|
56
|
+
- `/orc-grill`: the recon line names `claude-sonnet-5-5 / medium`.
|
|
57
|
+
|
|
58
|
+
**Not changed:** Sonnet 4.6 stays for the graph noter, the scout, the `orc diy`
|
|
59
|
+
choices and the opt-in executors (`orc-executor-sonnet-4-6-med`,
|
|
60
|
+
`orc-executor-sonnet-4-6-high`).
|
|
61
|
+
|
|
62
|
+
**What you have to do:** `orc update --prune`. If a script names
|
|
63
|
+
`orc-recon-sonnet-4-6-med`, change it to `orc-recon-sonnet-5-med`.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
13
67
|
### v2.1.0 — the review counts, the score table moves up, and old logs can go _(2026-10-02)_
|
|
14
68
|
|
|
15
69
|
**Still on the unscoped `orc` package?** Do this once first - your `orc upgrade`
|
package/README.md
CHANGED
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
*Intake → analyze → plan → score → parallel subagents → review → verify → ship.*
|
|
8
8
|
|
|
9
9
|

|
|
10
|
-

|
|
11
11
|

|
|
12
12
|

|
|
13
13
|

|
|
14
14
|

|
|
15
15
|

|
|
16
16
|
|
|
17
|
-
**Latest: v2.1.
|
|
17
|
+
**Latest: v2.1.1** · updated 02-10-2026 · [full changelog](CHANGELOG.md)
|
|
18
18
|
|
|
19
19
|
**On npm: [`@azure-id/orc`](https://www.npmjs.com/package/@azure-id/orc)** — `npm i -g @azure-id/orc`
|
|
20
20
|
|
|
@@ -741,39 +741,27 @@ a current audit: [EVAL-REPORT.md](EVAL-REPORT.md).
|
|
|
741
741
|
**Full history: [CHANGELOG.md](CHANGELOG.md)** — or `orc changelog`, which prints
|
|
742
742
|
only what is newer than the version you have.
|
|
743
743
|
|
|
744
|
-
### v2.1.
|
|
745
|
-
|
|
746
|
-
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
-
|
|
752
|
-
|
|
753
|
-
- **The
|
|
754
|
-
|
|
755
|
-
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
- **New lane `/orc-fix`.** It records a fix after a review (Sonar, a red CI, a
|
|
761
|
-
bug) and who added the bug. The reviewer card shows it, and the panel shows
|
|
762
|
-
"Fixes after review".
|
|
763
|
-
- **`orc clear logs`** shows old traces and run folders. `--apply` deletes them.
|
|
764
|
-
Nothing is deleted automatically unless you set `log_retention_auto: on`.
|
|
765
|
-
- **The status line timer:** 1, 2, 3, 5 or 10 seconds. Below 1 second is refused,
|
|
766
|
-
because Claude Code does not support it.
|
|
767
|
-
- **Behaviour tab fixes:** with `habits: off` you still see Review quality and
|
|
768
|
-
Gotchas, `/orc-fast` records your answers, and the answer log shows the question.
|
|
769
|
-
|
|
770
|
-
**What you have to do:** `orc update`. If you compiled an `orc diy` flow, run
|
|
771
|
-
`orc diy compile`. If a script names `orc-reviewer-opus-5-med`, change it to
|
|
772
|
-
`orc-reviewer-opus-5-low`. The cost changes: read the CHANGELOG.
|
|
744
|
+
### v2.1.1 — the quick and fast lanes move to Sonnet 5 _(2026-10-02)_
|
|
745
|
+
|
|
746
|
+
- **`/orc-quick` offers Sonnet 5 medium and Opus 5.5 low.** For code:
|
|
747
|
+
`orc-executor-sonnet-5-med` or `orc-executor-opus-5-low`. For a question:
|
|
748
|
+
`orc-recon-sonnet-5-med` or `orc-recon-opus-5-low`.
|
|
749
|
+
- **`/orc-fast` uses `orc-executor-sonnet-5-med`** (it used Sonnet 4.6 high). Run it
|
|
750
|
+
from a Sonnet 5 or Opus main session.
|
|
751
|
+
- **`/orc` examples and the "cheap subagent" rule name Sonnet 5.** Sonnet 4.6 stays
|
|
752
|
+
only for the graph noter, the scout, `orc diy` choices and opt-in names.
|
|
753
|
+
- **The recon agent has a new name:** `orc-recon-sonnet-4-6-med` →
|
|
754
|
+
`orc-recon-sonnet-5-med`.
|
|
755
|
+
- **`orc extra`:** the `quick-executor` slot uses `orc-executor-sonnet-5-med`. The
|
|
756
|
+
`extra_fallback_agent` options show `orc-executor-sonnet-5-high` (not Sonnet 4.6).
|
|
757
|
+
|
|
758
|
+
**What you have to do:** `orc update --prune`. If a script names
|
|
759
|
+
`orc-recon-sonnet-4-6-med`, change it to `orc-recon-sonnet-5-med`.
|
|
773
760
|
|
|
774
761
|
<details>
|
|
775
|
-
<summary><strong>Earlier releases</strong> —
|
|
762
|
+
<summary><strong>Earlier releases</strong> — 128 of them, titles only. Full text in <a href="CHANGELOG.md">CHANGELOG.md</a>.</summary>
|
|
776
763
|
|
|
764
|
+
- **v2.1.0** — the review counts, the score table moves up, and old logs can go · _2026-10-02_
|
|
777
765
|
- **v2.0.4** — design the status line to the last cell · _2026-09-30_
|
|
778
766
|
- **v2.0.3** — the status line you designed is the status line you see · _2026-09-30_
|
|
779
767
|
- **v2.0.2** — the lanes record what you answered, and the reviewer always gets the card · _2026-09-28_
|
package/bin/build-agents.js
CHANGED
|
@@ -1,205 +1,205 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
/**
|
|
4
|
-
* Agent generator. Two sets, one rule: a file that differs from its siblings
|
|
5
|
-
* ONLY in frontmatter and the "You are … (<model>, <effort>)" line is GENERATED.
|
|
6
|
-
*
|
|
7
|
-
* 1. The 10 executors — agents-src/executor.template.md × VARIANTS
|
|
8
|
-
* (name/model/effort + score band).
|
|
9
|
-
* 2. The 8 model-twin pairs (v2.0.0 T15) — agents-src/twins/<family>.template.md
|
|
10
|
-
* × TWINS (name/model/effort/description + the `who` line). One body per
|
|
11
|
-
* family: before T15 each pair was two hand-kept copies that nothing
|
|
12
|
-
* checked against each other.
|
|
13
|
-
*
|
|
14
|
-
* Edit the template (or the table), run `npm run build:agents`, and every copy
|
|
15
|
-
* is stamped out identically. A model change is still a RENAME: a new row, not
|
|
16
|
-
* an edited one.
|
|
17
|
-
*
|
|
18
|
-
* Modes:
|
|
19
|
-
* node bin/build-agents.js write both sets into templates/agents/
|
|
20
|
-
* node bin/build-agents.js --check fail if any generated file drifted from
|
|
21
|
-
* its template (runs in `npm run verify`
|
|
22
|
-
* and prepack — a hand-edit to a generated
|
|
23
|
-
* agent file fails the build)
|
|
24
|
-
*/
|
|
25
|
-
const fs = require("fs");
|
|
26
|
-
const path = require("path");
|
|
27
|
-
|
|
28
|
-
const ROOT = path.join(__dirname, "..");
|
|
29
|
-
const TEMPLATE = path.join(ROOT, "agents-src", "executor.template.md");
|
|
30
|
-
const OUT_DIR = path.join(ROOT, "templates", "agents");
|
|
31
|
-
|
|
32
|
-
// Score→model bands mirror templates/agents/MODEL-MAPPING.md and the single
|
|
33
|
-
// score→model table in skills/orc/config.md (documented drift — change
|
|
34
|
-
// together). `effort: null` = a model with NO effort ladder (haiku): the
|
|
35
|
-
// generator omits the `effort:` frontmatter line entirely.
|
|
36
|
-
//
|
|
37
|
-
// v1.0.0 W4 — the table was SIX bands, not eight, and the `opus5_only` ladder is
|
|
38
|
-
// TWO, not three. v2.1.0 made the table FIVE bands (Sonnet 5 low/med/high, then
|
|
39
|
-
// Opus 5.5 low/med). Seven of these agents are named by NO default row:
|
|
40
|
-
// names them, and they ship anyway (D14). They stay reachable through
|
|
41
|
-
// `rubric_bands_override`, `orc diy` `fixed_executor` and `extra_fallback_agent`,
|
|
42
|
-
// and deleting a generated file that no row happens to name today would be a
|
|
43
|
-
// rename-shaped migration for what is only a table change. Their `band` string
|
|
44
|
-
// says so rather than naming a range that no longer exists — a band label that
|
|
45
|
-
// lies is worse than one that admits it is unreachable by default.
|
|
46
|
-
const VARIANTS = [
|
|
47
|
-
{ name: "orc-executor-opus-5-med", model: "claude-opus-5-5", effort: "medium", band: "highest-complexity [90,100]" },
|
|
48
|
-
{ name: "orc-executor-opus-5-low", model: "claude-opus-5-5", effort: "low", band: "upper-complexity [41,90)" },
|
|
49
|
-
{ name: "orc-executor-sonnet-5-high", model: "claude-sonnet-5-5", effort: "high", band: "mid-complexity [31,41)" },
|
|
50
|
-
{ name: "orc-executor-sonnet-5-med", model: "claude-sonnet-5-5", effort: "medium", band: "low-complexity [21,31)" },
|
|
51
|
-
{ name: "orc-executor-sonnet-5-low", model: "claude-sonnet-5-5", effort: "low", band: "lowest-complexity [0,21)" },
|
|
52
|
-
// NAMED BY NO BAND since v1.0.0 W4 / v2.1.0 (D14) — kept on disk, dispatched
|
|
53
|
-
// only when a user names one explicitly.
|
|
54
|
-
{ name: "orc-executor-sonnet-4-6-high", model: "claude-sonnet-4-6", effort: "high", band: "none (opt-in only, see MODEL-MAPPING.md)" },
|
|
55
|
-
{ name: "orc-executor-sonnet-4-6-med", model: "claude-sonnet-4-6", effort: "medium", band: "none (opt-in only, see MODEL-MAPPING.md)" },
|
|
56
|
-
{ name: "orc-executor-haiku-4-5", model: "claude-haiku-4-5", effort: null, band: "none (opt-in only, see MODEL-MAPPING.md)" },
|
|
57
|
-
{ name: "orc-executor-opus-5-high", model: "claude-opus-5-5", effort: "high", band: "none (opt-in only, see MODEL-MAPPING.md)" },
|
|
58
|
-
{ name: "orc-executor-opus-4-8-high", model: "claude-opus-4-8", effort: "high", band: "none (opt-in only, see MODEL-MAPPING.md)" },
|
|
59
|
-
{ name: "orc-executor-opus-4-7-high", model: "claude-opus-4-7", effort: "high", band: "none (opt-in only, see MODEL-MAPPING.md)" },
|
|
60
|
-
{ name: "orc-executor-opus-4-7-med", model: "claude-opus-4-7", effort: "medium", band: "none (opt-in only, see MODEL-MAPPING.md)" },
|
|
61
|
-
];
|
|
62
|
-
|
|
63
|
-
// v2.0.0 T15 — the model twins. Each family is one template under
|
|
64
|
-
// agents-src/twins/; a row fills five placeholders: {{NAME}}, {{MODEL}},
|
|
65
|
-
// {{EFFORT}}, {{DESC}} (the one-line frontmatter description) and {{WHO}} (the
|
|
66
|
-
// "(Sonnet 5, high)" part of the "You are …" line — absent for a family whose
|
|
67
|
-
// body never names its own model). The pair is a runtime choice (`opus5_only`,
|
|
68
|
-
// the recon gate, the wiki tier ladder), so both halves always ship.
|
|
69
|
-
const TWIN_DIR = path.join(ROOT, "agents-src", "twins");
|
|
70
|
-
const TWINS = [
|
|
71
|
-
{ family: "analyze-mini", variants: [
|
|
72
|
-
{ name: "orc-analyze-mini-sonnet-5-high", model: "claude-sonnet-5-5", effort: "high", who: "Sonnet 5, high",
|
|
73
|
-
desc: "ORC mini System Analyst — claude-sonnet-5-5, high effort. Dispatched by /orc-mini and /orc-analyze-mini at analysis: single pass, no deep mode, no scouts." },
|
|
74
|
-
{ name: "orc-analyze-mini-opus-5-med", model: "claude-opus-5-5", effort: "medium", who: "Opus 5.5, medium",
|
|
75
|
-
desc: "ORC mini System Analyst — claude-opus-5-5, medium effort. Dispatched by /orc-mini and /orc-analyze-mini, instead of orc-analyze-mini-sonnet-5-high when `opus5_only: true`." },
|
|
76
|
-
] },
|
|
77
|
-
{ family: "planner-mini", variants: [
|
|
78
|
-
{ name: "orc-planner-mini-sonnet-5-high", model: "claude-sonnet-5-5", effort: "high", who: "Sonnet 5, high",
|
|
79
|
-
desc: "ORC mini Requirement Planner — claude-sonnet-5-5, high effort. Dispatched by /orc-mini at planning." },
|
|
80
|
-
{ name: "orc-planner-mini-opus-5-med", model: "claude-opus-5-5", effort: "medium", who: "Opus 5.5, medium",
|
|
81
|
-
desc: "ORC mini Requirement Planner — claude-opus-5-5, medium effort. Dispatched by /orc-mini at planning, instead of orc-planner-mini-sonnet-5-high when `opus5_only: true`." },
|
|
82
|
-
] },
|
|
83
|
-
{ family: "pattern-codifier", variants: [
|
|
84
|
-
{ name: "orc-pattern-codifier-sonnet-5-high", model: "claude-sonnet-5-5", effort: "high",
|
|
85
|
-
desc: "ORC Pattern Codifier — claude-sonnet-5-5, high effort. Dispatched by orc-pattern (lazy /orc miss, eager orc-wiki, or manual /orc-pattern) for ONE language." },
|
|
86
|
-
{ name: "orc-pattern-codifier-opus-5-med", model: "claude-opus-5-5", effort: "medium",
|
|
87
|
-
desc: "ORC Pattern Codifier — claude-opus-5-5, medium effort. Dispatched by orc-pattern, instead of orc-pattern-codifier-sonnet-5-high when `opus5_only: true`." },
|
|
88
|
-
] },
|
|
89
|
-
{ family: "recon", variants: [
|
|
90
|
-
{ name: "orc-recon-sonnet-
|
|
91
|
-
desc: "ORC Recon — claude-sonnet-
|
|
92
|
-
{ name: "orc-recon-opus-5-low", model: "claude-opus-5-5", effort: "low", who: "Opus 5.5, low",
|
|
93
|
-
desc: "ORC Recon — claude-opus-5-5, low effort. Dispatched by /orc-quick at the dispatch gate, for a WIDE or SUBTLE read-only question. It never edits." },
|
|
94
|
-
] },
|
|
95
|
-
{ family: "retro", variants: [
|
|
96
|
-
{ name: "orc-retro-sonnet-5-high", model: "claude-sonnet-5-5", effort: "high", who: "Sonnet 5, high",
|
|
97
|
-
desc: "ORC Retro miner — claude-sonnet-5-5, high effort. Dispatched by /orc-retro to mine the behavior traces. Read-only, report-only." },
|
|
98
|
-
{ name: "orc-retro-opus-5-med", model: "claude-opus-5-5", effort: "medium", who: "Opus 5.5, medium",
|
|
99
|
-
desc: "ORC Retro miner — claude-opus-5-5, medium effort. Dispatched by /orc-retro, instead of orc-retro-sonnet-5-high when `opus5_only: true`. Read-only." },
|
|
100
|
-
] },
|
|
101
|
-
{ family: "scout", variants: [
|
|
102
|
-
{ name: "orc-scout-sonnet-4-6-high", model: "claude-sonnet-4-6", effort: "high", who: "Sonnet 4.6, high",
|
|
103
|
-
desc: "ORC Code Scout — claude-sonnet-4-6, high effort. Dispatched by orc in the analyst's DEEP mode (≤max_scouts in parallel), ONE coverage area each." },
|
|
104
|
-
{ name: "orc-scout-opus-5-low", model: "claude-opus-5-5", effort: "low", who: "Opus 5.5, low",
|
|
105
|
-
desc: "ORC Code Scout — claude-opus-5-5, low effort. Dispatched by orc in the analyst's DEEP mode, instead of orc-scout-sonnet-4-6-high when `opus5_only: true`." },
|
|
106
|
-
] },
|
|
107
|
-
{ family: "claude-writer", variants: [
|
|
108
|
-
{ name: "orc-claude-writer-opus-5-low", model: "claude-opus-5-5", effort: "low", who: "Opus 5.5, low",
|
|
109
|
-
desc: "ORC CLAUDE.md Writer — claude-opus-5-5, low effort. Dispatched by /orc-claude to create, update or refresh the repo-root CLAUDE.md." },
|
|
110
|
-
] },
|
|
111
|
-
{ family: "wiki-scanner", variants: [
|
|
112
|
-
{ name: "orc-wiki-scanner-opus-5-low", model: "claude-opus-5-5", effort: "low",
|
|
113
|
-
desc: "ORC Wiki Scanner — claude-opus-5-5, low effort. Dispatched by orc-wiki per scan-task (DEEP tier), ONE coverage area each." },
|
|
114
|
-
] },
|
|
115
|
-
];
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
// Line endings are NOT content here: .gitattributes stores these files with LF
|
|
119
|
-
// and checks them out native, so a Windows worktree is CRLF and a Linux one LF.
|
|
120
|
-
// The injected frontmatter line therefore borrows the TEMPLATE's own ending, and
|
|
121
|
-
// --check compares LF-normalized text — otherwise the guard fails on Windows for
|
|
122
|
-
// a difference git itself does not record.
|
|
123
|
-
const eolOf = (s) => (/\r\n/.test(s) ? "\r\n" : "\n");
|
|
124
|
-
const lf = (s) => s.replace(/\r\n/g, "\n");
|
|
125
|
-
|
|
126
|
-
function render(template, v) {
|
|
127
|
-
const effortDesc = v.effort ? `, ${v.effort} effort` : " (no effort ladder)";
|
|
128
|
-
const effortFm = v.effort ? `effort: ${v.effort}${eolOf(template)}` : "";
|
|
129
|
-
return template
|
|
130
|
-
.replace(/\{\{NAME\}\}/g, v.name)
|
|
131
|
-
.replace(/\{\{MODEL\}\}/g, v.model)
|
|
132
|
-
.replace(/\{\{EFFORT_DESC\}\}/g, effortDesc)
|
|
133
|
-
.replace(/\{\{EFFORT_FM\}\}/g, effortFm)
|
|
134
|
-
.replace(/\{\{BAND\}\}/g, v.band);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function renderTwin(template, v) {
|
|
138
|
-
return template
|
|
139
|
-
.replace(/\{\{NAME\}\}/g, v.name)
|
|
140
|
-
.replace(/\{\{MODEL\}\}/g, v.model)
|
|
141
|
-
.replace(/\{\{EFFORT\}\}/g, v.effort)
|
|
142
|
-
.replace(/\{\{DESC\}\}/g, v.desc)
|
|
143
|
-
.replace(/\{\{WHO\}\}/g, v.who || "");
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Every [template, row, renderer] the generator owns, executors first.
|
|
147
|
-
function jobs() {
|
|
148
|
-
const template = fs.readFileSync(TEMPLATE, "utf8");
|
|
149
|
-
if (/\{\{(?!NAME|MODEL|EFFORT_DESC|EFFORT_FM|BAND)\w/.test(template)) {
|
|
150
|
-
console.error("❌ build-agents: unknown {{placeholder}} in executor.template.md");
|
|
151
|
-
process.exit(1);
|
|
152
|
-
}
|
|
153
|
-
const out = VARIANTS.map((v) => ({ v, out: render(template, v), src: "agents-src/executor.template.md" }));
|
|
154
|
-
for (const fam of TWINS) {
|
|
155
|
-
const src = "agents-src/twins/" + fam.family + ".template.md";
|
|
156
|
-
const t = fs.readFileSync(path.join(TWIN_DIR, fam.family + ".template.md"), "utf8");
|
|
157
|
-
if (/\{\{(?!(?:NAME|MODEL|EFFORT|DESC|WHO)\}\})\w/.test(t)) {
|
|
158
|
-
console.error("❌ build-agents: unknown {{placeholder}} in " + src);
|
|
159
|
-
process.exit(1);
|
|
160
|
-
}
|
|
161
|
-
if (/\{\{WHO\}\}/.test(t) !==fam.variants.every((v) => v.who)) {
|
|
162
|
-
console.error("❌ build-agents: " + src + " and its TWINS rows disagree about {{WHO}}");
|
|
163
|
-
process.exit(1);
|
|
164
|
-
}
|
|
165
|
-
for (const v of fam.variants) out.push({ v, out: renderTwin(t, v), src });
|
|
166
|
-
}
|
|
167
|
-
return out;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function main() {
|
|
171
|
-
const checkMode = process.argv.includes("--check");
|
|
172
|
-
const all = jobs();
|
|
173
|
-
let drifted = 0;
|
|
174
|
-
for (const { v, out, src } of all) {
|
|
175
|
-
const dest = path.join(OUT_DIR, v.name + ".md");
|
|
176
|
-
if (checkMode) {
|
|
177
|
-
const current = fs.existsSync(dest) ? fs.readFileSync(dest, "utf8") : null;
|
|
178
|
-
if (current === null || lf(current) !== lf(out)) {
|
|
179
|
-
drifted++;
|
|
180
|
-
console.error(
|
|
181
|
-
`❌ generated agent drifted: templates/agents/${v.name}.md\n` +
|
|
182
|
-
` This agent is GENERATED — edit ${src}\n` +
|
|
183
|
-
` (or the VARIANTS / TWINS table in bin/build-agents.js) and run: npm run build:agents`
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
} else {
|
|
187
|
-
fs.writeFileSync(dest, out);
|
|
188
|
-
console.log(` gen templates/agents/${v.name}.md`);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (checkMode) {
|
|
193
|
-
if (drifted) process.exit(1);
|
|
194
|
-
const twins = all.length - VARIANTS.length;
|
|
195
|
-
console.log(
|
|
196
|
-
`✅ ORC executor agents OK — ${VARIANTS.length} files match the template; ${twins} model twins match agents-src/twins/.`
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Required by test/payload.test.js, which checks the line-ending rule above
|
|
202
|
-
// without writing to templates/agents/.
|
|
203
|
-
module.exports = { VARIANTS, TWINS, render, renderTwin, lf, eolOf };
|
|
204
|
-
|
|
205
|
-
if (require.main === module) main();
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Agent generator. Two sets, one rule: a file that differs from its siblings
|
|
5
|
+
* ONLY in frontmatter and the "You are … (<model>, <effort>)" line is GENERATED.
|
|
6
|
+
*
|
|
7
|
+
* 1. The 10 executors — agents-src/executor.template.md × VARIANTS
|
|
8
|
+
* (name/model/effort + score band).
|
|
9
|
+
* 2. The 8 model-twin pairs (v2.0.0 T15) — agents-src/twins/<family>.template.md
|
|
10
|
+
* × TWINS (name/model/effort/description + the `who` line). One body per
|
|
11
|
+
* family: before T15 each pair was two hand-kept copies that nothing
|
|
12
|
+
* checked against each other.
|
|
13
|
+
*
|
|
14
|
+
* Edit the template (or the table), run `npm run build:agents`, and every copy
|
|
15
|
+
* is stamped out identically. A model change is still a RENAME: a new row, not
|
|
16
|
+
* an edited one.
|
|
17
|
+
*
|
|
18
|
+
* Modes:
|
|
19
|
+
* node bin/build-agents.js write both sets into templates/agents/
|
|
20
|
+
* node bin/build-agents.js --check fail if any generated file drifted from
|
|
21
|
+
* its template (runs in `npm run verify`
|
|
22
|
+
* and prepack — a hand-edit to a generated
|
|
23
|
+
* agent file fails the build)
|
|
24
|
+
*/
|
|
25
|
+
const fs = require("fs");
|
|
26
|
+
const path = require("path");
|
|
27
|
+
|
|
28
|
+
const ROOT = path.join(__dirname, "..");
|
|
29
|
+
const TEMPLATE = path.join(ROOT, "agents-src", "executor.template.md");
|
|
30
|
+
const OUT_DIR = path.join(ROOT, "templates", "agents");
|
|
31
|
+
|
|
32
|
+
// Score→model bands mirror templates/agents/MODEL-MAPPING.md and the single
|
|
33
|
+
// score→model table in skills/orc/config.md (documented drift — change
|
|
34
|
+
// together). `effort: null` = a model with NO effort ladder (haiku): the
|
|
35
|
+
// generator omits the `effort:` frontmatter line entirely.
|
|
36
|
+
//
|
|
37
|
+
// v1.0.0 W4 — the table was SIX bands, not eight, and the `opus5_only` ladder is
|
|
38
|
+
// TWO, not three. v2.1.0 made the table FIVE bands (Sonnet 5 low/med/high, then
|
|
39
|
+
// Opus 5.5 low/med). Seven of these agents are named by NO default row:
|
|
40
|
+
// names them, and they ship anyway (D14). They stay reachable through
|
|
41
|
+
// `rubric_bands_override`, `orc diy` `fixed_executor` and `extra_fallback_agent`,
|
|
42
|
+
// and deleting a generated file that no row happens to name today would be a
|
|
43
|
+
// rename-shaped migration for what is only a table change. Their `band` string
|
|
44
|
+
// says so rather than naming a range that no longer exists — a band label that
|
|
45
|
+
// lies is worse than one that admits it is unreachable by default.
|
|
46
|
+
const VARIANTS = [
|
|
47
|
+
{ name: "orc-executor-opus-5-med", model: "claude-opus-5-5", effort: "medium", band: "highest-complexity [90,100]" },
|
|
48
|
+
{ name: "orc-executor-opus-5-low", model: "claude-opus-5-5", effort: "low", band: "upper-complexity [41,90)" },
|
|
49
|
+
{ name: "orc-executor-sonnet-5-high", model: "claude-sonnet-5-5", effort: "high", band: "mid-complexity [31,41)" },
|
|
50
|
+
{ name: "orc-executor-sonnet-5-med", model: "claude-sonnet-5-5", effort: "medium", band: "low-complexity [21,31)" },
|
|
51
|
+
{ name: "orc-executor-sonnet-5-low", model: "claude-sonnet-5-5", effort: "low", band: "lowest-complexity [0,21)" },
|
|
52
|
+
// NAMED BY NO BAND since v1.0.0 W4 / v2.1.0 (D14) — kept on disk, dispatched
|
|
53
|
+
// only when a user names one explicitly.
|
|
54
|
+
{ name: "orc-executor-sonnet-4-6-high", model: "claude-sonnet-4-6", effort: "high", band: "none (opt-in only, see MODEL-MAPPING.md)" },
|
|
55
|
+
{ name: "orc-executor-sonnet-4-6-med", model: "claude-sonnet-4-6", effort: "medium", band: "none (opt-in only, see MODEL-MAPPING.md)" },
|
|
56
|
+
{ name: "orc-executor-haiku-4-5", model: "claude-haiku-4-5", effort: null, band: "none (opt-in only, see MODEL-MAPPING.md)" },
|
|
57
|
+
{ name: "orc-executor-opus-5-high", model: "claude-opus-5-5", effort: "high", band: "none (opt-in only, see MODEL-MAPPING.md)" },
|
|
58
|
+
{ name: "orc-executor-opus-4-8-high", model: "claude-opus-4-8", effort: "high", band: "none (opt-in only, see MODEL-MAPPING.md)" },
|
|
59
|
+
{ name: "orc-executor-opus-4-7-high", model: "claude-opus-4-7", effort: "high", band: "none (opt-in only, see MODEL-MAPPING.md)" },
|
|
60
|
+
{ name: "orc-executor-opus-4-7-med", model: "claude-opus-4-7", effort: "medium", band: "none (opt-in only, see MODEL-MAPPING.md)" },
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
// v2.0.0 T15 — the model twins. Each family is one template under
|
|
64
|
+
// agents-src/twins/; a row fills five placeholders: {{NAME}}, {{MODEL}},
|
|
65
|
+
// {{EFFORT}}, {{DESC}} (the one-line frontmatter description) and {{WHO}} (the
|
|
66
|
+
// "(Sonnet 5, high)" part of the "You are …" line — absent for a family whose
|
|
67
|
+
// body never names its own model). The pair is a runtime choice (`opus5_only`,
|
|
68
|
+
// the recon gate, the wiki tier ladder), so both halves always ship.
|
|
69
|
+
const TWIN_DIR = path.join(ROOT, "agents-src", "twins");
|
|
70
|
+
const TWINS = [
|
|
71
|
+
{ family: "analyze-mini", variants: [
|
|
72
|
+
{ name: "orc-analyze-mini-sonnet-5-high", model: "claude-sonnet-5-5", effort: "high", who: "Sonnet 5, high",
|
|
73
|
+
desc: "ORC mini System Analyst — claude-sonnet-5-5, high effort. Dispatched by /orc-mini and /orc-analyze-mini at analysis: single pass, no deep mode, no scouts." },
|
|
74
|
+
{ name: "orc-analyze-mini-opus-5-med", model: "claude-opus-5-5", effort: "medium", who: "Opus 5.5, medium",
|
|
75
|
+
desc: "ORC mini System Analyst — claude-opus-5-5, medium effort. Dispatched by /orc-mini and /orc-analyze-mini, instead of orc-analyze-mini-sonnet-5-high when `opus5_only: true`." },
|
|
76
|
+
] },
|
|
77
|
+
{ family: "planner-mini", variants: [
|
|
78
|
+
{ name: "orc-planner-mini-sonnet-5-high", model: "claude-sonnet-5-5", effort: "high", who: "Sonnet 5, high",
|
|
79
|
+
desc: "ORC mini Requirement Planner — claude-sonnet-5-5, high effort. Dispatched by /orc-mini at planning." },
|
|
80
|
+
{ name: "orc-planner-mini-opus-5-med", model: "claude-opus-5-5", effort: "medium", who: "Opus 5.5, medium",
|
|
81
|
+
desc: "ORC mini Requirement Planner — claude-opus-5-5, medium effort. Dispatched by /orc-mini at planning, instead of orc-planner-mini-sonnet-5-high when `opus5_only: true`." },
|
|
82
|
+
] },
|
|
83
|
+
{ family: "pattern-codifier", variants: [
|
|
84
|
+
{ name: "orc-pattern-codifier-sonnet-5-high", model: "claude-sonnet-5-5", effort: "high",
|
|
85
|
+
desc: "ORC Pattern Codifier — claude-sonnet-5-5, high effort. Dispatched by orc-pattern (lazy /orc miss, eager orc-wiki, or manual /orc-pattern) for ONE language." },
|
|
86
|
+
{ name: "orc-pattern-codifier-opus-5-med", model: "claude-opus-5-5", effort: "medium",
|
|
87
|
+
desc: "ORC Pattern Codifier — claude-opus-5-5, medium effort. Dispatched by orc-pattern, instead of orc-pattern-codifier-sonnet-5-high when `opus5_only: true`." },
|
|
88
|
+
] },
|
|
89
|
+
{ family: "recon", variants: [
|
|
90
|
+
{ name: "orc-recon-sonnet-5-med", model: "claude-sonnet-5-5", effort: "medium", who: "Sonnet 5, medium",
|
|
91
|
+
desc: "ORC Recon — claude-sonnet-5-5, medium effort. Dispatched by /orc-quick at the dispatch gate, for a read-only question. It never edits." },
|
|
92
|
+
{ name: "orc-recon-opus-5-low", model: "claude-opus-5-5", effort: "low", who: "Opus 5.5, low",
|
|
93
|
+
desc: "ORC Recon — claude-opus-5-5, low effort. Dispatched by /orc-quick at the dispatch gate, for a WIDE or SUBTLE read-only question. It never edits." },
|
|
94
|
+
] },
|
|
95
|
+
{ family: "retro", variants: [
|
|
96
|
+
{ name: "orc-retro-sonnet-5-high", model: "claude-sonnet-5-5", effort: "high", who: "Sonnet 5, high",
|
|
97
|
+
desc: "ORC Retro miner — claude-sonnet-5-5, high effort. Dispatched by /orc-retro to mine the behavior traces. Read-only, report-only." },
|
|
98
|
+
{ name: "orc-retro-opus-5-med", model: "claude-opus-5-5", effort: "medium", who: "Opus 5.5, medium",
|
|
99
|
+
desc: "ORC Retro miner — claude-opus-5-5, medium effort. Dispatched by /orc-retro, instead of orc-retro-sonnet-5-high when `opus5_only: true`. Read-only." },
|
|
100
|
+
] },
|
|
101
|
+
{ family: "scout", variants: [
|
|
102
|
+
{ name: "orc-scout-sonnet-4-6-high", model: "claude-sonnet-4-6", effort: "high", who: "Sonnet 4.6, high",
|
|
103
|
+
desc: "ORC Code Scout — claude-sonnet-4-6, high effort. Dispatched by orc in the analyst's DEEP mode (≤max_scouts in parallel), ONE coverage area each." },
|
|
104
|
+
{ name: "orc-scout-opus-5-low", model: "claude-opus-5-5", effort: "low", who: "Opus 5.5, low",
|
|
105
|
+
desc: "ORC Code Scout — claude-opus-5-5, low effort. Dispatched by orc in the analyst's DEEP mode, instead of orc-scout-sonnet-4-6-high when `opus5_only: true`." },
|
|
106
|
+
] },
|
|
107
|
+
{ family: "claude-writer", variants: [
|
|
108
|
+
{ name: "orc-claude-writer-opus-5-low", model: "claude-opus-5-5", effort: "low", who: "Opus 5.5, low",
|
|
109
|
+
desc: "ORC CLAUDE.md Writer — claude-opus-5-5, low effort. Dispatched by /orc-claude to create, update or refresh the repo-root CLAUDE.md." },
|
|
110
|
+
] },
|
|
111
|
+
{ family: "wiki-scanner", variants: [
|
|
112
|
+
{ name: "orc-wiki-scanner-opus-5-low", model: "claude-opus-5-5", effort: "low",
|
|
113
|
+
desc: "ORC Wiki Scanner — claude-opus-5-5, low effort. Dispatched by orc-wiki per scan-task (DEEP tier), ONE coverage area each." },
|
|
114
|
+
] },
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
// Line endings are NOT content here: .gitattributes stores these files with LF
|
|
119
|
+
// and checks them out native, so a Windows worktree is CRLF and a Linux one LF.
|
|
120
|
+
// The injected frontmatter line therefore borrows the TEMPLATE's own ending, and
|
|
121
|
+
// --check compares LF-normalized text — otherwise the guard fails on Windows for
|
|
122
|
+
// a difference git itself does not record.
|
|
123
|
+
const eolOf = (s) => (/\r\n/.test(s) ? "\r\n" : "\n");
|
|
124
|
+
const lf = (s) => s.replace(/\r\n/g, "\n");
|
|
125
|
+
|
|
126
|
+
function render(template, v) {
|
|
127
|
+
const effortDesc = v.effort ? `, ${v.effort} effort` : " (no effort ladder)";
|
|
128
|
+
const effortFm = v.effort ? `effort: ${v.effort}${eolOf(template)}` : "";
|
|
129
|
+
return template
|
|
130
|
+
.replace(/\{\{NAME\}\}/g, v.name)
|
|
131
|
+
.replace(/\{\{MODEL\}\}/g, v.model)
|
|
132
|
+
.replace(/\{\{EFFORT_DESC\}\}/g, effortDesc)
|
|
133
|
+
.replace(/\{\{EFFORT_FM\}\}/g, effortFm)
|
|
134
|
+
.replace(/\{\{BAND\}\}/g, v.band);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function renderTwin(template, v) {
|
|
138
|
+
return template
|
|
139
|
+
.replace(/\{\{NAME\}\}/g, v.name)
|
|
140
|
+
.replace(/\{\{MODEL\}\}/g, v.model)
|
|
141
|
+
.replace(/\{\{EFFORT\}\}/g, v.effort)
|
|
142
|
+
.replace(/\{\{DESC\}\}/g, v.desc)
|
|
143
|
+
.replace(/\{\{WHO\}\}/g, v.who || "");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Every [template, row, renderer] the generator owns, executors first.
|
|
147
|
+
function jobs() {
|
|
148
|
+
const template = fs.readFileSync(TEMPLATE, "utf8");
|
|
149
|
+
if (/\{\{(?!NAME|MODEL|EFFORT_DESC|EFFORT_FM|BAND)\w/.test(template)) {
|
|
150
|
+
console.error("❌ build-agents: unknown {{placeholder}} in executor.template.md");
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
const out = VARIANTS.map((v) => ({ v, out: render(template, v), src: "agents-src/executor.template.md" }));
|
|
154
|
+
for (const fam of TWINS) {
|
|
155
|
+
const src = "agents-src/twins/" + fam.family + ".template.md";
|
|
156
|
+
const t = fs.readFileSync(path.join(TWIN_DIR, fam.family + ".template.md"), "utf8");
|
|
157
|
+
if (/\{\{(?!(?:NAME|MODEL|EFFORT|DESC|WHO)\}\})\w/.test(t)) {
|
|
158
|
+
console.error("❌ build-agents: unknown {{placeholder}} in " + src);
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
if (/\{\{WHO\}\}/.test(t) !==fam.variants.every((v) => v.who)) {
|
|
162
|
+
console.error("❌ build-agents: " + src + " and its TWINS rows disagree about {{WHO}}");
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
for (const v of fam.variants) out.push({ v, out: renderTwin(t, v), src });
|
|
166
|
+
}
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function main() {
|
|
171
|
+
const checkMode = process.argv.includes("--check");
|
|
172
|
+
const all = jobs();
|
|
173
|
+
let drifted = 0;
|
|
174
|
+
for (const { v, out, src } of all) {
|
|
175
|
+
const dest = path.join(OUT_DIR, v.name + ".md");
|
|
176
|
+
if (checkMode) {
|
|
177
|
+
const current = fs.existsSync(dest) ? fs.readFileSync(dest, "utf8") : null;
|
|
178
|
+
if (current === null || lf(current) !== lf(out)) {
|
|
179
|
+
drifted++;
|
|
180
|
+
console.error(
|
|
181
|
+
`❌ generated agent drifted: templates/agents/${v.name}.md\n` +
|
|
182
|
+
` This agent is GENERATED — edit ${src}\n` +
|
|
183
|
+
` (or the VARIANTS / TWINS table in bin/build-agents.js) and run: npm run build:agents`
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
} else {
|
|
187
|
+
fs.writeFileSync(dest, out);
|
|
188
|
+
console.log(` gen templates/agents/${v.name}.md`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (checkMode) {
|
|
193
|
+
if (drifted) process.exit(1);
|
|
194
|
+
const twins = all.length - VARIANTS.length;
|
|
195
|
+
console.log(
|
|
196
|
+
`✅ ORC executor agents OK — ${VARIANTS.length} files match the template; ${twins} model twins match agents-src/twins/.`
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Required by test/payload.test.js, which checks the line-ending rule above
|
|
202
|
+
// without writing to templates/agents/.
|
|
203
|
+
module.exports = { VARIANTS, TWINS, render, renderTwin, lf, eolOf };
|
|
204
|
+
|
|
205
|
+
if (require.main === module) main();
|