arcane-cli 0.22.3 โ 0.22.5
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/dist/assets/.arcane/governance/git-conventions.md +4 -2
- package/dist/assets/.arcane/governance/naming-conventions.md +13 -0
- package/dist/assets/.arcane/governance/new-business-setup.md +2 -2
- package/dist/assets/.arcane/governance/spell-authoring-standards.md +7 -1
- package/dist/assets/.github/agents/mercurio.agent.md +1 -1
- package/dist/assets/.github/instructions/agent-output.instructions.md +53 -2
- package/dist/assets/.github/prompts/spell-bump.prompt.md +4 -1
- package/dist/assets/.github/prompts/spell-review-batch.prompt.md +4 -4
- package/dist/assets/.github/prompts/spell-review.prompt.md +1 -1
- package/dist/assets/.github/prompts/spell-summon-venture.prompt.md +1 -1
- package/dist/index.js +40 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -567,13 +567,15 @@ See also: [Agent Workflow โ Sync with main before opening a PR](#agent-workflo
|
|
|
567
567
|
| **Description** | Must include: summary of changes, rationale, verification steps, and a linked work item ID if applicable |
|
|
568
568
|
| **Pre-PR sync** | **Mandatory rebase on latest `origin/<target>` before pushing.** See [๐ Agent-mandatory pre-PR guard](#-agent-mandatory-pre-pr-guard) above โ this applies even when opening PRs via raw `az repos pr create` / `gh pr create` / MCP tools. |
|
|
569
569
|
| **Branch cleanup** | After merge, delete the source branch (local and remote). |
|
|
570
|
-
| **PR link format** | All PR references in agent output
|
|
570
|
+
| **PR link format** | All PR references in agent output **must** be clickable markdown links to the PR, not a bare `PR #NNN`. |
|
|
571
|
+
| **Doc-ID link format** | The same rule extends to `ADR-NNN`/`ARC-NNN`/`EF-NN`/journal entries/named governance docs โ never a bare ID. See `.github/instructions/agent-output.instructions.md` โ Doc-ID Link Format for the full rule and required formats per context. |
|
|
571
572
|
|
|
572
573
|
### Agent PR Workflow
|
|
573
574
|
|
|
574
575
|
1. **Create a work item** (if one doesn't exist) using `spell-suggest-feature` or `spell-bug`.
|
|
575
576
|
2. **Open PR** with proper title, description, and work-item link.
|
|
576
|
-
3. **Notify the human** through your configured channel with
|
|
577
|
+
3. **Notify the human** through your configured channel with a clickable link, not a bare number:
|
|
578
|
+
`[PR #{number} โ {title}]({url}) โ {repo} (#{workItemId})`.
|
|
577
579
|
4. **Address review feedback** if a reviewer requests changes.
|
|
578
580
|
5. **After merge**, delete the branch (local + remote) and confirm cleanup.
|
|
579
581
|
|
|
@@ -92,6 +92,19 @@ Arcane ships with twelve default agent personas, organized by functional cluster
|
|
|
92
92
|
|
|
93
93
|
Twelve legendary agents โ the Arcanos. Every namesake is public domain: golden-age stage magicians (Kellar, Lafayette, Adelaide, Alexander, Bess), myth and classic literature (Merlin, Circe, Iris, Prospero, Mercurio), and coinages from living vocabulary (Lince โ *ojo de lince*; Custodio โ *รกngel custodio*). Lince is never anglicized.
|
|
94
94
|
|
|
95
|
+
### Agent Definition Schema โ Fields for Template/Renderer Authors
|
|
96
|
+
|
|
97
|
+
The table above is the human-readable roster; agent definitions (`agents/*.yaml`) and the generated roster (`.arcane/agents.yaml`) carry the same data as structured fields. This is where they live, for anyone building a third-party renderer (DMC is renderer #1):
|
|
98
|
+
|
|
99
|
+
| Field | Lives on | Populated by | Consumed by |
|
|
100
|
+
| --- | --- | --- | --- |
|
|
101
|
+
| `persona.description` / `behavioral_rules` | Agent definition (role-level, naming-strategy-agnostic) | Template author | Every client renderer (Copilot, Claude, Codex, OpenClaw) |
|
|
102
|
+
| `persona.personality` / `persona.voice` / `persona.catchphrases` | Agent definition | Template author | Renderers that want tone/flavor beyond the bare role |
|
|
103
|
+
| `persona.visual_description` | Agent definition | Template author | **Declared, not yet rendered by any client this repo ships.** Kept with the definition rather than a renderer, by design โ data belongs with the thing it describes, not with any one consumer. A third-party image/avatar renderer reads this field directly from the YAML; no other wiring is required. |
|
|
104
|
+
| `epithet` (e.g. "the Archmage") | **Roster entry**, not the agent definition โ see below | Naming strategy (`arcanos` only; `generic`/`random`/`custom` are deliberately epithet-less) | `.arcane/generated/openclaw-roster.json`'s `identity.epithet`, when present |
|
|
105
|
+
|
|
106
|
+
**Why `epithet` is schema v2's roster field, not a persona field:** an agent definition YAML (e.g. `architecture-lead.yaml`) is a role archetype reused across every naming strategy โ "the Archmage" belongs to the persona *Merlin*, not to the "architecture-lead" role itself. Putting it in the definition would leak Arcanos-specific flavor into naming strategies designed to be epithet-neutral (`generic`'s plain role labels). It is populated during `spell agents init`'s naming step and flows through the roster entry (`.arcane/agents.yaml`) to `openclaw-roster.json` โ never guessed or re-derived by a renderer.
|
|
107
|
+
|
|
95
108
|
**Role consolidation (ADR-031):** Framework-specific dev roles (.NET, Blazor, React, Flutter, MAUI) are consolidated into generic roles (Full-Stack, Frontend, Mobile). The framework/stack is a prompt parameter, not an agent identity. This scales without needing a new agent for every technology.
|
|
96
109
|
|
|
97
110
|
**Naming policy:** All agent names should be positive characters โ beings, not objects โ with a clear thematic connection to their role.
|
|
@@ -113,12 +113,12 @@ See [[agents/installation|Runtime Installation]].
|
|
|
113
113
|
|
|
114
114
|
### Step 3.3 โ Agent Git Identity Setup
|
|
115
115
|
|
|
116
|
-
Every agent that will produce commits for this business must have a registered Git identity **before making its first commit**. See [[
|
|
116
|
+
Every agent that will produce commits for this business must have a registered Git identity **before making its first commit**. See [[git-conventions#agent-attribution-model|Git Conventions โ Agent Attribution]].
|
|
117
117
|
|
|
118
118
|
For each agent assigned to this business:
|
|
119
119
|
|
|
120
120
|
1. **Confirm persona name** is registered in [[naming-conventions|Naming Conventions]] agent roster.
|
|
121
|
-
2. **Confirm Git identity** exists in the registered identities table in [[
|
|
121
|
+
2. **Confirm Git identity** exists in the registered identities table in [[git-conventions#agent-email-convention|Git Conventions]].
|
|
122
122
|
3. **Add agent to `.mailmap`** in every repo the agent will commit to:
|
|
123
123
|
```
|
|
124
124
|
{AGENT_NAME} <{AGENT_EMAIL}>
|
|
@@ -15,7 +15,13 @@ against a consistent bar. This is the spell-prompt analogue of [[product-excelle
|
|
|
15
15
|
grades PRDs, not prompts). It is an **authoring-time standard** โ a checklist for contributors writing
|
|
16
16
|
or reviewing spells. It is **not** meant to be read at runtime by the spells it grades.
|
|
17
17
|
|
|
18
|
-
See [ARC-014](
|
|
18
|
+
See [ARC-014](https://github.com/codemagicianhq/arcane/blob/main/DECISIONS.md#arc-014--spell-authoring-standards-a-quality-rubric-for-spell-prompts)
|
|
19
|
+
for the decision that established this standard. **Full canonical URL, not a same-repo
|
|
20
|
+
wiki-link** (corrected 2026-08-31, BC-06) โ this file ships to consumer repos, and
|
|
21
|
+
`DECISIONS.md` does not: `src/assets/DECISIONS.md` (what consumers actually receive via
|
|
22
|
+
`spell init`) is an empty starter template for the *consumer's own* decisions, so a
|
|
23
|
+
same-repo wiki-link to a specific ARC id would resolve to the wrong document once
|
|
24
|
+
installed, not simply fail to resolve.
|
|
19
25
|
|
|
20
26
|
## The Spell Quality Rubric
|
|
21
27
|
|
|
@@ -35,7 +35,7 @@ deliverables; every word in motion.
|
|
|
35
35
|
- Follow platform guidelines (HIG for iOS, Material Design for Android)
|
|
36
36
|
- App store submissions require a complete release checklist โ no shortcuts
|
|
37
37
|
- Performance profiling is required before marking any feature complete
|
|
38
|
-
-
|
|
38
|
+
- Backward compatibility: support at least the two previous major OS versions
|
|
39
39
|
|
|
40
40
|
## Tools
|
|
41
41
|
|
|
@@ -4,7 +4,7 @@ applyTo: "**"
|
|
|
4
4
|
|
|
5
5
|
# Agent Output Formatting Rules
|
|
6
6
|
|
|
7
|
-
Source of truth: `.arcane/governance/git-conventions.md` โ PR Standards
|
|
7
|
+
Source of truth: `.arcane/governance/git-conventions.md` โ PR Standards โ PR Requirements.
|
|
8
8
|
|
|
9
9
|
## PR Link Format
|
|
10
10
|
|
|
@@ -20,9 +20,60 @@ All PR references in agent output **must** be clickable markdown links. Never wr
|
|
|
20
20
|
[PR #270 โ fix: remove Unix-only preinstall script and approve esbuild builds](https://dev.azure.com/{org}/{project}/_git/{repo}/pullrequest/270)
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
## Doc-ID Link Format
|
|
24
|
+
|
|
25
|
+
The same rule extends to every other identifier class this framework uses to reference
|
|
26
|
+
a decision or record: `ADR-NNN`, `ARC-NNN`, `EF-NN`, venture decision IDs, journal
|
|
27
|
+
entries, and named governance docs. A bare decision ID in agent output is the same
|
|
28
|
+
defect as a bare `PR #NNN` โ the reader has to go find it themselves, and a typo or
|
|
29
|
+
stale number is invisible until someone tries.
|
|
30
|
+
|
|
31
|
+
**Required format, by context:**
|
|
32
|
+
- **Inside a repo's own local markdown** (e.g. this repo's `journal/`, `PLAN.md`, or
|
|
33
|
+
`DECISIONS.md` cross-referencing itself): a wiki-link (`[[DECISIONS#ARC-NNN|ARC-NNN]]`)
|
|
34
|
+
or a relative markdown link that actually resolves from that file's own location.
|
|
35
|
+
- **Chat or PR output:** a clickable markdown link to the canonical file/anchor.
|
|
36
|
+
- **Governance docs this framework ships** (`.arcane/governance/*.md`): the full
|
|
37
|
+
canonical-repo URL. A file here lives in *this* repo today but ships into every
|
|
38
|
+
consumer repo's own tree, where a wiki-link or relative path to Arcane's own
|
|
39
|
+
`DECISIONS.md` either fails to resolve, or worse, silently resolves to the
|
|
40
|
+
*consumer's own* `DECISIONS.md` (an empty per-consumer starter template, not
|
|
41
|
+
Arcane's real decision record) โ wrong content, not just a dead link.
|
|
42
|
+
- **Spell prompts and instructions this framework ships**
|
|
43
|
+
(`.github/prompts/*.prompt.md`, `.github/instructions/*.md`): **plain text only โ
|
|
44
|
+
cite the bare ID, no link at all** (`ARC-035`, not `[ARC-035](...)`). A full
|
|
45
|
+
canonical URL is *also* unsafe here, not just a same-repo link: it bakes
|
|
46
|
+
`github.com/codemagicianhq/arcane` โ this project's own org and repo name โ into
|
|
47
|
+
content the org-token portability gate (`scripts/org-token-lint.ts`, D2 in
|
|
48
|
+
`spell-authoring-standards.md`) exists specifically to keep out of distributed
|
|
49
|
+
spells. **Confirmed live 2026-08-31 (BC-06):** the build's `Org-token lint` failed
|
|
50
|
+
on exactly this pattern in three prompt files, including two this same correction
|
|
51
|
+
pass had introduced minutes earlier while fixing the *other* link defect. Known
|
|
52
|
+
gap: the portability scan today only walks `.github/prompts/`, so a full URL in a
|
|
53
|
+
`.github/instructions/*.md` file โ like this one's own Merge Strategy section, two
|
|
54
|
+
paragraphs down โ ships uncaught; tracked in `TODO.md`.
|
|
55
|
+
- **Cross-repo references from anywhere else** (chat, PR output, another repo
|
|
56
|
+
entirely): the full canonical-repo URL.
|
|
57
|
+
|
|
58
|
+
**Never fabricate an unresolvable link.** If you cannot confirm the ID exists and where
|
|
59
|
+
it resolves to, say so and cite the ID as plain text rather than inventing a link that
|
|
60
|
+
looks real but goes nowhere.
|
|
61
|
+
|
|
23
62
|
## Merge Strategy
|
|
24
63
|
|
|
25
|
-
All PRs must use **Rebase and fast-forward**. Never squash. Never merge commit. See
|
|
64
|
+
All PRs must use **Rebase and fast-forward**. Never squash. Never merge commit. See
|
|
65
|
+
ARC-009 ยง7 (Arcane's own framework decision โ cited as plain text, not linked, per
|
|
66
|
+
Doc-ID Link Format above: this file ships). **Corrected 2026-08-31 (BC-06), three
|
|
67
|
+
times over:** this file previously cited `ADR-048`, which does exist in the shipped
|
|
68
|
+
`framework-decisions.md` reference โ but its actual topic ("Code Versus Docs Branch
|
|
69
|
+
Policy") has nothing to do with merge strategy, a topical miscitation rather than a
|
|
70
|
+
missing one. The first fix replaced it with a same-repo wiki-link to this repo's own
|
|
71
|
+
`DECISIONS.md` โ itself the exact cross-repo defect this section warns about
|
|
72
|
+
(`DECISIONS.md` doesn't ship). The second fix replaced that with a full canonical
|
|
73
|
+
URL, which held up locally but failed the build's org-token portability gate: a
|
|
74
|
+
`github.com/codemagicianhq/arcane` URL bakes this project's own org into a file the
|
|
75
|
+
gate treats as distributed content (see Doc-ID Link Format above). Settled on the
|
|
76
|
+
bare ID โ the gate's own documented safe form.
|
|
26
77
|
|
|
27
78
|
When completing a PR via `az repos pr update`, do **not** pass `--squash true`. The correct command:
|
|
28
79
|
|
|
@@ -9,7 +9,10 @@ agent: agent
|
|
|
9
9
|
|
|
10
10
|
- This spell determines whether a version bump is required and applies the correct semver type.
|
|
11
11
|
- Use it whenever you've changed `src/assets/`, `registry.ts`, or `profiles.ts` in the arcane repo.
|
|
12
|
-
- It enforces the rule from
|
|
12
|
+
- It enforces the rule from `project.md` โ Constraints โ Self-hosting: "Anything touching
|
|
13
|
+
`src/assets/` requires a version bump." (Corrected 2026-08-31, BC-06 โ this previously cited
|
|
14
|
+
CLAUDE.md, which now carries only the working protocol; the rule's live prose home moved to
|
|
15
|
+
`project.md:56-58`.)
|
|
13
16
|
|
|
14
17
|
---
|
|
15
18
|
|
|
@@ -51,7 +51,7 @@ For each PR in the batch, independently:
|
|
|
51
51
|
1. Fetch the PR diff and metadata (`gh pr diff <n>` / `az repos pr show --id <n>` + diff). If a PR can't be fetched (deleted, no access, network error), **skip it, record it as `SKIPPED` with the reason, and continue** โ never abort the whole batch for one bad PR.
|
|
52
52
|
2. Run the `spell-review` workflow against that diff โ the full dimension-coverage sweep (correctness, security, performance, tests, naming/clarity, architecture), severity-ranked findings (`HIGH` / `MEDIUM` / `LOW`, matching the `spell-review` ladder) with stable IDs, and the PASS/WARN/FAIL coverage table.
|
|
53
53
|
3. Record a per-PR result: verdict (**approve** / **request-changes**), finding counts by severity, and the coverage table.
|
|
54
|
-
4. **Post the formal round state per PR** โ apply `spell-review`'s step 10 (
|
|
54
|
+
4. **Post the formal round state per PR** โ apply `spell-review`'s step 10 (ARC-035 decision 3 โ cited as plain text, not linked; see `agent-output.instructions.md` โ Doc-ID Link Format) to *this* PR's number, not just this workflow's own single-run PR: a **request-changes** verdict posts `gh pr review <n> --request-changes` (or `az repos pr set-vote --id <n> --vote reject`); a re-review that clears a round this batch previously blocked dismisses/supersedes that prior review. An **approve** verdict posts nothing. Never let a posting failure (unauthenticated CLI, PR closed mid-batch) skip or abort the remaining PRs in the batch โ record the result already computed and continue.
|
|
55
55
|
|
|
56
56
|
Reviews are adversarial โ actively look for problems, do not just confirm quality. Apply the same no-fabrication coverage mandate as `spell-review`: a clean lens is reported as "no issues," never padded.
|
|
57
57
|
|
|
@@ -68,11 +68,11 @@ Build one report:
|
|
|
68
68
|
|
|
69
69
|
| PR | Repo | Title | Verdict | High | Med | Low |
|
|
70
70
|
| -- | ---- | ----- | ------- | ---- | --- | --- |
|
|
71
|
-
| #n | repo | โฆ | approve / request-changes | 0 | 1 | 2 |
|
|
72
|
-
| #n | repo | โฆ | SKIPPED โ {reason} | โ | โ | โ |
|
|
71
|
+
| [#n]({url}) | repo | โฆ | approve / request-changes | 0 | 1 | 2 |
|
|
72
|
+
| [#n]({url}) | repo | โฆ | SKIPPED โ {reason} | โ | โ | โ |
|
|
73
73
|
|
|
74
74
|
## Per-PR detail
|
|
75
|
-
### PR #n โ {title}
|
|
75
|
+
### [PR #n]({url}) โ {title}
|
|
76
76
|
- Verdict: โฆ
|
|
77
77
|
- Coverage: {PASS/WARN/FAIL per lens}
|
|
78
78
|
- Findings: {R1 โฆ}
|
|
@@ -122,7 +122,7 @@ Workflow:
|
|
|
122
122
|
|
|
123
123
|
9. **Write back to the pull request** โ when a PR exists for these changes, sync a short verdict plus a findings summary (verdict + finding counts/IDs + a pointer to the saved report) into the PR body/description. Use provider-agnostic phrasing that works for either GitHub or Azure DevOps. Mark it clearly as a snapshot at review time, and fall back to a top-level PR comment if the description can't be updated.
|
|
124
124
|
|
|
125
|
-
10. **Post the formal round state** (
|
|
125
|
+
10. **Post the formal round state** (ARC-035 decision 3 โ Arcane's own framework decision, cited as plain text, not linked, since this file ships; see `agent-output.instructions.md` โ Doc-ID Link Format) โ step 9's write-back is a human-readable summary; this is the separate, machine-readable signal the `Review round clear` required check actually reads. Only a confirmed blocker posts a formal review โ an `APPROVE` or `DEFER` verdict posts nothing here, preserving the zero-friction path for PRs nobody formally blocks:
|
|
126
126
|
- **REQUEST CHANGES verdict:**
|
|
127
127
|
- GitHub: `gh pr review <n> --request-changes --body "<one-line blocker summary + pointer to the saved report>"`.
|
|
128
128
|
- Azure DevOps: `az repos pr set-vote --id <n> --vote reject` โ the same primitive `spell-commit-work.prompt.md`'s author-side self-approval step already uses (`az repos pr set-vote --vote approve`), applied to the reviewer side.
|
|
@@ -11,7 +11,7 @@ agent: agent
|
|
|
11
11
|
- **Hub-gated:** requires `role: "hub"` in `.arcane.json`. Refuses cleanly in a consumer repo, pointing at the right place to capture the idea instead.
|
|
12
12
|
- Use this when starting a new venture (business, product, or personal project) that the hub should track.
|
|
13
13
|
- Input format is: `Venture Name | Venture Type | Description` โ prompt will ask for missing fields.
|
|
14
|
-
- Renamed from `spell-bootstrap-business` โ venture books and the registry didn't exist when that spell was named. Clean rename, no compatibility alias, per
|
|
14
|
+
- Renamed from `spell-bootstrap-business` โ venture books and the registry didn't exist when that spell was named. Clean rename, no compatibility alias, per ARC-008's precedent (cited as plain text, not linked โ this file ships; see `agent-output.instructions.md` โ Doc-ID Link Format).
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
package/dist/index.js
CHANGED
|
@@ -946,6 +946,20 @@ var ARCANOS_NAME_MAP = {
|
|
|
946
946
|
"collaborator": "Iris",
|
|
947
947
|
"security-ops": "Custodio"
|
|
948
948
|
};
|
|
949
|
+
var ARCANOS_EPITHET_MAP = {
|
|
950
|
+
"orchestrator": "the Maestro",
|
|
951
|
+
"architecture-lead": "the Archmage",
|
|
952
|
+
"fullstack-dev": "the Conjuror",
|
|
953
|
+
"qa-lead": "the Unmasker",
|
|
954
|
+
"devops": "the Stormcaller",
|
|
955
|
+
"frontend-dev": "the Illusionist",
|
|
956
|
+
"mobile-dev": "the Swift",
|
|
957
|
+
"research-analyst": "the Man Who Knows",
|
|
958
|
+
"marketing-strategist": "the Charmweaver",
|
|
959
|
+
"operations-comms": "the Herald",
|
|
960
|
+
"collaborator": "the Emissary",
|
|
961
|
+
"security-ops": "the Warden"
|
|
962
|
+
};
|
|
949
963
|
var RANDOM_NAME_POOL = [
|
|
950
964
|
"Aiden",
|
|
951
965
|
"Blake",
|
|
@@ -985,10 +999,11 @@ function applyGenericNames(roleIds) {
|
|
|
985
999
|
}));
|
|
986
1000
|
}
|
|
987
1001
|
function applyArcanosNames(roleIds) {
|
|
988
|
-
return roleIds.map((id) =>
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1002
|
+
return roleIds.map((id) => {
|
|
1003
|
+
const name = ARCANOS_NAME_MAP[id] ?? GENERIC_NAME_MAP[id] ?? id;
|
|
1004
|
+
const epithet = ARCANOS_NAME_MAP[id] ? ARCANOS_EPITHET_MAP[id] : void 0;
|
|
1005
|
+
return epithet ? { definition: id, name, epithet } : { definition: id, name };
|
|
1006
|
+
});
|
|
992
1007
|
}
|
|
993
1008
|
function applyRandomNames(roleIds) {
|
|
994
1009
|
const shuffled = [...RANDOM_NAME_POOL].sort(() => Math.random() - 0.5);
|
|
@@ -1182,6 +1197,7 @@ function validateAgentRoster(value, source) {
|
|
|
1182
1197
|
requireString(item.definition, `roster[${index}].definition`, issues);
|
|
1183
1198
|
if (item.name !== null) requireString(item.name, `roster[${index}].name`, issues);
|
|
1184
1199
|
requireString(item.id, `roster[${index}].id`, issues);
|
|
1200
|
+
if (item.epithet !== void 0) requireString(item.epithet, `roster[${index}].epithet`, issues);
|
|
1185
1201
|
});
|
|
1186
1202
|
}
|
|
1187
1203
|
if (issues.length > 0) throw new AgentConfigValidationError(source, issues);
|
|
@@ -1442,6 +1458,7 @@ ${rows}
|
|
|
1442
1458
|
async function syncAgents(targetDir, assetsDir, roster, options = {}) {
|
|
1443
1459
|
const synced = [];
|
|
1444
1460
|
const skipped = [];
|
|
1461
|
+
let hasUnresolvedRoles = false;
|
|
1445
1462
|
const projectDefs = projectAgentsDir(targetDir);
|
|
1446
1463
|
const bundledDefs = join2(assetsDir, "agents");
|
|
1447
1464
|
const resolved = [];
|
|
@@ -1454,6 +1471,7 @@ async function syncAgents(targetDir, assetsDir, roster, options = {}) {
|
|
|
1454
1471
|
def = await loadAgentDefinition(bundledDefs, entry.definition);
|
|
1455
1472
|
} catch {
|
|
1456
1473
|
skipped.push(`${entry.definition} (definition not found)`);
|
|
1474
|
+
hasUnresolvedRoles = true;
|
|
1457
1475
|
continue;
|
|
1458
1476
|
}
|
|
1459
1477
|
}
|
|
@@ -1506,6 +1524,7 @@ async function syncAgents(targetDir, assetsDir, roster, options = {}) {
|
|
|
1506
1524
|
id: entry.id,
|
|
1507
1525
|
identity: {
|
|
1508
1526
|
name: entry.name ?? entry.definition,
|
|
1527
|
+
...entry.epithet ? { epithet: entry.epithet } : {},
|
|
1509
1528
|
role: def.role
|
|
1510
1529
|
}
|
|
1511
1530
|
}))
|
|
@@ -1575,7 +1594,7 @@ async function syncAgents(targetDir, assetsDir, roster, options = {}) {
|
|
|
1575
1594
|
);
|
|
1576
1595
|
if (codexMerged) synced.push("AGENTS.md (agents section)");
|
|
1577
1596
|
}
|
|
1578
|
-
return { synced, skipped };
|
|
1597
|
+
return { synced, skipped, hasUnresolvedRoles };
|
|
1579
1598
|
}
|
|
1580
1599
|
|
|
1581
1600
|
// src/modules/agents.ts
|
|
@@ -1660,9 +1679,9 @@ All 12 roles across ${allCategories.join(", ")}` : "\nPick individual roles from
|
|
|
1660
1679
|
}
|
|
1661
1680
|
printStep("Assigning names...");
|
|
1662
1681
|
const named = await applyNamingStrategy(namingStrategy, selectedRoleIds);
|
|
1663
|
-
const rosterEntries = named.map(({ definition, name }) => {
|
|
1682
|
+
const rosterEntries = named.map(({ definition, name, epithet }) => {
|
|
1664
1683
|
const id = definition === "orchestrator" ? "main" : name.toLowerCase().replace(/\s+/g, "-");
|
|
1665
|
-
return { definition, name, id };
|
|
1684
|
+
return epithet ? { definition, name, id, epithet } : { definition, name, id };
|
|
1666
1685
|
});
|
|
1667
1686
|
const roster = {
|
|
1668
1687
|
schema_version: 2,
|
|
@@ -1706,7 +1725,7 @@ All 12 roles across ${allCategories.join(", ")}` : "\nPick individual roles from
|
|
|
1706
1725
|
return;
|
|
1707
1726
|
}
|
|
1708
1727
|
printStep("Syncing agent definitions to all clients...");
|
|
1709
|
-
const { synced, skipped } = await syncAgents(
|
|
1728
|
+
const { synced, skipped, hasUnresolvedRoles } = await syncAgents(
|
|
1710
1729
|
targetDir,
|
|
1711
1730
|
assetsDir,
|
|
1712
1731
|
roster,
|
|
@@ -1721,6 +1740,12 @@ All 12 roles across ${allCategories.join(", ")}` : "\nPick individual roles from
|
|
|
1721
1740
|
console.log();
|
|
1722
1741
|
console.log(` \u{1F4A1} Customize: edit .arcane/agents.yaml \u2192 spell agents sync`);
|
|
1723
1742
|
console.log();
|
|
1743
|
+
if (hasUnresolvedRoles) {
|
|
1744
|
+
console.error(
|
|
1745
|
+
' \u2717 One or more rostered roles could not be resolved (see "Skipped" above) \u2014 a malformed or missing agent template must not pass silently.'
|
|
1746
|
+
);
|
|
1747
|
+
process.exitCode = 1;
|
|
1748
|
+
}
|
|
1724
1749
|
}
|
|
1725
1750
|
async function runAgentsSync(options, targetDir, assetsDir) {
|
|
1726
1751
|
let roster;
|
|
@@ -1739,7 +1764,7 @@ async function runAgentsSync(options, targetDir, assetsDir) {
|
|
|
1739
1764
|
} else {
|
|
1740
1765
|
console.log("\n Syncing agents...\n");
|
|
1741
1766
|
}
|
|
1742
|
-
const { synced, skipped } = await syncAgents(
|
|
1767
|
+
const { synced, skipped, hasUnresolvedRoles } = await syncAgents(
|
|
1743
1768
|
targetDir,
|
|
1744
1769
|
assetsDir,
|
|
1745
1770
|
roster,
|
|
@@ -1763,6 +1788,12 @@ async function runAgentsSync(options, targetDir, assetsDir) {
|
|
|
1763
1788
|
}
|
|
1764
1789
|
}
|
|
1765
1790
|
console.log();
|
|
1791
|
+
if (hasUnresolvedRoles) {
|
|
1792
|
+
console.error(
|
|
1793
|
+
' \u2717 One or more rostered roles could not be resolved (see "Skipped" above) \u2014 a malformed or missing agent template must not pass silently.'
|
|
1794
|
+
);
|
|
1795
|
+
process.exitCode = 1;
|
|
1796
|
+
}
|
|
1766
1797
|
}
|
|
1767
1798
|
async function runAgentsList(targetDir) {
|
|
1768
1799
|
let roster;
|