@stofone/dsh-agent-roster 0.9.2 → 0.10.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 +68 -0
- package/README.md +80 -5
- package/README.zh.md +73 -6
- package/bin/cli.mjs +228 -2
- package/docs/roster-v2.md +220 -0
- package/examples/dist/core-review-1.0.0.json +160 -0
- package/examples/dist/research-0.1.0.json +95 -0
- package/examples/roles/core-review-correctness/AGENT.md +20 -0
- package/examples/roles/core-review-lead/AGENT.md +23 -0
- package/examples/roles/core-review-lead-captain/AGENT.md +13 -0
- package/examples/roles/core-review-maintainer/AGENT.md +20 -0
- package/examples/roles/research-scout/AGENT.md +16 -0
- package/examples/roles/research-verifier/AGENT.md +16 -0
- package/examples/rosters/core-review/roster.yaml +39 -0
- package/examples/rosters/research/roster.yaml +20 -0
- package/examples-orchestration/dist/core-review-1.0.0.json +185 -0
- package/examples-orchestration/dist/core-review-1.0.0.tasks.json +76 -0
- package/examples-orchestration/roles/fixer/AGENT.md +18 -0
- package/examples-orchestration/roles/lead/AGENT.md +18 -0
- package/examples-orchestration/roles/reviewer/AGENT.md +23 -0
- package/examples-orchestration/roles/reviewer/parts/checklist.md +6 -0
- package/examples-orchestration/rosters/core-review/overrides/fixer.md +1 -0
- package/examples-orchestration/rosters/core-review/roster.yaml +43 -0
- package/index.js +8 -1
- package/lib/compile2.js +522 -0
- package/lib/format-v2.js +196 -0
- package/lib/frontmatter.js +113 -0
- package/lib/migrate.js +326 -0
- package/lib/plan.js +167 -0
- package/lib/registry.js +65 -11
- package/lib/role.js +498 -0
- package/lib/roster2.js +591 -0
- package/lib/run.js +188 -0
- package/lib/write.js +14 -8
- package/orgs/agent-roster-collective/dist/api-design-1.0.0.json +130 -0
- package/orgs/agent-roster-collective/dist/content-ops-1.0.0.json +130 -0
- package/orgs/agent-roster-collective/dist/contract-review-1.0.0.json +130 -0
- package/orgs/agent-roster-collective/dist/data-quality-1.0.0.json +130 -0
- package/orgs/agent-roster-collective/dist/db-migration-1.0.0.json +130 -0
- package/orgs/agent-roster-collective/dist/docs-writer-1.0.0.json +130 -0
- package/orgs/agent-roster-collective/dist/finance-recon-1.0.0.json +130 -0
- package/orgs/agent-roster-collective/dist/frontend-ui-1.0.0.json +130 -0
- package/orgs/agent-roster-collective/dist/i18n-l10n-1.0.0.json +130 -0
- package/orgs/agent-roster-collective/dist/incident-response-1.0.0.json +130 -0
- package/orgs/agent-roster-collective/dist/literature-review-1.0.0.json +130 -0
- package/orgs/agent-roster-collective/dist/market-research-1.0.0.json +130 -0
- package/orgs/agent-roster-collective/dist/ml-eval-1.0.0.json +130 -0
- package/orgs/agent-roster-collective/dist/performance-1.0.0.json +130 -0
- package/orgs/agent-roster-collective/dist/product-discovery-1.0.0.json +130 -0
- package/orgs/agent-roster-collective/dist/qa-regression-1.0.0.json +130 -0
- package/orgs/agent-roster-collective/dist/recruiting-1.0.0.json +130 -0
- package/orgs/agent-roster-collective/dist/release-manager-1.0.0.json +130 -0
- package/orgs/agent-roster-collective/dist/security-audit-1.0.0.json +132 -0
- package/orgs/agent-roster-collective/dist/sre-oncall-1.0.0.json +130 -0
- package/orgs/agent-roster-collective/dist/support-triage-1.0.0.json +130 -0
- package/orgs/agent-roster-collective/roles/api-design-failure-mode/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/api-design-lead/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/api-design-schema/AGENT.md +20 -0
- package/orgs/agent-roster-collective/roles/content-ops-distributor/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/content-ops-editor/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/content-ops-lead/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/contract-review-clause-reader/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/contract-review-lead/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/contract-review-risk-note/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/data-quality-lead/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/data-quality-profiler/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/data-quality-rule-author/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/db-migration-lead/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/db-migration-migrator/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/db-migration-verifier/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/docs-writer-example-runner/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/docs-writer-lead/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/docs-writer-writer/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/finance-recon-exception/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/finance-recon-lead/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/finance-recon-matcher/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/frontend-ui-a11y-reviewer/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/frontend-ui-implementer/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/frontend-ui-lead/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/i18n-l10n-lead/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/i18n-l10n-reviewer/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/i18n-l10n-translator/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/incident-response-commander/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/incident-response-scribe/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/incident-response-triage/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/literature-review-critic/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/literature-review-extractor/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/literature-review-lead/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/market-research-lead/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/market-research-researcher/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/market-research-synthesizer/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/ml-eval-dataset/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/ml-eval-lead/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/ml-eval-metric/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/performance-lead/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/performance-load-tester/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/performance-profiler/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/product-discovery-interviewer/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/product-discovery-lead/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/product-discovery-skeptic/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/qa-regression-case-writer/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/qa-regression-flake-hunter/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/qa-regression-lead/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/recruiting-lead/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/recruiting-scheduler/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/recruiting-screener/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/release-manager-changelog/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/release-manager-lead/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/release-manager-rollback/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/security-audit-code-auditor/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/security-audit-lead/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/security-audit-threat-modeler/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/sre-oncall-capacity/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/sre-oncall-lead/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/sre-oncall-runbook/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/support-triage-classifier/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/support-triage-lead/AGENT.md +19 -0
- package/orgs/agent-roster-collective/roles/support-triage-responder/AGENT.md +19 -0
- package/orgs/agent-roster-collective/rosters/api-design/roster.yaml +26 -0
- package/orgs/agent-roster-collective/rosters/content-ops/roster.yaml +25 -0
- package/orgs/agent-roster-collective/rosters/contract-review/roster.yaml +25 -0
- package/orgs/agent-roster-collective/rosters/data-quality/roster.yaml +26 -0
- package/orgs/agent-roster-collective/rosters/db-migration/roster.yaml +26 -0
- package/orgs/agent-roster-collective/rosters/docs-writer/roster.yaml +25 -0
- package/orgs/agent-roster-collective/rosters/finance-recon/roster.yaml +25 -0
- package/orgs/agent-roster-collective/rosters/frontend-ui/roster.yaml +26 -0
- package/orgs/agent-roster-collective/rosters/i18n-l10n/roster.yaml +26 -0
- package/orgs/agent-roster-collective/rosters/incident-response/roster.yaml +26 -0
- package/orgs/agent-roster-collective/rosters/literature-review/roster.yaml +25 -0
- package/orgs/agent-roster-collective/rosters/market-research/roster.yaml +25 -0
- package/orgs/agent-roster-collective/rosters/ml-eval/roster.yaml +25 -0
- package/orgs/agent-roster-collective/rosters/performance/roster.yaml +25 -0
- package/orgs/agent-roster-collective/rosters/product-discovery/roster.yaml +25 -0
- package/orgs/agent-roster-collective/rosters/qa-regression/roster.yaml +26 -0
- package/orgs/agent-roster-collective/rosters/recruiting/roster.yaml +25 -0
- package/orgs/agent-roster-collective/rosters/release-manager/roster.yaml +25 -0
- package/orgs/agent-roster-collective/rosters/security-audit/roster.yaml +30 -0
- package/orgs/agent-roster-collective/rosters/sre-oncall/roster.yaml +26 -0
- package/orgs/agent-roster-collective/rosters/support-triage/roster.yaml +25 -0
- package/package.json +3 -1
- package/schema/compiled.v1.schema.json +391 -0
- package/schema/role.v1.schema.json +240 -0
- package/schema/roster.v2.schema.json +437 -0
- package/examples/core-review/roster.yaml +0 -82
- package/examples/research/roster.yaml +0 -42
- package/orgs/agent-roster-collective/api-design/roster.yaml +0 -67
- package/orgs/agent-roster-collective/content-ops/roster.yaml +0 -55
- package/orgs/agent-roster-collective/contract-review/roster.yaml +0 -58
- package/orgs/agent-roster-collective/data-quality/roster.yaml +0 -64
- package/orgs/agent-roster-collective/db-migration/roster.yaml +0 -64
- package/orgs/agent-roster-collective/docs-writer/roster.yaml +0 -59
- package/orgs/agent-roster-collective/finance-recon/roster.yaml +0 -63
- package/orgs/agent-roster-collective/frontend-ui/roster.yaml +0 -60
- package/orgs/agent-roster-collective/i18n-l10n/roster.yaml +0 -60
- package/orgs/agent-roster-collective/incident-response/roster.yaml +0 -65
- package/orgs/agent-roster-collective/literature-review/roster.yaml +0 -61
- package/orgs/agent-roster-collective/market-research/roster.yaml +0 -60
- package/orgs/agent-roster-collective/ml-eval/roster.yaml +0 -63
- package/orgs/agent-roster-collective/performance/roster.yaml +0 -63
- package/orgs/agent-roster-collective/product-discovery/roster.yaml +0 -55
- package/orgs/agent-roster-collective/qa-regression/roster.yaml +0 -64
- package/orgs/agent-roster-collective/recruiting/roster.yaml +0 -58
- package/orgs/agent-roster-collective/release-manager/roster.yaml +0 -59
- package/orgs/agent-roster-collective/security-audit/roster.yaml +0 -65
- package/orgs/agent-roster-collective/sre-oncall/roster.yaml +0 -60
- package/orgs/agent-roster-collective/support-triage/roster.yaml +0 -58
- /package/orgs/agent-roster-collective/{security-audit → rosters/security-audit}/capabilities.yaml +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,74 @@
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.10.1] - 2026-11-03
|
|
8
|
+
|
|
9
|
+
### Docs
|
|
10
|
+
|
|
11
|
+
- The Chinese README said `migrate` was "not implemented", listed one example library where there are
|
|
12
|
+
two, and had a file tree from before `roles/`, `roster.v2` and `compiled.v1` existed — docs that
|
|
13
|
+
describe last week's format read as current, which is worse than no docs.
|
|
14
|
+
- Both READMEs now carry the same two sections: one-command v1→v2 migration (including why role names
|
|
15
|
+
are prefixed and why a colliding captain gets `-captain`), and feeding a failed round back into the
|
|
16
|
+
plan with `compile --run`.
|
|
17
|
+
|
|
18
|
+
## [0.10.0] - 2026-11-03
|
|
19
|
+
|
|
20
|
+
### Roles, rosters and compiled artifacts (v2)
|
|
21
|
+
|
|
22
|
+
- **A roster is no longer one document doing four jobs.** Personas move to `roles/<dir>/AGENT.md`
|
|
23
|
+
(frontmatter for parameters, markdown body for the persona, optional `parts/`), assemblies stay in
|
|
24
|
+
`rosters/<id>/roster.yaml` (members are `name@x.y.z` references, the captain gets its own section),
|
|
25
|
+
and everything consumers read is a self-contained `dist/<id>-<ver>.json`.
|
|
26
|
+
- **Assembly is part of the format**: `executionPrompt = join("\n\n", [body, ...parts, ...append])`,
|
|
27
|
+
implemented once in `lib/frontmatter.js#assemblePrompt`. The compiled artifact records character
|
|
28
|
+
count, block list and SHA-256 per prompt.
|
|
29
|
+
- **The discriminator decides the version** — `dsh-agent-roster/role@1`, `/roster@2`, `/compiled@1`.
|
|
30
|
+
v1 is frozen; two formats never share a directory.
|
|
31
|
+
- **Identity comes from the document**, so the "directory name must equal `id`" rule is replaced by a
|
|
32
|
+
stronger one: role names are unique within a library, and duplicate `name` is an error.
|
|
33
|
+
- **`orchestration` is compiled as a declaration.** Stages and gates are validated (a gate's `after`
|
|
34
|
+
must name real stages, `by` must name a real member), the artifact marks
|
|
35
|
+
`status: "declaration"`, and every non-executed field is listed in `declarations[]`.
|
|
36
|
+
- **New CLI**: `dsh-agent-roster compile --roster <dir> [--roles <dir>] [--write] [--json]`.
|
|
37
|
+
- **Schema alignment tests**: `test/v2-schema.test.js` pins field sets, enums, limits and regexes to
|
|
38
|
+
`lib/format-v2.js`, and `test/examples-orchestration.test.js` compiles `examples-orchestration/` and fails if `dist/` drifts.
|
|
39
|
+
- Design contract: [`docs/roster-v2.md`](./docs/roster-v2.md).
|
|
40
|
+
- **Gates must be runnable.** Every gate carries at least one `verify` entry (`command`, `expectExit`);
|
|
41
|
+
a gate without one is rejected as `gate-unverifiable`, because a gate whose only criterion is a
|
|
42
|
+
sentence is judged by the person signing it.
|
|
43
|
+
- **A construction plan, not an execution.** `compile` now also writes
|
|
44
|
+
`dist/<id>-<ver>.tasks.json` (`plan@1`): stages and gates flattened into cards with dependencies,
|
|
45
|
+
`executor: "captain"`, and `loop.onGateFailure` naming where a failed gate goes back to and how many
|
|
46
|
+
rounds it may take. The plugin never calls `agent_teams_create_task` — the plan is a drawing.
|
|
47
|
+
|
|
48
|
+
### Migrating what already existed
|
|
49
|
+
|
|
50
|
+
- **`migrate --dir <library> [--write]`** rewrites a v1 library in place: each member's
|
|
51
|
+
`systemPrompt` becomes the body of `roles/<roster>-<member>/AGENT.md`, the member turns into a
|
|
52
|
+
reference, the captain becomes a role file of its own, `capabilities.yaml` moves next to the
|
|
53
|
+
assembly, every roster is compiled into `dist/`, and the old flat directory is removed.
|
|
54
|
+
Without `--write` it only reports what it would do — it is the one command that deletes things.
|
|
55
|
+
- **Role names carry the roster id prefix.** v2 requires role names to be unique per library; a
|
|
56
|
+
library whose 21 rosters all call their captain `lead` cannot have 21 files named `lead`. When the
|
|
57
|
+
captain collides with a member it gets a `-captain` suffix instead of being merged — they are two
|
|
58
|
+
personas and merging them would silently drop one.
|
|
59
|
+
- **This repository's own archives were migrated**: `examples/` (2 rosters, 5 members, one captain)
|
|
60
|
+
and `orgs/agent-roster-collective/` (21 rosters, 63 members) now live as `roles/ + rosters/ + dist/`.
|
|
61
|
+
- `maintainers` accepts handles (`STofone`) rather than the lowercase id pattern — migration must not
|
|
62
|
+
rename a person to satisfy a validator.
|
|
63
|
+
- **Both layouts are readable**: the scanner, the document route and the writer accept
|
|
64
|
+
`<root>/<id>/roster.*` and `<root>/rosters/<id>/roster.*`, so a v1 library keeps working while it is
|
|
65
|
+
being migrated, and the panel shows v2 rosters through the same `rosterViewOf` shape.
|
|
66
|
+
|
|
67
|
+
### An execution record feeds the next compile
|
|
68
|
+
|
|
69
|
+
- **`run@1`** (`lib/run.js`): rounds → tasks → findings/commands, with unknown fields rejected.
|
|
70
|
+
- **`compile --run <file>`** merges the previous round's failures into the retry card's description
|
|
71
|
+
(and stamps `round` on the plan). A failure that only lives on a red card never reaches the next
|
|
72
|
+
compile — which is how a gate becomes decoration.
|
|
73
|
+
- The record is written by the captain session, not by the plugin: task ids belong to the team.
|
|
74
|
+
|
|
7
75
|
## [0.9.2] - 2026-11-03
|
|
8
76
|
|
|
9
77
|
### The member list is a table
|
package/README.md
CHANGED
|
@@ -84,7 +84,7 @@ captain: # optional: what the captain (the session that crea
|
|
|
84
84
|
|
|
85
85
|
JSON works too (`roster.json`); YAML and JSON are probed in the order `roster.yaml`, `roster.yml`, `roster.json`. Exactly one file per directory — a second one is reported, never silently picked.
|
|
86
86
|
|
|
87
|
-
This package ships two example libraries
|
|
87
|
+
This package ships two example libraries: [`examples/`](./examples) (v2, migrated from the v1 archives this repo grew up with) and [`examples-orchestration/`](./examples-orchestration) (v2, hand-written to show gates and a construction plan). `rosterDirs` is empty by
|
|
88
88
|
default, meaning the global library above, which is empty in a fresh install — so the panel shows
|
|
89
89
|
"0 rosters" until you create one in the UI. To look at the examples instead, point the CLI at them:
|
|
90
90
|
|
|
@@ -221,6 +221,79 @@ This file creates nothing. It does not write `team.json`, does not create member
|
|
|
221
221
|
node bin/cli.mjs validate --dir orgs/agent-roster-collective
|
|
222
222
|
```
|
|
223
223
|
|
|
224
|
+
## Roles, rosters and compiled artifacts (v2)
|
|
225
|
+
|
|
226
|
+
v1 keeps everything in one `roster.yaml`: the persona sits in a YAML block scalar, the assembly protocol lives in plugin code, the captain is written twice, and identity comes from the directory name. v2 splits that into three layers, one authority each:
|
|
227
|
+
|
|
228
|
+
```text
|
|
229
|
+
roles/<any-dir>/AGENT.md role: frontmatter is parameters, body is the persona, parts/ optional
|
|
230
|
+
rosters/<id>/roster.yaml assembly: members are references, the captain has its own section
|
|
231
|
+
dist/<id>-<ver>.json compiled: self-contained, hashable, the only thing consumers read
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
The discriminator decides the version — `dsh-agent-roster/role@1`, `dsh-agent-roster/roster@2`, `dsh-agent-roster/compiled@1`. Two formats never share a directory: v1 is frozen, and `roster migrate` (not yet built) rewrites it in place.
|
|
235
|
+
|
|
236
|
+
Assembly is written into the format rather than hidden in code:
|
|
237
|
+
|
|
238
|
+
```text
|
|
239
|
+
executionPrompt = join("\n\n", [body, ...parts, ...member.append])
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
`assemblePrompt()` in `lib/frontmatter.js` is the only implementation, and the compiled artifact records the character count, the block list and a SHA-256 for every prompt, so "which text did this member actually get" is answerable without re-reading the sources.
|
|
243
|
+
|
|
244
|
+
A gate must be runnable, not self-certified: every gate carries at least one `verify` entry (`command` + `expectExit`), and a gate without one is rejected at validation time (`gate-unverifiable`). `requires` stays, but only as prose for the human reading the verdict.
|
|
245
|
+
|
|
246
|
+
`compile` also emits the construction plan — `dist/<id>-<ver>.tasks.json`:
|
|
247
|
+
|
|
248
|
+
```json
|
|
249
|
+
{ "kind": "dsh-agent-roster/plan@1", "executor": "captain",
|
|
250
|
+
"tasks": [ { "id": "build", "kind": "stage", "member": "fixer", "dependencies": [] },
|
|
251
|
+
{ "id": "audit", "kind": "stage", "member": "reviewer", "dependencies": ["build"] },
|
|
252
|
+
{ "id": "tests-pass", "kind": "gate", "member": "reviewer", "dependencies": ["audit"] } ],
|
|
253
|
+
"loop": { "onGateFailure": { "retry": "build", "maxRounds": 1 } } }
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
`executor: "captain"` is the whole point: the plugin never calls `agent_teams_create_task`, because writing team state belongs to the captain session. The plan is a drawing the captain builds from, not the plugin building for it.
|
|
257
|
+
|
|
258
|
+
The example library in [`examples-orchestration/`](./examples-orchestration/) compiles end to end:
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
node bin/cli.mjs compile --roster examples-orchestration/rosters/core-review --write
|
|
262
|
+
node bin/cli.mjs compile --roster examples-orchestration/rosters/core-review --json
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
`orchestration` is part of the format but is compiled with `status: "declaration"`: stages and gates are validated (a gate's `after` must name real stages, `by` must name a real member), yet nothing executes them. Declaring a field is not the same as proving it runs, so the artifact says which is which in `declarations[]`. The full contract — field ownership, assembly semantics, identity and uniqueness, migration rules — is in [`docs/roster-v2.md`](./docs/roster-v2.md); the schemas are [`schema/role.v1.schema.json`](./schema/role.v1.schema.json), [`schema/roster.v2.schema.json`](./schema/roster.v2.schema.json) and [`schema/compiled.v1.schema.json`](./schema/compiled.v1.schema.json).
|
|
266
|
+
|
|
267
|
+
### From v1 to v2 in one command
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
node bin/cli.mjs migrate --dir examples # 只出计划:要写哪些文件、能不能编译
|
|
271
|
+
node bin/cli.mjs migrate --dir examples --write # 原地重写:roles/ + rosters/ + dist/,删掉旧目录
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Migration keeps personas verbatim: a member's `systemPrompt` becomes the body of
|
|
275
|
+
`roles/<roster>-<member>/AGENT.md`, and the member turns into a reference. The captain stops being a
|
|
276
|
+
loose field and becomes a role file of its own — `<roster>-<captain>-captain` when its name collides
|
|
277
|
+
with a member, because those are two different personas and silently merging them would lose one.
|
|
278
|
+
|
|
279
|
+
Role names carry the roster id prefix because v2 requires role names to be **unique per library**,
|
|
280
|
+
and a library whose 21 rosters all call their captain `lead` cannot have 21 files with the same name.
|
|
281
|
+
|
|
282
|
+
### Feeding a failed round back into the plan
|
|
283
|
+
|
|
284
|
+
`compile --run <file>` reads an execution record (`run@1`) and appends the previous round's failures
|
|
285
|
+
to the retry card's description:
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
node bin/cli.mjs compile --roster examples-orchestration/rosters/core-review \
|
|
289
|
+
--run examples-orchestration/dist/core-review-1.0.0.run.json
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
The record is written by whoever executes the plan (the captain session), not by the plugin: a task
|
|
293
|
+
id belongs to the team, and the plugin never writes team state. What it does own is the merge — a
|
|
294
|
+
failure that only exists on a red card never reaches the next compile, which is exactly how a gate
|
|
295
|
+
turns into decoration.
|
|
296
|
+
|
|
224
297
|
## Tools
|
|
225
298
|
|
|
226
299
|
| Tool | Purpose |
|
|
@@ -377,10 +450,12 @@ lib/ format, parse, validate, registry, bridge, verify, render, lib
|
|
|
377
450
|
config-view, json, http, write, service, teams-state, tools
|
|
378
451
|
client.js client plugin: sidebar entry, main panel (rosters/teams/health), settings section
|
|
379
452
|
locale/ panel + listing dictionaries (en/zh)
|
|
380
|
-
schema/ roster /
|
|
381
|
-
|
|
453
|
+
schema/ roster v1 / role v1 / roster v2 / compiled v1 / capabilities / organization / registry
|
|
454
|
+
schemas (editor completion; kept aligned by tests)
|
|
455
|
+
bin/cli.mjs validate/list/compile/migrate/registry for CI
|
|
382
456
|
scripts/ host-smoke.mjs, client-smoke.mjs, check-locale.mjs (dev only, not shipped)
|
|
383
|
-
examples/
|
|
457
|
+
examples/ a v2 library migrated from the v1 archives
|
|
458
|
+
examples-orchestration/ a v2 library with orchestration, gates and a construction plan
|
|
384
459
|
orgs/ a worked example library with its own organization.yaml (21 rosters)
|
|
385
460
|
test/ unit tests
|
|
386
461
|
```
|
|
@@ -389,7 +464,7 @@ test/ unit tests
|
|
|
389
464
|
|
|
390
465
|
Verified on this machine:
|
|
391
466
|
|
|
392
|
-
- `npm test` —
|
|
467
|
+
- `npm test` — 286/286 passing (format, parse, validate, registry, paths, bridge, verify buckets, digests and provenance, roster diff, capability requirements and the capability gate, library organization, registry schema, lossless JSON, write plan/apply, all routes, panel snapshot and hints, dictionary consistency, tool contracts, client loader contract, library snapshot, config view, package manifest, declared fields and captain block, schema/constant alignment, the v2 role/roster/compiled layers and their schema alignment, v1→v2 migration, run records feeding the plan, remote registry and one-click install).
|
|
393
468
|
- `npm run smoke` — `apply()` registers all six tools through a real `defineTool` from a DSH bundle; `roster_list` / `roster_show` / `roster_create` / `roster_verify` / `roster_diff` execute, every output survives `lossless` checks, `path` overrides are honored, effect disposal runs cleanly. The same command then loads `client.js` against a stubbed module table and asserts the three slot registrations, style injection and effect disposal.
|
|
394
469
|
- CLI: `validate --dir examples` returns exit 0 over both example libraries.
|
|
395
470
|
- Installed into a real profile with the plugin manager (`plugin_manager install_bundle` against this directory → `link:` dependency + bundle entry); the profile re-reads the linked files on restart.
|
package/README.zh.md
CHANGED
|
@@ -82,7 +82,7 @@ captain: # 可选:记录队长(=创建团队的那个
|
|
|
82
82
|
|
|
83
83
|
JSON 也可以(`roster.json`)。同一目录下按 `roster.yaml` → `roster.yml` → `roster.json` 顺序探测,**只认一份**;出现第二份会报诊断,绝不静默挑一份。
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
本包自带两份示例档案库:[`examples/`](./examples)(v2,由本仓历史档案迁移而来)与 [`examples-orchestration/`](./examples-orchestration)(v2,手写,用来演示门与施工图)。`rosterDirs` 默认留空,也就是上面那座全局库;
|
|
86
86
|
新装时它是空的,所以面板一开始显示「0 份档案」——在面板里新建一份即可(目录会自动创建)。想先看示例库:
|
|
87
87
|
|
|
88
88
|
```bash
|
|
@@ -217,6 +217,71 @@ rosters:
|
|
|
217
217
|
node bin/cli.mjs validate --dir orgs/agent-roster-collective
|
|
218
218
|
```
|
|
219
219
|
|
|
220
|
+
## 角色 / 组装 / 编译产物(v2)
|
|
221
|
+
|
|
222
|
+
v1 把一切都塞进一份 `roster.yaml`:人设住在 YAML 块标量里,拼装协议藏在插件代码里,队长写两遍,身份靠目录名。v2 把它拆成三层,每层只有一个权威:
|
|
223
|
+
|
|
224
|
+
```text
|
|
225
|
+
roles/<任意目录>/AGENT.md 角色:frontmatter 是参数,正文是人设,parts/ 可选
|
|
226
|
+
rosters/<id>/roster.yaml 组装:成员是引用,队长独立成段
|
|
227
|
+
dist/<id>-<ver>.json 编译产物:自包含、可哈希,消费者只看它
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
判别键决定版本——`dsh-agent-roster/role@1`、`dsh-agent-roster/roster@2`、`dsh-agent-roster/compiled@1`。同一目录不允许两种格式共存:v1 冻结,`migrate` 负责原地重写。
|
|
231
|
+
|
|
232
|
+
拼装写进格式,而不是藏在代码里:
|
|
233
|
+
|
|
234
|
+
```text
|
|
235
|
+
executionPrompt = join("\n\n", [body, ...parts, ...member.append])
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
`lib/frontmatter.js` 的 `assemblePrompt()` 是唯一实现;编译产物为每份人设记录字符数、块清单与 SHA-256,所以「这名成员到底拿到了哪段文字」不必回头读源文件就能回答。
|
|
239
|
+
|
|
240
|
+
门必须能被跑,而不是自己给自己盖章:每个门至少带一条 `verify`(`command` + `expectExit`),没有判据的门在校验阶段就被拒(`gate-unverifiable`)。`requires` 保留,但只是给读结论的人看的说明。
|
|
241
|
+
|
|
242
|
+
`compile` 还会产出施工图 `dist/<id>-<ver>.tasks.json`:
|
|
243
|
+
|
|
244
|
+
```json
|
|
245
|
+
{ "kind": "dsh-agent-roster/plan@1", "executor": "captain",
|
|
246
|
+
"tasks": [ { "id": "build", "kind": "stage", "member": "fixer", "dependencies": [] },
|
|
247
|
+
{ "id": "audit", "kind": "stage", "member": "reviewer", "dependencies": ["build"] },
|
|
248
|
+
{ "id": "tests-pass", "kind": "gate", "member": "reviewer", "dependencies": ["audit"] } ],
|
|
249
|
+
"loop": { "onGateFailure": { "retry": "build", "maxRounds": 1 } } }
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
`executor: "captain"` 就是关键:插件从不调用 `agent_teams_create_task`,因为写团队状态的能力属于队长会话。施工图是给队长照着建的图,不是插件替队长建。
|
|
253
|
+
|
|
254
|
+
[`examples-orchestration/`](./examples-orchestration/) 里的示例库可端到端编译:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
node bin/cli.mjs compile --roster examples-orchestration/rosters/core-review --write
|
|
258
|
+
node bin/cli.mjs compile --roster examples-orchestration/rosters/core-review --json
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
`orchestration` 是格式的一部分,但编译时标 `status: "declaration"`:阶段与门会被校验(门的 `after` 必须指向真实阶段,`by` 必须指向真实成员),但没有任何东西执行它。能声明不等于已被证明会运行,所以产物在 `declarations[]` 里逐条说明谁属于哪一类。完整契约——字段归宿、拼装语义、身份与唯一性、迁移规则——见 [`docs/roster-v2.md`](./docs/roster-v2.md);schema 见 [`schema/role.v1.schema.json`](./schema/role.v1.schema.json)、[`schema/roster.v2.schema.json`](./schema/roster.v2.schema.json)、[`schema/compiled.v1.schema.json`](./schema/compiled.v1.schema.json)。
|
|
262
|
+
|
|
263
|
+
### 从 v1 到 v2,一条命令
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
node bin/cli.mjs migrate --dir examples # 只出计划:要写哪些文件、能不能编译
|
|
267
|
+
node bin/cli.mjs migrate --dir examples --write # 原地重写:roles/ + rosters/ + dist/,删掉旧目录
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
迁移逐字保留人设:成员的 `systemPrompt` 原文成为 `roles/<档案>-<成员>/AGENT.md` 的正文,成员本身变成引用。队长不再是一个散字段,而是自己的角色文件——与成员同名时得到 `-captain` 后缀,因为那是两份人设,悄悄合并会丢掉一份。
|
|
271
|
+
|
|
272
|
+
角色名带档案 id 前缀,是因为 v2 要求同库角色名唯一:一座库里 21 套组装都叫自己的队长 `lead`,不可能有 21 个同名文件。
|
|
273
|
+
|
|
274
|
+
### 把失败的一轮喂回施工图
|
|
275
|
+
|
|
276
|
+
`compile --run <文件>` 读执行回执(`run@1`),把上一轮的失败并进重试卡的 description:
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
node bin/cli.mjs compile --roster examples-orchestration/rosters/core-review \
|
|
280
|
+
--run examples-orchestration/dist/core-review-1.0.0.run.json
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
回执由执行施工图的队长会话写,不由插件写:任务 id 属于团队,插件从不写团队状态。插件负责的是合并——只活在红卡上的失败,下一次编译就消失了,门也就变成了装饰。
|
|
284
|
+
|
|
220
285
|
## 工具
|
|
221
286
|
|
|
222
287
|
| 工具 | 作用 |
|
|
@@ -362,11 +427,13 @@ lib/ format / parse / validate / registry / bridge / verify / rende
|
|
|
362
427
|
config-view / json / http / write / service / teams-state / tools
|
|
363
428
|
client.js 客户端半边:左侧栏图标 + 主面板(档案/团队/体检)+ 设置页小节
|
|
364
429
|
locale/ 面板与列表词典(en/zh)
|
|
365
|
-
schema/ roster / capabilities / organization / registry
|
|
366
|
-
|
|
430
|
+
schema/ roster v1 / role v1 / roster v2 / compiled v1 / capabilities / organization / registry
|
|
431
|
+
schema(编辑器补全用,与校验器由测试对齐)
|
|
432
|
+
bin/cli.mjs validate / list / compile / migrate / registry,给 CI 用
|
|
367
433
|
scripts/ host-smoke.mjs、client-smoke.mjs、check-locale.mjs(开发用,不随包发布)
|
|
368
|
-
examples/
|
|
369
|
-
|
|
434
|
+
examples/ 由历史 v1 档案迁移来的 v2 库(roles/ + rosters/ + dist/)
|
|
435
|
+
examples-orchestration/ 手写的 v2 库:编排、门与施工图
|
|
436
|
+
orgs/ 带 organization.yaml 的示例库(21 套 v2 档案)
|
|
370
437
|
test/ 单测
|
|
371
438
|
```
|
|
372
439
|
|
|
@@ -374,7 +441,7 @@ test/ 单测
|
|
|
374
441
|
|
|
375
442
|
本机已验证:
|
|
376
443
|
|
|
377
|
-
- `npm test` ——
|
|
444
|
+
- `npm test` —— 286/286 通过(格式、解析、校验、扫描、路径、桥接、核对四桶、摘要与溯源、档案差异、能力要求与能力闸门、档案库组织、索引 schema、无损 JSON、写入计划与执行、六条路由、快照与提示、两本词典一致、工具契约、客户端加载器契约、包清单、配置视图、样式 token 约束、声明层字段与队长段、schema 与常量对齐、v2 角色/组装/编译三层及其 schema 对齐、v1→v2 迁移、执行回执并入施工图)。
|
|
378
445
|
- `npm run smoke` —— 用真实 DSH bundle 的 `defineTool` 注册六个工具,逐个调用:输出全部通过无损校验、`path` 覆盖生效、释放干净;同一条命令随后用桩模块表加载 `client.js`,断言三处槽位注册、样式只注入一次、effect 可回收。
|
|
379
446
|
- CLI:`validate --dir examples` 对两座示例库退出码 0。
|
|
380
447
|
- 用插件管理器真的装进了本机 profile(`plugin_manager install_bundle` 指向本仓库 → `link:` 依赖 + bundle 条目),重启后按链接读取本仓库文件。
|
package/bin/cli.mjs
CHANGED
|
@@ -11,8 +11,10 @@
|
|
|
11
11
|
* dsh-agent-roster registry validate --file index/registry.json [--json]
|
|
12
12
|
* dsh-agent-roster registry fetch --file index/registry.json [--local-only]
|
|
13
13
|
* dsh-agent-roster registry sync --file index/registry.json [--write]
|
|
14
|
+
* dsh-agent-roster compile --roster rosters/money-lab [--roles roles] [--run run.json] [--write] [--json]
|
|
15
|
+
* dsh-agent-roster migrate --dir examples [--write] [--json]
|
|
14
16
|
*/
|
|
15
|
-
import { readFileSync, writeFileSync } from "node:fs";
|
|
17
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
16
18
|
import nodePath from "node:path";
|
|
17
19
|
|
|
18
20
|
import { resolveDirSpec } from "../lib/home.js";
|
|
@@ -20,6 +22,10 @@ import { scanRosterLibrary } from "../lib/registry.js";
|
|
|
20
22
|
import { renderRosterList } from "../lib/render.js";
|
|
21
23
|
import { REGISTRY_KIND } from "../lib/format.js";
|
|
22
24
|
import { fetchCollection, readRegistryFile } from "../lib/remote.js";
|
|
25
|
+
import { compileRosterAt } from "../lib/compile2.js";
|
|
26
|
+
import { migrateLibrary } from "../lib/migrate.js";
|
|
27
|
+
import { compilePlan } from "../lib/plan.js";
|
|
28
|
+
import { mergeRunIntoPlan, validateRun } from "../lib/run.js";
|
|
23
29
|
|
|
24
30
|
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
25
31
|
|
|
@@ -29,6 +35,8 @@ const USAGE = `用法:
|
|
|
29
35
|
dsh-agent-roster registry validate --file <路径> 校验公共索引(registry)
|
|
30
36
|
dsh-agent-roster registry fetch --file <路径> 逐条确认 collection 真的能取到
|
|
31
37
|
dsh-agent-roster registry sync --file <路径> 按仓库实际内容重算 roster 列表
|
|
38
|
+
dsh-agent-roster compile --roster <目录> 把 v2 组装编译成自包含产物(不写盘)
|
|
39
|
+
dsh-agent-roster migrate --dir <目录> 把 v1 档案原地迁成 v2(roles/ + rosters/ + dist/)
|
|
32
40
|
dsh-agent-roster --version 输出版本号
|
|
33
41
|
|
|
34
42
|
选项:
|
|
@@ -37,7 +45,10 @@ const USAGE = `用法:
|
|
|
37
45
|
--file <path> registry 文件(registry 子命令用)
|
|
38
46
|
--local-only fetch 只验本仓库登记的 collection(CI 里用,不依赖外网)
|
|
39
47
|
--write sync 把重算结果写回文件
|
|
40
|
-
--
|
|
48
|
+
--roster <path> v2 组装目录(compile 用)
|
|
49
|
+
--run <path> 执行回执(compile 用;把上一轮失败并入重试卡)
|
|
50
|
+
--roles <path> 角色库目录(compile 用;缺省按 rosters/<id> 推同级的 roles/)
|
|
51
|
+
--json 以 JSON 输出(validate / list / compile 都支持)
|
|
41
52
|
--help 显示本帮助
|
|
42
53
|
`;
|
|
43
54
|
|
|
@@ -53,6 +64,9 @@ function parseArgs(argv) {
|
|
|
53
64
|
file: null,
|
|
54
65
|
localOnly: false,
|
|
55
66
|
write: false,
|
|
67
|
+
roster: null,
|
|
68
|
+
roles: null,
|
|
69
|
+
run: null,
|
|
56
70
|
};
|
|
57
71
|
for (let index = 0; index < argv.length; index += 1) {
|
|
58
72
|
const arg = argv[index];
|
|
@@ -71,6 +85,21 @@ function parseArgs(argv) {
|
|
|
71
85
|
if (value === undefined) throw new UsageError("--file 需要一个路径");
|
|
72
86
|
options.file = value;
|
|
73
87
|
index += 1;
|
|
88
|
+
} else if (arg === "--roster") {
|
|
89
|
+
const value = argv[index + 1];
|
|
90
|
+
if (value === undefined) throw new UsageError("--roster 需要一个路径");
|
|
91
|
+
options.roster = value;
|
|
92
|
+
index += 1;
|
|
93
|
+
} else if (arg === "--run") {
|
|
94
|
+
const value = argv[index + 1];
|
|
95
|
+
if (value === undefined) throw new UsageError("--run 需要一个路径");
|
|
96
|
+
options.run = value;
|
|
97
|
+
index += 1;
|
|
98
|
+
} else if (arg === "--roles") {
|
|
99
|
+
const value = argv[index + 1];
|
|
100
|
+
if (value === undefined) throw new UsageError("--roles 需要一个路径");
|
|
101
|
+
options.roles = value;
|
|
102
|
+
index += 1;
|
|
74
103
|
} else if (arg === "--known-policy") {
|
|
75
104
|
const value = argv[index + 1];
|
|
76
105
|
if (value === undefined) throw new UsageError("--known-policy 需要一个 id");
|
|
@@ -112,6 +141,8 @@ async function main(argv) {
|
|
|
112
141
|
process.stdout.write(`${pkg.name} ${pkg.version}\n`);
|
|
113
142
|
return 0;
|
|
114
143
|
}
|
|
144
|
+
if (options.command === "compile") return runCompile(options);
|
|
145
|
+
if (options.command === "migrate") return runMigrate(options);
|
|
115
146
|
if (options.command === "registry") {
|
|
116
147
|
if (options.file === null) {
|
|
117
148
|
process.stderr.write(`registry 子命令需要 --file\n\n${USAGE}`);
|
|
@@ -201,6 +232,136 @@ async function main(argv) {
|
|
|
201
232
|
* - `fetch` 确认索引里写的每份档案真的存在(本仓库的走本地文件,其余走网络);
|
|
202
233
|
* - `sync` 用仓库实际内容重算 `rosters` 与 `generatedAt`,把人肉维护改成机器盖章。
|
|
203
234
|
*/
|
|
235
|
+
/**
|
|
236
|
+
* 编译一份 v2 组装。
|
|
237
|
+
*
|
|
238
|
+
* 三条约定:
|
|
239
|
+
* 1. 默认**不写盘**——编译是纯函数,`--write` 才落 `dist/`;
|
|
240
|
+
* 2. 有 error 就是退出码 1,警告不影响;
|
|
241
|
+
* 3. 产物是消费者唯一入口:桥接层与面板不需要再解析源文件。
|
|
242
|
+
*/
|
|
243
|
+
async function runCompile(options) {
|
|
244
|
+
if (options.roster === null) {
|
|
245
|
+
process.stderr.write(`compile 需要 --roster\n\n${USAGE}`);
|
|
246
|
+
return 2;
|
|
247
|
+
}
|
|
248
|
+
const rosterDir = nodePath.resolve(process.cwd(), options.roster);
|
|
249
|
+
const parent = nodePath.dirname(rosterDir);
|
|
250
|
+
const root = options.roles === null && nodePath.basename(parent) === "rosters" ? nodePath.dirname(parent) : null;
|
|
251
|
+
const rolesDir =
|
|
252
|
+
options.roles === null
|
|
253
|
+
? root === null
|
|
254
|
+
? nodePath.join(parent, "roles")
|
|
255
|
+
: nodePath.join(root, "roles")
|
|
256
|
+
: nodePath.resolve(process.cwd(), options.roles);
|
|
257
|
+
|
|
258
|
+
const result = compileRosterAt(rosterDir, {
|
|
259
|
+
rolesDir,
|
|
260
|
+
knownToolPolicies: options.knownPolicies.length > 0 ? options.knownPolicies : null,
|
|
261
|
+
source: { repo: ownRepo(), ref: "main" },
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
const diagnostics = [...(result.diagnostics ?? [])];
|
|
265
|
+
const planResult = result.compiled === null ? { plan: null, diagnostics: [] } : compilePlan(result.compiled);
|
|
266
|
+
let plan = planResult.plan;
|
|
267
|
+
diagnostics.push(...planResult.diagnostics);
|
|
268
|
+
let feedback = [];
|
|
269
|
+
if (options.run !== null) {
|
|
270
|
+
const runPath = nodePath.resolve(process.cwd(), options.run);
|
|
271
|
+
let runValue = null;
|
|
272
|
+
try {
|
|
273
|
+
runValue = JSON.parse(readFileSync(runPath, "utf8"));
|
|
274
|
+
} catch (error) {
|
|
275
|
+
diagnostics.push({ severity: "error", code: "run-unreadable", path: "", file: options.run, reason: `读不到执行回执:${error?.message ?? error}` });
|
|
276
|
+
}
|
|
277
|
+
if (runValue !== null) {
|
|
278
|
+
const validation = validateRun(runValue, { file: options.run });
|
|
279
|
+
diagnostics.push(...validation.diagnostics);
|
|
280
|
+
if (validation.ok) {
|
|
281
|
+
const merged = mergeRunIntoPlan(plan, validation.run);
|
|
282
|
+
plan = merged.plan;
|
|
283
|
+
feedback = merged.feedback;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
const errors = diagnostics.filter((item) => item.severity === "error");
|
|
288
|
+
|
|
289
|
+
if (options.json) {
|
|
290
|
+
process.stdout.write(
|
|
291
|
+
`${JSON.stringify(
|
|
292
|
+
{
|
|
293
|
+
plugin: pkg.name,
|
|
294
|
+
version: pkg.version,
|
|
295
|
+
rolesDir,
|
|
296
|
+
rosterDir,
|
|
297
|
+
ok: result.ok,
|
|
298
|
+
compiled: result.compiled,
|
|
299
|
+
plan: plan === null ? null : planResult.plan,
|
|
300
|
+
diagnostics,
|
|
301
|
+
},
|
|
302
|
+
null,
|
|
303
|
+
2,
|
|
304
|
+
)}\n`,
|
|
305
|
+
);
|
|
306
|
+
return errors.length > 0 ? 1 : 0;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (!result.ok || result.compiled === null) {
|
|
310
|
+
process.stderr.write(`编译失败:${nodePath.basename(rosterDir)}\n`);
|
|
311
|
+
for (const item of diagnostics) process.stderr.write(`${formatDiagnostic(item)}\n`);
|
|
312
|
+
return 1;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const compiled = result.compiled;
|
|
316
|
+
const chars = compiled.members.reduce((sum, item) => sum + item.promptStats.chars, 0);
|
|
317
|
+
process.stdout.write(
|
|
318
|
+
`产物 ${compiled.source.roster}@${compiled.source.version} 成员 ${compiled.members.length} ` +
|
|
319
|
+
`人设合计 ${chars} 字符 声明 ${compiled.declarations.length} 项 targets ${compiled.targets.join(",") || "-"}\n`,
|
|
320
|
+
);
|
|
321
|
+
if (compiled.captain !== null) {
|
|
322
|
+
process.stdout.write(` 队长 ${compiled.captain.role ?? "-"} persona ${compiled.captain.personaChars} 字符\n`);
|
|
323
|
+
}
|
|
324
|
+
if (compiled.orchestration !== null) {
|
|
325
|
+
process.stdout.write(
|
|
326
|
+
` 编排 ${compiled.orchestration.shape ?? "-"} stages ${compiled.orchestration.stages.length} ` +
|
|
327
|
+
`gates ${compiled.orchestration.gates.length} status ${compiled.orchestration.status}\n`,
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
if (plan !== null) {
|
|
331
|
+
const gates = plan.tasks.filter((task) => task.kind === "gate").length;
|
|
332
|
+
const loop = plan.loop.onGateFailure;
|
|
333
|
+
process.stdout.write(
|
|
334
|
+
` 施工图 ${plan.tasks.length} 张卡 门 ${gates} executor ${plan.executor} ` +
|
|
335
|
+
`重试回到 ${loop.retry ?? "-"}(最多 ${loop.maxRounds} 轮)\n`,
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
if (feedback.length > 0) {
|
|
339
|
+
process.stdout.write(` 回执 上一轮失败 ${feedback.length} 张卡,已并入重试卡\n`);
|
|
340
|
+
}
|
|
341
|
+
for (const member of compiled.members) {
|
|
342
|
+
process.stdout.write(
|
|
343
|
+
` ${member.name.padEnd(24)} ${member.role.padEnd(24)} ${String(member.promptStats.chars).padStart(5)} 字符 ` +
|
|
344
|
+
`${member.promptStats.sha256.slice(0, 12)}\n`,
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
for (const item of diagnostics) process.stdout.write(`${formatDiagnostic(item)}\n`);
|
|
348
|
+
|
|
349
|
+
if (options.write) {
|
|
350
|
+
const outDir = nodePath.join(root ?? parent, "dist");
|
|
351
|
+
mkdirSync(outDir, { recursive: true });
|
|
352
|
+
const outFile = nodePath.join(outDir, `${compiled.source.roster}-${compiled.source.version}.json`);
|
|
353
|
+
writeFileSync(outFile, `${JSON.stringify(compiled, null, 2)}\n`);
|
|
354
|
+
process.stdout.write(` 已写出 ${nodePath.relative(process.cwd(), outFile) || outFile}\n`);
|
|
355
|
+
if (plan !== null) {
|
|
356
|
+
const planFile = nodePath.join(outDir, `${compiled.source.roster}-${compiled.source.version}.tasks.json`);
|
|
357
|
+
writeFileSync(planFile, `${JSON.stringify(plan, null, 2)}\n`);
|
|
358
|
+
process.stdout.write(` 已写出 ${nodePath.relative(process.cwd(), planFile) || planFile}\n`);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return 0;
|
|
363
|
+
}
|
|
364
|
+
|
|
204
365
|
async function runRegistry(options) {
|
|
205
366
|
const file = nodePath.resolve(process.cwd(), options.file);
|
|
206
367
|
const output = (text) => process.stdout.write(`${text}\n`);
|
|
@@ -333,3 +494,68 @@ function isOwnCollection(collection, own) {
|
|
|
333
494
|
}
|
|
334
495
|
|
|
335
496
|
process.exitCode = await main(process.argv.slice(2));
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* `migrate`:把一座 v1 档案库原地迁到 v2 布局。
|
|
500
|
+
*
|
|
501
|
+
* 默认只出计划(要写哪些文件、编译能不能过),`--write` 才真的落盘;这是唯一一个会删旧目录
|
|
502
|
+
* 的命令,所以默认不动手。
|
|
503
|
+
*/
|
|
504
|
+
function runMigrate(options) {
|
|
505
|
+
const dirs = options.dirs.length > 0 ? options.dirs : [""];
|
|
506
|
+
const knownToolPolicies = options.knownPolicies.length > 0 ? options.knownPolicies : null;
|
|
507
|
+
let rc = 0;
|
|
508
|
+
const reports = [];
|
|
509
|
+
for (const dir of dirs) {
|
|
510
|
+
const rootDir = resolveDirSpec(dir, process.cwd());
|
|
511
|
+
const report = migrateLibrary(rootDir, { write: options.write, knownToolPolicies, source: { repo: ownRepo(), ref: "main" } });
|
|
512
|
+
reports.push({ dir, rootDir, report });
|
|
513
|
+
if (report.diagnostics.some((item) => item.severity === "error")) rc = 1;
|
|
514
|
+
if (options.json) continue;
|
|
515
|
+
process.stdout.write(`${options.write ? "迁移" : "迁移计划(未写盘)"} ${rootDir}\n`);
|
|
516
|
+
if (report.entries.length === 0) process.stdout.write(" 没有找到 v1 档案(每个子目录里的 roster.yaml)\n");
|
|
517
|
+
for (const entry of report.entries) {
|
|
518
|
+
const extra = [entry.hasCaptain ? "含队长" : null, entry.capabilities ? "能力要求" : null].filter(Boolean).join(" · ");
|
|
519
|
+
process.stdout.write(` ${entry.id}@${entry.version} 成员 ${entry.memberCount} 文件 ${entry.files.length}${extra ? ` ${extra}` : ""}\n`);
|
|
520
|
+
if (!options.write) {
|
|
521
|
+
for (const file of entry.files) process.stdout.write(` + ${file.path}\n`);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (options.write) {
|
|
525
|
+
process.stdout.write(` 写入 ${report.written.length} 个文件,删除 ${report.removed.length} 个旧目录,编译 ${report.compiled.length} 份\n`);
|
|
526
|
+
}
|
|
527
|
+
for (const item of report.diagnostics) process.stdout.write(` ${formatDiagnostic(item)}\n`);
|
|
528
|
+
}
|
|
529
|
+
if (options.json) {
|
|
530
|
+
process.stdout.write(
|
|
531
|
+
`${JSON.stringify(
|
|
532
|
+
{
|
|
533
|
+
plugin: pkg.name,
|
|
534
|
+
version: pkg.version,
|
|
535
|
+
write: options.write,
|
|
536
|
+
reports: reports.map((item) => ({
|
|
537
|
+
dir: item.dir,
|
|
538
|
+
rootDir: item.rootDir,
|
|
539
|
+
write: item.report.write,
|
|
540
|
+
entries: item.report.entries.map((entry) => ({
|
|
541
|
+
id: entry.id,
|
|
542
|
+
version: entry.version,
|
|
543
|
+
from: entry.from,
|
|
544
|
+
files: entry.files.map((file) => file.path),
|
|
545
|
+
memberCount: entry.memberCount,
|
|
546
|
+
hasCaptain: entry.hasCaptain,
|
|
547
|
+
capabilities: entry.capabilities,
|
|
548
|
+
})),
|
|
549
|
+
written: item.report.written,
|
|
550
|
+
removed: item.report.removed,
|
|
551
|
+
compiled: item.report.compiled,
|
|
552
|
+
diagnostics: item.report.diagnostics,
|
|
553
|
+
})),
|
|
554
|
+
},
|
|
555
|
+
null,
|
|
556
|
+
2,
|
|
557
|
+
)}\n`,
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
return rc;
|
|
561
|
+
}
|